Skip to content

Release from a workflow, changelog from the release tags - #2257

Merged
soutaro merged 5 commits into
masterfrom
claude/steep-release-process-etf9u0
Aug 6, 2026
Merged

Release from a workflow, changelog from the release tags#2257
soutaro merged 5 commits into
masterfrom
claude/steep-release-process-etf9u0

Conversation

@soutaro

@soutaro soutaro commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Ports the release-process improvements recently made in ruby/rbs (ruby/rbs#3047, ruby/rbs#3048, ruby/rbs#3053–#3058, ruby/rbs#3061–#3064) to Steep. A release becomes a pull request and one workflow run: nothing is built or pushed from a laptop, and no personal RubyGems credentials are involved. The changelog is derived from the commits since the previous release tag instead of milestones; pull requests labeled skip-changelog are left out, which is the label the automated PRs now carry — it has to be created in this repository before merging.

claude added 5 commits August 6, 2026 01:55
`rake changelog` searched for pull requests by milestone, which only works
while the milestone is maintained by hand and says nothing about what is
actually merged into the branch being released.

Replace it with `rake gem:changelog[version]`, which walks the commits
between the given version (derived from `Steep::VERSION` by default) and
`HEAD`, and asks GitHub which pull request each commit came from. Going
through `associatedPullRequests` rather than parsing commit messages means
every merge strategy works, and commits pushed straight to the branch are
left out on their own.

Where the changelog starts is the step that is easy to get wrong by hand: a
prerelease documents what changed since the previous prerelease, so it starts
from the latest tag, while a release proper documents the whole cycle and has
to skip the prerelease tags in between. Both follow from `Steep::VERSION`.

Pull requests labeled `skip-changelog` are omitted, and the omitted ones are
reported so they do not disappear silently. Commits that record a
`git cherry-pick -x` origin are attributed to the pull request the change was
written in rather than to the one that carried the backport.

Only the changelog itself goes to STDOUT, so the output can be piped.
`rake gem:changelog:json` prints the same pull requests with the changed
files, labels, and body of each, as the input for classifying them into the
sections of CHANGELOG.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qm8vCN39XbRnUumtJKaXfR
Milestones were the input to `rake changelog`, which now derives the list
from the commits between the previous release tag and `HEAD`. Nothing reads
a milestone any more, so requiring one on every pull request -- and the
`no-milestone` label for the ones that legitimately have none -- is upkeep
with no consumer.

The automated pull requests that carried `no-milestone` now carry
`skip-changelog` instead, which is the label the changelog generation
actually looks at, and dependabot labels its own pull requests with it too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qm8vCN39XbRnUumtJKaXfR
README had the maintainer release from a laptop: bump `version.rb`, run
`bundle exec rake release`, and let the enhanced task chain draft the GitHub
release and label the released pull requests. That puts the RubyGems
credentials of whoever runs it in the path, and publishes whatever state the
working copy happens to be in.

`Release gem` does it instead. Dispatched with the commit being released and
the version that commit declares, it builds the gem, checks it, pushes it to
RubyGems through trusted publishing, and publishes the GitHub release. A
release is now a pull request and one workflow run.

The two inputs state the same fact twice -- once as a commit, once as a name
-- and the run stops before anything is built unless they agree with each
other and with the repository, so dispatching the wrong commit, or the right
one under the wrong name, is a failed run rather than a gem that has to be
yanked.

The built gem is installed the way a user would install it and used to type
check a small project, one that has to pass and one that has to fail: the
gemspec filters `git ls-files` by hand, so a missing file or a broken
dependency only shows when the installed gem actually runs.

Publishing is ordered so that the reversible step always comes first. The
tag is created by the workflow once the gem is known to build and run; the
artifact is uploaded before the push, so a failed push still leaves the gem
behind; and the GitHub release comes last, so a failed push never announces
a release that has no gem. `dry_run` stops after the artifact, which is how
a release is rehearsed.

`gem:check_release`, `gem:tag`, and `gem:gh_release` are what the workflow
runs; all three work locally too, which is the fallback if a release ever
has to be assembled by hand. `gem:gh_release` publishes rather than drafts:
the notes are the CHANGELOG.md section that was already reviewed in the
release pull request, so there is nothing left to edit. `.dev.N` versions
are not written up, so they get a tag and a gem and nothing else.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qm8vCN39XbRnUumtJKaXfR
`rake release` published from a working copy, `release:note` and
`release:github` drafted what `gem:gh_release` now publishes, and
`release:release-prs` labeled pull requests `Released` so that the
milestone-based `rake changelog` could exclude them -- a consumer that is
gone now that the changelog is derived from the release tags. README pointed
at `rake release`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qm8vCN39XbRnUumtJKaXfR
How a release is prepared and cut, what the three kinds of release are, what
the version on `master` means, how a new minor is started, and how the
changelog is assembled -- with `rake gem:changelog`, or with the GitHub MCP
tools from a session where `gh` cannot reach the API.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qm8vCN39XbRnUumtJKaXfR
@soutaro soutaro added the skip-changelog PRs not included in CHANGELOG label Aug 6, 2026
@soutaro
soutaro merged commit 6732575 into master Aug 6, 2026
22 checks passed
@soutaro
soutaro deleted the claude/steep-release-process-etf9u0 branch August 6, 2026 02:19
@soutaro soutaro mentioned this pull request Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-changelog PRs not included in CHANGELOG

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants