Управление окружением через chezmoi и brew

Зачем управлять dotfiles

С тех пор как я начал использовать Git для управления моими dotfiles, прошло уже несколько лет. Но необходимость этого я осознал не сразу. Осознание пришло уже после моего первого опыта использования Linux. Первым дистрибутивом Linux, которым я пользовался достаточно долгое время, был Pop!_OS. Я использовал его, пока изучал программирование. Я выполнил несколько личных проектов и уже создал некоторое подобие окружения разработчика с установленными pyenv, neovim, VS Code. Соответственно, у меня в системе уже было определённое количество dotfiles, которые я редактировал для улучшения моего процесса работы в ОС.

Но при очередном обновлении система не смогла обновиться до конца. Её стало невозможно запустить. Вместе с ней были потеряны и мои dotfiles. Не то чтобы я следил за ними в тот момент, но я расстроился, когда не смог их восстановить.

С тех пор я стал управлять моими dotfiles при помощи системы контроля версий. И я сохраняю мои dotfiles в удалённом репозитории на GitHub.

Какие инструменты я использовал

На протяжении нескольких лет я использовал несколько разных способов и инструментов для управления dotfiles. Почему несколько? Потому что я стремлюсь к упрощению и автоматизации процессов внесения изменений в dotfiles и установки dotfiles и зависимостей в операционной системе.

Итак, какие этапы я прошёл.

Управление dotfiles при помощи git bare

Этот метод заключается в сохранении «голого» репозитория Git bare repository в отдельной папке (например, $HOME/.cfg или $HOME/.myconfig) и использовании специально подготовленного псевдонима (alias), чтобы команды выполнялись именно для этого репозитория, а не для обычной локальной папки .git, которая может конфликтовать с другими Git-репозиториями в системе.

git init --bare $HOME/.cfg
alias config='/usr/bin/git --git-dir=$HOME/.cfg/ --work-tree=$HOME'
config config --local status.showUntrackedFiles no
echo "alias config='/usr/bin/git --git-dir=$HOME/.cfg/ --work-tree=$HOME'" >> $HOME/.bashrc
  • Первая строка создаёт папку ~/.cfg, которая будет являться «голым» Git-репозиторием для отслеживания ваших файлов.
  • Затем создаётся псевдоним config, который будет использоваться вместо обычной команды git при работе с конфигурационным репозиторием.
  • Устанавливается локальный для этого репозитория флаг, скрывающий файлы, которые ещё не добавлены в отслеживание. Это позволяет команде config status и другим не отображать файлы, которые вы не хотите отслеживать, как «untracked» (неотслеживаемые).
  • Определение псевдонима можно добавить вручную в ваш .bashrc либо воспользоваться четвёртой строкой выше для удобства.

Преимущества этого подхода:

  • Отсутствие зависимостей от дополнительных программ и утилит.
  • Явное добавление файлов в репозиторий.

Недостатки этого подхода:

  • Относительная сложность начальной настройки.
  • Ручное разрешение конфликтов.
  • Риск перезаписи существующих данных.
  • Необходимость лишних шагов по добавлению файлов и папок в репозиторий.
  • Дополнительные файлы и директории (.cfg, .gitignore) в домашней директории.
  • Отсутствие шаблонов для работы в разных окружениях (рабочая машина, домашний компьютер).

Такой подход уже использует дополнительную утилиту GNU Stow. Эта программа позволяет сохранить все dotfiles в одной директории, а затем создать символические ссылки на них в домашней директории.

  1. Создаём Git-репозиторий (обычно называемый, например, dotfiles), внутри которого создаём подкаталоги по «пакетам» — например, .config/vim/, .config/zsh/, .config/tmux/.
  2. Внутри каждого подкаталога размещаем файлы так, как они должны появиться в $HOME.
    Например, файл zsh/.zshrc в репозитории будет ссылаться на ~/.zshrc.
  3. Из корня репозитория выполняем:
stow .

Это создаст символические ссылки в $HOME, указывающие на соответствующие файлы в нашем репозитории.

Преимущества подхода с Stow:

  • Оригинальные файлы хранятся только в репозитории; в $HOME — только ссылки.
  • Простота переноса. Клонируем репозиторий и выполняем stow — конфигурация готова.
  • Весь репозиторий — обычный Git, без специальных флагов или алиасов. Именно поэтому я перешёл на этот способ вместо git bare.
  • Изменения в символических ссылках отражаются в оригинальных dotfiles.

