Skip to content

Fix automated RubyGems publishing - #179

Merged
lloydwatkin merged 1 commit into
mainfrom
janus-rubygems-autodeploy
Sep 4, 2026
Merged

lloydwatkin merged 1 commit into
mainfrom
janus-rubygems-autodeploy

Conversation

@lloydwatkin

Copy link
Copy Markdown
Member

Why

The publish workflow has never successfully released a gem. Only one run has ever fired — tag v8.0.1, 2026-06-19 — and it died in release:guard_clean:

janus-ar 8.0.0 built to pkg/janus-ar-8.0.0.gem.
rake aborted!
There are files that need to be committed first.
Tasks: TOP => release => release:guard_clean

8.0.1 was then published by hand three minutes later.

Two independent things dirtied the checkout:

  1. Gemfile.lock records the gem's own version in its PATH section. It said janus-ar (8.0.0) while version.rb said 8.0.1, so bundle install in CI rewrote it. This would recur on every release, since the lock has to be bumped alongside version.rb.
  2. bundler-cache: true writes vendor/bundle (not gitignored) and rewrites the tracked .bundle/config.

Separately, the v8.0.1 tag points at a commit where version.rb still said 8.0.0 — so even a clean tree would have published the wrong version.

Trusted publishing is already set up correctly. rubygems/configure-rubygems-credentials completed and exported the OIDC token in that run; the job only failed afterwards. No credential work is needed, and the unused RUBYGEMS_API_KEY repo secret (created 2024) can be deleted.

What changed

  • .github/workflows/publish.yml — plain bundle install instead of bundler-cache so nothing is written into the working tree; ruby-version: '3.4' instead of ruby (was resolving to 4.0.5); fetch-depth: 0 so release:source_control_push sees the tag as already created; two guard steps that fail with a readable message on tag/version mismatch or a dirty tree, rather than the cryptic guard_clean error.
  • bin/release.sh — was gem build && gem push, i.e. the manual path that bypassed CI entirely. Now validates the version, rewrites version.rb, regenerates Gemfile.lock, commits and tags — keeping the two in lockstep so cause Bump rake from 13.1.0 to 13.2.1 #1 can't come back.
  • janus-ar.gemspec — the gem was shipping bin/release.sh as an installed executable onto users' PATH (spec.executables == ["release.sh"]). Now [], and spec.files drops bin/ spec/ assets/ gemfiles/ .github/ and dotfiles (28 files → 19).
  • Gemfile — http://rubygems.org → https. Gemfile.lock resynced to 8.0.1.
  • actions/checkout v4 → v7 (v4 runs on deprecated Node 20 — visible as a warning in the failed run) and dependabot now covers github-actions, which was bundler-only and let checkout fall three majors behind.
  • .gitignore — /pkg/, /vendor/. README.md — release process documented.

Verification

  • rubocop --parallel clean; all three YAML files parse.
  • Both guard scripts exercised against matching and mismatching tags.
  • bundle exec rake build produces janus-ar-8.0.1.gem; bundle install leaves the tree clean.
  • bin/release.sh tested end-to-end in a scratch repo: rejects malformed versions, rejects existing tags, handles pre-release suffixes (8.2.0.rc1).
  • rspec not run locally (needs MySQL with the replica/primary users) — CI covers it, and no library code is touched.

Note / open question

Nothing gates publishing on tests: CI runs on main and PRs but not on tag pushes, so publish.yml will ship a gem regardless of whether the suite is green at that commit. In practice tags are cut from an already-tested main. Happy to add an rspec step (plus the MySQL service block) to the publish job, or require the CI run on the tagged SHA to have passed, if you'd rather it were enforced.

Next release

bin/release.sh 8.0.2
git push origin HEAD --follow-tags

🤖 Generated with Claude Code

The publish workflow has never successfully released a gem. The one run
that fired (tag v8.0.1) died in `release:guard_clean` with "There are
files that need to be committed first", and the version was pushed by
hand instead.

Two independent things made the checkout dirty:

  * `Gemfile.lock` records the gem's own version in its PATH section. It
    said 8.0.0 while version.rb said 8.0.1, so `bundle install` in CI
    rewrote it. This would have recurred on every release.
  * `bundler-cache: true` writes `vendor/bundle` (not ignored) and
    rewrites the tracked `.bundle/config`.

The v8.0.1 tag also pointed at a commit where version.rb still said
8.0.0, so even a clean tree would have published the wrong version.

Trusted publishing is already configured on rubygems.org and worked in
that run, so no credential changes are needed. The unused
RUBYGEMS_API_KEY repo secret can be deleted.

Changes:

  * publish.yml installs to the system gem dir instead of using
    bundler-cache, pins ruby 3.4 (was resolving to 4.0.5), fetches full
    history so source_control_push sees the existing tag, and adds
    guards for tag/version mismatch and a dirty tree.
  * bin/release.sh replaces the manual `gem build && gem push` path with
    a bump-commit-tag script that keeps version.rb and Gemfile.lock in
    lockstep.
  * The gemspec was shipping bin/release.sh as an installed executable
    onto users' PATH. Ship no executables, and drop non-runtime files
    from the package (28 files -> 19).
  * Gemfile source http -> https; Gemfile.lock resynced to 8.0.1.
  * Bump actions/checkout v4 -> v7 (v4 runs on deprecated Node 20) and
    add the github-actions ecosystem to dependabot, which was
    bundler-only and let checkout fall three majors behind.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lloydwatkin
lloydwatkin merged commit 84d08cc into main Sep 4, 2026
12 checks passed
@lloydwatkin
lloydwatkin deleted the janus-rubygems-autodeploy branch September 4, 2026 08:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant