Skip to content

feat(ruby): read .ruby-version when resolving the version - #290

Merged
CalvinAllen merged 1 commit into
mainfrom
feat/ruby/version-file
Oct 22, 2026
Merged

CalvinAllen merged 1 commit into
mainfrom
feat/ruby/version-file

Conversation

@CalvinAllen

Copy link
Copy Markdown
Contributor

Description

dtvem now reads .ruby-version when resolving the Ruby version. It's the file rbenv, chruby and ruby/setup-ruby use to pin a project's Ruby. A repository that pins Ruby for CI, or already carries the file for rbenv or chruby, gets the same version on a dtvem machine without a second, dtvem-specific file.

It reuses the version-file mechanism from #286 and #288.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code refactoring (no functional changes)
  • Performance improvement
  • Test coverage improvement
  • CI/CD or build process change

Related Issues

Resolves #285
Relates to #283, #284, #287

Changes Made

  • Where and precedence: the same upward walk as .dtvem/runtimes.json, so subdirectories inherit the file. The nearest directory wins. Within one directory .dtvem/runtimes.json comes first, then .ruby-version, and the global version stays the fallback.
  • Syntax:
    • An exact (3.3.6) or partial (3.3) version, with the optional ruby- prefix (ruby-3.3.6) that rbenv strips and setup-ruby reads.
    • An exact pre-release, x.y.z-previewN or x.y.z-rcN, since dtvem's Ruby manifest includes previews (3.5.0-preview1, 4.0.0-preview2). A partial pre-release such as 4.0-preview2 is rejected.
    • Blank lines and # comments are skipped, and the first word of the first remaining line is used.
  • Unsupported forms are reported, not replaced by the global version:
    • other engines (jruby-9.4.5.0, truffleruby-24.1.0)
    • engine-only names (ruby, truffleruby)
    • system
    • anything else dtvem can't install, e.g. old patch levels like 2.0.0-p648
  • Release over preview: internal/version now ranks a pre-release below the release it precedes. Before, 4.0.0-preview2 and 4.0.0 compared equal, so with both available, a partial 4.0 or 4 could pick either.
    • The fix applies to version files and to dtvem install ruby 4.0, which uses the same matcher.
    • A partial version still picks a pre-release when that's the only match (3.5 → 3.5.0-preview1). That keeps a version file consistent with the dtvem install ruby 3.5 command the shim suggests: excluding previews only on the resolution side would leave that install unrecognized.
    • It also fixes 23.0.0-rc.1-style versions, where the digits after rc. used to count as a fourth version part and ranked the pre-release above its release.
  • Rules: versionFileRules gains prereleases, set only for Ruby. Node.js and Python still reject pre-release suffixes.
  • Not installed / visibility: as for the other runtimes. The shim names the file and suggests dtvem install ruby <version>, and dtvem current shows the file in its Source column. gem and the other Ruby shims follow the file too.
  • Docs: README feature line, dtvem current --help, and the CLAUDE.md "Version Resolution" section.

Testing

Test Environment

  • OS: Windows 11
  • Go Version: 1.25.4 (module targets 1.23)
  • dtvem Version: built from this branch; resolved against a real 0.6.5 install with Ruby 3.3.9 and 3.4.7 installed, 3.4.7 global

Test Steps

  1. ./rnr check: gofmt, golangci-lint (0 issues), and all tests pass.
  2. src/internal/version/matcher_test.go:
    • Comparison cases for pre-releases: release vs. its preview, a later patch vs. a preview, preview vs. older release, preview ordering, rc after preview, rc.1.
    • A partial resolution test that runs with both list orders, since the old tie depended on order.
  3. src/internal/config/versionfile_test.go:
    • Ruby parsing: exact, ruby- prefix, partial, preview, rc with prefix, comments.
    • Every unsupported form listed in the issue.
    • Pre-releases are rejected for Node.js and Python.
    • Resolution against installed versions that include 3.5.0-preview1, 4.0.0-preview2 and 4.0.0: 4.0 and 4 pick 4.0.0, 3.5 picks the preview, and an exact preview resolves as written.
    • Precedence against runtimes.json, and an unsupported file not falling back to the global version.
  4. End-to-end: built the CLI and shim into a temp directory, put the shim there as ruby.exe and gem.exe, and ran them against the real install from temp project directories.

Test Results

In the directory ruby -e 'puts RUBY_VERSION'
no file 3.4.7 (global)
.ruby-version = 3.3.9 3.3.9
ruby-3.3.9 3.3.9
# CI pin + 3.3, run from lib/deep 3.3.9
.dtvem/runtimes.json = 3.3.9 + .ruby-version = 3.4.7 3.3.9
jruby-9.4.5.0 error naming the file, exit 1
system error naming the file, exit 1
4.0.0-preview2 (not installed) "configured but not installed", Set by: the file, dtvem install ruby 4.0.0-preview2, exit 1
3.2 (not installed) "configured but not installed", Set by: the file, dtvem install ruby 3.2, exit 1

With .ruby-version = 3.3, gem env reported Ruby 3.3.9, and dtvem current ruby showed 3.3.9 with the file as its Source. Preview-over-release isn't covered end to end, because no 4.0 versions are installed locally; the unit tests cover it with installed-version fixtures.

Checklist

  • My code follows the Google Go Style Guide
  • I have run golangci-lint run and fixed all issues (includes gofmt, govet, and 40+ linters)
  • I have run go mod tidy to clean up dependencies (no dependency changes in this PR)
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes (go test ./...)
  • I have commented my code, particularly in hard-to-understand areas
  • I have updated the documentation (README.md, CONTRIBUTING.md, etc.) if needed
  • My changes generate no new warnings or errors
  • I have checked my code works on multiple platforms (Windows, macOS, Linux) if applicable (tested on Windows; CI covers macOS and Linux)
  • I have added appropriate error handling
  • I have followed conventional commit format in my commit messages

Breaking Changes

None intended. As with the other version files, a .ruby-version in any parent directory now affects the Ruby shims. One holding system or another engine, such as an rbenv or chruby user's ~/.ruby-version, makes them stop with an error where they used to run the global version.

Additional Notes

Out of scope, as the issue lists: the Gemfile ruby directive, mise.toml, and .tool-versions (#287, next).

Version resolution now reads .ruby-version, the file rbenv, chruby and
ruby/setup-ruby use to pin a project's Ruby version, through the same
upward walk and precedence as the Node.js and Python version files.
Within one directory .dtvem/runtimes.json still comes first.

- An exact (3.3.6) or partial (3.3) version is accepted, with the
  optional ruby- prefix rbenv strips and setup-ruby reads. A partial
  version resolves to the highest installed match.
- An exact pre-release (4.0.0-preview2, 3.4.0-rc1) is accepted too,
  since dtvem can install Ruby previews.
- Other engines (jruby-..., truffleruby-...), engine-only names, system,
  and anything else dtvem cannot install are reported as unsupported
  instead of falling back to the global version.

Version comparison now ranks a pre-release below the release it
precedes. Before, "4.0.0-preview2" and "4.0.0" compared equal, so a
partial "4.0" could pick either one, both in version files and in
dtvem install. A partial version still picks a pre-release when it is
the only match ("3.5" -> "3.5.0-preview1"), so a version file and the
dtvem install command it suggests agree.
@CalvinAllen
CalvinAllen merged commit 78c0003 into main Oct 22, 2026
20 checks passed
@CalvinAllen
CalvinAllen deleted the feat/ruby/version-file branch October 22, 2026 19:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

feat(ruby): read .ruby-version when resolving the version

1 participant