Skip to content

fix(config): don't write the global config into a conf.d drop-in - #11633

Merged
jdx merged 1 commit into
jdx:mainfrom
JamBalaya56562:fix/global-config-path-skips-confd
Aug 2, 2026
Merged

fix(config): don't write the global config into a conf.d drop-in#11633
jdx merged 1 commit into
jdx:mainfrom
JamBalaya56562:fix/global-config-path-skips-confd

Conversation

@JamBalaya56562

@JamBalaya56562 JamBalaya56562 commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Reported in #5842: with ~/.config/mise/config.toml not yet created and conf.d/*.toml present, mise use --global edits a drop-in instead.

Measured on v2026.7.15, config dir holding only conf.d/10-a.toml:

$ mise use -g --dry-run jq@1.7.1
mise would update .../cfg/conf.d/10-a.toml (add: jq@1.7.1)

Cause

first_config_file deliberately skips conf.d entries while choosing, but falls back to files.first() when nothing else qualifies — and config_files_from_dir inserts conf.d entries before CONFIG_FILENAMES. So a config dir holding only drop-ins hands one straight back to global_config_path, whose own doc comment reads "the preferred global config file to write to, or the path where it should be created".

That also explains the reporter's observation that it works correctly once conf.d is empty.

Change

One .filter in global_config_path, so an unqualified candidate falls through to MISE_GLOBAL_CONFIG_FILE and then to <config dir>/config.toml — the name mise use --help already documents for --global.

first_config_file itself is left alone: its files.first() fallback is what makes a .tool-versions-only global config work, and other callers rely on it. Only the global write-target resolution needed narrowing.

What else this touches

All nine callers of global_config_path() are write-target resolution, so they are all fixed by the same change and none of them wanted a drop-in:

  • Config::global_config() — the base for settings writes
  • resolve_target_config_path — the --global branch and the in-$HOME branch
  • cli/unuse.rs, cli/edit.rs, cli/settings/set.rs, cli/settings/unset.rs

Unchanged: reading. Drop-ins are still discovered and loaded exactly as before — the filter only decides where writes go. Also unchanged: a .tool-versions global config (reachable with MISE_USE_TOML=false), which is not a conf.d file and passes the filter.

If <config dir>/mise.toml already exists it is still preferred over creating config.toml, since first_config_file finds it before the fallback is reached.

Tests

e2e/cli/test_global_config_confd, modelled on the existing test_global_config_file_home: with only a drop-in present, mise use -g / mise set -g / mise settings set all write to config.toml, the drop-in is byte-for-byte untouched, and it is still loaded before and after — the reading path is the thing most worth guarding here.

No unit test: config/mod.rs's mod tests is #[cfg(unix)] and snapshot-based, and global_config_files() reads dirs::CONFIG through a Mutex-cached static that a test cannot vary.

Note

@jdx said in the thread that this was "probably easier said than done" — it turned out to be one predicate, because the intent was already encoded in first_config_file; only the fallback leaked past it.

Summary by CodeRabbit

  • Bug Fixes
    • Global configuration updates now consistently write to the primary config.toml file instead of conf.d drop-in files.
    • Existing drop-in configuration files remain preserved when global settings, tools, or environment values are added or removed.
  • Tests
    • Added end-to-end coverage confirming correct configuration file selection and preservation of drop-in settings.

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 0f3f937d-eb97-4a5e-84ee-2aed90e78e61

📥 Commits

Reviewing files that changed from the base of the PR and between 88ed12d and ef7eae2.

📒 Files selected for processing (2)
  • e2e/cli/test_global_config_confd
  • src/config/mod.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/config/mod.rs
  • e2e/cli/test_global_config_confd

📝 Walkthrough

Walkthrough

The change prevents global configuration writes from targeting conf.d drop-ins. It adds end-to-end coverage for drop-in reads, global writes, tool removal, and drop-in preservation.

Changes

Global configuration selection

Layer / File(s) Summary
Write-target resolution and validation
src/config/mod.rs, e2e/cli/test_global_config_confd
global_config_path excludes conf.d files from fallback selection. The end-to-end test verifies global tool, environment, and settings writes, plus tool removal without changing the drop-in.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • jdx/mise#11571: Both changes update configuration write-target resolution.
  • jdx/mise#11575: Both changes update configuration-path resolution in src/config/mod.rs.

