Ты переезжаешь на новый Mac. Час за часом ставишь руками: brew install того, brew install сего, потом приложения через cask, потом пара утилит из App Store, потом расширения VS Code. Через неделю вспоминаешь, что забыл поставить jq и какой-то LSP. Знакомо? А ещё на старой машине у тебя крутятся postgresql и redis для разработки, и ты смутно помнишь, что когда-то правил руками plist в LaunchAgents, но как именно - уже нет.
Базовый Homebrew (install/upgrade/uninstall) ты уже знаешь. Эта глава про то, как Homebrew превращается из "ставилки пакетов" в инструмент управления окружением Mac целиком: подключение сторонних репозиториев (homebrew tap), декларативное описание всего софта (brew bundle и Brewfile), управление фоновыми демонами через launchd (brew services) и регулярное обслуживание, чтобы система не зарастала мусором. Цель - чтобы развёртывание рабочего Mac превратилось в одну команду, а не в день кликанья.
Напомню про базу, она пригодится ниже: на Apple Silicon (M-серия) префикс Homebrew - /opt/homebrew, на старых Intel - /usr/local. Всё, что ставит brew, физически лежит в Cellar (формулы) или Caskroom (приложения), а в bin кладутся симлинки. Это важно для понимания link/unlink. Проверь свой префикс:
Код: Выделить всё
brew --prefix
# /opt/homebrew (Apple Silicon)

