Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion docs/bootstrap.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

`mise bootstrap` sets up the machine-level pieces around a mise config: OS
packages, dotfiles, macOS defaults, macOS LaunchAgents, the user's login
shell, tools, and any final project-specific task.
shell, tools, and any final project-specific task. You can also add hooks that
run at named points in the bootstrap sequence.

Use bootstrap for things that are needed before a project or workstation is
ready, but that do not belong in `[tools]`: native libraries, Homebrew
Expand All @@ -20,6 +21,12 @@ machine setup.
5. `mise bootstrap user apply` applies `[bootstrap.user]`.
6. `mise install` installs missing `[tools]`.
7. `mise run bootstrap` runs a task named `bootstrap`, if one exists.
8. `[bootstrap.hooks.final]` runs after the bootstrap task, if configured.

Hook phases can also run before and after the built-in steps:
`pre-packages`, `post-packages`, `pre-dotfiles`, `post-dotfiles`,
`pre-defaults`, `post-defaults`, `pre-user`, `post-user`, `pre-tools`, and
`post-tools`.

The declarative steps converge: if a package is already installed, a dotfile
already matches, or a default is already set, mise skips it. The `bootstrap`
Expand Down Expand Up @@ -48,6 +55,12 @@ run_at_load = true
[bootstrap.user]
login_shell = "/bin/zsh"

[bootstrap.hooks.pre-packages]
run = "softwareupdate --install-rosetta --agree-to-license"

[bootstrap.hooks.post-defaults]
run = "killall Dock || true"

[tools]
node = "lts"
python = "3.12"
Expand Down Expand Up @@ -96,6 +109,7 @@ place but should not install anything during that check.
| `[bootstrap.macos.defaults]` | macOS user preferences written through `defaults write` |
| `[bootstrap.macos.launchd.agents]` | macOS user LaunchAgents written and loaded with `launchctl` |
| `[bootstrap.user]` | Current-user settings such as `login_shell` |
| `[bootstrap.hooks]` | Commands that run at named bootstrap phases |
| `[tools]` | Versioned dev tools managed by mise |
| `[tasks.bootstrap]` | Anything custom that should run after tools are installed |

Expand All @@ -104,6 +118,40 @@ Use declarative sections when mise can inspect and converge the state. Use
such as cloning a private repository, running an auth flow, or seeding local
data.

## Hooks

Hooks run only during explicit `mise bootstrap` invocations. A hook can be
specified as a command string, an array of command strings, or a table with a
`run` field. They use the same default inline shell setting as tasks, stop the
bootstrap if they fail, and print the command instead of running it during
`mise bootstrap --dry-run`. Hooks run in the current process environment; use
`mise exec -- ...` inside a hook, or use `[tasks.bootstrap]`, when the command
needs tools from `[tools]` on PATH.

```toml
[bootstrap.hooks.pre-packages]
run = "softwareupdate --install-rosetta --agree-to-license"

[bootstrap.hooks.post-tools]
run = [
"mise exec -- corepack enable",
"mise exec -- rustup component add rustfmt clippy",
]

[bootstrap.hooks.final]
run = "gh auth status || gh auth login"
```

As shorthand, a hook phase can also be set directly:

```toml
[bootstrap.hooks]
post-defaults = "killall Dock || true"
```

Hooks merge across the config hierarchy from global to local, so shared config
can define broad machine setup while a project adds its own phase commands.

## Common Workflows

### New Machine
Expand Down
7 changes: 7 additions & 0 deletions docs/cli/bootstrap.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,23 @@

Runs the bootstrap steps for the current config in order:

0. `[bootstrap.hooks.pre-packages]` — optional setup hook
1. `mise bootstrap packages install` — install missing
`[bootstrap.packages]`
then `[bootstrap.hooks.post-packages]`
2. `mise dotfiles apply` — apply dotfiles from `[dotfiles]`
surrounded by `pre-dotfiles`/`post-dotfiles` hooks
3. `mise bootstrap macos-defaults apply` — write
`[bootstrap.macos.defaults]` entries (macOS)
surrounded by `pre-defaults`/`post-defaults` hooks
4. `mise bootstrap launchd apply` — install/load macOS LaunchAgents
5. `mise bootstrap user apply` — set `[bootstrap.user].login_shell`
(Unix)
surrounded by `pre-user`/`post-user` hooks
6. `mise install` — install missing tools from `[tools]`
surrounded by `pre-tools`/`post-tools` hooks
7. `mise run bootstrap` — if a task named `bootstrap` is defined
8. `[bootstrap.hooks.final]` — optional final hook

