feat(config): recognize devbox.jsonc as an alternate config filename - #2950
Conversation
devbox.json is parsed as JSONC and supports comments, but editors and
GitHub diffs flag comments in a .json file as syntax errors. Allow users
to name their config devbox.jsonc so standard tooling highlights it
correctly, without any per-repo configuration.
- Add configfile.AltName ("devbox.jsonc") and configfile.ValidNames, and
search both names during directory discovery (devbox.json wins when
both are present, preserving existing behavior).
- Make SaveTo preserve the config's original filename via a new
FileName() helper so `devbox add` and friends write back to
devbox.jsonc instead of creating a stray devbox.json. Configs without
an on-disk path (e.g. pulled from a URL) still default to devbox.json.
Closes #2602
…vbox.jsonc-aware Recognizing devbox.jsonc in searchDir alone left several code paths that still hardcoded devbox.json: - devconfig.Init created a devbox.json next to an existing devbox.jsonc, which would then silently take precedence. It now returns fs.ErrExist when any recognized config name is present. - boxcli/multi and pullbox only matched devbox.json when walking directories; they now accept any ValidNames entry. - The generated dev Dockerfile always did COPY devbox.json; it now copies the project's actual config filename. - The generated .envrc now watch_file's both devbox.json and devbox.jsonc. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
@claude please review this PR. |
|
Claude finished @mikeland73's task in 3m 12s —— View job Claude finished reviewing this PR
SummaryThis is a clean, well-scoped implementation. Finding
if !dirEntry.IsDir() && slices.Contains(configfile.ValidNames, filepath.Base(path)) {
...
box, err := devbox.Open(&optsCopy)
It's an edge case (requires both files to coexist, which Minor / non-blocking
Note on verificationI wasn't able to run |
ReviewReviewed at Verified locally (merged head, on top of current
Correctness of the core change:
Follow-up commit — completeness check. I grepped the tree for other spots that hardcoded the config name; the commit caught all the functional ones:
Nits (non-blocking):
Nothing here blocks merge. Generated by Claude Code |
| } | ||
|
|
||
| if !dirEntry.IsDir() && filepath.Base(path) == configfile.DefaultName { | ||
| if !dirEntry.IsDir() && slices.Contains(configfile.ValidNames, filepath.Base(path)) { | ||
| optsCopy := *opts | ||
| optsCopy.Dir = path |
There was a problem hiding this comment.
Bug: matching both config names in the multi-project walk can create duplicate project entries.
filepath.Base(path) == configfile.DefaultName was changed to slices.Contains(configfile.ValidNames, filepath.Base(path)). Since WalkDir visits every file independently, a directory containing both devbox.json and devbox.jsonc will now match twice, producing two *devbox.Devbox entries for the same project directory (there's no dedup by directory in this loop or in boxes = append(boxes, box) below).
This is inconsistent with the precedence this PR establishes elsewhere — devconfig.searchDir (and the new DefaultNameWinsWhenBothExist test) treat such a directory as a single project where devbox.json wins, and devconfig.Init refuses to create a second config file for exactly this reason.
Downstream consumers that call multi.Open (e.g. internal/boxcli/run.go's script loop, internal/boxcli/update.go) would then run the script/update twice for the same project directory.
Suggest deduping by directory in this walk (e.g. track directories already matched, or search each directory once using configfile.ValidNames order) so devbox.json wins here too, matching searchDir's behavior.
devbox/internal/boxcli/multi/multi.go
Lines 23 to 27 in 2201bf8
…sonc exist multi.Open walks the tree and opened a project for every file whose basename is a recognized config name. A directory containing both devbox.json and devbox.jsonc therefore matched twice, so `devbox run/update --all-projects` would run against that project twice — inconsistent with the "devbox.json wins" precedence enforced in config.searchDir and devconfig.Init. Deduplicate on the containing directory and open by directory (not by the specific file) so devconfig applies its filename precedence. Adds a regression test covering a directory that holds both config names. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01554vf5pDDVmFCcAadatBea
|
Good catch on the
Fix:
Verified locally on the updated head: On the nits: the Generated by Claude Code |
The devbox.jsonc-awareness commit changed two user-visible outputs but left their testscript golden files stale, failing CI: - The generated .envrc now watch_file's devbox.jsonc as well, so every direnv testscript that compares the rendered .envrc needed the extra path in its expected output. - `devbox init` on an already-configured directory now warns "A devbox config already exists in ..." (it guards against any recognized config name, not just devbox.json), so init/empty.test's stderr assertion was updated to match. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01554vf5pDDVmFCcAadatBea
Summary
Fixes #2602 (cc @gjoseph92).
devbox.jsonis parsed as JSONC and already supports comments, but editors and GitHub diffs treat comments in a.jsonfile as syntax errors. This PR lets a project name its configdevbox.jsoncso standard tooling highlights it correctly, with no per-repofiles.associationsor.gitattributesworkarounds.Changes (all in
internal/devconfig):configfile.AltName("devbox.jsonc") andconfigfile.ValidNames, and havesearchDirtry both names during discovery.devbox.jsonis listed first so it still wins when a directory contains both files; existing projects are unaffected.ConfigFile.FileName()and makeSaveTouse it, sodevbox add/rm/etc. write back todevbox.jsoncinstead of creating a straydevbox.json. Configs with no on-disk path (e.g. loaded from a URL bydevbox global pull) still fall back todevbox.json.Scope / known follow-ups
This PR covers config discovery and save round-tripping. A few code paths still assume
devbox.jsonand are left for a follow-up so this change stays small:devbox run --all-projects/devbox update --all-projects(internal/boxcli/multi) only matchdevbox.jsonwhen walking subdirectories.Dockerfile/.envrctemplates (devbox generate) hardcodedevbox.json.devbox global pull"is default config" checks (internal/pullbox) only considerdevbox.json.devbox.jsonc.How was it tested?
go build ./...,go vet ./internal/devconfig/..., andgo test ./internal/devconfig/...pass. New unit tests:TestJSONCConfig(internal/devconfig/config_test.go):OpenandFinddiscoverdevbox.jsonc;devbox.jsonwins when both exist;SaveTowrites back todevbox.jsoncwithout creating adevbox.json.TestFileName/TestSaveToPreservesFileName(internal/devconfig/configfile/file_test.go):FileName()maps each path to the right basename and falls back todevbox.json;SaveTowrites the expected filename.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.