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
18 changes: 17 additions & 1 deletion docs/bootstrap.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,23 @@ task runs every time, so keep it idempotent.
"~/.config/nvim" = { mode = "symlink" }
"~/.zshrc/activate" = { block = 'eval "$(mise activate zsh)"' }

[bootstrap.macos.dock]
autohide = true
orientation = "left"
tilesize = 48

[bootstrap.macos.finder]
show_pathbar = true

[bootstrap.macos.keyboard]
key_repeat = 2
initial_key_repeat = 15

[bootstrap.macos.trackpad]
tap_to_click = true

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

[bootstrap.macos.launchd.agents.my-sync]
program = "~/.local/bin/my-sync"
Expand Down Expand Up @@ -106,6 +121,7 @@ place but should not install anything during that check.
| ---------------------------------- | ------------------------------------------------------------- |
| `[bootstrap.packages]` | OS packages from apt, dnf, pacman, or brew |
| `[dotfiles]` | Whole-file dotfiles and small managed edits to existing files |
| `[bootstrap.macos.*]` | Curated macOS preferences for Dock/Finder/keyboard/trackpad |
| `[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` |
Expand Down
79 changes: 76 additions & 3 deletions docs/bootstrap/macos-defaults.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,85 @@ mise can declare macOS user defaults (preferences) in the
`mise bootstrap macos-defaults apply`:

```toml
[bootstrap.macos.dock]
autohide = true
orientation = "left"
tilesize = 48
show_recents = false

[bootstrap.macos.finder]
show_all_files = true
show_pathbar = true
preferred_view_style = "list"

[bootstrap.macos.keyboard]
key_repeat = 2
initial_key_repeat = 15
press_and_hold = false

[bootstrap.macos.trackpad]
tap_to_click = 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 }
"com.apple.finder" = { AppleShowAllFiles = true }
```

The curated sections compile to raw defaults entries. Use
`[bootstrap.macos.defaults]` for preferences not covered by the friendly
sections. Within the same config file, raw defaults override the raw
`(domain, key)` generated by a friendly setting. Across config files, normal
global to local precedence still applies, so a local friendly setting can
override a global raw default for the same pair.

## Friendly sections

`[bootstrap.macos.dock]` supports:

| Key | Raw default |
| --------------- | ------------------------------ |
| `autohide` | `com.apple.dock.autohide` |
| `orientation` | `com.apple.dock.orientation` |
| `tilesize` | `com.apple.dock.tilesize` |
| `magnification` | `com.apple.dock.magnification` |
| `largesize` | `com.apple.dock.largesize` |
| `show_recents` | `com.apple.dock.show-recents` |
| `mru_spaces` | `com.apple.dock.mru-spaces` |

`orientation` must be `bottom`, `left`, or `right`.

`[bootstrap.macos.finder]` supports:

| Key | Raw default |
| ------------------------- | ------------------------------------------------- |
| `show_all_files` | `com.apple.finder.AppleShowAllFiles` |
| `show_pathbar` | `com.apple.finder.ShowPathbar` |
| `show_status_bar` | `com.apple.finder.ShowStatusBar` |
| `show_extensions_warning` | `com.apple.finder.FXEnableExtensionChangeWarning` |
| `preferred_view_style` | `com.apple.finder.FXPreferredViewStyle` |

`preferred_view_style` must be `icon`, `list`, `column`, or `gallery`.

`[bootstrap.macos.keyboard]` supports:

| Key | Raw default |
| -------------------- | ------------------------------------------- |
| `key_repeat` | `NSGlobalDomain.KeyRepeat` |
| `initial_key_repeat` | `NSGlobalDomain.InitialKeyRepeat` |
| `press_and_hold` | `NSGlobalDomain.ApplePressAndHoldEnabled` |
| `fn_state` | `NSGlobalDomain.com.apple.keyboard.fnState` |

`[bootstrap.macos.trackpad]` supports:

| Key | Raw defaults |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `tap_to_click` | `com.apple.AppleMultitouchTrackpad.Clicking`, `com.apple.driver.AppleBluetoothMultitouch.trackpad.Clicking` |
| `three_finger_drag` | `com.apple.AppleMultitouchTrackpad.TrackpadThreeFingerDrag`, `com.apple.driver.AppleBluetoothMultitouch.trackpad.TrackpadThreeFingerDrag` |

