Repository navigation
feat(node): read .node-version and .nvmrc when resolving the version - #286
Merged
Merged
Conversation
Version resolution now reads the files the rest of the Node ecosystem uses to pin a project's version, so a repository that pins Node for CI with actions/setup-node gets the same version on a dtvem machine without a second, dtvem-specific file. - The files are read in the same upward walk as .dtvem/runtimes.json; the nearest directory that sets a version wins. Within one directory, runtimes.json comes first, then .node-version, then .nvmrc. - A file holds an exact (22.12.0) or partial (22, 22.12) version, with an optional leading v. Blank lines and # comments are skipped. A partial version resolves to the highest installed match. - Anything else, such as an .nvmrc alias like lts/*, or an empty file, is reported rather than silently replaced by the global version. - A version that is not installed names the file that asked for it. - dtvem current shows the file each version came from, and installs a partial version as its latest available match. config.Resolve returns the version together with its source file, and config.ErrNoVersion separates "nothing configured" (the shim falls back to PATH) from a version file it cannot use (the shim stops). Version files are listed per runtime in config.versionFiles so other runtimes can add theirs.
This was referenced Oct 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
dtvem now reads
.node-versionand.nvmrcwhen resolving the Node.js version. These are the filesactions/setup-node(vianode-version-file), fnm, nvm and nodenv use. A repository that pins Node for CI gets the same version on a dtvem machine, without a second, dtvem-specific file.Type of Change
Related Issues
Resolves #283
Relates to #284, #285 (the same support for
.python-versionand.ruby-version, which can reuse this mechanism)Changes Made
Where: the files are read in the same upward walk as
.dtvem/runtimes.json, so subdirectories inherit them. The nearest directory that sets a version wins. Within one directory the order is.dtvem/runtimes.json, then.node-version, then.nvmrc. The global version is still the fallback.Syntax: a file can hold an exact version (
22.12.0) or a partial one (22,22.12), with or without a leadingv. Whitespace, blank lines and#comments are ignored, and the first version line is used. A partial version resolves to the highest installed match, compared numerically:22never matches220.x.Unsupported content:
.nvmrcaliases (lts/*,lts/jod,node), anything else that isn't a version number, and empty files are reported, not silently replaced by the global version. The shim stops with:✗ …\.nvmrc asks for "lts/*": only version numbers (x, x.y or x.y.z) are supported, not aliasesNot installed: the shim's error now includes
Set by: <file>above thedtvem install node <version>hint. For a partial version that matches nothing installed, the hint isdtvem install node 22, whichinstallalready resolves to the latest 22.x.Visibility:
dtvem currenthas a new Source column naming the file each version came from, including the globalruntimes.json. Its install prompt resolves a partial version against the available versions before installing.whichandwhereprint a version-file error as itself, not as "No version configured".API:
config.Resolvereturns aResolution{Version, Source}. Its errors fall into three groups:config.ErrNoVersion: nothing is configured, so the shim falls back to PATH as before.*config.VersionFileError: a file names something dtvem can't use.ResolveVersionandLocalVersionkeep their signatures. Version files are listed per runtime inconfig.versionFiles, so Python and Ruby each need one entry (plus any runtime-specific parsing).Docs: README feature line and the CLAUDE.md "Version Resolution" section.
Testing
Test Environment
Test Steps
./rnr check: gofmt, golangci-lint (0 issues), and all tests pass.src/internal/config/versionfile_test.go:vprefix, CRLF, blank lines, comments, aliases, empty files, malformed versions.DTVEM_ROOTand project tree: each file on its own, subdirectory inheritance, precedence within one directory, nearest directory wins in both directions, partial matching against installed versions, unsupported or empty files not falling back (even with a valid pin further up or a global version set), global fallback,ErrNoVersion, version files being per-runtime, andLocalVersion/ResolveVersion.src/cmd/current_test.gocoversinstallVersion: an exact version installs as given, a partial one installs the latest available match, and no match installs nothing.node.exe, and ran it against the real install from temp project directories.Test Results
node --versionv25.2.0(global).node-version=22.0.0v22.0.0.nvmrc=22.0.0v22.0.0.node-version=v22.0.0, run fromsub/deepv22.0.0.node-version=22(blank lines + padding)v22.12.0.nvmrc=22.0v22.0.0.node-version=22.0.0+.nvmrc=25v22.0.0.dtvem/runtimes.json=22.12.0+.node-version=22.0.0v22.12.0runtimes.json=22.12.0, child.nvmrc=22.0.0v22.0.0.nvmrc=lts/*.node-version.node-version=20.11.1(not installed)Set by:the file, install hint, exit 1Checklist
golangci-lint runand fixed all issues (includes gofmt, govet, and 40+ linters)go mod tidyto clean up dependencies (no dependency changes in this PR)go test ./...)Breaking Changes
None intended, but there are two behaviour changes worth knowing:
.node-versionor.nvmrcin any parent directory now affects Node shims, including one in the home directory. If such a file holds an alias likelts/*,node/npm/npxunder that directory stop with an error where they used to run the global version. The issue asks for exactly this ("report them as unsupported rather than silently falling back"), but it may surprise nvm users who keep a~/.nvmrc.Additional Notes
.nvmrcaliases (lts/*etc.),22.x-style ranges, and bulkdtvem installreading.node-version/.nvmrc. Bulk install still reads only.dtvem/runtimes.json..dtvem/runtimes.jsonkeeps its exact-version semantics. Partial matching applies only to the ecosystem version files.