brew tap: сторонние репозитории формул
Tap - это просто git-репозиторий с формулами (рецептами сборки), который ты подключаешь к Homebrew поверх стандартного. Слово "tap" тут в смысле "врезка в трубу": ты подключаешься к ещё одному источнику пакетов. Зачем? В core-репозитории Homebrew нет всего на свете: специфический софт, бета-версии, корпоративные внутренние пакеты, драйверы шрифтов - всё это живёт в отдельных tap.
Классический homebrew tap выглядит так:
Код: Выделить всё
brew tap hashicorp/tap
brew install hashicorp/tap/terraform
Посмотреть подключённые tap и удалить ненужный:
Код: Выделить всё
brew tap
# homebrew/cask
# hashicorp/tap
brew untap hashicorp/tap
Грабли: любой tap - это чужой код, который соберётся и выполнится у тебя с твоими правами. Не подключай tap с непонятных GitHub-аккаунтов так же бездумно, как curl | bash. Перед brew install из нового tap не грех глянуть саму формулу: brew cat hashicorp/tap/terraform покажет рецепт.
brew bundle и Brewfile: всё окружение одним файлом
Вот тут начинается магия воспроизводимости. Brewfile - это текстовый манифест, где декларативно перечислено всё, что должно стоять на Mac: tap, формулы, cask-приложения, пакеты из Mac App Store (через mas) и даже расширения VS Code. Один файл - вся машина.
Сначала снимем слепок текущей системы. brew bundle dump пройдётся по установленному и запишет Brewfile:
Код: Выделить всё
brew bundle dump --describe --force
Код: Выделить всё
tap "homebrew/bundle"
tap "hashicorp/tap"
# Distributed revision control system
brew "git"
brew "jq"
brew "postgresql@16"
brew "redis"
cask "visual-studio-code"
cask "iterm2"
cask "rectangle"
mas "Xcode", id: 497799835
vscode "golang.go"
vscode "ms-python.python"
Про vscode-строки есть нюанс: dump по умолчанию вытащит ВСЕ твои расширения VS Code, и если ты их не хочешь в манифесте - исключай флагом --no-vscode либо переменной HOMEBREW_BUNDLE_DUMP_NO_VSCODE. Это частая претензия: "почему мой Brewfile распух на 60 строк vscode" - вот поэтому.
Теперь самое ценное - развернуть это на новом Mac. Кладёшь Brewfile (например, в ~/Brewfile или в git-репозиторий с дотфайлами) и говоришь:
Код: Выделить всё
brew bundle install --file ~/Brewfile
Полезные подкоманды bundle:
- brew bundle check - проверить, всё ли из Brewfile стоит, без установки. Удобно в CI или скриптом перед работой.
- brew bundle list - показать, что объявлено в манифесте.
- brew bundle cleanup - показать установленное, чего НЕТ в Brewfile (то есть "лишнее"). Добавишь --force - оно это удалит. Так Brewfile становится единственным источником истины: что не в файле, того не должно быть на машине.
Идея на вооружение: держи Brewfile в git вместе с дотфайлами. Тогда история софта версионируется, а новый Mac или Mac коллеги поднимается из репозитория. Это и есть инфраструктура как код, только для рабочей станции.
brew services: фоновые демоны через launchd
Разработчику постоянно нужны фоновые сервисы: база postgresql, кэш redis, иногда nginx. На Linux это systemd, в классическом Unix - init или cron. На macOS системный менеджер процессов - это launchd, и работает он через plist-файлы (LaunchAgents для пользователя, LaunchDaemons для системы). Писать эти XML руками - боль. brew services - это удобная обёртка над launchd: ты говоришь "запусти postgresql", а brew сам генерирует plist, кладёт его в нужное место и регистрирует в launchctl.
Код: Выделить всё
brew services start postgresql@16
brew services list
Код: Выделить всё
Name Status User File
postgresql@16 started khovan ~/Library/LaunchAgents/homebrew.mxcl.postgresql@16.plist
redis none
nginx error root /Library/LaunchDaemons/homebrew.mxcl.nginx.plist
Ключевая развилка - start против run:
- brew services start postgresql@16 - запустить СЕЙЧАС и прописать автозапуск при входе в систему (boot/login). Демон переживёт перезагрузку.
- brew services run postgresql@16 - запустить только сейчас, БЕЗ автозапуска. Перезагрузился - сервис не поднялся. Идеально для разовых задач, когда не хочешь, чтобы база всегда висела в фоне и ела память.
Код: Выделить всё
sudo brew services start nginx
brew services restart postgresql@16
brew services stop redis
Обслуживание: doctor, cleanup, autoremove, pin, link
Homebrew со временем зарастает: старые версии формул, скачанные архивы, осиротевшие зависимости. Регулярная гигиена держит систему в форме.
brew doctor - первое, что запускаешь, когда что-то странно или раз в пару недель профилактически. Он проверяет окружение: чужие файлы в префиксе, сломанные симлинки, конфликты PATH, недокачанное.
Код: Выделить всё
brew doctor
# Warning: Some installed formulae are missing dependencies.
# Warning: You have unlinked kegs in your Cellar ...
brew cleanup убирает устаревшее: старые версии формул, протухшие загрузки из кэша, lock-файлы. Сухой прогон с --dry-run покажет, что удалится и сколько места освободится, без фактического удаления:
Код: Выделить всё
brew cleanup --dry-run
# Would remove: postgresql@16 15.4 (1.2GB)
# ==> This operation would free approximately 3.1GB of disk space.
brew autoremove - снимает зависимости, которые ставились только ради другого пакета, а теперь никому не нужны (тот пакет удалён). Аналог apt autoremove. Тоже поддерживает --dry-run - проверяй перед сносом, иногда захочет удалить то, чем ты пользуешься напрямую, но забыл явно установить.
pin / unpin - заморозка версии. Если конкретная формула должна остаться на текущей версии (несовместимость, баг в новой), пиннишь её, и brew upgrade её пропускает:
Код: Выделить всё
brew pin postgresql@16
brew upgrade # postgresql@16 не тронут
brew unpin postgresql@16
Аналитика и приватность. Homebrew по умолчанию шлёт анонимную аналитику установок. Если по политике компании или просто по вкусу это не нужно - выключается одной командой:
Код: Выделить всё
brew analytics off
brew analytics state
# Analytics are disabled.
Код: Выделить всё
brew update
brew upgrade # обновить сами пакеты
brew outdated # что устарело, без установки
Мини-лаба: воспроизводимое окружение за 10 минут
Повтори руками на своей машине, это закрепляет лучше любого текста:
- 1. Сними слепок текущей системы: brew bundle dump --describe --force --no-vscode. Открой получившийся Brewfile и прочитай его - убедись, что узнаёшь свои tap, brew, cask.
- 2. Добавь в Brewfile руками строку brew "jq" (если уже есть - возьми любую отсутствующую утилиту). Сохрани.
- 3. Проверь расхождение: brew bundle check --verbose. Он скажет, что jq не установлен.
- 4. Доведи систему до манифеста: brew bundle install. jq встанет.
- 5. Подними сервис: brew services start redis, затем brew services list - найди статусом started и путь к plist в ~/Library/LaunchAgents.
- 6. Останови без автозапуска на будущее: brew services stop redis.
- 7. Гигиена: brew cleanup --dry-run и brew autoremove --dry-run - прочитай, что система предлагает убрать, но НЕ запускай без dry-run, пока не разобрался в списке.
Контрольные вопросы
- 1. Чем brew services start отличается от brew services run, и в каком каталоге окажется сгенерированный plist для пользовательского сервиса против системного (под sudo)?
- 2. Ты сделал brew bundle dump, а потом на новой машине brew bundle install. Что произойдёт со строками mas и vscode, и какая утилита должна стоять, чтобы mas-строки отработали?
- 3. Чем опасен brew bundle cleanup --force и как проверить последствия заранее?
- 4. Зачем нужна пара unlink/link, если пакет физически остаётся в Cellar, и какую реальную проблему она решает?
Продвинутый Homebrew - это не про "ставить пакеты", а про управление состоянием Mac как кодом. tap подключает сторонние источники формул (осторожно с доверием). Brewfile и brew bundle превращают весь софт - формулы, brew cask, mas, расширения редактора - в один версионируемый манифест, и развёртывание Mac становится командой brew bundle install. brew services - человеческая обёртка над launchd для postgresql, redis, nginx, с ключевой развилкой start (с автозапуском) против run (разово). А doctor, cleanup, autoremove, pin и link/unlink держат всё это в чистоте и под контролем. Освоишь - и переезд на новый Mac перестанет быть потерянным днём.