Unknown friendly keys, invalid enum values, and unsupported value types warn
and are ignored.

## Raw defaults

Each key under `[bootstrap.macos.defaults]` is a preferences domain. Quote
domains containing dots. Values map to the matching `defaults write` type:

Expand Down
8 changes: 6 additions & 2 deletions docs/tips-and-tricks.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,12 @@ defaults, then LaunchAgents, then login shell, then tools, then a
"~/.config/nvim" = { mode = "symlink" }
"~/.zshrc/activate" = { block = 'eval "$(mise activate zsh)"' }

[bootstrap.macos.defaults] # macOS defaults write
"com.apple.dock" = { autohide = true }
[bootstrap.macos.dock] # friendly macOS defaults
autohide = true
orientation = "left"

[bootstrap.macos.finder]
show_pathbar = true

[bootstrap.macos.launchd.agents.my-sync] # macOS user LaunchAgents
program = "~/.local/bin/my-sync"
Expand Down
30 changes: 30 additions & 0 deletions e2e/cli/test_system_defaults
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,28 @@ cat <<EOF >mise.toml
[bootstrap.macos.defaults]
NSGlobalDomain = { KeyRepeat = 2 }
"com.apple.dock" = { autohide = true, tilesize = 48, orientation = "left" }

[bootstrap.macos.dock]
show_recents = false

[bootstrap.macos.finder]
show_all_files = true
preferred_view_style = "list"

[bootstrap.macos.keyboard]
initial_key_repeat = 15

[bootstrap.macos.trackpad]
tap_to_click = true
EOF

# status renders on any platform; on non-macOS entries are skipped, not errors
assert_succeed "mise bootstrap macos-defaults status"
assert_contains "mise bootstrap macos-defaults status" "com.apple.dock"
assert_contains "mise bootstrap macos-defaults status" "show-recents"
assert_contains "mise bootstrap macos-defaults status" "AppleShowAllFiles"
assert_contains "mise bootstrap macos-defaults status" "InitialKeyRepeat"
assert_contains "mise bootstrap macos-defaults status" "Clicking"
assert_contains "mise bootstrap macos-defaults status --json" '"macos_defaults"'
if [[ $(uname) != "Darwin" ]]; then
assert_contains "mise bootstrap macos-defaults status" "skipped"
Expand Down Expand Up @@ -38,6 +55,19 @@ EOF
assert_succeed "mise bootstrap macos-defaults status"
assert_contains "mise bootstrap macos-defaults status 2>&1" "expected a table"

# friendly defaults validate keys and enum values
cat <<EOF >mise.toml
[bootstrap.macos.dock]
orientation = "top"
future_key = true

[bootstrap.macos.finder]
preferred_view_style = "coverflow"
EOF
assert_succeed "mise bootstrap macos-defaults status"
assert_contains "mise bootstrap macos-defaults status 2>&1" "invalid value"
assert_contains "mise bootstrap macos-defaults status 2>&1" "unknown key"

