Skip to content

Release 1.0.1 - #10

Merged
seifertd merged 9 commits into
masterfrom
release/1.0.1
Jul 24, 2026
Merged

Release 1.0.1#10
seifertd merged 9 commits into
masterfrom
release/1.0.1

Conversation

@seifertd

Copy link
Copy Markdown
Owner

Prepares the 1.0.1 patch release and modernizes the build/release tooling.

Release contents (1.0.1)

Two bug fixes plus an internal search optimization, all backwards-compatible — semver PATCH:

  • Range queries now visit every subtree that can contain an in-range key (previously omitted values in later subtrees).
  • Duplicate keys promoted into an internal node by a split are now rejected instead of being inserted twice.
  • Keys are located by a hybrid linear/binary search.
  • LINEAR_SCAN_LIMIT made a private constant so it stays out of the public API (keeps this a clean patch bump).

Tooling

  • Removed Travis CI (defunct; pinned rvm 2.4.1) and added a GitHub Actions workflow testing Ruby 3.2–3.4.
  • Dropped shoulda-matchers / the activesupport tree — the suite only uses shoulda-context's DSL. Lockfile went 30 → 6 gems.
  • Upgraded shoulda-context to 2.0.
  • Replaced Bones with a plain checked-in gemspec and explicit rake tasks. Bones still required net/smtp (gone from stdlib in 3.1) and pinned rdoc two majors back.
  • Hardened the release path: release:check aggregates pre-flight checks (clean tree, HEAD on a remote, no existing tag/version, History.txt entry, credentials present); release:verify installs the packaged gem into a throwaway GEM_HOME and smoke-tests it; release:push is irreversible-by-design with a typed confirmation and tag-before-push ordering. Gemspec declares rubygems_mfa_required.

Verification

rake test green throughout (17 runs, 92 assertions). The built gem was installed into an isolated GEM_HOME and exercised (Btree.version, range queries, lookups). This PR is also the first run of the new CI workflow.

🤖 Generated with Claude Code

seifertd and others added 9 commits July 23, 2026 09:25
Two bug fixes and an internal search optimization have landed since v1.0.0.
No public API was added, changed, or removed, so this is a patch release
under semver.

Make LINEAR_SCAN_LIMIT a private constant. It is a tuning value used only by
Node#key_index, but Ruby constants are public by default, so leaving it
exposed would have made this a minor release and locked the value into the
public API. Doing it now avoids a breaking change later.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
travis-ci.org shut down in 2021 and the config still pinned rvm 2.4.1, so
nothing has been running it. Its deploy block was also the only automated
path to RubyGems; releases are now manual via `rake gem:release`.

Drop the now-dead .travis.yml entry from the Bones exclude list as well.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The tests use the context/should DSL, which comes from shoulda-context. The
shoulda meta-gem also pulls in shoulda-matchers, whose Rails model assertions
the suite never uses, and which depends on activesupport. Depending on
shoulda-context directly takes the lockfile from 30 gems to 14, dropping
activesupport, i18n, tzinfo, concurrent-ruby, connection_pool and the rest of
that tree.

shoulda-matchers has been pinned at 3.1.3 (2016) by shoulda's ~> 3.0
constraint, and was emitting method-redefinition warnings on every run. Those
are now gone.

This does not change the supported Ruby range: 3.2 remains the floor, now set
by minitest 6, erb 6 and bundler 4 rather than by activesupport 8.1. These are
all development dependencies, so the published gem is unaffected either way --
it still declares no runtime dependencies.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replaces the Travis config removed in the previous commit. Runs the suite on
Ruby 3.2, 3.3 and 3.4 for pushes to master and for pull requests.

Exclude .github from the gem manifest, for the same reason .gitignore and
.ruby-version are excluded.

