feat: ✨ add release-promote composite action - #77
Merged
Conversation
Consolidates the release-main.yml logic every tomgrv/perspikapps repo duplicated (and had let drift -- different setup-node pins, an unpinned gitutils install from a moving default branch) into one reusable workflow other repos call via workflow_call. Pulls git-release-beta/ git-release-prod from tomgrv/scripts (pinned via scripts_ref) instead of installing the whole gitutils devcontainer feature just to get two scripts, and adds a dry_run input for safe verification. This repo's own release-main.yml now calls it too, and docs/release- process.md documents the canonical flow plus the tag/branch-protection bypass checklist every repo needs applied by hand (no tool has API access to repo rulesets, so this stays a manual step). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dau8uW2rrctkyAxngdR1ix
release-promote was a workflow_call reusable workflow; convert it to a tomgrv/actions composite action (release-promote/action.yml + run.sh), matching every other action in this repo, so it composes as a normal step in a release-main job instead of taking over the whole job via workflow_call. Reuses config-bot for git identity (was hand-rolled before) rather than duplicating it. Same behavior otherwise: pulls git-release-beta/ git-release-prod from tomgrv/scripts (pinned via scripts-ref), dry-run input, clear error pointing at the tag/branch-protection bypass checklist on a protected-ref push failure. This repo's own release-main.yml now calls it as `uses: ./release-promote` within its job instead of delegating the whole job via workflow_call. Adds release-promote/run.bats (6 tests, all passing) and a README following the repo's action-doc convention. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dau8uW2rrctkyAxngdR1ix
Merged
4 tasks
Pulls the idempotent zz_use bootstrap out of release-promote into its own reusable composite action, setup-scripts, with optional `scripts` (space-separated tool names) and `scripts-ref` (shared pin) inputs -- generic enough for any future action that needs specific tomgrv/scripts tools on PATH, not just release-promote. release-promote now composes it (`uses: tomgrv/actions/setup-scripts`) for installing git-release-beta/git-release-prod, the same way check-dockerfile already composes setup-reviewdog. Its own run.sh is now just the beta/prod-running logic; the bootstrap test coverage moved to setup-scripts/run.bats (6 tests) and release-promote/run.bats was trimmed to the 6 tests that are actually its own concern. All 12 pass. release-promote's public interface (scripts-ref, dry-run, github-token inputs) is unchanged, so no other repo's release-main.yml needs to change for this. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dau8uW2rrctkyAxngdR1ix
tomgrv
commented
Sep 3, 2026
| # that ships it, or a re-run in the same job) is reused as-is -- only | ||
| # fetch/install when it's genuinely missing. | ||
| if ! command -v zz_use > /dev/null 2>&1; then | ||
| curl -fsSL "${ZZ_SCRIPTS_SETUP_URL:-https://raw.githubusercontent.com/tomgrv/scripts/main/setup.sh}" -o /tmp/zz_setup.sh |
Owner
Author
There was a problem hiding this comment.
Allow branch specification via optional parameter. Default is main.
Owner
Author
There was a problem hiding this comment.
✅ Done. Added scripts-branch input to action.yml (defaults to main), updated run.sh to use the environment variable, and documented it in README.md. This allows callers to specify which branch of tomgrv/scripts to fetch setup.sh from, useful for testing with development branches during integration.
Tests still pass (6/6 ✓). New commit pushed: c7e62d8
Generated by Claude Code
…input parameter Add optional scripts-branch input to setup-scripts composite action, defaulting to 'main'. This allows callers to specify which branch of tomgrv/scripts to fetch setup.sh from, enabling easier testing with development branches during integration testing. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
git-release-beta/git-release-prod (from tomgrv/scripts) call gv/bump-tag/ bump-changelog/bump-version directly to compute the release version and bump the changelog/tag. Neither tomgrv/scripts nor release-promote's existing setup-scripts step installs this toolchain (a docker-wrapped GitVersion CLI plus those bin scripts, from the gitversion devcontainer feature) -- the old per-repo release-main.yml got it for free as a side effect of installing the whole gitutils feature, and that installation step was dropped when release-promote replaced it. Every live release would fail at the first `gv -showvariable` call without this. release-promote now composes setup-gitversion alongside setup-scripts and config-bot, mirroring the same composition pattern. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dau8uW2rrctkyAxngdR1ix
tomgrv
added a commit
to tomgrv/scripts
that referenced
this pull request
Sep 3, 2026
…iner-features (#3) ## Summary Part of a coordinated consistency pass across `tomgrv/devcontainer-features`, `tomgrv/actions`, `tomgrv/scripts`, and `perspikapps/vps`. Companion PRs: tomgrv/actions#77, tomgrv/devcontainer-features#143, perspikapps/vps#10. - Migrates the ~32 `git-*` scripts formerly under `devcontainer-features/src/gitutils/bin/` into individual workspace packages here (`/{run.sh,package.json,README.md,test.bats}`), mirroring the exact migration `common-utils` already went through. One source of truth for the script implementations now, fetched via `zz_use` instead of duplicated per consumer. - Switches this repo's own `release-main.yml` to call `tomgrv/actions`' new `release-promote` composite action as a step, dogfooding the same release path every consumer repo now uses. - Adds a "Git utilities" table to the README documenting the new packages. - **Adds a `.gitversion` config file at repo root.** `git-release-beta`/`git-release-prod` call `gv -showvariable MajorMinorPatch`, which hardcodes `-config ".gitversion"` — this repo never had that file (no devcontainer here to deploy it via the `gitversion` feature's configure step). Content matches `tomgrv/actions/.gitversion`, the same bump-message rules used across the rest of the family. ## Cross-repo dependency `tomgrv/actions`' `release-promote` action (this PR's release-main.yml step depends on it, including its new `setup-gitversion` sub-step — see tomgrv/actions#77) and `devcontainer-features`' `gitutils` feature (now depends on the packages added here) are being updated in sibling PRs on the same branch name. Until those merge and tag, the workflow points at `tomgrv/actions@claude/multi-repo-consistency-release-7se0jy` — marked with a `TODO` to pin to a released tag afterward. This repo's own `release-main.yml` has a chicken-and-egg problem releasing itself (it needs its own `git-release-beta`/`git-release-prod` before any tag of this repo exists) — the operational plan for the actual release run pins `scripts-ref` to the merge commit SHA for that one bootstrap run, then updates it to the real tag afterward. ## Test plan - [x] `npx bats --recursive .` — all 68 tests pass (32 new + 36 existing) - [x] Each new script's `run.sh` verified `sh -n` clean via its `test.bats` - [x] `npx prettier --check` clean on all new/changed files - [ ] Bump the workflow's `uses:` ref to a released tag once this merges and tags 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Dau8uW2rrctkyAxngdR1ix --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: bot <bot@example.com>
config-bot/setup-scripts/setup-gitversion were pinned to @main, which 404s for any sibling action that hasn't reached main yet -- exactly the case for setup-scripts and setup-gitversion right now, both new in this same PR. Verified live: tomgrv/scripts' release-main dry-run failed with "Can't find 'action.yml' ... for action 'tomgrv/actions/setup-scripts@main'". Relative `./` references resolve against the same ref release-promote itself was invoked at (branch or tag), the same mechanism this repo's own release-main.yml already relies on for `uses: ./release-promote`. This also means release-promote never needs updating when a sibling action's ref changes -- it always tracks whichever ref/tag called it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dau8uW2rrctkyAxngdR1ix
…ses:
The previous ./config-bot/./setup-scripts/./setup-gitversion fix was
itself wrong: a relative `uses:` path in a composite action resolves
against the *calling* repository's checkout (GITHUB_WORKSPACE), not this
action's own. Verified live: tomgrv/scripts' release-main dry-run failed
with "Can't find 'action.yml' ... under
'/home/runner/work/scripts/scripts/config-bot' -- Did you forget to run
actions/checkout".
Neither a fixed `@main` (404s until a sibling reaches main -- true right
now for setup-scripts/setup-gitversion, and for anything a future first
release introduces) nor `./<name>` (wrong repo for any external caller)
can express "whichever ref release-promote itself was invoked at". Since
`uses:` doesn't support expressions, route around it: invoke each
sibling's run.sh directly via `${{ github.action_path }}/../<name>/run.sh`
with the same env vars its own action.yml would set. action_path always
resolves inside this action's own checkout, and GitHub fetches the whole
repo (not just the one subpath) to run any of its actions, so the
siblings are right there alongside it at the same commit -- this works
for every caller/ref, including this repo's own first release.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dau8uW2rrctkyAxngdR1ix
Verified live: release-promote's direct-invocation step (sh -c ".../setup-gitversion/run.sh") failed with "Permission denied" / exit 126 -- the file was written without the executable bit every other action's run.sh already carries. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dau8uW2rrctkyAxngdR1ix
Verified live: the feature-install call failed with "Unknown command: gitversion" -- this repo's own CLAUDE.md documents the current CLI as `npx tomgrv/devcontainer-features -- add <feature>`, not a bare feature name (the old release-main.yml this pattern was copied from predates that CLI's add subcommand). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dau8uW2rrctkyAxngdR1ix
…re installer Verified live and locally: `devcontainer-features -- add gitversion` deploys that feature's dev-environment stub files (.gitattributes, package.json merges, VS Code tasks, skills) straight into the checked-out repo -- unwanted noise for a CI release job -- and its bin/ scripts never actually land on PATH outside a real devcontainer postCreate context (confirmed: install ran, target dir had no bin/ subdir, "gv not on PATH after install"). Rewritten to build the docker-gitversion wrapper and fetch gv/bump-tag/ bump-changelog/bump-version directly from the gitversion feature's source via curl, mirroring install-tools.sh's own logic without going through the feature installer at all. No repo pollution, and it actually ends up on PATH. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dau8uW2rrctkyAxngdR1ix
Verified live: the first real release attempt for tomgrv/scripts failed with "git: 'flow' is not a git command" -- git-release-beta/git-release-prod call `git flow release start`/`git flow <flow> finish` directly, but neither the git-flow binary nor its local git config (gitflow.branch.*/gitflow.prefix.*, normally set once by a developer's own devcontainer setup) exists on a bare CI checkout. The old workflow got both for free as a side effect of installing the whole gitutils feature (its own install-gitflow.sh/configure-gitflow.sh); nothing replaced that when release-promote stopped installing gitutils wholesale. Adds setup-gitflow, mirroring install-gitflow.sh's package-manager install fallback chain and configure-gitflow.sh's `git flow init` call (without their zz_log/zz_context dependencies, and without the "create master/develop as an orphan branch" logic meant for a fresh repo scaffold -- a CI checkout of an existing repo already has real ones). release-promote composes it alongside setup-scripts/setup-gitversion. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dau8uW2rrctkyAxngdR1ix
Verified live: the install failed with "E: Unable to lock directory /var/lib/apt/lists/" -- GitHub-hosted runners run as a non-root user with passwordless sudo, but the install commands were being run directly, matching neither this repo's own conventions nor install-gitflow.sh (the script this was based on), which already handles the root-vs-sudo split this dropped. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dau8uW2rrctkyAxngdR1ix
… tag tomgrv/scripts just released v0.3.0 (which includes git-release-beta/ git-release-prod), so this repo's own release-main.yml no longer needs to point scripts-ref at the working branch. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dau8uW2rrctkyAxngdR1ix
tomgrv
marked this pull request as ready for review
September 3, 2026 19:43
tomgrv
added a commit
to tomgrv/devcontainer-features
that referenced
this pull request
Sep 3, 2026
…rv/scripts, remove pecl (#143) ## Summary Part of a coordinated consistency pass across this repo, `tomgrv/actions` (#77, merged, released as v2.22.0), `tomgrv/scripts` (#3, merged, released as v0.3.0), and `perspikapps/vps` (#10). - **gitutils migration**: `src/gitutils/bin/`'s ~32 `git-*` scripts now live in `tomgrv/scripts` as individual packages (tomgrv/scripts#3) — one source of truth, mirroring the exact move `common-utils` already made. `install.sh` now bootstraps `zz_use` and pulls them in; `gitutils` keeps only the config/alias wiring and git-flow install/configure lifecycle. - **Shared release workflow**: `src/gitutils/stubs/.github/workflows/release-main.yml` (the actual template every consumer repo's copy is stub-deployed from) now checks out the repo and calls `tomgrv/actions`' new `release-promote` composite action (tomgrv/actions#77), pinned to `v2.22.0`, pulling `git-release-beta`/`git-release-prod` from `tomgrv/scripts@v0.3.0`. This repo's own root `.github/workflows/release-main.yml` is redeployed from that same stub, since this repo dogfoods its own features. - **Removes `pecl` entirely** (rather than bringing it to v8 parity) — it was already flagged in `docs/reviews/2026-08-repo-review.md` §2.5 as the one feature diverging from the standard `install-feature` pattern. `CLAUDE.md`'s "minimal reference" pointer moves to `src/scripting/`. All other references cleaned up; the historical audit doc's findings are left as-is with a dated addendum note rather than rewritten. - **Stale internal `dependsOn` pins**: 7 features' own `dependsOn` blocks still pinned `common-utils:7`/`gitutils:7`/`gitversion:7` even though those targets have been on major 8 for a while — bumped all to `:8`. - **Stale README self-install snippets**: every feature's own README Quick Start snippet (plus the two in root `README.md`) still showed its pre-v8 self-install version — bumped to `:8` (left `scripting:1` alone, since that one's genuinely major v1). - **VS Code tasks**: the stub's "🎈 Beta"/"🚀 Prod" local-CLI tasks are dropped now that release is fully automated via GitHub's "Run workflow" button. ## Fixed during review - `validate-pr-title` failed on the original PR title (`feat(scope)!: ...`) — `devmoji`'s autocorrection inserts two emoji back-to-back for that breaking-change position, and commitlint's parser chokes on the result. Dropped the `!` from the title; the breaking-change signal is still carried by the commit's `BREAKING CHANGE:` footer. - `release-promote` was originally a `workflow_call` reusable workflow; restructured into a composite action per explicit direction, so it composes as a normal step instead of taking over the whole job. It went through several more live-verified fixes upstream (sibling-action resolution, a missing GitVersion toolchain, a missing git-flow install) before `tomgrv/scripts`' and `tomgrv/actions`' own real releases succeeded — see tomgrv/actions#77 for the full history. ## Cross-repo dependency `install.sh`'s `zz_use` calls and the release-main stub's `scripts-ref`/`uses:` refs are now pinned to the released tags (`tomgrv/scripts@v0.3.0`, `tomgrv/actions/release-promote@v2.22.0`) — both sibling PRs have merged and tagged. ## Test plan - [x] `bats src/gateway/tests src/common-utils/tests` — all 24 pass (unaffected by this change, run as a regression check) - [x] Every touched `devcontainer-feature.json` validated as parseable JSON - [x] `install.sh` syntax-checked (`sh -n`) - [x] Workflow YAML and `tasks.json` files parse cleanly - [x] `npx prettier --check` clean on all changed files - [x] `scripts-ref`/`uses:` pinned to released tags 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Dau8uW2rrctkyAxngdR1ix --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: bot <bot@example.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Part of a coordinated consistency pass across
tomgrv/devcontainer-features,tomgrv/actions,tomgrv/scripts, andperspikapps/vps. Companion PRs: tomgrv/scripts#3 (merged, released as v0.3.0), tomgrv/devcontainer-features#143, perspikapps/vps#10.release-promote/— a composite action (matching every other action in this repo:action.yml+run.sh) consolidating thegit beta && git prodlogic every repo in this family had copy-pasted and let drift (differentsetup-nodepins, an unpinnedgitutilsinstall from a moving default branch). It pullsgit-release-beta/git-release-prodfromtomgrv/scripts(pinned via a requiredscripts-refinput, nowv0.3.0) instead of installing the wholegitutilsdevcontainer feature just for two scripts, reusesconfig-botfor the git identity instead of hand-rolling it, and adds adry-runinput for safe verification without pushing tomain.setup-scripts/— the idempotentzz_usebootstraprelease-promoteneeded, extracted into its own composite action with optionalscripts(space-separated tool names) andscripts-ref(shared pin) inputs. Also takes an optionalscripts-branchinput (defaultmain) for which branch to bootstrapzz_usefrom.setup-gitversion/— a composite action installing the GitVersion toolchain (docker-wrapped GitVersion CLI plusgv/bump-tag/bump-changelog/bump-version) thatgit-release-beta/git-release-prodcall directly. Fetches these directly from thegitversiondevcontainer feature's source (mirroringinstall-tools.sh's own logic) rather than running that feature's own installer — verified live and locally that the installer deploys unrelated dev-environment stub files into the checked-out repo and doesn't reliably land itsbin/scripts onPATHoutside a real devcontainer.setup-gitflow/— installs the git-flow extension if missing and runsgit flow initagainst the checkout.git-release-beta/git-release-prodcallgit flow release start/git flow <flow> finishdirectly, and neither the binary nor its local git config exist on a bare CI runner. Mirrorsinstall-gitflow.sh/configure-gitflow.sh, including their root-vs-sudo install branching (GitHub-hosted runners are non-root with passwordless sudo).release-promotecomposes all three (setup-scripts,setup-gitversion,setup-gitflow) alongsideconfig-bot, invoking each sibling'srun.shdirectly viagithub.action_pathrather than nesteduses:— verified live that neither a fixed@mainref (404s on a sibling that hasn't reachedmainyet) nor a relative./name(resolves against the calling repo's checkout, not this one) works for every caller/ref.release-promotestarted as aworkflow_callreusable workflow, then was restructured into a composite action per explicit direction, since a composite action composes naturally as one step in each repo's ownrelease-mainjob rather than taking over the whole job.setup-scripts/setup-gitversion/setup-gitflowwere then split out of it, for the same compositional reason and because each is independently reusable.release-main.ymlnow callsrelease-promotetoo (dogfooding) asuses: ./release-promote, pinned totomgrv/scripts@v0.3.0.docs/release-process.md: the canonical release flow plus the tag/branch-protection bypass checklist every repo in the family needs applied by hand (no tool here has API access to repo rulesets, so this stays a manual, documented step rather than something a workflow silently bypasses).Live-verified
All three of
setup-gitversion/setup-gitflow's bugs (missing toolchain install, wrong install mechanism, missing sudo) and the sibling-actionuses:resolution bug were caught by actually runningtomgrv/scripts'release-mainworkflow against this branch — dry-run rehearsals first, then culminating in a real release:tomgrv/scriptsis now taggedv0.3.0onmain(run 33797784005).Cross-repo dependency
References
tomgrv/scriptsv0.3.0 (released, tag pinned).release-promote's public interface (scripts-ref,dry-run,github-token) is unchanged by thesetup-scripts/setup-gitversion/setup-gitflowextractions, so no other repo'srelease-main.ymlneeds updating for that part — only thescripts-ref/uses:pins themselves, once this repo tags a release.Test plan
release-promote/run.bats— 6 testssetup-scripts/run.bats— 6 testssetup-gitversion/run.bats— 4 testssetup-gitflow/run.bats— 6 testsaction.yml, workflow YAML, and JSON files all parse cleanlynpx prettier --checkclean on all YAML/JSON/MD (.sh/.batsaren't in this repo's prettier glob — confirmed against the pre-existingconfig-bot/run.bats, which hits the same formatter error, so this isn't a regression)tomgrv/scripts' real release succeeded through this exact branch'srelease-promoteuses:ref to pin the tag instead of this branch🤖 Generated with Claude Code
https://claude.ai/code/session_01Dau8uW2rrctkyAxngdR1ix