Reusable Dependabot action-pin-comment sync workflow - #8
Conversation
Dependabot's github-actions updater bumps SHA pins but cannot rewrite the # vX.Y.Z comment on lines that carry a second trailing comment (e.g. a zizmor suppression), so those comments go stale on every actions-group bump across our repos. Port openclaw-basecamp's hardened comment-sync automation as a reusable workflow: callers trigger it via workflow_run after CI completes on a Dependabot actions branch; it runs only trusted default-branch code, fetches the head branch as data (never executes it), rewrites stale version comments with an embedded zero-dependency updater script, proves the diff touches nothing but pinned uses lines, and pushes behind a compare-and-swap lease with a dedicated write deploy key (App tokens, GITHUB_TOKEN included, cannot push workflow-file changes; SSH deploy keys are exempt — openclaw-basecamp@54c96d3e). The updater script is embedded in the workflow heredoc so the caller's SHA pin covers it byte for byte; scripts/sync-action-pin-comments.mjs (verbatim from openclaw-basecamp) is the tested source of truth, with the vitest suite ported to node --test and a CI drift gate asserting the embedded copy matches.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 51c7df4123
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
Ports the hardened “Dependabot action-pin-comment sync” automation into this repo as a reusable workflow, including an embedded updater script (covered by the caller’s SHA pin) plus CI checks to ensure the embedded copy never drifts from the tested source.
Changes:
- Add
sync-action-pin-comments.mjscore+CLI script to rewrite stale# vX.Y.Zcomments on SHA-pinneduses:lines while preserving trailing annotations byte-for-byte. - Add a reusable workflow (
dependabot-sync-actions-comments.yml) that runs on reviewed code paths and pushes fixes behind strict PR gates + an SSH deploy-key lease push. - Add tests and CI drift protection (
node --testsuite + embedded-script equivalence check).
Tip
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
scripts/sync-action-pin-comments.mjs |
Zero-dependency updater that computes safe, token-only edits for pinned uses: version comments. |
scripts/sync-action-pin-comments.test.mjs |
node:test suite validating parsing, tag selection, edit planning, and token-only diff guard behavior. |
scripts/check-embedded-sync.sh |
CI guard that extracts the embedded heredoc script and diffs it against the source script to prevent drift. |
.github/workflows/dependabot-sync-actions-comments.yml |
Reusable hardened workflow that gates eligible Dependabot PRs and performs a leased deploy-key push of comment fixes. |
.github/workflows/ci.yml |
Adds a job to run the script tests and embedded-script drift check in CI. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
zizmor's secrets-outside-env audit (regular-persona in the 1.23.x this repo's pinned zizmor-action resolves) flagged the repo-scoped secret — and the environment model is better anyway, matching our zero repo-scoped-secrets practice: the SYNC_ACTIONS_DEPLOY_KEY lives in each caller's dependabot-sync environment whose deployment branch policy allows only the default branch, so no other workflow (and no non-default ref) can read it. Environment secrets cannot be forwarded explicitly to a reusable workflow, so callers switch to secrets: inherit and the job declares the environment itself.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
.github/workflows/dependabot-sync-actions-comments.yml:81
environment: dependabot-syncin a reusable workflow cannot load environment secrets from the caller repository; environment secrets are scoped to the repository that defines the reusable workflow. As a result, the deploy key described in the comment will not be available from caller repos and the push step will fail. Prefer passingSYNC_ACTIONS_DEPLOY_KEYas a normal repo/org secret viaworkflow_callsecrets (and drop the environment here), or keep this workflow non-reusable if environment-only scoping is a hard requirement.
# The write deploy key lives in the caller repo's `dependabot-sync`
# environment, whose deployment branch policy is restricted to the default
# branch: only default-branch runs of this job can read it, and no other
# workflow in the repo can. Callers pass `secrets: inherit` — environment
# secrets cannot be forwarded explicitly; they resolve only when the
# running job declares the environment.
environment: dependabot-sync
* ci: sync Dependabot action pin comments via reusable workflow Thin caller for basecamp/.github's dependabot-sync-actions-comments reusable workflow (basecamp/.github#8): after CI completes on a Dependabot github_actions branch, trusted default-branch code fixes any stale pin version comments and pushes back behind a compare-and-swap lease, using the write deploy key scoped to this repo's dependabot-sync environment (deployment branches: default branch only). * chore(actions): sync seed workflow pin comments One-time catch-up: the ref-version-mismatch audit is online, so upstream tag moves since the last CI run left 33 stale version comments in the seed templates (zizmor --fix=all, with the two cache-poisoning ignore annotations it clobbers restored by hand). Seed templates stay manually maintained; the new sync workflow covers only .github/workflows. * deps: bump golang.org/x/sys to v0.44.0 Clears CVE-2026-39824 (integer overflow in NewNTUnicodeString, golang.org/x/sys/windows) flagged by the Trivy gate. Indirect dependency; go build ./... verified. * ci: filter workflow_run to dependabot branches at the trigger Review feedback: the job-level if only skips the job after the run is created; a trigger-level branches filter stops runs from being created for non-Dependabot branches at all. The if stays as defense-in-depth.
* ci: sync Dependabot action pin comments via reusable workflow Thin caller for basecamp/.github's dependabot-sync-actions-comments reusable workflow (basecamp/.github#8): after CI completes on a Dependabot github_actions branch, trusted default-branch code fixes any stale pin version comments and pushes back behind a compare-and-swap lease, using the write deploy key scoped to this repo's dependabot-sync environment (deployment branches: default branch only). * chore(actions): sync action pin comments One-time catch-up applying what the new sync workflow will maintain automatically: version comments on SHA-pinned uses lines whose trailing zizmor annotations prevented Dependabot from rewriting them. * ci: filter workflow_run to dependabot branches at the trigger Review feedback: the job-level if only skips the job after the run is created; a trigger-level branches filter stops runs from being created for non-Dependabot branches at all. The if stays as defense-in-depth.
* ci: sync Dependabot action pin comments via reusable workflow Thin caller for basecamp/.github's dependabot-sync-actions-comments reusable workflow (basecamp/.github#8): after CI completes on a Dependabot github_actions branch, trusted default-branch code fixes any stale pin version comments and pushes back behind a compare-and-swap lease, using the write deploy key scoped to this repo's dependabot-sync environment (deployment branches: default branch only). Also adds workflow_dispatch to test.yml and security.yml so the reusable workflow's CI-dispatch fallback can re-run checks on the pushed head. * chore(actions): sync action pin comments One-time catch-up applying what the new sync workflow will maintain automatically: version comments on SHA-pinned uses lines whose trailing zizmor annotations prevented Dependabot from rewriting them. * ci: filter workflow_run to dependabot branches at the trigger Review feedback: the job-level if only skips the job after the run is created; a trigger-level branches filter stops runs from being created for non-Dependabot branches at all. The if stays as defense-in-depth.
* CI: sync Dependabot action pin comments via reusable workflow Thin caller for basecamp/.github's dependabot-sync-actions-comments reusable workflow (basecamp/.github#8): after CI completes on a Dependabot github_actions branch, trusted default-branch code fixes any stale pin version comments and pushes back behind a compare-and-swap lease, using the write deploy key scoped to this repo's dependabot-sync environment (deployment branches: main only). Replaces the push-triggered port withdrawn from #458: that lineage executed the freshly-bumped unreviewed checkout action with a write token, and GITHUB_TOKEN cannot push workflow-file changes at all. * CI: filter workflow_run to dependabot branches at the trigger Review feedback: the job-level if only skips the job after the run is created; a trigger-level branches filter stops runs from being created for non-Dependabot branches at all. The if stays as defense-in-depth. * CI: re-pin dependabot-sync reusable workflow past the dispatch fallback basecamp/.github#10 removed the CI-dispatch fallback (Codex P1 on fizzy-cli#189: dispatching branch workflows executes unreviewed action pins outside the Dependabot sandbox); the workflow now verifies CI started on the pushed head and warns a maintainer if not.
* ci: sync Dependabot action pin comments via reusable workflow Thin caller for basecamp/.github's dependabot-sync-actions-comments reusable workflow (basecamp/.github#8): after CI completes on a Dependabot github_actions branch, trusted default-branch code fixes any stale pin version comments and pushes back behind a compare-and-swap lease, using the write deploy key scoped to this repo's dependabot-sync environment (deployment branches: default branch only). Also adds workflow_dispatch to test.yml and security.yml so the reusable workflow's CI-dispatch fallback can re-run checks on the pushed head. * ci: filter workflow_run to dependabot branches; give test.yml a unique name Review feedback (Copilot + Codex): a trigger-level branches filter stops workflow runs from being created for non-Dependabot branches (the job-level if only skips the job after the fact), and ci.yml and test.yml both being named CI made the workflow_run anchor fire twice per head and the ci-workflow-name detection ambiguous. test.yml becomes Test (matching basecamp-cli and cli); required status checks are job-name contexts (test, lint, security, race-check) so the rename does not affect the main-gate ruleset. Consolidating ci.yml vs test.yml overlap is left as a follow-up. * deps: require Go 1.26.2 Clears the nine stdlib vulnerabilities govulncheck flags in go1.26.1 (GO-2026-4866 et al., all fixed in 1.26.2). The workflows resolve the toolchain from go.mod, so this moves CI and local builds together. * ci: re-pin dependabot-sync reusable workflow past the dispatch fallback basecamp/.github#10 removed the CI-dispatch fallback (Codex P1 on fizzy-cli#189: dispatching branch workflows executes unreviewed action pins outside the Dependabot sandbox); the workflow now verifies CI started on the pushed head and warns a maintainer if not. * deps: require Go 1.26.5 1.26.2 was not enough: govulncheck flags stdlib vulnerabilities fixed across 1.26.3–1.26.5 (GO-2026-5856 in crypto/tls et al.). 1.26.5 is the newest patch release, clearing all five remaining findings.
What
Ports openclaw-basecamp's hardened
dependabot-sync-actions-commentsautomation as a reusable workflow, for thin SHA-pinned callers in basecamp-sdk, basecamp-cli, hey-cli, fizzy-cli, and cli.Dependabot's github-actions updater bumps SHA pins but can't rewrite the
# vX.Y.Zcomment on lines that carry a second trailing comment (e.g. a# zizmor: ignore[...]suppression), so those comments go stale on every actions-group bump (latest instance: basecamp-sdk#454, fixed by hand in basecamp-sdk#458). zizmor ≥1.26 no longer parses compound comments as version comments either, so the audit can't catch or fix them.Security model (unchanged from openclaw-basecamp)
The naive push-triggered form (basecamp-mcp-server lineage) had two fatal flaws surfaced in basecamp-sdk#458 review: it executes the freshly-bumped, unreviewed checkout action with a write token, and
GITHUB_TOKENcannot push workflow-file changes at all (openclaw-basecamp hit this live and moved to a deploy key in openclaw-basecamp@54c96d3e). This model instead:workflow_runfrom the default branch — only reviewed code executes; the Dependabot head branch is fetched as data, never executed (no checkout into an executing context, nouses:steps from it)pull_requestCI runs ondependabot/github_actions/*only; exactly one open PR, base = default branch, head still at the audited SHA, authored byapp/dependabot(or, in e2e proof mode, a draft PR by the dispatcher on ane2e-proof-*branch)uses:line; the updater script separately asserts changes are confined to the version tokenSYNC_ACTIONS_DEPLOY_KEYin each caller repo) — compare-and-swap--force-with-leasepinned to the audited head SHA; GitHub's SSH host keys pinned from the authenticated APIworkflow_runcompletions (hey-cli has two workflows both named "CI") serialize instead of racing the leaseReusable-workflow adaptation
The one structural change from openclaw: the updater script can't be fetched via
git show $GITHUB_SHA:...(that's the caller's commit here), andjob_workflow_shaexists only as an OIDC claim, not in thegithubcontext. Instead the script is embedded in the workflow heredoc, so the caller's SHA pin covers it byte for byte with no runtime fetch, no mutable ref, and no extra permissions.scripts/sync-action-pin-comments.mjs(byte-identical copy of openclaw's, sha1 5acb3555) remains the reviewable, tested source of truth:node --test(20 tests)scripts/check-embedded-sync.shfails CI if the embedded copy drifts from the fileCaller inputs cover the per-repo differences:
ci-workflow-name(Test/CI),default-branch(fizzy-cli ismaster),dispatch-workflows(fallback list).Verification
actionlintandzizmor1.28.0: cleannode --test "scripts/*.test.mjs": 20/20 passgithub_actionsbump in a caller repo (basecamp-cli#566 is a candidate)Caller PRs + deploy-key provisioning follow once this merges and pins can point at the mainline SHA.