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
4 changes: 4 additions & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ export default withMermaid(
{ 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",
},
],
},
{
Expand Down
6 changes: 4 additions & 2 deletions docs/cli/system.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
- **Usage**: `mise system <SUBCOMMAND>`
- **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]`
[experimental] Manage system packages from `[system.packages]` 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).
macOS defaults are user preferences written with `defaults write`.
Unlike `[tools]`, they are not version-pinned per-project and are only
ever installed when explicitly requested with `mise system install`.
ever applied when explicitly requested with `mise system install`.

## Subcommands

Expand Down
9 changes: 6 additions & 3 deletions docs/cli/system/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@
- **Aliases**: `i`
- **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]`
Install missing system packages from `[system.packages]` and apply 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).
root (see the `system_packages.sudo` setting). On macOS, also writes any
`[system.defaults]` entries that are unset or differ from the config.

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
the config.
the config. Explicit packages and `--manager` scope the run to packages
only.

## Arguments

Expand Down
5 changes: 3 additions & 2 deletions docs/cli/system/status.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
- **Aliases**: `ls`
- **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]`
Show the status of system packages from `[system.packages]` and macOS
defaults from `[system.defaults]`

## Flags

Expand All @@ -15,7 +16,7 @@ Output in JSON format

### `--missing`

Exit with code 1 if any configured packages are missing
Exit with code 1 if any configured packages are missing or defaults are out of sync

Examples:

Expand Down
102 changes: 102 additions & 0 deletions docs/system-packages/defaults.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# 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`:

```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
```

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

| TOML value | written as | example |
| ---------- | ------------------ | ---------------------- |
| boolean | `-bool true/false` | `autohide = true` |
| integer | `-int <n>` | `tilesize = 48` |
| float | `-float <n>` | `scale = 1.5` |
| string | `-string <s>` | `orientation = "left"` |

Other plist shapes (arrays, dicts, dates, data) are not supported; entries
using them parse fine but are skipped with a warning, so configs written for
newer mise versions still work.

## Semantics

