feat(bootstrap): add dotfiles workflow#10376
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR introduces experimental ChangesDotfiles Feature and System Scope Refactor
Estimated code review effort Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
Greptile SummaryThis PR extracts dotfile management (whole-file entries and in-file edits) out of
Confidence Score: 4/5Safe to merge after addressing the silent --source discard in dotfiles add; the rest of the restructuring is clean. The DotfilesAdd command silently discards a user-supplied --source flag when the target is already managed — the file copy goes to the old configured path with no diagnostic, leaving the config and the new path untouched. Every other aspect of the rename and new command group looks correct and well-tested. src/cli/dotfiles/add.rs — the already-managed path ignores --source without warning; src/oci/builder.rs — annotation rename may affect existing OCI consumers. Important Files Changed
Reviews (9): Last reviewed commit: "feat(dotfiles): add add and edit workflo..." | Re-trigger Greptile |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
docs/system-edits.md (1)
106-112: ⚡ Quick winInconsistent wording for
--missingflag semantics across file/edit docs.Line 107 in
system-edits.mdstates:exit 1 if anything is missing (CI check), but the equivalent insystem-files.mdline 87 says:exit 1 if anything is out of sync (CI check). The--missingflag should have consistent semantics for both files and edits — it should exit with code 1 if any entry is not in its desired state (applied), not just the "missing" status specifically.For clarity and consistency, update line 107 to match the files semantics:
-mise dotfiles status --missing # exit 1 if anything is missing (CI check) +mise dotfiles status --missing # exit 1 if anything is out of sync (CI check)🤖 Prompt for 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. In `@docs/system-edits.md` around lines 106 - 112, Update the wording for the --missing flag in the "mise dotfiles status --missing" help text in system-edits.md to match the files docs: change the phrase "exit 1 if anything is missing (CI check)" to "exit 1 if anything is out of sync (CI check)" so that the --missing flag semantics consistently mean exiting with code 1 whenever any entry is not in its desired/applied state; modify the line that documents "mise dotfiles status --missing" to use this exact wording and ensure it aligns with the corresponding description in system-files.md.
🤖 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 `@mise.usage.kdl`:
- Around line 289-294: The documentation bullet list is out of sync with the
runtime ordering in src/cli/bootstrap.rs: the dotfiles phase runs before the
macOS defaults phase, so split the single "system" bullet or move the
"[system.defaults]" note so the order reflects actual execution; specifically,
ensure the list shows "mise system install" (macOS packages), then "mise
dotfiles install" (apply dotfiles), then a separate "mise system defaults" or a
moved note for `[system.defaults]`, and update the example summary below to
explicitly mention "mise dotfiles install" along with the other commands
(referencing the commands mise system install, mise dotfiles install, mise
install, mise run bootstrap and the bootstrap handling in src/cli/bootstrap.rs).
In `@src/cli/bootstrap.rs`:
- Around line 13-18: Update the top-of-file step list to match the actual
execution order in run(): move or reword the mention of applying
`[system.defaults]` so it appears after the `[dotfiles]` step (i.e., dotfiles
are applied first, then system defaults are written/applied, then tools
installed, then `run bootstrap`), ensuring the comment block describing steps
1–4 aligns exactly with the behavior implemented in the run() function.
In `@src/system/files.rs`:
- Around line 133-138: file_entry_from_toml currently rejects toml tables unless
they contain a "mode" key, which causes valid whole-file entries like { source =
"..." } to be misrouted; update file_entry_from_toml to explicitly accept tables
that represent whole-file entries (e.g., tables containing a "source" key or
another agreed discriminator) and only treat tables as edit-typed when the
explicit discriminator (e.g., "mode" or "edits") is present; make the
discriminator contract explicit and apply the same logic in the parser in
src/system/edits.rs so both parsers agree, and add regression tests covering
source-only table entries to ensure { source = "..." } is parsed as a
FileTomlEntry with default mode (e.g., symlink) rather than being dropped or
treated as edits.
---
Nitpick comments:
In `@docs/system-edits.md`:
- Around line 106-112: Update the wording for the --missing flag in the "mise
dotfiles status --missing" help text in system-edits.md to match the files docs:
change the phrase "exit 1 if anything is missing (CI check)" to "exit 1 if
anything is out of sync (CI check)" so that the --missing flag semantics
consistently mean exiting with code 1 whenever any entry is not in its
desired/applied state; modify the line that documents "mise dotfiles status
--missing" to use this exact wording and ensure it aligns with the corresponding
description in system-files.md.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: a94916bc-8c4b-4ba2-9e96-af0e843b1882
📒 Files selected for processing (30)
docs/.vitepress/config.tsdocs/cli/bootstrap.mddocs/cli/dotfiles.mddocs/cli/dotfiles/install.mddocs/cli/dotfiles/status.mddocs/cli/index.mddocs/cli/system.mddocs/cli/system/install.mddocs/cli/system/status.mddocs/system-edits.mddocs/system-files.mddocs/system-packages/index.mddocs/tips-and-tricks.mde2e/cli/test_bootstrape2e/cli/test_dotfiles_editse2e/cli/test_dotfiles_filesmise.usage.kdlsrc/cli/bootstrap.rssrc/cli/dotfiles/install.rssrc/cli/dotfiles/mod.rssrc/cli/dotfiles/status.rssrc/cli/mod.rssrc/cli/system/install.rssrc/cli/system/mod.rssrc/cli/system/status.rssrc/config/config_file/mise_toml.rssrc/config/config_file/mod.rssrc/system/edits.rssrc/system/files.rssrc/system/mod.rs
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.6.5 x -- echo |
17.8 ± 0.8 | 16.4 | 23.2 | 1.00 |
mise x -- echo |
18.8 ± 1.6 | 17.1 | 49.3 | 1.05 ± 0.10 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.6.5 env |
17.7 ± 0.8 | 16.1 | 20.8 | 1.00 |
mise env |
18.4 ± 0.7 | 17.1 | 22.0 | 1.04 ± 0.06 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.6.5 hook-env |
18.4 ± 0.7 | 16.9 | 22.2 | 1.00 |
mise hook-env |
19.0 ± 0.8 | 17.3 | 22.6 | 1.04 ± 0.06 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.6.5 ls |
14.9 ± 0.7 | 13.4 | 17.9 | 1.00 |
mise ls |
15.7 ± 0.7 | 14.1 | 18.8 | 1.05 ± 0.07 |
xtasks/test/perf
| Command | mise-2026.6.5 | mise | Variance |
|---|---|---|---|
| install (cached) | 129ms | 131ms | -1% |
| ls (cached) | 57ms | 58ms | -1% |
| bin-paths (cached) | 62ms | 63ms | -1% |
| task-ls (cached) | 119ms | 123ms | -3% |
25c2c19 to
49bbff7
Compare
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
schema/mise.json (1)
3034-3075:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd the new
system.brewcontract to the generated schema and remove the stale files/edits wording.
src/system/mod.rsnow accepts[system.brew.taps], but this schema still models[system]as onlypackages/defaultsand describes it as owning files/edits. That makes a supported config look invalid in editors while still steering users toward removed keys.Suggested schema shape
- "system": { - "type": "object", - "description": "[experimental] machine-global bootstrapping (system packages, files, edits, macOS defaults)", - "properties": { + "system": { + "type": "object", + "description": "[experimental] machine-global bootstrapping (system packages, Homebrew taps, macOS defaults)", + "properties": { + "brew": { + "type": "object", + "description": "Homebrew-specific system config", + "properties": { + "taps": { + "type": "object", + "description": "Homebrew tap remotes keyed by owner/tap", + "additionalProperties": { + "type": "string" + } + } + } + }, "packages": { "type": "object", "description": "system packages to install with `mise system install`, keyed by \"manager:package\" (e.g. \"apt:libssl-dev\", \"brew:postgresql@17\", \"dnf:openssl-devel\", \"pacman:base-devel\")",🤖 Prompt for 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. In `@schema/mise.json` around lines 3034 - 3075, The schema's "system" object is outdated: add a new "brew" property describing the Homebrew-specific contract (including a "taps" property that models [system.brew.taps] as an object keyed by tap name with string or object values) and remove the stale wording that claims "system" owns files/edits; specifically update the "system" properties to include a "brew" entry (type: object, description referencing brew settings) with a "taps" sub-property (type: object, propertyNames pattern, additionalProperties allowing string or object) alongside the existing "packages" and "defaults", and edit the "system" description to drop "files/edits" language so it reflects the actual supported keys (packages, defaults, brew).
🤖 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 `@e2e/cli/test_dotfiles_files`:
- Around line 193-195: Remove the first immediate execution assert_fail "mise
dotfiles apply --yes" so the command is only run once; run the command once and
capture its combined stdout/stderr into install_out using install_out="$(mise
dotfiles apply --yes 2>&1 || true)", then make assertions against that captured
output (e.g. assert_contains_text "$install_out" "sources do not exist") and, if
you need to assert it failed, check the exit status from the single run rather
than re-running the command.
In `@src/cli/dotfiles/add.rs`:
- Around line 72-110: The loop in add.rs currently treats targets as unmanaged
if files_from_config() doesn't return an exact whole-file entry, allowing adding
a whole-file when a `[dotfiles]` edit (e.g. "~/.zshrc/<id>") already exists;
update the planning logic in the loop that builds PlannedAdd so that after
resolving target with system::files::resolve_target_arg you check
managed.iter().find(...) for any matching req via system::files::matches_target
and if a match exists but req.target_raw != target_raw (i.e. the config has a
per-file edit for this path) bail early with a clear error (fail-fast) instead
of proceeding to create a whole-file PlannedAdd; keep the existing behavior only
when the match is an exact whole-file entry.
In `@src/cli/dotfiles/edit.rs`:
- Around line 89-114: source_for_target currently returns the first matching
edit source/config when multiple edits target the same path; change it to detect
ambiguous matches: iterate system::edits::edits_from_config(config) and collect
all matching req entries (use their unique ids from req.id or
req.config_path+req.path if no id), if the collection has more than one element
return an Err listing the candidate ids instead of picking the first, if exactly
one element return its source as before, and if none fall back to the earlier
system::files behavior; update the function's return/error path accordingly and
ensure you reference source_for_target, system::edits::edits_from_config,
EditOp, BlockSource, and req.config_path when locating and implementing this
change.
In `@src/system/edits.rs`:
- Around line 152-160: The current early-return in edit_entry_from_toml is
skipping all non-Table values so malformed or legacy edit entries never reach
the try_into() warning; change the match so we only suppress true whole-file
entries (e.g., an empty Table or a Table that signals whole-file via the "mode"
key) and let all other non-table or malformed values fall through to the
try_into() path (so split_edit_key()/try_into() will emit warnings). Update the
match in edit_entry_from_toml (and any related guards) to only return None for
the explicit whole-file case and not for every non-Table value.
---
Outside diff comments:
In `@schema/mise.json`:
- Around line 3034-3075: The schema's "system" object is outdated: add a new
"brew" property describing the Homebrew-specific contract (including a "taps"
property that models [system.brew.taps] as an object keyed by tap name with
string or object values) and remove the stale wording that claims "system" owns
files/edits; specifically update the "system" properties to include a "brew"
entry (type: object, description referencing brew settings) with a "taps"
sub-property (type: object, propertyNames pattern, additionalProperties allowing
string or object) alongside the existing "packages" and "defaults", and edit the
"system" description to drop "files/edits" language so it reflects the actual
supported keys (packages, defaults, brew).
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 4cdfb4c8-2c7f-43f2-9437-aa6e844ae1ff
📒 Files selected for processing (39)
docs/.vitepress/cli_commands.tsdocs/.vitepress/config.tsdocs/cli/bootstrap.mddocs/cli/dotfiles.mddocs/cli/dotfiles/add.mddocs/cli/dotfiles/apply.mddocs/cli/dotfiles/edit.mddocs/cli/dotfiles/status.mddocs/cli/index.mddocs/cli/system.mddocs/cli/system/install.mddocs/cli/system/status.mddocs/system-edits.mddocs/system-files.mddocs/system-packages/index.mddocs/tips-and-tricks.mde2e/cli/test_bootstrape2e/cli/test_dotfiles_editse2e/cli/test_dotfiles_filesman/man1/mise.1mise.usage.kdlschema/mise.jsonsettings.tomlsrc/cli/bootstrap.rssrc/cli/dotfiles/add.rssrc/cli/dotfiles/apply.rssrc/cli/dotfiles/edit.rssrc/cli/dotfiles/mod.rssrc/cli/dotfiles/status.rssrc/cli/mod.rssrc/cli/system/install.rssrc/cli/system/mod.rssrc/cli/system/status.rssrc/config/config_file/mise_toml.rssrc/config/config_file/mod.rssrc/system/edits.rssrc/system/files.rssrc/system/mod.rsxtasks/fig/src/mise.ts
✅ Files skipped from review due to trivial changes (12)
- docs/cli/dotfiles/edit.md
- docs/system-packages/index.md
- docs/cli/dotfiles/apply.md
- docs/cli/dotfiles/add.md
- src/cli/system/mod.rs
- docs/cli/system/status.md
- docs/cli/system/install.md
- docs/cli/index.md
- docs/.vitepress/config.ts
- docs/cli/dotfiles/status.md
- docs/cli/bootstrap.md
- docs/cli/system.md
🚧 Files skipped from review as they are similar to previous changes (8)
- src/config/config_file/mod.rs
- e2e/cli/test_bootstrap
- src/cli/bootstrap.rs
- docs/tips-and-tricks.md
- src/cli/dotfiles/status.rs
- src/config/config_file/mise_toml.rs
- src/cli/system/status.rs
- src/cli/system/install.rs
4e63a11 to
94c5cff
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/cli/dotfiles/add.rs (1)
249-273: 💤 Low valueDead code: the
already_managedbranch is unreachable.
inline_entryis only called whenitem.already_managed.is_none()(lines 139 and 196), so the condition at line 258 checkingitem.already_managed.is_some()can never be true.Simplify by removing unreachable branch
fn inline_entry(item: &PlannedAdd) -> Value { let mut table = InlineTable::new(); if !item.implied_source { table.insert( "source", Value::String(toml_edit::Formatted::new( item.source.display_user().to_string(), )), ); - } else if let Some(req) = &item.already_managed - && !system::files::source_is_implied(req) - { - table.insert( - "source", - Value::String(toml_edit::Formatted::new( - item.source.display_user().to_string(), - )), - ); } table.insert( "mode", Value::String(toml_edit::Formatted::new(item.mode.name().to_string())), ); Value::InlineTable(table) }🤖 Prompt for 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. In `@src/cli/dotfiles/add.rs` around lines 249 - 273, The else-if branch in inline_entry is unreachable because inline_entry is only called when PlannedAdd.already_managed is None; remove the entire else if let Some(req) = &item.already_managed && !system::files::source_is_implied(req) { ... } branch and simplify the function to only insert "source" when !item.implied_source, leaving the existing insertion of "mode" unchanged; keep references to inline_entry, PlannedAdd, implied_source, already_managed, and system::files::source_is_implied in your mental model to locate and remove the unreachable block.
🤖 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/cli/dotfiles/add.rs`:
- Around line 172-225: The final log is including planned items the user
skipped; fix by tracking which items were actually written and use that list for
logging and any post-actions: inside the loop over planned (where you currently
call write_entry(doc, item) and file::write/create paths), create a Vec<String>
written = Vec::new() and push item.target_raw.clone() (or another identifier)
whenever you successfully write/create or call write_entry for that item; then
replace the final filters that iterate planned.iter().filter(|item|
item.already_managed.is_none()) with the collected written list (use
written.join(", ")) for both the doc-present branch (after
file::write(&config_path, doc.to_string())) and the doc-absent branch so skipped
items are not reported as added/updated; ensure the code paths that continue on
user-decline do not push to written (so skipped items remain excluded).
---
Nitpick comments:
In `@src/cli/dotfiles/add.rs`:
- Around line 249-273: The else-if branch in inline_entry is unreachable because
inline_entry is only called when PlannedAdd.already_managed is None; remove the
entire else if let Some(req) = &item.already_managed &&
!system::files::source_is_implied(req) { ... } branch and simplify the function
to only insert "source" when !item.implied_source, leaving the existing
insertion of "mode" unchanged; keep references to inline_entry, PlannedAdd,
implied_source, already_managed, and system::files::source_is_implied in your
mental model to locate and remove the unreachable block.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 6a609edb-3800-4f03-b9a8-64bc6b470fd8
📒 Files selected for processing (30)
docs/.vitepress/cli_commands.tsdocs/cli/bootstrap.mddocs/cli/dotfiles.mddocs/cli/dotfiles/add.mddocs/cli/dotfiles/apply.mddocs/cli/dotfiles/edit.mddocs/cli/dotfiles/status.mddocs/cli/index.mddocs/dev-tools/mise-oci.mddocs/system-edits.mddocs/system-files.mddocs/tips-and-tricks.mde2e/cli/test_dotfiles_editse2e/cli/test_dotfiles_filese2e/oci/test_oci_build_slowman/man1/mise.1mise.usage.kdlschema/mise.jsonsettings.tomlsrc/cli/bootstrap.rssrc/cli/dotfiles/add.rssrc/cli/dotfiles/apply.rssrc/cli/dotfiles/edit.rssrc/cli/dotfiles/mod.rssrc/cli/dotfiles/status.rssrc/cli/oci/common.rssrc/oci/builder.rssrc/system/edits.rssrc/system/files.rsxtasks/fig/src/mise.ts
✅ Files skipped from review due to trivial changes (9)
- docs/cli/index.md
- docs/cli/dotfiles/status.md
- docs/cli/dotfiles/add.md
- docs/cli/dotfiles/edit.md
- docs/cli/bootstrap.md
- docs/cli/dotfiles/apply.md
- docs/cli/dotfiles.md
- src/cli/bootstrap.rs
- docs/system-files.md
🚧 Files skipped from review as they are similar to previous changes (18)
- docs/.vitepress/cli_commands.ts
- src/cli/oci/common.rs
- docs/dev-tools/mise-oci.md
- docs/tips-and-tricks.md
- settings.toml
- xtasks/fig/src/mise.ts
- schema/mise.json
- src/cli/dotfiles/mod.rs
- src/cli/dotfiles/apply.rs
- src/cli/dotfiles/status.rs
- docs/system-edits.md
- src/oci/builder.rs
- src/cli/dotfiles/edit.rs
- man/man1/mise.1
- mise.usage.kdl
- src/system/edits.rs
- src/system/files.rs
- e2e/cli/test_dotfiles_files
10827e8 to
b500ed0
Compare
b500ed0 to
7ad6d0a
Compare
a40b13c to
358399d
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 358399d. Configure here.
358399d to
99d3847
Compare
99d3847 to
3c0a041
Compare
mise v2026.6.6 has added an experimental dotfiles feature: - https://github.com/jdx/mise/releases/tag/v2026.6.6 - jdx/mise#10376 Try it out and drop stow completely. Benefits: - fewer tools to manage - stow's blocklist may lead to dangerous surprises - an allowlist is much safer (even though it's more annoying to maintain) - the list of symlinks is easier to keep in sync with the list of tools since they are in the same file now
mise v2026.6.6 has added an experimental dotfiles feature: - https://github.com/jdx/mise/releases/tag/v2026.6.6 - jdx/mise#10376 Try it out and drop stow completely. Benefits: - fewer tools to manage - stow's blocklist may lead to dangerous surprises - an allowlist is much safer (even though it's more annoying to maintain) - the list of symlinks is easier to keep in sync with the list of tools since they are in the same file now

Summary
mise dotfilesworkflow withadd,apply,edit, andstatusaround a flat[dotfiles]table.dotfiles.rootanddotfiles.default_mode, with symlink as the default mode and implied source paths under the dotfiles root.[bootstrap.packages],[bootstrap.macos.defaults], and[bootstrap.user]; remove the publicmise systemcommand surface.mise bootstrapto converge packages, dotfiles, macOS defaults, user login shell, tools, and the optional bootstrap task in order.Notable behavior
[system.*],mise system *, ormise dotfiles installshapes.mise bootstrap packages installis package-only; dotfiles and login shell are applied bymise bootstrapor their narrower bootstrap/dotfiles commands.[dotfiles], with source-only tables treated as whole-file entries.Validation
mise run lintmise run docs:buildmise run test:e2e e2e/cli/test_dotfiles_files e2e/cli/test_dotfiles_edits e2e/cli/test_bootstrapThis PR was prepared by an AI coding assistant.