From 34b118830781688ead0531622eb2fd048bb0f148 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 15:25:18 +0000 Subject: [PATCH 1/3] Say what the version on `master` means A bare `X.Y.0.dev` means `X.Y.0` is being developed; any other version means the one after it is. So a release needs no follow-up bump, and starting a new minor is the only version change made by hand. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01PhJnDiMKwunqnLpuXzRY7v --- docs/release.md | 74 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 6 deletions(-) diff --git a/docs/release.md b/docs/release.md index 9719479ca3..a2cd6c6576 100644 --- a/docs/release.md +++ b/docs/release.md @@ -36,6 +36,14 @@ is what lets you dispatch the workflow. ## Steps +The release pull request in step 1 is merged by a person who has reviewed it. Its merge commit is +what step 2 dispatches, tags, and pushes to RubyGems, and none of that can be taken back — so +prepare that pull request and stop there, rather than merging it and carrying on to step 2. + +The bump that starts a new minor is the only other pull request that sets `RBS::VERSION`. It +publishes nothing and another bump undoes it, so one opened on an explicit request can go through +on its own. + ### 1. Prepare the release Open a pull request that carries everything the release needs: @@ -142,13 +150,67 @@ Checking the `dry_run` box runs everything up to the artifact and stops — no t no release — which is how the build is exercised without releasing. `version` still has to match the commit, so a dry run is also how a release is rehearsed before it is cut. -### 3. Start the next development cycle +## The version on `master` -Open another pull request setting `RBS::VERSION` to the next prerelease (`4.1.1` → `4.1.2.pre`), -with `Gemfile.lock` regenerated, labeled `skip-changelog` like the release pull request itself. -Without it the version on `master` keeps claiming to be the released version for the whole -development period, and `rake gem:changelog` reads that version to decide where the next changelog -starts. +`RBS::VERSION` on `master` is read one of two ways, told apart by how the version ends: + +| On `master` | Means | +| --- | --- | +| `X.Y.0.dev` — a bare `.dev` | `X.Y.0` is being developed | +| A complete version — `X.Y.Z`, `X.Y.Z.pre.N`, `X.Y.Z.dev.N` | The version *after* the one named is being developed | + +So `4.1.1` on `master` is not a claim that `master` is 4.1.1. It says 4.1.1 has shipped and what +comes after it is being worked on. `4.1.2.dev.1` says the same thing about itself: that release is +out, and the line continues towards 4.1.2. + +Both become true the moment the release is tagged, so **nothing has to be done to `master` after a +release**. `4.0.1` was followed by `4.0.2` with no version change in between, and `4.1.2.dev.1` is +what `master` carries today. + +The bare `X.Y.0.dev` is the exception because it is the one version that names a target rather than +a predecessor: a new minor is developed towards `X.Y.0` for a long time, before it is known whether +the next thing to ship is `X.Y.0.pre.1` or `X.Y.0` itself. Setting it is the only version change +that has to be made deliberately. + +`rake gem:changelog` reads `RBS::VERSION` too, to decide where the next changelog starts — but the +version is set to the one being released before the changelog is generated, so it sees that rather +than whatever `master` was carrying. + +## Starting a new minor + +`master` is the development line of one minor at a time. Moving it from `X.Y` to `X.(Y+1)` is not +part of any one release — it is the decision that the `X.Y` line is done, taken whenever that +becomes true — and it is the one moment the version on `master` is changed by hand. Two changes, in +opposite places: + +1. **Branch the line being left behind**, from the last `master` commit that belongs to it: + + ```console + $ git switch --create aaa-X.Y.x + $ git push -u origin aaa-X.Y.x + ``` + + Branch from the commit *before* the bump below, so the branch keeps the version its line was + released under. Patch releases of `X.Y` are cut from here from now on, with their changes + cherry-picked from `master` — see [Backports](#backports). The `aaa-` prefix carries no meaning + beyond sorting the release branches to the top of the branch list. + +2. **Bump `master`** to `X.(Y+1).0.dev`, in a pull request with `Gemfile.lock` regenerated and + labeled `skip-changelog` like the release pull request itself. `4.1` was started exactly this + way: `aaa-4.0.x` was branched at the commit before `Start 4.1 development`, which set + `RBS::VERSION` to `4.1.0.dev`. + +Two loose ends that are easy to forget: + +- **The release note of the new line.** `rake gem:gh_release` links every published release to + `https://github.com/ruby/rbs/wiki/Release-Note-X.Y`, built from the version number without + checking that the page is there. Nothing has to be written when the line starts — the page comes + together as the first release proper of the line comes into view — but it does have to exist by + the time that release is published, or its notes link to an empty page. +- **Release branches that are done.** A branch is worth keeping only while its line might still + get a patch. The ones that exist do not cover every line that ever had one — `3.8.1` shipped and + there is no `aaa-3.8.x` — so this is housekeeping rather than a rule, but starting a new minor is + the natural moment to look at the bottom of the branch list and delete what has been superseded. ## Backports From 1da55ae9b58341668e2e44ffd74a1918b3549eeb Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 15:25:27 +0000 Subject: [PATCH 2/3] Remove the release path that predates the release workflow `rake release` published from a working copy and built no `-java` gem, and `release:note` drafted the GitHub release that `gem:gh_release` now publishes. README pointed at both. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01PhJnDiMKwunqnLpuXzRY7v --- README.md | 2 +- Rakefile | 67 ------------------------------------------------------- 2 files changed, 1 insertion(+), 68 deletions(-) diff --git a/README.md b/README.md index ec2773c091..85e758b4f5 100644 --- a/README.md +++ b/README.md @@ -196,7 +196,7 @@ Here is a list of some places you can talk with active maintainers. After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. -To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). +To install this gem onto your local machine, run `bundle exec rake install`. Releases are cut by the `Release gems` workflow rather than from a working copy -- see [docs/release.md](docs/release.md). ### C Code Formatting diff --git a/Rakefile b/Rakefile index d8da5994c7..98cfe8d389 100644 --- a/Rakefile +++ b/Rakefile @@ -438,73 +438,6 @@ task :test_generate_stdlib do sh "ruby -c /tmp/Thread_Mutex_test.rb" end -Rake::Task[:release].enhance do - Rake::Task[:"release:note"].invoke -end - -namespace :release do - desc "Explain the post-release steps automatically" - task :note do - version = Gem::Version.new(RBS::VERSION) - major, minor, patch, *_ = RBS::VERSION.split(".") - major = major.to_i - minor = minor.to_i - patch = patch.to_i - - puts "🎉🎉🎉🎉 Congratulations for **#{version}** release! 🎉🎉🎉🎉" - puts - puts "There are a few things left to complete the release. 💪" - puts - - if patch == 0 || version.prerelease? - puts "* [ ] Update release note: https://github.com/ruby/rbs/wiki/Release-Note-#{major}.#{minor}" - end - - if patch == 0 && !version.prerelease? - puts "* [ ] Delete `RBS XYZ is the latest version of...` from release note: https://github.com/ruby/rbs/wiki/Release-Note-#{major}.#{minor}" - end - - puts "* [ ] Publish a release at GitHub" - puts "* [ ] Make some announcements on Twitter/Mustdon/Slack/???" - - puts - puts - - puts "✏️ Making a draft release on GitHub..." - - content = File.read(File.join(__dir__, "CHANGELOG.md")) - changelog = content.scan(/^## \d.*?(?=^## \d)/m)[0] - changelog = changelog.sub(/^.*\n^.*\n/, "").rstrip - - notes = <> Done! Open #{output.chomp} and publish the release!" - end - end -end - - # Pull requests with one of these labels are omitted from the changelog. CHANGELOG_SKIP_LABELS = ["skip-changelog"] From a8913a884acb5ae09f28c75d99a2e63cc27eedc8 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 15:25:36 +0000 Subject: [PATCH 3/3] Document the JRuby container, and correct what pins the jar versions `Dockerfile.jruby` was not mentioned in the wasm README. Chicory and ASM are pinned once, in `rbs.gemspec`, not in the Dockerfile as docs/release.md claimed. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01PhJnDiMKwunqnLpuXzRY7v --- docs/release.md | 7 +++++-- wasm/README.md | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/docs/release.md b/docs/release.md index a2cd6c6576..319c7a0ab6 100644 --- a/docs/release.md +++ b/docs/release.md @@ -232,8 +232,11 @@ that is why the 4.0.3 changelog credits its three entries to the same pull reque - Prereleases (`X.Y.Z.pre.N`) are only installed with `gem install rbs --pre`; a plain `gem install rbs` is unaffected. On JRuby, `gem install rbs [--pre]` resolves to the `-java` gem automatically. -- `Dockerfile.jruby` pins the WASI SDK / Chicory / ASM versions to match the - `wasm`, `jruby`, and `release-gems` workflows. Keep them in sync when bumping. +- The WASI SDK version is pinned in `wasm.yml`, `jruby.yml`, `release-gems.yml`, and + `Dockerfile.jruby`, each carrying its own copy. Keep them in sync when bumping. The + Chicory/ASM versions are not duplicated: they are the `jar` requirements in + `rbs.gemspec`, which is where the workflow, `Dockerfile.jruby` and `gem install` all + read them from. - `rake 'gem:check_release[X.Y.Z]'` and `rake gem:tag` are what the workflow runs to check the release and to create the tag. Both work locally, which is the fallback if the tag ever has to be created by hand. diff --git a/wasm/README.md b/wasm/README.md index 89a652cfde..c1993b19fd 100644 --- a/wasm/README.md +++ b/wasm/README.md @@ -27,6 +27,39 @@ $ rake wasm:install_jars # download the Chicory/ASM jars into ~/.m2 (run on JRub The compiled `rbs_parser.wasm` is a build artifact and is not checked in. +The WASI SDK is needed for the *build*, not for running the result — the host clang already +knows the `wasm32` target, but there is no wasm32 libc on a normal machine, so it picks up the +host headers and fails on the first `#include`. That is what the SDK supplies, along with the +builtins the link step needs. + +## Running the suite on JRuby + +[`Dockerfile.jruby`](../Dockerfile.jruby) builds an image that has everything this needs, so no +JRuby, JDK or WASI SDK has to be installed to work on the JRuby side: + +```console +$ docker build -f Dockerfile.jruby -t rbs-jruby . +$ docker run --rm rbs-jruby # run the test suite +$ docker run --rm -e RBS_PLATFORM=java rbs-jruby \ + gem build rbs.gemspec # build the -java gem +``` + +Two things in it are not obvious: + +- `build-essential` is for prism, which builds `libprism.so` and loads it through FFI on JRuby + rather than as an MRI C extension. It needs `cc` and `make`. +- Bundler is skipped. The development `Gemfile` pulls in CRuby-only C extensions (bigdecimal, + stackprof, …) that cannot build on JRuby, so the few gems the suite needs are installed + directly, in the same set as [`jruby.yml`](../.github/workflows/jruby.yml). + +The image compiles `rbs_parser.wasm` itself, which is why it carries the WASI SDK. That is not +the only arrangement: the build needs the SDK but not JRuby, and running the suite needs JRuby +but not the SDK, so `jruby.yml` splits them instead — it compiles the module on CRuby and then +switches engines to test against the result. + +`rake wasm:install_jars` is the step that has to be on JRuby either way: it resolves the `jar` +requirements from `rbs.gemspec` through the JVM. + ## Exported functions The module is built as a "reactor": it has no `main`, and the host calls