Also exclude ^pkg/. Setting exclude at all replaces the Bones defaults, one of
which was ^pkg/, so build products from previous releases sitting in the
working tree were being swept into the manifest -- the 1.0.0 gem, its tarball
and its unpacked tree were all listed in s.files, and two pkg/ paths had ended
up in extra_rdoc_files. Releasing from a tree with a populated pkg/ would have
shipped the old gem inside the new one.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Everything else already resolved to its latest release. The one exception is
rdoc, held at 6.17.0 by bones' `rdoc (~> 6.0)` pin -- 8.0.0 is out but is not
reachable while bones is the build tool.

The suite passes unchanged on shoulda-context 2.0, and the major bump clears
the last "assigned but unused variable" warning from the test run. It declares
no required_ruby_version, so the 3.2 floor and the CI matrix are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bones 3.9.0 is the end of its line: it still requires net/smtp, which left the
stdlib in Ruby 3.1, so every rake invocation opened with load errors. It also
pinned rdoc to ~> 6.0, holding the project two majors back, and its exclude
list was what let build products under pkg/ leak into the gem manifest.

btree.gemspec is now checked in and hand-written. It reads version.txt so that
stays the single source of truth for both the package and Btree.version, and
it builds its file list from git ls-files, which makes the pkg/ leak
structurally impossible rather than a matter of remembering an exclude entry.
It also declares the MIT license the README always claimed -- Bones was
shipping licenses = [nil] -- and required_ruby_version >= 2.3.0, the floor set
by Array#bsearch_index in Node#key_index.

The Rakefile gains explicit publishing tasks. release:check refuses to proceed
on a dirty tree, an existing tag, a version already on rubygems.org, or a
missing History.txt entry, and warns when the branch has drifted from
origin/master. release:tag and release:push are separately invokable for
recovery when one half of a release fails. The full release task runs the
checks and the suite before either.

release:push depends on repackage rather than build. The packaged gem is a
file task, so an existing pkg/*.gem newer than its sources satisfies build
without rebuilding, which is harmless locally and wrong when the next step is
an irreversible push.

Dropping bones takes the lockfile from 14 gems to 6. The Gemfile now uses the
gemspec directive, and CI moves from rake test:run to rake test.

Verified by installing the built gem into a throwaway GEM_HOME: Btree.version
resolves version.txt from the installed layout and queries return correctly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ordering: everything reversible now happens before anything that is not. A
tag is cheap to delete, so it goes first; the gem push is permanent -- RubyGems
refuses to re-push a version even after a yank -- so it goes last, and only
after the packaged gem has been built and exercised. An earlier note in review
had this backwards.

release:check now reports every problem at once instead of aborting on the
first, and gained two. It requires HEAD to exist on some remote branch, so a
published gem can never be built from work that only exists on one laptop. And
it checks for rubygems credentials up front, because the alternative is
discovering a missing or wrongly-scoped key as a 403 after the tag is already
pushed. Branch drift stays a warning rather than an abort, so releasing from a
release branch is still possible.

release:verify is new: it rebuilds the gem, prints the manifest, installs it
into a throwaway GEM_HOME and runs a smoke test against the installed copy
with Bundler's environment stripped. This is the only check that covers what
was actually packaged rather than what happens to sit in the working tree -- a
file missing from the manifest looks perfectly fine locally and only breaks
for whoever installs it.

release:push now states plainly that the push is irreversible and requires the
version number to be typed back before proceeding. In a non-interactive shell
it refuses outright unless FORCE=1, rather than hanging on a prompt nobody can
answer. release:confirm checks afterwards that the version really is live.

The gemspec declares rubygems_mfa_required, so a leaked API key is no longer
sufficient to publish on its own. This requires MFA enabled on the
rubygems.org account or the push is rejected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
release:check hardcoded ~/.gem/credentials, but modern RubyGems stores the key
at the XDG path (~/.local/share/gem/credentials), so a signed-in machine was
reported as having no credentials. Ask Gem.configuration.credentials_path
instead of guessing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v4 targets Node 20, which GitHub is deprecating and now forces onto Node 24,
producing a warning annotation on every job. v5 runs on Node 24 natively.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@seifertd
seifertd merged commit 1380789 into master Jul 24, 2026
3 checks passed
@seifertd
seifertd deleted the release/1.0.1 branch July 24, 2026 03:37
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