From 7cb4d6a5af7cbb28762054c0f96cc3882701458e Mon Sep 17 00:00:00 2001 From: Claude Code Bot Date: Mon, 19 Oct 2026 16:08:27 -0700 Subject: [PATCH] ci: auto-merge this repo's own Dependabot PRs github-workflows defines dependabot-auto-merge.yml but never called it, so every Dependabot PR here waited for a hand merge (most recently #196). Every fleet caller pins a hand-moved tag, so a merge to main here reaches no other repo, and the other locked repos (claude-config, dotfiles) already auto-merge their Dependabot PRs. The caller pins @dependabot-auto-merge-v2 instead of a local path: pull_request_target runs the base branch's copy, so a local path would let an unreleased change to the reusable workflow decide merges here. guard-no-checkout now checks the new caller too, since it also runs on pull_request_target. Checked against a copy with a checkout step added: the guard fails it and passes the real files. --- .../workflows/self-dependabot-auto-merge.yml | 19 +++++++++ .github/workflows/self-review.yml | 42 ++++++++++--------- README.md | 8 +++- 3 files changed, 48 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/self-dependabot-auto-merge.yml diff --git a/.github/workflows/self-dependabot-auto-merge.yml b/.github/workflows/self-dependabot-auto-merge.yml new file mode 100644 index 0000000..ccd243a --- /dev/null +++ b/.github/workflows/self-dependabot-auto-merge.yml @@ -0,0 +1,19 @@ +name: Self Dependabot Auto-Merge + +# Pins the released tag, not a local path, so unreleased changes never merge here. + +# Fleet callers pin hand-moved tags, so a merge here reaches no other repo. + +on: # zizmor: ignore[dangerous-triggers] required to run from base branch; no PR code executed here + pull_request_target: + types: [opened, synchronize, reopened] + +permissions: + contents: write + pull-requests: write + +jobs: + dependabot-auto-merge: + uses: smartwatermelon/github-workflows/.github/workflows/dependabot-auto-merge.yml@dependabot-auto-merge-v2 + with: + trusted_namespaces: 'smartwatermelon' diff --git a/.github/workflows/self-review.yml b/.github/workflows/self-review.yml index c5f9350..16fb75c 100644 --- a/.github/workflows/self-review.yml +++ b/.github/workflows/self-review.yml @@ -22,24 +22,26 @@ jobs: fetch-depth: 1 persist-credentials: false - - name: Fail if dependabot-auto-merge.yml uses actions/checkout + - name: Fail if a pull_request_target workflow uses actions/checkout run: | - file=".github/workflows/dependabot-auto-merge.yml" - if [ ! -f "$file" ]; then - echo "::notice::${file} not found — nothing to check." - exit 0 - fi - # Strip comment lines first — the header comment in this file - # legitimately names `actions/checkout` while explaining why it - # must never be added, which a bare substring grep would flag - # against itself. Match the real usage form (a `uses:` step), - # not just the string appearing anywhere in the file. - if grep -v '^\s*#' "$file" | grep -qE 'uses:\s*actions/checkout'; then - echo "::error::${file} must NEVER use actions/checkout." - echo "::error::This workflow uses pull_request_target with no-checkout as its" - echo "::error::core safety property. Adding actions/checkout would expose" - echo "::error::base-branch secrets to externally-authored PR content. See the" - echo "::error::header comment in ${file} and issue #64." - exit 1 - fi - echo "No actions/checkout found in ${file} — guardrail passed." + # The reusable workflow and this repo's own caller of it both + # run on pull_request_target. + for file in .github/workflows/dependabot-auto-merge.yml \ + .github/workflows/self-dependabot-auto-merge.yml; do + if [ ! -f "$file" ]; then + echo "::notice::${file} not found — nothing to check." + continue + fi + # Skip comments: the header names actions/checkout on purpose. + + # Match only a real `uses:` step. + if grep -v '^\s*#' "$file" | grep -qE 'uses:\s*actions/checkout'; then + echo "::error::${file} must NEVER use actions/checkout." + echo "::error::This workflow uses pull_request_target with no-checkout as its" + echo "::error::core safety property. Adding actions/checkout would expose" + echo "::error::base-branch secrets to externally-authored PR content. See the" + echo "::error::header comment in dependabot-auto-merge.yml and issue #64." + exit 1 + fi + echo "No actions/checkout found in ${file} — guardrail passed." + done diff --git a/README.md b/README.md index cf78d9f..9160eb5 100644 --- a/README.md +++ b/README.md @@ -237,9 +237,15 @@ executes PR code: the only actions are API calls (`dependabot/fetch-metadata`, to this file.** The guardrail below enforces this (closes #64): - `self-review.yml`'s `guard-no-checkout` job greps this repo's own - copy of `dependabot-auto-merge.yml` and fails the PR if + copy of `dependabot-auto-merge.yml`, and its caller + `self-dependabot-auto-merge.yml`, and fails the PR if `actions/checkout` appears. +This repo auto-merges its own Dependabot PRs through +`self-dependabot-auto-merge.yml`. It pins `@dependabot-auto-merge-v2` +rather than a local path, so an unreleased change to the reusable +workflow never decides merges here. + ### Versioning Tagged with a prefixed namespace — `dependabot-auto-merge-v1`,