Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 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
51 changes: 32 additions & 19 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,25 +88,6 @@ export default withMermaid(
{ text: "Security", link: "/security" },
{ text: "OCI Images (experimental)", link: "/dev-tools/mise-oci" },
{ text: "Deps", link: "/dev-tools/deps" },
{
text: "System Packages (experimental)",
link: "/system-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: "macOS Defaults",
link: "/system-packages/defaults",
},
],
},
{
text: "System Files (experimental)",
link: "/system-files",
},
{
text: "Backend Architecture",
link: "/dev-tools/backend_architecture",
Expand Down Expand Up @@ -155,6 +136,38 @@ export default withMermaid(
},
],
},
{
text: "System (experimental)",
items: [
{
text: "System Packages",
link: "/system-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: "System Files (dotfiles)",
link: "/system-files",
},
{
text: "System Edits",
link: "/system-edits",
},
{
text: "macOS Defaults",
link: "/system-packages/defaults",
},
{
text: "mise bootstrap",
link: "/cli/bootstrap",
},
],
},
{
text: "Environments",
items: [
Expand Down
3 changes: 2 additions & 1 deletion docs/cli/bootstrap.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
Runs the bootstrap steps for the current config in order:

1. `mise system install` — install missing `[system.packages]`, apply
`[system.files]`, and write `[system.defaults]` (macOS)
`[system.files]` and `[system.edits]`, and write `[system.defaults]`
(macOS)
2. `mise install` — install missing tools from `[tools]`
3. `mise run bootstrap` — if a task named `bootstrap` is defined

Expand Down
13 changes: 8 additions & 5 deletions docs/cli/system.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@
- **Source code**: [`src/cli/system/mod.rs`](https://github.com/jdx/mise/blob/main/src/cli/system/mod.rs)

[experimental] Manage system packages from `[system.packages]`, files
from `[system.files]`, and macOS defaults from `[system.defaults]`
from `[system.files]`, edits from `[system.edits]`, and macOS defaults
from `[system.defaults]`

System packages are machine-global packages installed by the OS package
manager (apt, dnf, pacman) or mise's Homebrew-bottle installer (brew).
System files are config files (dotfiles) symlinked, copied, or rendered
to machine-global paths. macOS defaults are user preferences written
with `defaults write`. Unlike `[tools]`, none of these are version-pinned
per-project and they are only ever acted on when explicitly requested
with `mise system install`.
to machine-global paths. System edits manage one piece of a file
something else owns — a marker-delimited block or a single line. macOS
defaults are user preferences written with `defaults write`. Unlike
`[tools]`, none of these are version-pinned per-project and they are only
ever acted on when explicitly requested with `mise system install` (or
`mise bootstrap`).

## Subcommands

Expand Down
8 changes: 5 additions & 3 deletions docs/cli/system/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
- **Source code**: [`src/cli/system/install.rs`](https://github.com/jdx/mise/blob/main/src/cli/system/install.rs)

Install missing system packages from `[system.packages]`, apply files
from `[system.files]`, and write macOS defaults from `[system.defaults]`
from `[system.files]` and edits from `[system.edits]`, and write macOS
defaults from `[system.defaults]`

Checks which configured packages are missing and installs them with the
system package manager. This may elevate with sudo when not running as
root (see the `system_packages.sudo` setting). Afterwards, `[system.files]`
entries that aren't in their desired state are applied, and on macOS any
`[system.defaults]` entries that are unset or differ are written.
and `[system.edits]` entries that aren't in their desired state are
applied, and on macOS any `[system.defaults]` entries that are unset or
differ are written.

Packages can also be given explicitly in `manager:package` form (e.g.
`apt:curl`, `brew:jq`); they are installed whether or not they appear in
Expand Down
3 changes: 2 additions & 1 deletion docs/cli/system/status.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
- **Source code**: [`src/cli/system/status.rs`](https://github.com/jdx/mise/blob/main/src/cli/system/status.rs)

Show the status of system packages from `[system.packages]`, files from
`[system.files]`, and macOS defaults from `[system.defaults]`
`[system.files]`, edits from `[system.edits]`, and macOS defaults from
`[system.defaults]`

## Flags

Expand Down
128 changes: 128 additions & 0 deletions docs/system-edits.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# System Edits <Badge type="warning" text="experimental" />

Where [System Files](/system-files.html) manages whole files, `[system.edits]`
manages one small piece of a file something else owns — the `mise activate`
line in your shell rc, an entry in `/etc/hosts`. Entries are keyed by target
path, then by an id naming each edit within the file:

```toml
[system.edits]
"~/.zshrc" = {
activate = 'eval "$(mise activate zsh)"',
aliases = '''
alias ll='ls -l'
alias la='ls -la'
''',
}
"/etc/hosts" = { dev = { line = "127.0.0.1 dev.local" } }
```

A string value is inline block content (TOML multiline strings keep larger
blocks readable); a table value spells out the operation. Dotted keys work
too when you prefer one entry per line:

```toml
[system.edits]
"~/.zshrc".activate = 'eval "$(mise activate zsh)"'
"~/.gitconfig".identity = { source = "snippets/git-identity.tmpl", template = "tera" }
```

## Blocks

A `block` is delimited by marker comments in the target file, named by the
entry's id:

```sh
# >>> mise:activate >>> managed by mise — do not edit between markers
eval "$(mise activate zsh)"
# <<< mise:activate <<<
```

The markers are the ownership record, stored in the file itself, so the
design stays stateless: applying replaces only what's between them (or
appends the block if absent), and everything else in the file is untouched.
Content can come from three places:

```toml
[system.edits]
"~/.zshrc" = {
activate = "...", # inline (string shorthand)
aliases = { source = "snippets/aliases.sh" }, # from a file, relative to this config
prompt = { source = "snippets/prompt.tmpl", template = "tera" }, # rendered with the template engine
}
```

Ids may contain letters, digits, `_`, `-`, and `.`. The marker comment
prefix is inferred from the file extension (`#` for shell/config files,
`--` for Lua, `//` for C-like languages, `;` for INI, `"` for vim) and can
be overridden with `comment = "..."`. Files that can't hold line comments
at all (strict JSON, XML) aren't a fit for blocks — use
[System Files](/system-files.html) to own the whole file instead.

`template = "tera"` names the engine rather than being a boolean so other
engines can be added later; unknown engines from newer mise versions warn
and are skipped, like unrecognized operations.

Detecting whether a template block has drifted requires rendering it, so
`mise system status` (and a real install) evaluates templates — including
any `exec()` calls — from your trusted config, just like `[env]` templates.
`--dry-run` is the exception: it promises to execute nothing, so it skips
template rendering and lists those entries as `(if changed)`.

## Lines

A `line` ensures an exact line exists somewhere in the file, appending it at
the end if absent. It never modifies or removes other lines, which is what
makes it safely idempotent — use it for files where a three-line marker
block is overkill or comments aren't tolerated. The id is only a label (and
the merge identity); it isn't written to the file.

## Semantics

Edits follow the same rules as the rest of [`[system]`](/system-packages/):

- **Declarative and additive** — entries merge across the
[config hierarchy](/configuration.html) (global → project) as a union,
keyed by `(path, id)`; a more local config overrides an edit with the
same id, exactly like [System Files](/system-files.html) overrides by
target.
- **Manual application only** — nothing is written implicitly. Only
`mise system install` (or [`mise bootstrap`](/cli/bootstrap.html)) applies
edits.
- **Idempotent** — entries already in their desired state are skipped;
re-running is always safe.
- **Surgical** — edits never create conflicts with existing content and
never need `--force`: a block owns only what's between its markers, a
line only ever appends. Two cases are refused with an error instead of
guessed at: corrupted markers (a begin without an end, or duplicates) and
targets that are symlinks — an edit through a symlink would modify
whatever the link points at (often a `[system.files]` source), so point
the edit at the real file instead.

Removing an entry from config leaves its block or line in the file (mise
keeps no state database); delete it by hand. Blocks at least carry their
provenance — the markers name mise and the id — while a stray line looks
like any other, which is a reason to prefer blocks for anything
non-obvious.

## Commands

```sh
mise system status # shows edit state: applied/missing/differs
mise system status --missing # exit 1 if anything is missing (CI check)

mise system install # packages, then files, then edits (prompts first)
mise system install --dry-run # print what would be done
mise system install --yes # skip the confirmation prompt
```

`mise system status` reports each edit as `applied`, `missing` (no markers
or line yet), `differs` (block content changed, corrupted markers, or a
symlink target), or `source missing` (a block whose `source` file doesn't
exist).

## Root-owned files

Edits write as the current user — there is no sudo here. Editing
`/etc/hosts` works when running as root (containers, CI); otherwise mise
fails with an ordinary permission error.
4 changes: 4 additions & 0 deletions docs/system-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ the directory of the config file that declares the entry, so a global
`~/.config/mise/config.toml` can manage dotfiles kept next to it, and a
project config can ship machine setup from the repo.

To manage one piece of a file something else owns (a line in `.zshrc`, an
entry in `/etc/hosts`) rather than the whole file, see
[System Edits](/system-edits.html).

## Modes

| Mode | Behavior |
Expand Down
37 changes: 37 additions & 0 deletions docs/tips-and-tricks.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,43 @@ mise generate task-stubs --mise-bin ./bin/mise
The generated task stubs behave like small project commands, while `bin/mise`
downloads and runs the pinned mise binary for the project.

## Machine bootstrapping with `[system]` <Badge type="warning" text="experimental" />

Beyond `[tools]`, the `[system]` config section can declare everything else a
machine needs, and [`mise bootstrap`](/cli/bootstrap.html) converges all of
it in one command — system packages, then files and edits, then tools, then
a `bootstrap` task if you define one:

```toml
[system.packages] # OS packages (apt/dnf/pacman/brew)
"apt:build-essential" = "latest"
"brew:postgresql@17" = "latest"

[system.files] # dotfiles: symlink/copy/template
"~/.gitconfig" = "dotfiles/gitconfig"
"~/.config/nvim" = "dotfiles/nvim"

[system.edits] # one piece of a file you don't own
"~/.zshrc".activate = 'eval "$(mise activate zsh)"'

[system.defaults] # macOS defaults write
"com.apple.dock.autohide" = true

[tasks.bootstrap] # anything else, with tools on PATH
run = "gh auth status || gh auth login"
```

```sh
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 system status --missing` makes a CI
check, and nothing is ever applied implicitly. See
[System Packages](/system-packages/), [System Files](/system-files.html),
[System Edits](/system-edits.html), and
[macOS Defaults](/system-packages/defaults.html).

## Installation via zsh zinit

[Zinit](https://github.com/zdharma-continuum/zinit) is a plugin manager for ZSH, which this snippet you will get mise (and usage for shell completion):
Expand Down
10 changes: 7 additions & 3 deletions e2e/cli/test_bootstrap
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@ cat <<EOF >mise.toml
EOF
assert_succeed "mise bootstrap --yes"

# bootstrap applies system files, installs tools, and runs the `bootstrap`
# task afterwards, with the installed tools on PATH
# bootstrap applies system files and edits, installs tools, and runs the
# `bootstrap` task afterwards, with the installed tools on PATH
echo "gitconfig content" >gitconfig
cat <<EOF >mise.toml
cat <<'EOF' >mise.toml
[tools]
tiny = "1.0.0"

[system.files]
"~/.gitconfig" = "gitconfig"

[system.edits]
"~/.zshrc".activate = 'eval "$(mise activate zsh)"'

# the task only succeeds if the installed tool is on PATH
[tasks.bootstrap]
run = "command -v rtx-tiny && echo task-done > bootstrap_ran"
Expand All @@ -24,6 +27,7 @@ assert_succeed "mise bootstrap --yes"
assert "cat bootstrap_ran" "task-done"
assert_contains "mise ls tiny" "1.0.0"
assert "readlink ~/.gitconfig" "$PWD/gitconfig"
assert_contains "cat ~/.zshrc" ">>> mise:activate >>>"

# idempotent: re-running is fine
assert_succeed "mise bootstrap --yes"
Expand Down
Loading
Loading