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
48 changes: 28 additions & 20 deletions docs/.vitepress/cli_commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ export const commands: { [key: string]: Command } = {
},
bootstrap: {
hide: false,
subcommands: {
"macos-defaults": {
hide: false,
},
packages: {
hide: false,
},
user: {
hide: false,
},
},
},
cache: {
hide: false,
Expand Down Expand Up @@ -101,6 +112,23 @@ export const commands: { [key: string]: Command } = {
},
},
},
dotfiles: {
hide: false,
subcommands: {
add: {
hide: false,
},
apply: {
hide: false,
},
edit: {
hide: false,
},
status: {
hide: false,
},
},
},
edit: {
hide: false,
},
Expand Down Expand Up @@ -316,26 +344,6 @@ export const commands: { [key: string]: Command } = {
},
},
},
system: {
hide: false,
subcommands: {
brew: {
hide: false,
},
install: {
hide: false,
},
status: {
hide: false,
},
upgrade: {
hide: false,
},
use: {
hide: false,
},
},
},
tasks: {
hide: false,
subcommands: {
Expand Down
29 changes: 13 additions & 16 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,34 +137,31 @@ export default withMermaid(
],
},
{
text: "System (experimental)",
text: "Bootstrap (experimental)",
items: [
{ text: "Overview", link: "/bootstrap" },
{
text: "System Packages",
link: "/system-packages/",
text: "Bootstrap Packages",
link: "/bootstrap/packages/",
collapsed: true,
items: [
{ text: "apt", link: "/system-packages/apt" },
{ text: "dnf", link: "/system-packages/dnf" },
{ text: "pacman", link: "/system-packages/pacman" },
{ text: "brew", link: "/system-packages/brew" },
{ text: "apt", link: "/bootstrap/packages/apt" },
{ text: "dnf", link: "/bootstrap/packages/dnf" },
{ text: "pacman", link: "/bootstrap/packages/pacman" },
{ text: "brew", link: "/bootstrap/packages/brew" },
],
},
{
text: "System Files (dotfiles)",
link: "/system-files",
},
{
text: "System Edits",
link: "/system-edits",
text: "Dotfiles",
link: "/dotfiles",
},
{
text: "macOS Defaults",
link: "/system-packages/defaults",
link: "/bootstrap/macos-defaults",
},
{
text: "mise bootstrap",
link: "/cli/bootstrap",
text: "User Login Shell",
link: "/bootstrap/user",
},
],
},
Expand Down
151 changes: 151 additions & 0 deletions docs/bootstrap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# Bootstrap <Badge type="warning" text="experimental" />

`mise bootstrap` sets up the machine-level pieces around a mise config: OS
packages, dotfiles, macOS defaults, the user's login shell, tools, and any
final project-specific task.

Use bootstrap for things that are needed before a project or workstation is
ready, but that do not belong in `[tools]`: native libraries, Homebrew
formulae, shell rc files, editor config, macOS preferences, and one-time
machine setup.

## How it runs

`mise bootstrap` runs these steps in order:

1. `mise bootstrap packages install` installs missing `[bootstrap.packages]`.
2. `mise dotfiles apply` applies `[dotfiles]`.
3. `mise bootstrap macos-defaults apply` writes `[bootstrap.macos.defaults]`.
4. `mise bootstrap user apply` applies `[bootstrap.user]`.
5. `mise install` installs missing `[tools]`.
6. `mise run bootstrap` runs a task named `bootstrap`, if one exists.

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`
task runs every time, so keep it idempotent.

## Example

```toml
[bootstrap.packages]
"apt:build-essential" = "latest"
"brew:postgresql@17" = "latest"

[dotfiles]
"~/.gitconfig" = { mode = "symlink" }
"~/.config/nvim" = { mode = "symlink" }
"~/.zshrc/activate" = { block = 'eval "$(mise activate zsh)"' }

[bootstrap.macos.defaults]
"com.apple.dock" = { autohide = true }

[bootstrap.user]
login_shell = "/bin/zsh"

[tools]
node = "lts"
python = "3.12"

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

Then run:

```sh
mise bootstrap --yes
```

For a dry run:

```sh
mise bootstrap --dry-run
```

## Inspecting State

Use the narrower commands when you want to inspect one part of the bootstrap
state:

```sh
mise bootstrap packages status
mise dotfiles status
mise dotfiles apply --dry-run
mise dotfiles apply --dry-run --verbose
mise bootstrap macos-defaults status
mise bootstrap user status
```

`mise bootstrap packages status --missing` and `mise dotfiles status
--missing` are useful CI checks when a repo expects machine setup to be in
place but should not install anything during that check.

## What Goes Where

| Config | Use for |
| ---------------------------- | ------------------------------------------------------------- |
| `[bootstrap.packages]` | OS packages from apt, dnf, pacman, or brew |
| `[dotfiles]` | Whole-file dotfiles and small managed edits to existing files |
| `[bootstrap.macos.defaults]` | macOS user preferences written through `defaults write` |
| `[bootstrap.user]` | Current-user settings such as `login_shell` |
| `[tools]` | Versioned dev tools managed by mise |
| `[tasks.bootstrap]` | Anything custom that should run after tools are installed |

Use declarative sections when mise can inspect and converge the state. Use
`[tasks.bootstrap]` for imperative setup that does not fit those sections,
such as cloning a private repository, running an auth flow, or seeding local
data.

## Common Workflows

### New Machine

```sh
mise trust
mise bootstrap --yes
```

### Add A Package

```sh
mise bootstrap packages use apt:libssl-dev
```

This writes `[bootstrap.packages]` and installs what is missing.

### Capture An Edited Dotfile

```sh
$EDITOR ~/.zshrc
mise dotfiles add ~/.zshrc
```

`mise dotfiles add` stores the live file under `dotfiles.root` and writes an
explicit `[dotfiles]` entry with `mode`.

### Edit A Managed Dotfile

```sh
mise dotfiles edit ~/.zshrc
mise dotfiles apply ~/.zshrc
```

For symlinked dotfiles, `edit` opens the managed source, so it works with the
default `symlink` mode.

## Advanced: Self-Managing Config

You can manage the dotfiles repository and the mise global config as
dotfiles:

```toml
[settings]
dotfiles.root = "~/.dotfiles"

[dotfiles]
"~/.dotfiles" = "~/src/dotfiles"
"~/.config/mise/config.toml" = "~/.dotfiles/mise/config.toml"
```

The repo/source must exist before the first apply. Replacing the active
global config affects future mise invocations, so use this pattern carefully.
Original file line number Diff line number Diff line change
@@ -1,28 +1,18 @@
# macOS Defaults <Badge type="warning" text="experimental" />

mise can declare macOS user defaults (preferences) in the
`[system.defaults]` section of `mise.toml` and apply them with
`mise system install`:
`[bootstrap.macos.defaults]` section of `mise.toml` and apply them with
`mise bootstrap macos-defaults apply`:

```toml
[system.defaults.NSGlobalDomain]
KeyRepeat = 2
InitialKeyRepeat = 15
ApplePressAndHoldEnabled = false

[system.defaults."com.apple.dock"]
autohide = true
tilesize = 48
orientation = "left"

[system.defaults."com.apple.finder"]
ShowPathbar = true
AppleShowAllFiles = true
[bootstrap.macos.defaults]
NSGlobalDomain = { KeyRepeat = 2, InitialKeyRepeat = 15, ApplePressAndHoldEnabled = false }
"com.apple.dock" = { autohide = true, tilesize = 48, orientation = "left" }
"com.apple.finder" = { ShowPathbar = true, AppleShowAllFiles = true }
```

Each `[system.defaults."<domain>"]` table holds the keys for one preferences
domain — quote domains containing dots. Values map to the matching
`defaults write` type:
Each key under `[bootstrap.macos.defaults]` is a preferences domain. Quote
domains containing dots. Values map to the matching `defaults write` type:

| TOML value | written as | example |
| ---------- | ------------------ | ---------------------- |
Expand All @@ -37,22 +27,25 @@ newer mise versions still work.

## Semantics

`[system.defaults]` follows the same rules as
[`[system.packages]`](/system-packages/):
`[bootstrap.macos.defaults]` follows the same rules as
[`[bootstrap.packages]`](/bootstrap/packages/):

- **Declarative and additive** — (domain, key) pairs merge across the
[config hierarchy](/configuration.html) (global → project) as a union; a
more local config overrides the value of a pair the global config declared
but cannot remove it. mise never deletes a default.
- **OS-filtered** — on anything other than macOS the section is inert:
`mise system status` and `mise doctor` list the entries as skipped (so
nothing is silently invisible) and `mise system install` ignores them, so
a shared config authored for both Linux and macOS just works.
`mise bootstrap macos-defaults status` and `mise doctor` list the entries
as skipped (so nothing is silently invisible) and
`mise bootstrap macos-defaults apply` ignores them, so a shared config
authored for both Linux and macOS just works.
- **Manual application only** — mise never writes defaults implicitly; only
`mise system install` does, after the usual confirmation prompt.
`mise bootstrap macos-defaults apply` does, after the usual confirmation
prompt.
- **Strictly typed** — an existing value only counts as in sync when both
the value and the plist type match: an integer `1` does not satisfy a
configured `true`. `mise system install` converges it to the typed value.
configured `true`. `mise bootstrap macos-defaults apply` converges it to the
typed value.

User defaults are per-user, so unlike system packages no sudo is ever
involved. Host-scoped preferences (`defaults -currentHost`) and `sudo
Expand All @@ -61,21 +54,17 @@ defaults` system domains are not supported.
## Commands

```sh
mise system status # shows defaults drift next to package status
mise system status --missing # exit 1 if anything is unset or differs
mise bootstrap macos-defaults status # shows defaults drift
mise bootstrap macos-defaults status --missing # exit 1 if anything is unset or differs

mise system install # writes unset/differing defaults (prompts first)
mise system install --dry-run # print the `defaults write` commands instead
mise system install --yes # skip the confirmation prompt
mise bootstrap macos-defaults apply # writes unset/differing defaults
mise bootstrap macos-defaults apply --dry-run # print the `defaults write` commands
mise bootstrap macos-defaults apply --yes # skip the confirmation prompt
```

`mise system status` reports each entry as `set` (matches), `differs` (a
value exists but doesn't match — the current value is shown), or `unset`.
`mise doctor` summarizes the same drift.

Note that explicit package arguments and `--manager` scope
`mise system install` to packages only — defaults are applied by the bare
converge-everything form.
`mise bootstrap macos-defaults status` reports each entry as `set` (matches),
`differs` (a value exists but doesn't match — the current value is shown), or
`unset`. `mise doctor` summarizes the same drift.

## App restarts

Expand Down
Loading
Loading