Poem

A rabbit guards the drop-in file,
While writes use paths with proper style.
Tools and settings hop in place,
Removal leaves the drop-in safe.
“Good paths!” says Bun with a smile.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main fix: preventing global configuration writes from targeting conf.d drop-ins.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR prevents implicit global configuration writes from selecting a conf.d drop-in while preserving explicit configuration overrides and read-time discovery.

  • Filters fallback write targets returned from first_config_file.
  • Adds isolated end-to-end coverage for tool, environment, settings, and removal operations.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/config/mod.rs Narrows global write-target selection so an implicitly selected drop-in falls back to the primary global configuration path.
e2e/cli/test_global_config_confd Adds correctly discovered and isolated regression coverage verifying that drop-ins remain loaded but unchanged by global writes.

Reviews (2): Last reviewed commit: "fix(config): don't write the global conf..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/config/mod.rs`:
- Around line 2228-2232: Filter out conf.d drop-in paths from the global
configuration file collection before invoking first_config_file, so its
files.first() fallback can only select eligible files such as .tool-versions.
Preserve the existing fallback behavior and add an end-to-end case covering both
a drop-in and .tool-versions, verifying that .tool-versions remains unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: fb2f08d5-7174-42b3-a901-05c353290ab1

📥 Commits

Reviewing files that changed from the base of the PR and between 832623e and 88ed12d.

📒 Files selected for processing (2)
  • e2e/cli/test_global_config_confd
  • src/config/mod.rs

Comment thread src/config/mod.rs
@JamBalaya56562
JamBalaya56562 force-pushed the fix/global-config-path-skips-confd branch from 88ed12d to ef7eae2 Compare August 2, 2026 08:34
@JamBalaya56562

Copy link
Copy Markdown
Contributor Author

The scenario does not occur, but the test it asks for is worth having, so I added that.

global_config_files() inserts ~/.tool-versions before extending with config_files_from_dir, so when both are candidates files.first() is .tool-versions, never a drop-in. The .filter passes it through untouched.

Measured on v2026.8.0 — i.e. without this patch — with a drop-in and ~/.tool-versions both present:

# default (MISE_USE_TOML is true, so ~/.tool-versions is not a candidate)
$ mise use -g --dry-run jq@1.7.1
mise would update ~/.config/mise/conf.d/10-drop-in.toml     # ← the bug this PR fixes

# MISE_USE_TOML=false, so both are candidates
$ MISE_USE_TOML=false mise use -g --dry-run jq@1.7.1
mise would update ~/.tool-versions                          # ← not the drop-in

The second line is the case the comment describes, and the fallback already returns .tool-versions there. Since the filter only rejects conf.d paths, that result is unchanged by this PR.

What the finding did surface is that the fix leans on an ordering invariant declared ~65 lines away, with nothing guarding it. If those two inserts were ever swapped, .tool-versions writes would start being diverted to config.toml. So e2e/cli/test_global_config_confd now pins it:

printf 'dummy 1.0.0\n' >"$HOME/.tool-versions"
MISE_USE_TOML=false mise use -g dummy@system
assert_contains "cat $HOME/.tool-versions" "dummy"
assert_not_contains "cat $HOME/.config/mise/conf.d/10-drop-in.toml" "dummy"
assert_fail "test -f $HOME/.config/mise/config.toml"

It runs before the rest of the file creates config.toml, since once that exists first_config_file finds it directly and the fallback is never reached.

I kept the filter where it is rather than pre-filtering the set: both produce identical results in every case above, and putting it on the return value keeps the intent local to the function that documents itself as "the preferred global config file to write to". Happy to switch to pre-filtering if a reviewer prefers not depending on the insert order at all.

@jdx
jdx merged commit cfbc15d into jdx:main Aug 2, 2026
29 checks passed
@JamBalaya56562
JamBalaya56562 deleted the fix/global-config-path-skips-confd branch August 2, 2026 13:14
donbeave pushed a commit to donbeave/mise that referenced this pull request Aug 3, 2026
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.

2 participants