From bc489c46c819232ada112af4147130a2166a08bc Mon Sep 17 00:00:00 2001 From: "Taro L. Saito" Date: Tue, 5 May 2026 13:02:36 -0700 Subject: [PATCH 1/3] Add: auto-merge workflow for Dependabot and Scala Steward PRs Mirrors the wvlet/uni auto-merge pattern, adapted for this repo: - Uses pull_request_target so GITHUB_TOKEN gets the declared write permissions on Dependabot-authored PRs (avoids needing a GitHub App). - Skips Dependabot major-version bumps via dependabot/fetch-metadata. - Skips Scala Steward PRs labeled semver-major. - Squash-merges via --auto, so GitHub waits for required checks and reviews before merging. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/auto-merge.yml | 37 ++++++++++++++++++ plans/2026-05-05-auto-merge.md | 65 ++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 .github/workflows/auto-merge.yml create mode 100644 plans/2026-05-05-auto-merge.md diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml new file mode 100644 index 00000000..f24ba3ad --- /dev/null +++ b/.github/workflows/auto-merge.yml @@ -0,0 +1,37 @@ +name: auto-merge +# pull_request_target runs in the base-branch context, which is required so +# that GITHUB_TOKEN gets the write permissions declared below even for PRs +# opened by Dependabot (which would otherwise receive a read-only token on +# regular pull_request events). This workflow never checks out PR head code, +# so the usual pull_request_target injection risk does not apply. +on: pull_request_target + +permissions: + contents: write + pull-requests: write + +jobs: + auto-merge-dependabot: + name: Auto-Merge Dependabot PRs + runs-on: ubuntu-latest + if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }} + steps: + - name: Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@v2 + - name: Enable auto-merge for non-major updates + if: ${{ steps.metadata.outputs.update-type != 'version-update:semver-major' }} + run: gh pr merge --squash --auto "${{ github.event.pull_request.html_url }}" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + auto-merge-scala-steward: + name: Auto-Merge Scala Steward PRs + runs-on: ubuntu-latest + if: ${{ github.event.pull_request.user.login == 'scala-steward' }} + steps: + - name: Enable auto-merge for non-major updates + if: ${{ !contains(github.event.pull_request.labels.*.name, 'semver-major') }} + run: gh pr merge --squash --auto "${{ github.event.pull_request.html_url }}" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/plans/2026-05-05-auto-merge.md b/plans/2026-05-05-auto-merge.md new file mode 100644 index 00000000..33e2992f --- /dev/null +++ b/plans/2026-05-05-auto-merge.md @@ -0,0 +1,65 @@ +# Add auto-merge GitHub Actions workflow + +## Goal + +Add a workflow that automatically enables auto-merge on PRs from trusted bots +(Dependabot and Scala Steward) for non-major version bumps, mirroring the +pattern used in `wvlet/uni/.github/workflows/auto-merge.yml`. + +## Context + +- Recent PR history shows the repo regularly receives many bot PRs: + - `dependabot[bot]` — for GitHub Actions version bumps + - `scala-steward` — for Scala/sbt/Java library updates +- These currently require manual merge from the maintainer. +- Repo settings already allow auto-merge (`allow_auto_merge: true`) and squash + is the default merge method. +- CI passes branch-protection checks on PRs; once a PR is approved and CI is + green, GitHub will merge it automatically. + +## Differences from wvlet/uni + +- **No GitHub App token.** wvlet/uni uses a GitHub App (`APP_ID` + + `APP_PRIVATE_KEY`) to bypass the read-only `GITHUB_TOKEN` that GitHub + hands to `pull_request` workflows triggered by Dependabot. msgpack/msgpack-java + doesn't have that App configured, so this workflow uses + `on: pull_request_target` instead — that event runs in the base-branch + context where the workflow's declared `permissions:` block actually grants + write access to `GITHUB_TOKEN`. We never check out PR head code, so the + usual `pull_request_target` injection risk does not apply. +- **Scala Steward actor is `scala-steward`**, not wvlet/uni's + `scala-steward-wvlet[bot]` or `xerial-bot` (visible in `gh pr list`). +- **Filter on `github.event.pull_request.user.login`**, not `github.actor`, + because under `pull_request_target` the latter can resolve to the merger + rather than the PR author. + +## Plan + +1. Add `.github/workflows/auto-merge.yml` with two jobs: + - **auto-merge-dependabot**: triggers when `github.actor == 'dependabot[bot]'`, + uses `dependabot/fetch-metadata@v3` to read the update type, and runs + `gh pr merge --squash --auto` only when the update is **not** + `version-update:semver-major`. + - **auto-merge-scala-steward**: triggers when `github.actor == 'scala-steward'` + and runs `gh pr merge --squash --auto` for all such PRs (Scala Steward + does not surface a structured "major" signal the way Dependabot's + fetch-metadata action does, so we rely on Scala Steward's own + `pullRequests.allowedUpdates` config — already filtered upstream — and + skip if the PR has a `semver-major` label). +2. Set workflow-level `permissions` to the minimum required: + `contents: write` and `pull-requests: write`. +3. Use `GITHUB_TOKEN` directly via `env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}`. + +## Out of scope + +- Setting up a GitHub App for elevated bot identity. +- Auto-approving PRs (a human approval may still be required by branch + protection — auto-merge will simply wait for it). +- Changing branch protection rules. + +## Validation + +- Lint the YAML by checking the file parses (visual review + actionlint if + available). +- After merge, watch the next dependabot/scala-steward PR to confirm + auto-merge gets enabled. From c91ff24fb4b6206bf767d124aad0ca75300f4fcf Mon Sep 17 00:00:00 2001 From: "Taro L. Saito" Date: Tue, 5 May 2026 13:06:19 -0700 Subject: [PATCH 2/3] Drop Scala Steward auto-merge job The semver-major label guard was a no-op: this repo's Scala Steward PRs only carry library-update (and sometimes internal), so the guard would have auto-merged every Scala Steward PR including major bumps. Defer Scala Steward auto-merge until the repo has proper semver labeling configured. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/auto-merge.yml | 11 ----------- plans/2026-05-05-auto-merge.md | 31 ++++++++++++++++++++++--------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml index f24ba3ad..36c1ef1d 100644 --- a/.github/workflows/auto-merge.yml +++ b/.github/workflows/auto-merge.yml @@ -24,14 +24,3 @@ jobs: run: gh pr merge --squash --auto "${{ github.event.pull_request.html_url }}" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - auto-merge-scala-steward: - name: Auto-Merge Scala Steward PRs - runs-on: ubuntu-latest - if: ${{ github.event.pull_request.user.login == 'scala-steward' }} - steps: - - name: Enable auto-merge for non-major updates - if: ${{ !contains(github.event.pull_request.labels.*.name, 'semver-major') }} - run: gh pr merge --squash --auto "${{ github.event.pull_request.html_url }}" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/plans/2026-05-05-auto-merge.md b/plans/2026-05-05-auto-merge.md index 33e2992f..a099c9a6 100644 --- a/plans/2026-05-05-auto-merge.md +++ b/plans/2026-05-05-auto-merge.md @@ -35,27 +35,40 @@ pattern used in `wvlet/uni/.github/workflows/auto-merge.yml`. ## Plan -1. Add `.github/workflows/auto-merge.yml` with two jobs: - - **auto-merge-dependabot**: triggers when `github.actor == 'dependabot[bot]'`, - uses `dependabot/fetch-metadata@v3` to read the update type, and runs +1. Add `.github/workflows/auto-merge.yml` with one job: + - **auto-merge-dependabot**: triggers when + `github.event.pull_request.user.login == 'dependabot[bot]'`, uses + `dependabot/fetch-metadata@v2` to read the update type, and runs `gh pr merge --squash --auto` only when the update is **not** `version-update:semver-major`. - - **auto-merge-scala-steward**: triggers when `github.actor == 'scala-steward'` - and runs `gh pr merge --squash --auto` for all such PRs (Scala Steward - does not surface a structured "major" signal the way Dependabot's - fetch-metadata action does, so we rely on Scala Steward's own - `pullRequests.allowedUpdates` config — already filtered upstream — and - skip if the PR has a `semver-major` label). 2. Set workflow-level `permissions` to the minimum required: `contents: write` and `pull-requests: write`. 3. Use `GITHUB_TOKEN` directly via `env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}`. +### Scala Steward (deferred) + +The initial draft also auto-merged Scala Steward PRs, gated on a +`semver-major` label being absent. But this repo's existing Scala Steward PRs +only carry `library-update` (and sometimes `internal`) — no semver labels are +configured, so a `!contains(..., 'semver-major')` guard is effectively a +no-op and would auto-merge every Scala Steward PR including major bumps. +Codex review caught this. Rather than ship an unsafe guard, we drop the +Scala Steward job from this PR. Re-adding it should be a follow-up that +either: +- Defines `early-semver-major`/`early-semver-minor`/`early-semver-patch` + labels in the repo (Scala Steward only applies labels that already exist) + and adds a `.scala-steward.conf` enabling them, then guards on + `early-semver-major`. +- Or uses a manual opt-in label like `auto-merge` that the maintainer adds + after a quick review. + ## Out of scope - Setting up a GitHub App for elevated bot identity. - Auto-approving PRs (a human approval may still be required by branch protection — auto-merge will simply wait for it). - Changing branch protection rules. +- Scala Steward auto-merge (see above). ## Validation From b77844a6293a63669dac86af00c1ddad3272e66a Mon Sep 17 00:00:00 2001 From: "Taro L. Saito" Date: Tue, 5 May 2026 13:10:45 -0700 Subject: [PATCH 3/3] Add Scala Steward auto-merge job Restore the Scala Steward auto-merge job dropped in the previous commit. Guards on both `semver-major` and `early-semver-major` labels for forward compatibility with future label setup. Until those labels are configured, the guard is effectively a no-op (matching wvlet/uni's behavior); a follow-up can tighten this by adding the labels to the repo and configuring Scala Steward to apply them. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/auto-merge.yml | 15 ++++++++++++ plans/2026-05-05-auto-merge.md | 40 ++++++++++++++++++-------------- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml index 36c1ef1d..c223994b 100644 --- a/.github/workflows/auto-merge.yml +++ b/.github/workflows/auto-merge.yml @@ -24,3 +24,18 @@ jobs: run: gh pr merge --squash --auto "${{ github.event.pull_request.html_url }}" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + auto-merge-scala-steward: + name: Auto-Merge Scala Steward PRs + runs-on: ubuntu-latest + if: ${{ github.event.pull_request.user.login == 'scala-steward' }} + steps: + # Skip auto-merge if the PR carries a semver-major label. Note: this repo + # does not yet apply semver labels to Scala Steward PRs, so this guard is + # a no-op until early-semver-* labels are added to the repo and Scala + # Steward is configured to apply them. + - name: Enable auto-merge for non-major updates + if: ${{ !contains(github.event.pull_request.labels.*.name, 'semver-major') && !contains(github.event.pull_request.labels.*.name, 'early-semver-major') }} + run: gh pr merge --squash --auto "${{ github.event.pull_request.html_url }}" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/plans/2026-05-05-auto-merge.md b/plans/2026-05-05-auto-merge.md index a099c9a6..0b43e132 100644 --- a/plans/2026-05-05-auto-merge.md +++ b/plans/2026-05-05-auto-merge.md @@ -35,32 +35,38 @@ pattern used in `wvlet/uni/.github/workflows/auto-merge.yml`. ## Plan -1. Add `.github/workflows/auto-merge.yml` with one job: +1. Add `.github/workflows/auto-merge.yml` with two jobs: - **auto-merge-dependabot**: triggers when `github.event.pull_request.user.login == 'dependabot[bot]'`, uses `dependabot/fetch-metadata@v2` to read the update type, and runs `gh pr merge --squash --auto` only when the update is **not** `version-update:semver-major`. + - **auto-merge-scala-steward**: triggers when + `github.event.pull_request.user.login == 'scala-steward'` and + auto-merges unless the PR carries a `semver-major` or + `early-semver-major` label. 2. Set workflow-level `permissions` to the minimum required: `contents: write` and `pull-requests: write`. 3. Use `GITHUB_TOKEN` directly via `env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}`. -### Scala Steward (deferred) +### Scala Steward label caveat -The initial draft also auto-merged Scala Steward PRs, gated on a -`semver-major` label being absent. But this repo's existing Scala Steward PRs -only carry `library-update` (and sometimes `internal`) — no semver labels are -configured, so a `!contains(..., 'semver-major')` guard is effectively a -no-op and would auto-merge every Scala Steward PR including major bumps. -Codex review caught this. Rather than ship an unsafe guard, we drop the -Scala Steward job from this PR. Re-adding it should be a follow-up that -either: -- Defines `early-semver-major`/`early-semver-minor`/`early-semver-patch` - labels in the repo (Scala Steward only applies labels that already exist) - and adds a `.scala-steward.conf` enabling them, then guards on - `early-semver-major`. -- Or uses a manual opt-in label like `auto-merge` that the maintainer adds - after a quick review. +This repo's current Scala Steward PRs only carry `library-update` (and +sometimes `internal`) — no semver labels are configured upstream, so the +`semver-major` / `early-semver-major` guard is effectively a no-op until: +- The labels are added to the repo (Scala Steward only applies labels that + already exist), and +- Scala Steward is configured (e.g. via `.scala-steward.conf`) to attach + them. + +This matches the same caveat in the wvlet/uni reference implementation, +which uses `semver-spec-major` against `github.event.issue.labels` (not even +the right field on a PR event) — so its guard is also effectively a no-op +in practice. We accept the same trade-off here: most Scala Steward PRs are +patch/minor library updates, CI runs across JDK 8/11/17/21/24 and will fail +the merge-readiness checks if anything regresses, and a major bump that +slips through can be reverted. A follow-up could tighten this by setting up +proper semver labels. ## Out of scope @@ -68,7 +74,7 @@ either: - Auto-approving PRs (a human approval may still be required by branch protection — auto-merge will simply wait for it). - Changing branch protection rules. -- Scala Steward auto-merge (see above). +- Configuring Scala Steward to apply semver labels (see caveat above). ## Validation