The declarative steps converge — anything already in its desired state
is skipped, so re-running is safe. The `bootstrap` task runs on every
Expand Down
8 changes: 7 additions & 1 deletion docs/tips-and-tricks.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ run_at_load = true
[bootstrap.user] # current user's login shell
login_shell = "/bin/zsh"

[bootstrap.hooks.post-defaults] # optional phase hooks
run = "killall Dock || true"
Comment thread
coderabbitai[bot] marked this conversation as resolved.

[tasks.bootstrap] # anything else, with tools on PATH
run = "gh auth status || gh auth login"
```
Expand All @@ -113,7 +116,10 @@ mise bootstrap --yes # new laptop or container -> ready to work
Everything is declarative and idempotent: re-running skips whatever is
already in its desired state, `mise bootstrap packages status --missing` and
`mise dotfiles status --missing` make CI checks, and nothing is ever applied
implicitly. See
implicitly. The exceptions are `[bootstrap.hooks]` and `[tasks.bootstrap]`,
which are imperative commands run during `mise bootstrap` and may have side
effects; treat hook commands as non-idempotent unless they are written to
converge safely. See
[Bootstrap](/bootstrap.html), [Bootstrap Packages](/bootstrap/packages/),
[Dotfiles](/dotfiles.html), [macOS Defaults](/bootstrap/macos-defaults.html),
[launchd](/bootstrap/launchd.html), and [User Login Shell](/bootstrap/user.html).
Expand Down
4 changes: 2 additions & 2 deletions e2e/backend/test_ubi
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ assert_contains "$MISE_DATA_DIR/shims/jc --version" "jc version: 1.25.3"

# only run on linux/amd64
if [ "$(uname -m)" = "x86_64" ] && [ "$(uname -s)" = "Linux" ]; then
mise use 'ubi:https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-linux64-gpl.tar.xz[exe=ffmpeg]'
assert_contains "$MISE_DATA_DIR/shims/ffmpeg -version" "ffmpeg version"
mise use 'ubi:https://github.com/sharkdp/fd/releases/download/v10.3.0/fd-v10.3.0-x86_64-unknown-linux-gnu.tar.gz[exe=fd]'
assert_contains "$MISE_DATA_DIR/shims/fd --version" "fd 10.3.0"
fi

cat <<EOF >mise.toml
Expand Down
51 changes: 51 additions & 0 deletions e2e/cli/test_bootstrap
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,54 @@ cat <<EOF >mise.toml
"pacman:bc" = "latest"
EOF
assert_succeed "mise bootstrap --dry-run"

# dotfiles can add config that contributes hooks to later phases in the same run
mkdir -p dotfiles/mise
cat <<'EOF' >dotfiles/mise/config.toml
[bootstrap.hooks.post-dotfiles]
run = "echo post-dotfiles > post_dotfiles_hook_ran"

[bootstrap.hooks.final]
run = "echo final > final_hook_ran"
EOF
cat <<EOF >mise.toml
[dotfiles]
"~/.config/mise/config.toml" = "dotfiles/mise/config.toml"
EOF
rm -f post_dotfiles_hook_ran final_hook_ran
assert_contains "mise bootstrap --dry-run" "echo post-dotfiles > post_dotfiles_hook_ran"
assert_contains "mise bootstrap --dry-run" "echo final > final_hook_ran"
assert_fail "cat post_dotfiles_hook_ran"
assert_fail "cat final_hook_ran"
assert_succeed "mise bootstrap --yes"
assert "cat post_dotfiles_hook_ran" "post-dotfiles"
assert "cat final_hook_ran" "final"

# dry-run previews hooks from templated config dotfiles
cat <<'EOF' >dotfiles/mise/config.local.toml.tmpl
[bootstrap.hooks.final]
run = "echo {{ vars.templated_hook_value }} > templated_hook_ran"
EOF
cat <<EOF >mise.toml
[vars]
templated_hook_value = "template-final"

[dotfiles]
"~/.config/mise/config.local.toml" = { source = "dotfiles/mise/config.local.toml.tmpl", mode = "template" }
EOF
rm -f templated_hook_ran
assert_contains "mise bootstrap --dry-run" "echo template-final > templated_hook_ran"
assert_fail "cat templated_hook_ran"

# dry-run recognizes local config targets such as ~/.mise/config.toml
cat <<'EOF' >dotfiles/mise/local-config.toml
[bootstrap.hooks.post-dotfiles]
run = "echo mise-dir > mise_dir_hook_ran"
EOF
cat <<EOF >mise.toml
[dotfiles]
"~/.mise/config.toml" = "dotfiles/mise/local-config.toml"
EOF
rm -f mise_dir_hook_ran
assert_contains "mise bootstrap --dry-run" "echo mise-dir > mise_dir_hook_ran"
assert_fail "cat mise_dir_hook_ran"
7 changes: 7 additions & 0 deletions man/man1/mise.1
Original file line number Diff line number Diff line change
Expand Up @@ -793,16 +793,23 @@ e.g.: ruby@3

Runs the bootstrap steps for the current config in order:

0. `[bootstrap.hooks.pre\-packages]` — optional setup hook
1. `mise bootstrap packages install` — install missing
`[bootstrap.packages]`
then `[bootstrap.hooks.post\-packages]`
2. `mise dotfiles apply` — apply dotfiles from `[dotfiles]`
surrounded by `pre\-dotfiles`/`post\-dotfiles` hooks
3. `mise bootstrap macos\-defaults apply` — write
`[bootstrap.macos.defaults]` entries (macOS)
surrounded by `pre\-defaults`/`post\-defaults` hooks
4. `mise bootstrap launchd apply` — install/load macOS LaunchAgents
5. `mise bootstrap user apply` — set `[bootstrap.user].login_shell`
(Unix)
surrounded by `pre\-user`/`post\-user` hooks
6. `mise install` — install missing tools from `[tools]`
surrounded by `pre\-tools`/`post\-tools` hooks
7. `mise run bootstrap` — if a task named `bootstrap` is defined
8. `[bootstrap.hooks.final]` — optional final hook

The declarative steps converge — anything already in its desired state
is skipped, so re\-running is safe. The `bootstrap` task runs on every
Expand Down
7 changes: 7 additions & 0 deletions mise.usage.kdl
Original file line number Diff line number Diff line change
Expand Up @@ -286,16 +286,23 @@ cmd bootstrap help="[experimental] Set up a machine for the current config in on

Runs the bootstrap steps for the current config in order:

0. `[bootstrap.hooks.pre-packages]` — optional setup hook
1. `mise bootstrap packages install` — install missing
`[bootstrap.packages]`
then `[bootstrap.hooks.post-packages]`
2. `mise dotfiles apply` — apply dotfiles from `[dotfiles]`
surrounded by `pre-dotfiles`/`post-dotfiles` hooks
3. `mise bootstrap macos-defaults apply` — write
`[bootstrap.macos.defaults]` entries (macOS)
surrounded by `pre-defaults`/`post-defaults` hooks
4. `mise bootstrap launchd apply` — install/load macOS LaunchAgents
5. `mise bootstrap user apply` — set `[bootstrap.user].login_shell`
(Unix)
surrounded by `pre-user`/`post-user` hooks
6. `mise install` — install missing tools from `[tools]`
surrounded by `pre-tools`/`post-tools` hooks
7. `mise run bootstrap` — if a task named `bootstrap` is defined
8. `[bootstrap.hooks.final]` — optional final hook

The declarative steps converge — anything already in its desired state
is skipped, so re-running is safe. The `bootstrap` task runs on every
Expand Down
Loading
Loading