Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
19 changes: 18 additions & 1 deletion schema/mise.json
Original file line number Diff line number Diff line change
Expand Up @@ -2959,7 +2959,7 @@
},
"system": {
"type": "object",
"description": "[experimental] machine-global bootstrapping (system packages)",
"description": "[experimental] machine-global bootstrapping (system packages, macOS defaults)",
"properties": {
"packages": {
"type": "object",
Expand All @@ -2971,6 +2971,23 @@
"type": "string",
"description": "version pin in the manager's native format, or \"latest\""
}
},
"defaults": {
"type": "object",
"description": "macOS user defaults to apply with `mise system install`, keyed by preferences domain (e.g. \"com.apple.dock\", \"NSGlobalDomain\")",
"additionalProperties": {
"type": "object",
"description": "defaults keys and their desired values for this domain",
"additionalProperties": {
"anyOf": [
{ "type": "boolean" },
{ "type": "integer" },
{ "type": "number" },
{ "type": "string" }
],
"description": "desired value, written with the matching `defaults write` type (-bool, -int, -float, -string)"
}
}
}
}
},
Expand Down
Loading
Loading