Motivation
Release automation is currently vendored per repo, and it's starting to drift:
d3mlabs/dev carries bin/release.rb (version bump, commit, tag, push, GitHub release, homebrew formula update), wired as dev release through its own dev.yml.
ast-transform / rspock have no script — just a tag-triggered trusted-publishing workflow plus README steps ("bump version.rb, commit, tag, push").
simplecov-sorbet was about to vendor a third adapted copy of bin/release.rb.
This is exactly the shape runner-setup already solved: "dev owns the install logic (one shared implementation) so repos declare just their runner identity instead of vendoring a bespoke setup script" (src/dev/runner.rb). Releases should get the same treatment — dev as the project integration manager, with release publishers as an array of integrations mirroring how deps integrations work.
Design
dev.yml gets a release: block (like runner:), and the release builtin registers only when it's present:
release:
version_file: lib/simplecov/sorbet/version.rb # VERSION constant (.rb) or raw VERSION file
publishers: [rubygems]
Dev::Release orchestrator (shared core, publisher-agnostic):
- Guards: clean working tree, on
main, up to date with origin/main.
- Version bump: auto-increment patch or explicit arg;
.rb files get the VERSION = "x.y.z" constant rewritten, raw files get the content replaced. If the requested version already matches (first release), skip the bump commit.
- Re-pin the project's own
name (x.y.z) PATH spec in Gemfile.lock when present.
- Commit, tag
v<version>, push main + tag, gh release create with notes from commits since the last tag.
- Run the configured publishers.
Publishers (array, per project):
rubygems: trusted publishing does the actual push on tag — the publisher verifies the release workflow file exists and reminds that the trusted publisher must be configured on rubygems.org. Covers simplecov-sorbet, ast-transform, rspock.
homebrew-formula: absorbs dev's own formula-bump logic (tarball sha256, surgical url+sha replacement, tap commit+push) so bin/release.rb can be deleted.
Testing: orchestrator tested against real temp git repos (org test-hygiene: no filesystem mocks); push/gh/formula-tap operations stubbed at the process boundary.
Adoption
- Gem repos each replace their README release choreography with the three-line
release: block; no scripts to vendor.
- Requires shipping a dev release first (repos consume dev via homebrew).
Open questions
- Phase 1 scope: gem/
rubygems flavor first with dev's homebrew release migrating as a follow-up, or absorb both flavors immediately?
- Adopters in the first pass:
simplecov-sorbet only, or all three gem repos?
Motivation
Release automation is currently vendored per repo, and it's starting to drift:
d3mlabs/devcarriesbin/release.rb(version bump, commit, tag, push, GitHub release, homebrew formula update), wired asdev releasethrough its owndev.yml.ast-transform/rspockhave no script — just a tag-triggered trusted-publishing workflow plus README steps ("bump version.rb, commit, tag, push").simplecov-sorbetwas about to vendor a third adapted copy ofbin/release.rb.This is exactly the shape
runner-setupalready solved: "dev owns the install logic (one shared implementation) so repos declare just their runner identity instead of vendoring a bespoke setup script" (src/dev/runner.rb). Releases should get the same treatment — dev as the project integration manager, with release publishers as an array of integrations mirroring how deps integrations work.Design
dev.ymlgets arelease:block (likerunner:), and thereleasebuiltin registers only when it's present:Dev::Releaseorchestrator (shared core, publisher-agnostic):main, up to date withorigin/main..rbfiles get theVERSION = "x.y.z"constant rewritten, raw files get the content replaced. If the requested version already matches (first release), skip the bump commit.name (x.y.z)PATH spec inGemfile.lockwhen present.v<version>, pushmain+ tag,gh release createwith notes from commits since the last tag.Publishers (array, per project):
rubygems: trusted publishing does the actual push on tag — the publisher verifies the release workflow file exists and reminds that the trusted publisher must be configured on rubygems.org. Coverssimplecov-sorbet,ast-transform,rspock.homebrew-formula: absorbs dev's own formula-bump logic (tarball sha256, surgical url+sha replacement, tap commit+push) sobin/release.rbcan be deleted.Testing: orchestrator tested against real temp git repos (org test-hygiene: no filesystem mocks); push/
gh/formula-tap operations stubbed at the process boundary.Adoption
release:block; no scripts to vendor.Open questions
rubygemsflavor first with dev's homebrew release migrating as a follow-up, or absorb both flavors immediately?simplecov-sorbetonly, or all three gem repos?