Достал из коробки новый Mac на Apple Silicon. Дальше два часа кликов: поставить браузер, накатить терминал, прикрутить шрифт, вспомнить как включался FileVault, скопировать ssh-ключи с флешки, по памяти проставить десяток defaults write, чтобы Finder показывал скрытые файлы и не плодил .DS_Store на сетевых шарах. Через год переезд на новую машину - и весь этот ритуал заново, но половину команд ты уже забыл.
Профи так не делает. У него настройка mac - это репозиторий. Один прогон скрипта превращает чистую систему в полностью рабочую среду: софт, конфиги, ключи, безопасность. Это финальный урок, и мы собираем всё, что прошли в курсе, в один воспроизводимый bootstrap. Идея простая: машина должна быть одноразовой. Сломалась, потерялась, выдали новую - не жалко, потому что состояние живёт в git, а не в голове.
В этом уроке мы построим скелет такого проекта: dotfiles в git, Homebrew плюс Brewfile (весь софт одной командой), скрипт системных настроек через defaults, zsh с человеческим prompt, ключи SSH и GPG, среда разработки (CLT и mise), базовая безопасность (FileVault, файрвол, проверка TCC). И, главное, идемпотентный скрипт-обёртка - почти как Ansible, только для одного Mac. В конце - карта роста до ACSP и вход в комьюнити Mac-админов.

