diff --git a/.github/workflows/dependabot-automerge.md b/.github/workflows/dependabot-automerge.md index af3f1ac..dad65cf 100644 --- a/.github/workflows/dependabot-automerge.md +++ b/.github/workflows/dependabot-automerge.md @@ -11,13 +11,13 @@ For repositories **with** required status checks: ```yaml name: dependabot-automerge on: pull_request -permissions: - contents: write - pull-requests: write - issues: write +permissions: {} jobs: automerge: uses: bufbuild/base-workflows/.github/workflows/dependabot-automerge.yaml@main + secrets: + DEPENDENCY_AUTOMERGE_APP_ID: ${{ secrets.DEPENDENCY_AUTOMERGE_APP_ID }} + DEPENDENCY_AUTOMERGE_APP_PRIVATE_KEY: ${{ secrets.DEPENDENCY_AUTOMERGE_APP_PRIVATE_KEY }} with: github-auto-merge: true ``` @@ -35,13 +35,13 @@ on: schedule: - cron: "0 13 * * *" # 13:00 UTC (8am EST / 9am EDT) workflow_dispatch: -permissions: - contents: write - pull-requests: write - issues: write +permissions: {} jobs: automerge: uses: bufbuild/base-workflows/.github/workflows/dependabot-automerge.yaml@main + secrets: + DEPENDENCY_AUTOMERGE_APP_ID: ${{ secrets.DEPENDENCY_AUTOMERGE_APP_ID }} + DEPENDENCY_AUTOMERGE_APP_PRIVATE_KEY: ${{ secrets.DEPENDENCY_AUTOMERGE_APP_PRIVATE_KEY }} with: github-auto-merge: false ``` @@ -123,3 +123,9 @@ workflow's approval, that's why. > merges eligible PRs immediately, without waiting for any checks. Only > set it to `true` once the branch-protection setup ("required status > checks" + "Allow auto-merge") is done. + +## Secrets + +The caller must pass the `DEPENDENCY_AUTOMERGE_APP_ID` and `DEPENDENCY_AUTOMERGE_APP_PRIVATE_KEY` org secrets through explicitly, as in the examples above. Every GitHub API operation (metadata lookup, labeling, approvals, merges) is performed with a GitHub App token minted from them; `GITHUB_TOKEN` is never used, so callers need no `permissions` grants. The app token is necessary because events created with the default `GITHUB_TOKEN` never trigger other workflows, so merging with it would silently skip CI on the default branch after the merge. Reusable workflows don't see org secrets unless the caller passes them through. Both secrets are declared `required`, so a caller that omits them fails immediately at workflow validation rather than at the token minting step. + +The two jobs resolve secrets from different stores, so the app ID and key must be registered as both org **Actions secrets** and org **Dependabot secrets**, under the same names. The mark job runs on Dependabot-triggered `pull_request` events, which only see Dependabot secrets ([GitHub Actions secrets are not available](https://docs.github.com/en/code-security/reference/supply-chain-security/troubleshoot-dependabot/dependabot-on-actions) in those runs). The sweep job runs on `workflow_run`, `schedule`, and `workflow_dispatch` events, which see regular Actions secrets. Missing either store breaks one of the two merge paths. diff --git a/.github/workflows/dependabot-automerge.yaml b/.github/workflows/dependabot-automerge.yaml index 7dd2537..3568cae 100644 --- a/.github/workflows/dependabot-automerge.yaml +++ b/.github/workflows/dependabot-automerge.yaml @@ -16,10 +16,12 @@ on: github-auto-merge: type: boolean required: true -permissions: - contents: write - pull-requests: write - issues: write + secrets: + DEPENDENCY_AUTOMERGE_APP_ID: + required: true + DEPENDENCY_AUTOMERGE_APP_PRIVATE_KEY: + required: true +permissions: {} # GITHUB_TOKEN is unused; everything runs on the app token. jobs: # Classifies each Dependabot PR with fetch-metadata and records the # verdict as the label. Re-runs on every Dependabot push, so a PR that @@ -32,16 +34,31 @@ jobs: github.event.pull_request.user.login == 'dependabot[bot]' && github.actor == 'dependabot[bot]' steps: + # Enabling auto-merge must use an app token, not GITHUB_TOKEN, + # so that CI runs post-merge. + # For simplicity, everything uses this token, instead of some steps + # using GITHUB_TOKEN + - name: Create App Token + id: app-token + uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2.2.2 + with: + app-id: ${{ secrets.DEPENDENCY_AUTOMERGE_APP_ID }} + private-key: ${{ secrets.DEPENDENCY_AUTOMERGE_APP_PRIVATE_KEY }} + permission-contents: write # required to toggle auto-merge + permission-pull-requests: write # required to label and approve PRs + permission-issues: write # required to create repo label - name: Fetch Dependabot Metadata id: metadata uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0 + with: + github-token: ${{ steps.app-token.outputs.token }} - name: Label Eligible PR id: label if: >- steps.metadata.outputs.update-type == 'version-update:semver-patch' || steps.metadata.outputs.update-type == 'version-update:semver-minor' env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ steps.app-token.outputs.token }} PR_URL: ${{ github.event.pull_request.html_url }} REPO: ${{ github.repository }} run: | @@ -52,7 +69,7 @@ jobs: steps.metadata.outputs.update-type != 'version-update:semver-patch' && steps.metadata.outputs.update-type != 'version-update:semver-minor' env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ steps.app-token.outputs.token }} PR_URL: ${{ github.event.pull_request.html_url }} run: | gh pr edit "${PR_URL}" --remove-label "automerge: eligible" || true @@ -60,7 +77,7 @@ jobs: - name: Enable Auto-Merge if: inputs.github-auto-merge && steps.label.outcome == 'success' env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ steps.app-token.outputs.token }} PR_URL: ${{ github.event.pull_request.html_url }} run: | gh pr review --approve "${PR_URL}" @@ -80,9 +97,21 @@ jobs: group: dependabot-automerge-sweep cancel-in-progress: false steps: + # Merging must use an app token, not GITHUB_TOKEN, + # so that CI runs post-merge. + - name: Create App Token + id: app-token + uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2.2.2 + with: + app-id: ${{ secrets.DEPENDENCY_AUTOMERGE_APP_ID }} + private-key: ${{ secrets.DEPENDENCY_AUTOMERGE_APP_PRIVATE_KEY }} + permission-contents: write # required to merge PRs + permission-pull-requests: write # required to approve PRs + permission-checks: read # required to read check results + permission-statuses: read # required to read commit statuses - name: Merge Labeled Dependabot PRs env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ steps.app-token.outputs.token }} REPO: ${{ github.repository }} run: | gh pr list --repo "${REPO}" --author 'app/dependabot' \