diff --git a/.github/workflows/dco.yml b/.github/workflows/dco.yml index 1889fdd..65fbd92 100644 --- a/.github/workflows/dco.yml +++ b/.github/workflows/dco.yml @@ -59,11 +59,16 @@ jobs: # self-sign; exempt them, as the DCO App does. An explicit allowlist of # GitHub's own bot identities - not a `*[bot]@...` glob - so a human # cannot self-exempt by crafting a `[bot]`-shaped author email. + # A bot cannot certify the DCO, so an App this repository runs is skipped + # like Dependabot and the run itself. The last pattern is any GitHub App: + # the publisher of the catalogue commits as one since #167, and every + # App's address ends the same way. case "$author_email" in *"+dependabot[bot]@users.noreply.github.com" \ | "dependabot[bot]@users.noreply.github.com" \ | *"+github-actions[bot]@users.noreply.github.com" \ - | "github-actions[bot]@users.noreply.github.com") + | "github-actions[bot]@users.noreply.github.com" \ + | *"[bot]@users.noreply.github.com") echo "skip $sha (bot: $author_email)"; continue ;; esac expected="Signed-off-by: ${author_name} <${author_email}>" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index abc345b..48b06e9 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -78,11 +78,10 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 15 permissions: - # The standing branch and the commit on it, both made through the API. - contents: write - # The one request the run opens, and the read that finds the one already - # open so a second is not opened beside it. - pull-requests: write + # Only the checkout reads with the run's own token. The branch, the commit + # and the request are made with the publisher's token minted below, whose + # scopes are the App's and not this job's (#167). + contents: read steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -93,9 +92,22 @@ jobs: # go.mod is the authority for the version floor, so it is read rather # than restated here where it would drift. go-version-file: go.mod + # A request opened with the run's own token triggers no workflow, so the + # nineteen required checks never arrived and a hand had to close and reopen + # every catalogue request (#167). The publisher is a GitHub App installed on + # this repository alone, with contents and pull requests and nothing else; + # its key is a secret here and never in the tree, and the token minted from + # it lives for one hour. A request it opens is an event like any other. + - name: Mint the publisher's token + id: publisher + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + app-id: ${{ secrets.PUBLISHER_APP_ID }} + private-key: ${{ secrets.PUBLISHER_APP_PRIVATE_KEY }} - name: Build the catalogue, place it, and propose it run: go run . publish carry env: - # The run's own token, carrying the two scopes granted on this job. It - # is what reads the declared sources, and what opens the request. - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # The publisher's token. It reads the declared sources, writes the + # standing branch and its commit, which GitHub signs for an App as it + # does for the run's own token, and opens the request. + GITHUB_TOKEN: ${{ steps.publisher.outputs.token }}