Архитектура bootstrap: что есть что
Разложим проект на слои. Каждый слой - отдельный кусок, который можно прогнать в отрыве от других.
- dotfiles - твои .zshrc, .gitconfig, .ssh/config, конфиги редактора. Лежат в git-репозитории, в систему попадают через симлинки.
- Brewfile - декларативный список всего софта: формулы (CLI), каски (.app), Mac App Store приложения через mas, шрифты.
- defaults-скрипт - системные и приложенческие настройки через команду defaults write.
- secrets - SSH/GPG-ключи. В репозиторий не коммитятся, восстанавливаются отдельно (1Password, зашифрованный бэкап, аппаратный ключ).
- bootstrap.sh - дирижёр, который вызывает всё перечисленное в правильном порядке и делает это идемпотентно.
Почему именно симлинки, а не копирование? Потому что правишь .zshrc прямо в репозитории (он у тебя под версионным контролем), а в домашней папке лежит ссылка на него. Поправил - сразу видно в git diff, закоммитил, на других машинах подтянул git pull. Копия бы разъехалась с источником на первой же правке.
Слой 1. Dotfiles в git
Создаём репозиторий и кладём туда конфиги. Структура обычно такая:
Код: Выделить всё
~/dotfiles/
Brewfile
zsh/.zshrc
git/.gitconfig
ssh/config
macos/defaults.sh
bootstrap.sh
Код: Выделить всё
#!/bin/zsh
set -euo pipefail
DOTFILES="$HOME/dotfiles"
link() {
local src="$1" dst="$2"
if [[ -L "$dst" ]]; then
rm "$dst" # снимаем старый симлинк
elif [[ -e "$dst" ]]; then
mv "$dst" "$dst.bak" # реальный файл - в бэкап, не теряем
fi
ln -s "$src" "$dst"
print "linked: $dst -> $src"
}
link "$DOTFILES/zsh/.zshrc" "$HOME/.zshrc"
link "$DOTFILES/git/.gitconfig" "$HOME/.gitconfig"
Слой 2. Homebrew и brew bundle - весь софт одной командой
На Apple Silicon Homebrew живёт в /opt/homebrew (на старых Intel был /usr/local). Это важно для PATH и для скриптов, которые ищут brew. Сначала ставим сам менеджер и активируем его в текущей сессии:
Код: Выделить всё
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
eval "$(/opt/homebrew/bin/brew shellenv)"
Код: Выделить всё
brew bundle dump --file=~/dotfiles/Brewfile --force
Код: Выделить всё
tap "homebrew/bundle"
brew "git"
brew "mise"
brew "ripgrep"
cask "iterm2"
cask "visual-studio-code"
cask "font-jetbrains-mono-nerd-font"
mas "Xcode", id: 497799835
Код: Выделить всё
brew bundle install --file=~/dotfiles/Brewfile
Код: Выделить всё
brew bundle check --file=~/dotfiles/Brewfile
Код: Выделить всё
brew bundle cleanup --force --file=~/dotfiles/Brewfile
Слой 3. Системные настройки через defaults write
Графические тумблеры в Системных настройках (System Settings, не старые Preferences) под капотом пишут в plist-домены. Команда defaults даёт прямой доступ к ним - значит, настройку можно заскриптовать. Несколько рабочих примеров с разбором:
Код: Выделить всё
# показывать расширения файлов всегда
defaults write NSGlobalDomain AppleShowAllExtensions -bool true
# не плодить .DS_Store на сетевых и USB-томах
defaults write com.apple.desktopservices DSDontWriteNetworkStores -bool true
defaults write com.apple.desktopservices DSDontWriteUSBStores -bool true
# Finder: строка пути и полный путь в заголовке
defaults write com.apple.finder ShowPathbar -bool true
defaults write com.apple.finder _FXShowPosixPathInTitle -bool true
# скриншоты в ~/Screenshots, формат png
mkdir -p "$HOME/Screenshots"
defaults write com.apple.screencapture location -string "$HOME/Screenshots"
defaults write com.apple.screencapture type -string "png"
Грабли номер один: многие демоны кешируют настройки и не перечитывают plist на лету. После пачки defaults write надо пнуть процессы, иначе будешь думать, что команда не сработала:
Код: Выделить всё
killall Finder
killall Dock
killall SystemUIServer
Слой 4. Среда разработки: CLT, mise, zsh и prompt
Базовый компилятор и git тянет за собой Command Line Tools. Их можно поставить отдельно от полного Xcode:
Код: Выделить всё
xcode-select --install
Код: Выделить всё
# в ~/.zshrc
eval "$(mise activate zsh)"
Код: Выделить всё
mise use node@22
mise use python@3.13
mise install
Prompt. zsh - дефолтная оболочка macOS с Catalina, так что bash-овые гайды не всегда подходят. Минимальный вменяемый prompt с веткой git без тяжёлых фреймворков:
Код: Выделить всё
# в ~/.zshrc
autoload -Uz vcs_info
precmd() { vcs_info }
zstyle ':vcs_info:git:*' formats ' (%b)'
setopt PROMPT_SUBST
PROMPT='%F{cyan}%~%f%F{yellow}${vcs_info_msg_0_}%f %# '
Слой 5. Ключи SSH/GPG и базовая безопасность
Ключи в git коммитить нельзя - это слой secrets, он восстанавливается отдельно. Генерация современного ssh-ключа:
Код: Выделить всё
ssh-keygen -t ed25519 -C "you@example.com"
Код: Выделить всё
Host *
AddKeysToAgent yes
UseKeychain yes
IdentityFile ~/.ssh/id_ed25519
Код: Выделить всё
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
FileVault. Шифрование диска. Статус:
Код: Выделить всё
fdesetup status
Файрвол приложений (фильтр входящих по приложениям, application firewall):
Код: Выделить всё
/usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate
SIP (System Integrity Protection) - защита системных томов. Должна быть включена:
Код: Выделить всё
csrutil status
TCC (Transparency, Consent, and Control) - подсистема приватности, та самая, что спрашивает доступ к камере, микрофону, диску, экрану. Сбросить разрешения конкретной службы (например, если приложение залипло в неправильном состоянии):
Код: Выделить всё
tccutil reset Camera com.example.app
tccutil reset All com.example.app
Слой 6. Дирижёр bootstrap.sh - Ansible для одного Mac
Собираем всё в один идемпотентный вход. Логика - последовательность шагов, каждый безопасен при повторе:
Код: Выделить всё
#!/bin/zsh
set -euo pipefail
# 1. Homebrew
if ! command -v brew >/dev/null 2>&1; then
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
fi
eval "$(/opt/homebrew/bin/brew shellenv)"
# 2. софт из Brewfile
brew bundle install --file="$HOME/dotfiles/Brewfile"
# 3. симлинки конфигов
"$HOME/dotfiles/link.sh"
# 4. системные настройки
"$HOME/dotfiles/macos/defaults.sh"
# 5. проверка безопасности (только репорт, без насилия)
print "== security =="
fdesetup status
csrutil status
Чек-лист готовности рабочей машины
- FileVault On (fdesetup status)
- SIP enabled (csrutil status)
- Файрвол приложений включён (socketfilterfw --getglobalstate)
- Автообновления macOS включены
- Brewfile в git, brew bundle check проходит
- dotfiles в git, симлинки на месте
- ssh-ключ ed25519, в Связке ключей, добавлен на GitHub/GitLab
- CLT стоят (xcode-select -p отвечает)
- Резервная копия (Time Machine или иной бэкап) настроена
- Ключ восстановления FileVault сохранён в надёжном месте (не на том же диске)
- Секреты в git. Закоммитил .ssh с приватным ключом в публичный репо - всё, ключ скомпрометирован, перевыпускай. Держи secrets отдельным слоем, в .gitignore - .ssh, .gnupg, .env.
- defaults без killall. Команда отработала, а изменений не видно - демон не перечитал plist. Пни Finder/Dock/SystemUIServer.
- Неверный тип в defaults write. Записал -string "true" вместо -bool true - настройка молча игнорируется.
- Жёсткий fdesetup enable из скрипта. Deprecated и требует Full Disk Access. На парке - MDM, на личной - тумблер.
- ln -sf на каталог. BSD-ln при -f на существующий каталог создаст симлинк внутри него, а не заменит - получишь вложенность. Снимай старый симлинк явно через rm.
- Путь /usr/local вместо /opt/homebrew. Перенёс скрипт с Intel на Apple Silicon и забыл - brew не находится. Используй brew shellenv, не хардкодь путь.
- Создай git-репозиторий ~/dotfiles, добавь в .gitignore секреты.
- Сними снимок текущего софта: brew bundle dump --file=~/dotfiles/Brewfile --force. Открой файл, удали лишнее, оставь только нужное.
- Положи .zshrc в ~/dotfiles/zsh/, напиши link.sh по примеру выше, прогони. Проверь, что в $HOME появился симлинк (ls -la ~/.zshrc).
- Напиши macos/defaults.sh с тремя настройками, запусти, не забудь killall Finder. Убедись глазами, что изменилось.
- Сгенерируй ed25519-ключ, пропиши UseKeychain в ~/.ssh/config, добавь ключ через ssh-add --apple-use-keychain.
- Прогони весь bootstrap.sh дважды подряд. Второй прогон не должен ничего ломать и должен отработать быстро - это и есть проверка идемпотентности.
- Закоммить и запушь. Теперь твоя машина воспроизводима.
Курс закончен, но Mac-администрирование - это глубокий колодец. Куда расти:
- Сертификация ACSP (Apple Certified Support Professional). Подтверждает умение развернуть, настроить и продиагностировать macOS и сервисы Apple для пользователей. По состоянию на 2025 экзамен опирается на актуальные macOS/iOS/iPadOS, целевая аудитория - специалисты поддержки, техкоординаторы, продвинутые пользователи. Готовишься по гайду Apple, сдаёшь экзамен, получаешь цифровой бейдж. Хороший внешний маркер, что ты не просто кликер.
- Управление парком. Один Mac ты теперь настраиваешь скриптом. Сотню - через MDM. Учи Jamf Pro (стандарт индустрии) или открытые стеки, осваивай DDM (Declarative Device Management) - в 2026 это магистральное направление MDM, устройство само поддерживает заданное состояние без постоянного опроса сервером. Профили конфигурации, PPPC для TCC, эскроу ключей FileVault - всё это масштаб парка.
- Скриптинг и автоматизация. zsh и bash ты уже трогал. Дальше - выпуск пакетов (pkgbuild, productbuild), подпись и нотаризация, упаковка софта для тихой раскатки, launchd для фоновых служб вместо cron.
- Сообщество MacAdmins. Главная точка входа - MacAdmins Slack (тысячи инженеров, каналы по Jamf, munki, packaging, security). Плюс осенние конференции (MacAdmins Conference в Penn State, MacSysAdmin, локальные митапы). Там живёт практика, которой нет в документации, и там быстрее всего догоняешь изменения каждой новой macOS.
Мы прошли путь от файловой системы APFS и устройства томов, через терминал, launchd, права POSIX/ACL/xattr, сеть, Homebrew, безопасность (FileVault, SIP, Gatekeeper, TCC) - и сошлись здесь, в одном bootstrap-проекте, где всё это работает вместе. Главная мысль курса: Mac - это не набор кнопок, а Unix-система с прозрачной механикой, которой можно управлять кодом. Машина одноразовая, состояние в git, настройка - команда, а не ритуал. Дальше - ACSP, парк, комьюнити. Открывай терминал и собирай свой dotfiles-репозиторий прямо сейчас, пока тёплая память от курса не остыла.