Недостатки подхода с Stow:

  • Stow не входит в базовую поставку большинства систем (хотя легко устанавливается через пакетные менеджеры).
  • Не поддерживает шаблонизацию или подстановку переменных.
  • Если файл уже существует в $HOME, Stow откажется создавать ссылку (нужно вручную удалять или перемещать).
  • Изменение структуры пакетов требует ручного обновления ссылок (stow -R помогает, но не всегда достаточно).
  • Некоторые программы плохо ведут себя с символическими ссылками.
  • При создании символической ссылки на директорию в неё будут попадать автоматически создаваемые и временные файлы. Нужно использовать .gitignore.
  • Риск попадания конфиденциальных данных в репозиторий.

Управление dotfiles при помощи Nix и Home Manager

Об этом способе у меня есть полноценная статья. Подробности можно посмотреть в ней. Здесь же перечислю только преимущества и недостатки использования.

Преимущества этого подхода:

  • Все конфигурации описываются в коде: мы объявляем желаемое состояние, а система приводит систему к нему.
  • Нет императивных скриптов вроде «скопируй этот файл» — только описание того, что должно быть.
  • Каждая конфигурация — это уникальное поколение (generation). При сбое легко откатиться к предыдущей рабочей версии через home-manager switch --rollback.
  • Изменения не «ломают» текущую систему — они применяются в новом профиле.
  • Home Manager может не только управлять dotfiles, но и устанавливать пользовательские пакеты (без прав root) через Nix. Например: «установить neovim, ripgrep, lazygit и настроить ~/.config/nvim» — всё в одном месте. Именно это привлекло меня в первую очередь. Nix может заменить стандартные пакетные менеджеры в Linux-дистрибутивах.
  • Пользовательские пакеты и конфиги не влияют на систему глобально.
  • Нет риска случайной перезаписи файлов — Home Manager управляет только тем, что описано.

Недостатки этого подхода:

  • Сложность первоначальной настройки. Нужно установить Nix, Nix flakes, Home Manager.
  • Требуется понимание языка Nix (функциональный, со своей спецификой), концепций Nix Store, derivations, flakes и т.д.
  • Для новичков кривая обучения может быть очень крутой.
  • Хотя Home Manager работает и на не-NixOS системах, он всё равно требует установки Nix — что не всегда возможно (ограниченные серверы, корпоративные политики).
  • Не все пакеты доступны или актуальны в Nixpkgs. Некоторые пакеты несовместимы с отдельными Linux-дистрибутивами.
  • Сложность работы с Nix в macOS.
  • Сложность реализации шаблонов для использования разных dotfiles в разных окружениях.
  • Сложность работы с конфиденциальными данными и опасность их попадания в репозиторий.

Управление dotfiles при помощи chezmoi и brew

Так как на работе я перешёл с использования WSL и Windows на macOS, мне потребовалась новая система управления dotfiles. Я уже не мог использовать Nix на macOS из соображений корпоративной безопасности и сложности работы с Nix на macOS. Кроме этого, мне нужна была поддержка шаблонов и возможность работы с конфиденциальными данными.

Решением для меня стала использование связки chezmoi и brew. О них и пойдёт речь далее.

Почему chezmoi и brew стали решением для меня

Для того, чтобы эффективно управлять dotfiles, мне нужны были следующие возможности:

  1. Возможность использования шаблонов и переменных для использования dotfiles как на корпоративных так и на личных ОС.
  2. Возможность управления конфиденциальными данными. Например возможность добавления токенов и секретных ключей в файлы конфигурации без опасности закоммитить их в git.
  3. Простой процесс переноса dotfiles на новую машину.
  4. Возможность быстрой автоматической установки программ и утилит при помощи скрипта или команды.
  5. Явное указание файлов которые необходимо добавить в git.

Как chezmoi и brew реализуют эти возможности:

  1. chezmoi имеет шаблоны и возможность использовать условные выражения и переменные (в том числе определенные пользователем) для форматирования dotfiles.
  2. chezmoi имеет возможность задавать значения кастомных переменных при форматировании dotfiles.
  3. chezmoi позволяет иницилизировать dotfiles локально напрямую через ссылку на удаленный репозиторий.
  4. Brew доступно как на MacOS так и на Linux. Программы можно установить путем передачи содержимого текстового файла на стандартный ввод команды brew install.
  5. dotfiles хранятся в виде шаблонов в изолированной директории и компилируются в финальные файлы в домашней директории.

