Skip to content

feat(config): add "auto" devbox_version policy to switch versions - #2991

Merged
mikeland73 merged 5 commits into
mainfrom
mikeland73/devbox-version-auto
Sep 28, 2026
Merged

mikeland73 merged 5 commits into
mainfrom
mikeland73/devbox-version-auto

Conversation

@mikeland73

@mikeland73 mikeland73 commented Sep 28, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Stacked on #2990, which adds devbox_version. Merge that first.

Adds an auto policy that switches to the pinned devbox version:

"devbox_version": {"version": "0.18.4", "on_mismatch": "auto"}
  • How switching works: when the running devbox doesn't match, it re-runs the same command through the devbox launcher ($LAUNCHER_PATH) with DEVBOX_USE_VERSION set to the pinned version.
  • The existing launcher already downloads, checksum-verifies and runs any release named by DEVBOX_USE_VERSION, so the launcher needs no changes.
  • Download progress goes to stderr, so shellenv and direnv output stays clean.
  • Exact versions only: auto requires an exact version; a range with auto is a config error. DEVBOX_VERSION_POLICY=auto with a range fails like error.
  • A DEVBOX_USE_VERSION set by the user wins: devbox warns instead of switching.
  • Loop guard: __DEVBOX_AUTO_VERSION marks a version that auto chose. That lets a nested shell switch again for a different project, and makes devbox fail instead of looping if the launcher doesn't switch.
  • Without a launcher (for example Nix or Homebrew installs), auto fails with install instructions.
  • Inheritance: DEVBOX_USE_VERSION carries into devbox shell, so nested devbox commands stay on the pinned version.

How was it tested?

  • Unit tests with a mocked exec cover: switching, replacing existing env values, switching again for a different project, no launcher, the loop guard, a user-set version winning, and the env override with a range.
  • testscripts/version/devbox_version_auto.test.txt uses a fake launcher script to check that the same command is re-run with DEVBOX_USE_VERSION set. It also covers the no-launcher error, a user-set version winning, and rejecting ranges.
  • Manual end-to-end run with the real launcher (v0.2.2): a binary built as 0.17.1, run in a project pinned to 0.18.4 with auto, ran devbox run under 0.18.4. Inside the script, devbox version printed 0.18.4 and DEVBOX_USE_VERSION=0.18.4.
  • go test ./internal/... and devbox run lint pass.

Community Contribution License

All community contributions in this pull request are licensed to the project
maintainers under the terms of the
Apache 2 License.

By creating this pull request, I represent that I have the right to license the
contributions to the project maintainers under the Apache 2 License as stated in
the
Community Contribution License.

🤖 Generated with Claude Code

@mikeland73

Copy link
Copy Markdown
Collaborator Author

@claude ptal

@claude

claude Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Claude finished @mikeland73's task in 2m 14s —— View job


Reviewing PR #2991

  • Gather context (read CLAUDE.md, diff, changed files)
  • Understand the request (code review requested via "ptal")
  • Review internal/vercheck/project.go changes
  • Review internal/devconfig/configfile/devbox_version.go changes
  • Review test coverage
  • Post review feedback

Review

I read through internal/vercheck/project.go, internal/devconfig/configfile/devbox_version.go, both test files, the testscript, and the schema diff (against origin/mikeland73/devbox-version-constraint). This is a clean, well-scoped implementation — I didn't find any bugs. Notes below are mostly confirmations of things I checked, plus a couple of minor observations.

Correctness checks I traced through and confirm are right:

  • internal/vercheck/project.go:96-103 — the "user-set version wins" check correctly distinguishes a user-set DEVBOX_USE_VERSION from one auto itself set, by comparing against __DEVBOX_AUTO_VERSION. Covered by the user_set_version_wins test.
  • internal/vercheck/project.go:105-110 — the loop guard (autoVersion == target → error) correctly fires only when we previously tried switching to exactly this target and are still mismatched after re-exec, rather than on every switch. The "different project" case (nested shell re-switching to a different pinned version) is correctly allowed through, per the switches_again_for_a_different_project test.
  • internal/devconfig/configfile/devbox_version.go:126-133 — auto + range is rejected at config-validation time, and switchVersion (project.go:86-94) independently re-checks ExactVersion() to cover the DEVBOX_VERSION_POLICY=auto env-override path, which bypasses static validation. Both are tested (env_override_with_range, plus the config validation table).
  • args := append([]string{launcher}, os.Args[1:]...) (project.go:126) — no backing-array aliasing issue since the literal slice's cap forces a new allocation.
  • syscall.Exec (project.go:12,34) — devbox only ships linux/darwin builds per .goreleaser.yaml, so this is safe; not an issue for a Windows target that doesn't exist.
  • Test coverage is thorough: unit tests mock execFunc and cover switching, env replacement (not duplication), re-switching for a different project, no-launcher, the loop guard, user-override, and the range env-override error. The testscript exercises the same behaviors end-to-end through a fake launcher script, consistent with the existing devbox_version.test.txt style.

