Skip to content

[INFRA-478] Upgrade Ruby to 3.4.12 and gemstash to 2.8.2 - #24

Merged
itskingori merged 2 commits into
masterfrom
ruby-3-4-gemstash-2-8
Oct 27, 2026
Merged

itskingori merged 2 commits into
masterfrom
ruby-3-4-gemstash-2-8

Conversation

@itskingori

@itskingori itskingori commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

Upgrades the image from Ruby 2.6.3, which is end of life, to Ruby 3.4.12 with gemstash 2.8.2. This is released as 2.8.2.

Background

The last published image is 1.4.1 from September 2020, and the current Dockerfile no longer builds: gem update --system fails on Ruby 2.6.3 because current RubyGems needs Ruby 3.2 or newer.

Changes

  • Ruby 3.4.12 on ruby:3.4.12-alpine, and gemstash 2.8.2, which needs Ruby 3.1 or newer.
  • Every dependency is relocked for Ruby 3.4. That moves pg to 1.x (0.18 no longer builds), puma to 7, sinatra to 4 and rack to 3. The lockfile targets x86_64-linux-musl and aarch64-linux-musl, so pg and sqlite3 install as precompiled gems. BUNDLE_FROZEN fails the build if the lockfile drifts.
  • The image pins Bundler 4.0.23 instead of upgrading RubyGems to whatever is latest at build time. It also adds yaml-dev for psych, drops the unused su-exec, creates the data directory owned by the gemstash user, and drops --no-daemonize, which gemstash now only warns about.

Every locked gem was released at least 7 days before locking. sqlite3 is held at 2.10.1 because 2.10.2 came out on 26 October. Ruby 3.4.12 itself is newer than that (23 October); I took it anyway because it includes the fix for a regexp security issue.

Risk

Gemstash 2.8 adds one migration, 05_authorization_names, which adds a nullable name column and a unique index to authorizations. It runs automatically on first start against an existing database. Existing API keys keep working. 1.4.1 also starts and serves against the migrated database, so reverting the image tag is a working rollback. Snapshotting the data volume before upgrading is still worthwhile.

Testing

I built the image and ran an upgrade on a SQLite data volume, with RACK_ENV=production, GEMSTASH_PROTECTED_FETCH=true, user 9999 and all capabilities dropped:

  • Seeded the volume with 1.4.1: a fetch/push key, a pushed private gem and a cached public gem.
  • 2.8.2 starts on that volume and applies the migration (schema version 5). /health returns 200 with every check OK.
  • The private gem returns 401 without the key and 200 with the key created under 1.4.1.
  • The cached public gem, a public gem fetched through the proxy for the first time and /specs.4.8.gz all return 200.
  • 1.4.1 starts again on the migrated volume and still serves the private gem.
  • The linux/amd64 build (under emulation) also starts on the migrated volume, passes /health and serves the private gem.

This repository has no CI of its own until the release workflows land, so this PR shows no build checks. The multi-arch test build passed against this branch's Dockerfile in an earlier run: https://github.com/Intellection/docker-gemstash/actions/runs/40500481883

I didn't exercise the MySQL and PostgreSQL compose setups, and their database images are unchanged.

Rollout

Merge this, then the release workflows PR, then tag 2.8.2 on master to publish the image.

Related

References

Ruby 2.6 reached end of life in 2022 and gemstash 2.8 needs Ruby 3.1 or
newer. Every dependency is relocked for Ruby 3.4, which moves pg to 1.x
(0.18 no longer builds), puma to 7, sinatra to 4 and rack to 3. The
lockfile targets the two musl platforms so pg and sqlite3 install as
precompiled gems, and BUNDLE_FROZEN fails the build if it drifts.

Gemstash 2.8 adds one migration that adds a nullable name column to
authorizations. It runs on start, and 2.0 keeps working against the
migrated database, so reverting the image remains a rollback path.

The image now pins Bundler 4.0.23 instead of updating RubyGems to
whatever is latest, adds yaml-dev for psych, drops the unused su-exec
and creates the data directory owned by the gemstash user. The removed
--no-daemonize flag only prints a warning now, so it is dropped too.
@itskingori
itskingori force-pushed the ruby-3-4-gemstash-2-8 branch from 003f866 to 6ee1e76 Compare October 27, 2026 13:13
@itskingori
itskingori marked this pull request as ready for review October 27, 2026 13:53
@itskingori
itskingori requested a review from a team as a code owner October 27, 2026 13:53
@itskingori
itskingori merged commit c3df9c5 into master Oct 27, 2026
1 check passed
@itskingori

Copy link
Copy Markdown
Member Author

Configuration review between gemstash 2.0.0 and 2.8.2, for the record.

Every key in app/config.yml.erb keeps its meaning in 2.8.2: base_path, cache_type, rubygems_url, bind, protected_fetch, fetch_timeout, puma_threads, db_adapter, db_url and db_connection_options. log_file: :stdout is still handled in Gemstash::Env#log_file, and Puma still defaults to one worker in cluster mode.

Behaviour that changes without any configuration change:

  • Puma request logging is on (log_requests in lib/gemstash/puma.rb), so every request writes an access-log line to stdout.
  • A new open_timeout, defaulting to 2 seconds, bounds opening the connection to the upstream gem server. Previously only fetch_timeout (20 seconds) applied, so a slow connect now fails sooner. It can be raised with :open_timeout if needed.
  • cache_expiration and cache_max_size are now configurable. They default to 30 minutes and 500 entries, the values 2.0.0 hard-coded, so caching is unchanged.
  • The Puma environment now defaults to production when neither APP_ENV nor RACK_ENV is set.

The only schema change is the 05_authorization_names migration described above.

@itskingori
itskingori deleted the ruby-3-4-gemstash-2-8 branch October 27, 2026 15:13
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