Из недостатков подобной схемы могу выделить:

  1. Необходимость установки brew и chezmoi перед тем как установить dotfiles.
  2. chezmoi не умеет удалять файлы которые были переименованы или удалены. После переименования или удалени файла в репозитории, файл со старым названием нужно удалять вручную из домашней директории.

Теперь посмотрим как это работает.

Как работать с chezmoi и brew

chezmoi

Для начала нужно установить chezmoi и brew.

Если репозиторий chezmoi ещё не создан, можно использовать команду:

chezmoi init

chezmoi создаст директорию по пути ~/.local/share/chezmoi/. Нужно добавить её в git. После этого можно добавить свои dotfiles в chezmoi командой:

chezmoi add ~/.bashrc

Если удалённый git-репозиторий уже существует, можно скачать его командой:

chezmoi init git@github.com:$GITHUB_USERNAME/dotfiles.git

При инициализации, если мы задавали пользовательские переменные, будет предложено заполнить значения этих переменных. Эти значения не будут фигурировать в файлах, сохранённых в git. Они будут применяться к шаблонам в момент применения dotfiles из chezmoi в домашнюю директорию командой:

chezmoi apply

brew

При помощи brew можно установить список приложений, используя текстовый файл:

tap "charmbracelet/tap"
tap "sst/tap"
brew "atuin"
brew "bat"
...

Для установки подойдёт команда:

brew bundle --file $HOMEBREW_BUNDLE_FILE

Она выполняет установку (и, при необходимости, синхронизацию) всех зависимостей, описанных в файле, путь к которому указан в переменной окружения $HOMEBREW_BUNDLE_FILE.

  • brew bundle — подкоманда Homebrew, предназначенная для управления наборами пакетов, приложений (cask) и даже Mas-приложений (из Mac App Store) через декларативный файл (обычно называемый Brewfile).
  • --file $HOMEBREW_BUNDLE_FILE — явно указывает, какой именно файл использовать (вместо стандартного Brewfile в текущей директории).

Также можно использовать просто перечень необходимых утилит:

atuin
bat
...

И воспользоваться командой:

xargs -I {} brew install {} < Brewfile

Она работает следующим образом:

  1. < Brewfile — перенаправляет содержимое файла Brewfile (каждая строка которого, как предполагается, содержит имя пакета для Homebrew) на стандартный ввод команды xargs.
  2. xargs читает этот ввод построчно.
  3. -I {} указывает xargs заменять {} на каждую прочитанную строку.
  4. Для каждой строки (например, neovim) xargs выполняет команду:
brew install neovim

Таким образом, команда устанавливает все пакеты, перечисленные в Brewfile, по одному, вызывая brew install для каждого.

Установка dotfiles и приложений с нуля

Предположим, мы создали виртуальную машину Linux и хотим установить в неё dotfiles. Для этого нужно выполнить несколько простых действий.

  1. Создать SSH-ключи в директории .ssh и добавить публичный ключ на GitHub.
  2. Установить brew через официальный установщик.
  3. Установить chezmoi через пакетный менеджер или brew:
/opt/homebrew/bin/brew install chezmoi # На macOS
/home/linuxbrew/.linuxbrew/bin/brew install chezmoi # На Linux
  1. Скачать и применить dotfiles из удалённого репозитория:
/opt/homebrew/bin/chezmoi init --apply git@github.com:GeorgeKuzora/dotfiles.git # На macOS
/home/linuxbrew/.linuxbrew/bin/chezmoi init --apply git@github.com:GeorgeKuzora/dotfiles.git # На Linux
  1. Ввести значения пользовательских переменных в вызванном диалоге.
  2. Перейти в директорию с Brewfile:
cd ~/.config/brewfile
  1. Установить приложения и утилиты из Brewfile:
/opt/homebrew/bin/brew bundle # На macOS
/home/linuxbrew/.linuxbrew/bin/brew bundle # На Linux
  1. Все указанные приложения и утилиты будут установлены.
  2. Теперь можно открыть новое окно терминала и проверить, что не возникает никаких ошибок при инициализации оболочки.

При необходимости эти шаги можно объединить в bash-скрипт.

Georgiy Kuzora

Не было в нем непоседливости и метаний, он проводил время в одних и тех же местах и занятиях.