# empty [bootstrap.macos.defaults] section
cat <<EOF >mise.toml
[bootstrap.macos.defaults]
Expand Down
102 changes: 102 additions & 0 deletions schema/mise.json
Original file line number Diff line number Diff line change
Expand Up @@ -3050,6 +3050,108 @@
"type": "object",
"description": "macOS-specific bootstrap config",
"properties": {
"dock": {
"type": "object",
"description": "curated Dock preferences that compile into macOS defaults",
"properties": {
"autohide": {
"type": "boolean",
"description": "hide and show the Dock automatically"
},
"orientation": {
"type": "string",
"enum": ["bottom", "left", "right"],
"description": "Dock screen edge"
},
"tilesize": {
"type": "integer",
"description": "Dock icon size"
},
"magnification": {
"type": "boolean",
"description": "enable Dock magnification"
},
"largesize": {
"type": "integer",
"description": "Dock magnified icon size"
},
"show_recents": {
"type": "boolean",
"description": "show recent applications in the Dock"
},
"mru_spaces": {
"type": "boolean",
"description": "automatically rearrange Spaces based on most recent use"
}
},
"additionalProperties": false
},
"finder": {
"type": "object",
"description": "curated Finder preferences that compile into macOS defaults",
"properties": {
"show_all_files": {
"type": "boolean",
"description": "show hidden files in Finder"
},
"show_pathbar": {
"type": "boolean",
"description": "show the Finder path bar"
},
"show_status_bar": {
"type": "boolean",
"description": "show the Finder status bar"
},
"show_extensions_warning": {
"type": "boolean",
"description": "show the warning when changing file extensions"
},
"preferred_view_style": {
"type": "string",
"enum": ["icon", "list", "column", "gallery"],
"description": "Finder preferred view style"
}
},
"additionalProperties": false
},
"keyboard": {
"type": "object",
"description": "curated keyboard preferences that compile into macOS defaults",
"properties": {
"key_repeat": {
"type": "integer",
"description": "keyboard repeat interval"
},
"initial_key_repeat": {
"type": "integer",
"description": "delay before key repeat starts"
},
"press_and_hold": {
"type": "boolean",
"description": "enable press-and-hold accent picker"
},
"fn_state": {
"type": "boolean",
"description": "use F1, F2, etc. as standard function keys"
}
},
"additionalProperties": false
},
"trackpad": {
"type": "object",
"description": "curated trackpad preferences that compile into macOS defaults",
"properties": {
"tap_to_click": {
"type": "boolean",
"description": "enable tap to click for built-in and Bluetooth trackpads"
},
"three_finger_drag": {
"type": "boolean",
"description": "enable three finger drag for built-in and Bluetooth trackpads"
}
},
"additionalProperties": false
},
"defaults": {
"type": "object",
"description": "macOS user defaults to apply with `mise bootstrap macos-defaults apply`, keyed by preferences domain (e.g. \"com.apple.dock\", \"NSGlobalDomain\")",
Expand Down
9 changes: 2 additions & 7 deletions src/cli/oci/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,17 +123,12 @@ fn reject_unsupported_system_defaults(config_files: &ConfigMap) -> Result<()> {
let Some(system) = cf.bootstrap_config() else {
continue;
};
defaults += system
.macos
.defaults
.values()
.map(|v| v.as_table().map_or(1, |t| t.len()))
.sum::<usize>();
defaults += system::macos_defaults_entry_count(&system.macos);
}

if defaults > 0 {
bail!(
"mise oci does not support [bootstrap.macos.defaults] (found {defaults} default entries); \
"mise oci does not support [bootstrap.macos.*] defaults (found {defaults} default entries); \
macOS defaults do not apply to OCI images."
);
}
Expand Down
33 changes: 33 additions & 0 deletions src/config/config_file/mise_toml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2277,6 +2277,19 @@ mod tests {
[bootstrap.macos.defaults]
NSGlobalDomain = { KeyRepeat = 2, ApplePressAndHoldEnabled = false }
"com.apple.dock" = { autohide = true, tilesize = 48, magnification-scale = 1.5, orientation = "left", future-array = [1, 2] }

[bootstrap.macos.dock]
show_recents = false

[bootstrap.macos.finder]
show_all_files = true
preferred_view_style = "list"

[bootstrap.macos.keyboard]
initial_key_repeat = 15

[bootstrap.macos.trackpad]
tap_to_click = true
"#,
)
.unwrap();
Expand All @@ -2300,6 +2313,26 @@ mod tests {
&toml::Value::String("left".into())
);
assert!(dock.get("future-array").unwrap().is_array());
assert_eq!(
system.macos.dock.get("show_recents").unwrap(),
&toml::Value::Boolean(false)
);
assert_eq!(
system.macos.finder.get("show_all_files").unwrap(),
&toml::Value::Boolean(true)
);
assert_eq!(
system.macos.finder.get("preferred_view_style").unwrap(),
&toml::Value::String("list".into())
);
assert_eq!(
system.macos.keyboard.get("initial_key_repeat").unwrap(),
&toml::Value::Integer(15)
);
assert_eq!(
system.macos.trackpad.get("tap_to_click").unwrap(),
&toml::Value::Boolean(true)
);
file::remove_file(&p).unwrap();
}

Expand Down
Loading
Loading