feat(bootstrap): add phase hooks#10395
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:
📝 WalkthroughWalkthroughAdds configurable phase-based bootstrap hooks to ChangesBootstrap Hooks Implementation
Sequence Diagram sequenceDiagram
participant BootstrapCLI as Bootstrap::run
participant ConfigLoader as hooks_from_config
participant HookParser as hooks::from_toml
participant PhaseRunner as hooks::run_phase
participant CmdLineRunner
BootstrapCLI->>ConfigLoader: request aggregated hooks (global→local)
ConfigLoader->>HookParser: parse per-entry into BootstrapHook
BootstrapCLI->>PhaseRunner: run_phase(phase, dry_run)
PhaseRunner->>CmdLineRunner: execute command (or print in dry_run)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 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 adds
Confidence Score: 5/5Safe to merge; the feature is experimental and explicit opt-in, and the one discrepancy found only affects dry-run preview output ordering, not actual hook execution. The hook execution path, fail-fast behavior, config reload after dotfiles and tools, and forward-compatible raw-TOML parsing all look correct. The dry-run simulation in hooks_after_dotfiles_dry_run appends dotfile-config entries at the end of the IndexMap, which inverts their position relative to real execution after Config::reset(). This is a preview-ordering discrepancy only — no hooks are silently dropped and real execution is unaffected. src/cli/bootstrap.rs — specifically the hooks_after_dotfiles_dry_run helper and where it inserts dotfile config entries into the config_files map. Important Files Changed
Reviews (7): Last reviewed commit: "test(ubi): replace stale direct url fixt..." | Re-trigger Greptile |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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 `@docs/bootstrap.md`:
- Around line 115-120: The docs currently say hooks are "command strings" which
is too narrow; update the wording in the bootstrap hooks docs to state that
[bootstrap.hooks] accepts command strings, arrays of strings, or table forms
with a run key (i.e., the same shorthand and expanded forms parsed by
src/system/hooks.rs), and keep the existing notes about default inline shell
behavior, failure stopping bootstrap, dry-run printing, and running in the
current process environment intact; simply replace phrases that imply only plain
strings with language like "hooks may be specified as a command string, an array
of command strings, or a table with a run field" so readers are aware of the
alternate forms.
In `@docs/tips-and-tricks.md`:
- Around line 101-102: Update the surrounding doc text that currently implies
the bootstrap flow is purely declarative/idempotent to explicitly scope that
claim: state that hooks (example block [bootstrap.hooks.post-defaults] with run
= "killall Dock || true") and entries under [tasks.bootstrap] are imperative
commands executed during mise bootstrap and may have side effects, and add a
short caution recommending reviewers treat hook commands as non-idempotent; keep
the example hook but prepend or follow it with a one-sentence note clarifying
hooks and tasks.bootstrap are the non-declarative pieces.
In `@src/system/hooks.rs`:
- Around line 121-129: run_phase currently assumes
Settings::get().default_inline_shell() returns at least one token and indexes
shell[0], which panics if the vec is empty; fix by validating the returned shell
Vec immediately after let shell = Settings::get().default_inline_shell()? so if
shell.is_empty() return an Err (or otherwise propagate a meaningful error)
explaining the misconfiguration before entering the phase_hooks loop, and then
use safe indexing (e.g. use shell[0] only after the emptiness check) when
constructing crate::cmd::CmdLineRunner::new(&shell[0]) and calling
.cmd_body_args(&shell[1..], &hook.run).
🪄 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: c238c6e9-b03b-4e0a-9d41-9cb7de054450
📒 Files selected for processing (8)
docs/bootstrap.mddocs/cli/bootstrap.mddocs/tips-and-tricks.mdmise.usage.kdlsrc/cli/bootstrap.rssrc/config/config_file/mise_toml.rssrc/system/hooks.rssrc/system/mod.rs
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.6.6 x -- echo |
19.6 ± 0.8 | 17.8 | 23.6 | 1.00 |
mise x -- echo |
20.2 ± 1.3 | 18.3 | 42.0 | 1.03 ± 0.08 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.6.6 env |
19.0 ± 0.8 | 17.2 | 22.7 | 1.00 |
mise env |
19.7 ± 0.9 | 17.8 | 25.5 | 1.04 ± 0.07 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.6.6 hook-env |
19.7 ± 0.8 | 18.1 | 23.0 | 1.00 |
mise hook-env |
20.2 ± 0.8 | 18.7 | 23.1 | 1.02 ± 0.06 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.6.6 ls |
16.3 ± 0.8 | 14.4 | 19.6 | 1.00 |
mise ls |
16.7 ± 0.7 | 15.2 | 19.9 | 1.02 ± 0.07 |
xtasks/test/perf
| Command | mise-2026.6.6 | mise | Variance |
|---|---|---|---|
| install (cached) | 132ms | 133ms | +0% |
| ls (cached) | 59ms | 59ms | +0% |
| bin-paths (cached) | 64ms | 65ms | -1% |
| task-ls (cached) | 125ms | 125ms | +0% |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 3b29b13. Configure here.
2304310 to
1bd40a8
Compare

Summary
[bootstrap.hooks]support for named bootstrap phases such aspre-packages,post-dotfiles,post-tools, andfinalValidation
cargo fmt --all --checkcargo test bootstrapcargo test system::hookscargo checkmise run render:usageThis PR was generated by an AI coding assistant.
Note
Medium Risk
Hooks execute arbitrary shell during bootstrap (including after dotfiles change config), so misconfiguration can abort setup or run unintended commands; scope is limited to explicit
mise bootstrapand the experimental bootstrap surface.Overview
Adds
[bootstrap.hooks]somise bootstrapcan run imperative shell commands at named phases (pre-packagesthroughpost-tools, plusfinalafter the bootstrap task). Hook values support a string, string array, or{ run = ... }; they merge global→local, use the default inline shell like tasks, fail fast, and print instead of run under--dry-run.The bootstrap runner now invokes hooks around packages, dotfiles, defaults, user, and tools, reloads config after dotfiles apply and tool install so hooks from newly applied mise config files take effect, and on dry-run parses templated/dotfile mise config targets so later-phase hooks show up in the preview.
Docs, generated CLI usage/man, config parsing tests, and e2e coverage for dotfile-sourced hooks are updated. The ubi e2e on linux/amd64 switches from an FFmpeg URL to fd as the direct-archive install case.
Reviewed by Cursor Bugbot for commit 1bd40a8. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit
New Features
Documentation
Tests