Minor observations (non-blocking):

  • internal/vercheck/project.go:127 — on execFunc failure the function returns a wrapped usererr; worth double-checking in practice that syscall.Exec failures (e.g. launcher not executable, ENOENT) produce a readable message via usererr.WithUserMessage, but I didn't find anything wrong with the wrapping itself.
  • I wasn't able to run go build/go test/git fetch in this sandboxed environment (those commands required approval that wasn't available here), so I verified everything by reading the diff and tests rather than executing them. The PR description states go test ./internal/... and devbox run lint already pass, and the tests I read look correct and would need to pass to compile as written.

No changes requested — this looks ready to merge from a correctness standpoint.
· Branch: mikeland73/devbox-version-auto

@mikeland73
mikeland73 added this pull request to stack #2992 September 28, 2026 18:00
@mikeland73
mikeland73 force-pushed the mikeland73/devbox-version-auto branch from 695ae2e to 83567de Compare September 28, 2026 18:24
mikeland73 and others added 4 commits September 28, 2026 12:00
Add a top-level `devbox_version` field to devbox.json that declares which
devbox versions a project supports (#1371). It accepts a semver constraint
string, or an object with an explicit mismatch policy:

    "devbox_version": "^0.18.0"
    "devbox_version": {"version": "0.18.4", "on_mismatch": "error"}

When the running devbox doesn't satisfy the constraint, "warn" (the default)
prints a warning once per project per process tree and "error" fails the
command. DEVBOX_VERSION_POLICY=off|warn|error overrides the policy.

The check runs in devbox.Open right after the config loads, before the
lockfile is read, so it covers every command that opens a project
(including `devbox global`). Development builds skip it. Constraints use
npm-style syntax via Masterminds/semver, which moves from an indirect to a
direct dependency.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
For range constraints, the mismatch message always suggested
`devbox version update`, but that only moves to the latest release. If the
running version is already past the constraint's upper bound, or the latest
release doesn't satisfy it, updating can't fix the mismatch. Now we only
suggest it when the latest version (DEVBOX_LATEST_VERSION) satisfies the
constraint, and otherwise point at DEVBOX_USE_VERSION alone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ntally

Two places opened the current directory's project on every devbox
invocation, so the devbox_version check fired for commands that don't use
the project (e.g. `devbox version` printed the mismatch warning), and a
swallowed error let `devbox run --list` ignore the "error" policy:

- `devbox run` computed ValidArgs eagerly while building the command tree.
  It's now a lazy ValidArgsFunction that only runs during shell completion,
  which also lets cobra parse --config for us. listScripts now returns its
  error, so `run --list` and bare `run` report it.
- The telemetry middleware opens the project after every command to record
  package names.

Add devopt.Opts.SkipVersionCheck for these incidental opens (telemetry and
completion), and cover `devbox version` and `run --list` in the testscript.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
With `"devbox_version": {"version": "0.18.4", "on_mismatch": "auto"}`, a
devbox that doesn't match re-runs the same command through the devbox
launcher with DEVBOX_USE_VERSION set to the required version. The launcher
already downloads and runs any release named by DEVBOX_USE_VERSION, so no
launcher change is needed.

- "auto" requires an exact version; a range with "auto" is a config error.
- A DEVBOX_USE_VERSION set by the user wins; devbox warns instead of
  switching. __DEVBOX_AUTO_VERSION tells the two apart and stops re-exec
  loops if the launcher doesn't switch.
- Without a launcher (e.g. Nix or Homebrew installs), "auto" fails with
  instructions, like "error".
- DEVBOX_VERSION_POLICY also accepts "auto".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mikeland73
mikeland73 force-pushed the mikeland73/devbox-version-auto branch from 83567de to 901a835 Compare September 28, 2026 19:00
Base automatically changed from mikeland73/devbox-version-constraint to main September 28, 2026 22:23
…sion-auto

main has the squash-merged devbox_version change (#2990). Conflicts in the
devbox_version files are add/add conflicts where this branch's version is
main's plus the "auto" policy, so this branch's side is kept.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mikeland73
mikeland73 merged commit 1d9ea45 into main Sep 28, 2026
28 checks passed
@mikeland73
mikeland73 deleted the mikeland73/devbox-version-auto branch September 28, 2026 22:41
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.

1 participant