`[system.defaults]` follows the same rules as
[`[system.packages]`](/system-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.
- **Manual application only** — mise never writes defaults implicitly; only
`mise system install` 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.

User defaults are per-user, so unlike system packages no sudo is ever
involved. Host-scoped preferences (`defaults -currentHost`) and `sudo
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 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 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.

## App restarts

Some applications only pick up changed defaults after a relaunch — mise
prints a reminder after writing. The usual suspects:

```sh
killall Dock
killall Finder
killall SystemUIServer
```

mise deliberately does not kill applications itself.

## Finding keys

To discover a setting's domain and key, change it in System Settings and
diff the output of `defaults read` before and after, or read a domain
directly:

```sh
defaults read com.apple.dock
defaults read-type com.apple.dock tilesize
```
4 changes: 4 additions & 0 deletions docs/system-packages/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ itself. Use them for shared libraries and build dependencies that dev tools
need (`libssl-dev`, `postgresql`, `ffmpeg`), not for the dev tools
themselves — those belong in `[tools]`.

The `[system]` section can also declare
[macOS defaults](/system-packages/defaults.html) (`[system.defaults]`),
applied by the same `mise system install` command.

## Supported package managers

| Manager | Platform | Page |
Expand Down
49 changes: 49 additions & 0 deletions e2e/cli/test_system_defaults
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env bash

cat <<EOF >mise.toml
[system.defaults.NSGlobalDomain]
KeyRepeat = 2

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

# status renders on any platform; on non-macOS entries are skipped, not errors
assert_succeed "mise system status"
assert_contains "mise system status" "com.apple.dock"
assert_contains "mise system status --json" '"defaults"'
if [[ $(uname) != "Darwin" ]]; then
assert_contains "mise system status" "skipped"
assert_contains "mise system status --json" '"available": false'
# unavailable entries don't count as missing (cross-platform configs)
assert_succeed "mise system status --missing"
# install skips defaults silently off-macOS
assert_succeed "mise system install --yes"
fi

# dry-run never writes anything
assert_succeed "mise system install --dry-run --yes"

# unsupported value types warn but don't fail
cat <<EOF >mise.toml
[system.defaults."com.apple.dock"]
future-array = [1, 2]
EOF
assert_succeed "mise system status"
assert_contains "mise system status 2>&1" "unsupported value type"

# a domain entry that isn't a table warns but doesn't fail
cat <<EOF >mise.toml
[system.defaults]
autohide = true
EOF
assert_succeed "mise system status"
assert_contains "mise system status 2>&1" "expected a table"

# empty [system.defaults] section
cat <<EOF >mise.toml
[system.defaults]
EOF
assert_succeed "mise system status"
20 changes: 12 additions & 8 deletions man/man1/mise.1
Original file line number Diff line number Diff line change
Expand Up @@ -458,16 +458,16 @@ Symlinks all tool versions from an external tool into mise
Symlinks all ruby tool versions from an external tool into mise
.TP
\fBsystem\fR
[experimental] Manage system packages from `[system.packages]`
[experimental] Manage system packages from `[system.packages]` and macOS
.TP
\fBsystem install\fR
Install missing system packages from `[system.packages]`
Install missing system packages from `[system.packages]` and apply macOS
.RS
\fIAliases: \fRi
.RE
.TP
\fBsystem status\fR
Show the status of system packages from `[system.packages]`
Show the status of system packages from `[system.packages]` and macOS
.RS
\fIAliases: \fRls
.RE
Expand Down Expand Up @@ -2745,15 +2745,18 @@ Symlinks all ruby tool versions from an external tool into mise
\fB\-\-brew\fR
Get tool versions from Homebrew
.SH "MISE SYSTEM INSTALL"
Install missing system packages from `[system.packages]`
Install missing system packages from `[system.packages]` and apply 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).
root (see the `system_packages.sudo` setting). On macOS, also writes any
`[system.defaults]` entries that are unset or differ from the config.

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
the config.
the config. Explicit packages and `\-\-manager` scope the run to packages
only.
.PP
\fBUsage:\fR mise system install [OPTIONS] [<PACKAGE>] ...
.PP
Expand All @@ -2777,7 +2780,8 @@ Refresh package manager metadata first (apt: `apt\-get update`)
\fB<PACKAGE>\fR
Packages in `manager:package` form; defaults to everything configured in [system.packages]
.SH "MISE SYSTEM STATUS"
Show the status of system packages from `[system.packages]`
Show the status of system packages from `[system.packages]` and macOS
defaults from `[system.defaults]`
.PP
\fBUsage:\fR mise system status [OPTIONS]
.PP
Expand All @@ -2788,7 +2792,7 @@ Show the status of system packages from `[system.packages]`
Output in JSON format
.TP
\fB\-\-missing\fR
Exit with code 1 if any configured packages are missing
Exit with code 1 if any configured packages are missing or defaults are out of sync
.SH "MISE SYSTEM UPGRADE"
Upgrade installed system packages from `[system.packages]`

Expand Down
32 changes: 23 additions & 9 deletions mise.usage.kdl
Original file line number Diff line number Diff line change
Expand Up @@ -2849,27 +2849,38 @@ Examples:
flag --brew help="Get tool versions from Homebrew"
}
}
cmd system subcommand_required=#true help="[experimental] Manage system packages from `[system.packages]`" {
cmd system subcommand_required=#true help=#"""
[experimental] Manage system packages from `[system.packages]` and macOS
defaults from `[system.defaults]`
"""# {
long_help #"""
[experimental] Manage system packages from `[system.packages]`
[experimental] Manage system packages from `[system.packages]` 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).
macOS defaults are user preferences written with `defaults write`.
Unlike `[tools]`, they are not version-pinned per-project and are only
ever installed when explicitly requested with `mise system install`.
ever applied when explicitly requested with `mise system install`.
"""#
cmd install help="Install missing system packages from `[system.packages]`" {
cmd install help=#"""
Install missing system packages from `[system.packages]` and apply macOS
defaults from `[system.defaults]`
"""# {
alias i
long_help #"""
Install missing system packages from `[system.packages]`
Install missing system packages from `[system.packages]` and apply 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).
root (see the `system_packages.sudo` setting). On macOS, also writes any
`[system.defaults]` entries that are unset or differ from the config.

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
the config.
the config. Explicit packages and `--manager` scope the run to packages
only.
"""#
after_long_help #"""
Examples:
Expand All @@ -2890,7 +2901,10 @@ Examples:
flag --update help="Refresh package manager metadata first (apt: `apt-get update`)"
arg "[PACKAGE]…" help="Packages in `manager:package` form; defaults to everything configured in [system.packages]" required=#false var=#true
}
cmd status help="Show the status of system packages from `[system.packages]`" {
cmd status help=#"""
Show the status of system packages from `[system.packages]` and macOS
defaults from `[system.defaults]`
"""# {
alias ls
after_long_help #"""
Examples:
Expand All @@ -2901,7 +2915,7 @@ Examples:

"""#
flag "-J --json" help="Output in JSON format"
flag --missing help="Exit with code 1 if any configured packages are missing"
flag --missing help="Exit with code 1 if any configured packages are missing or defaults are out of sync"
}
cmd upgrade help="Upgrade installed system packages from `[system.packages]`" {
alias up
Expand Down
Loading
Loading