Автоматизируем настройку macOS с помощью Ansible
В какой-то момент мой мак "зарос" всяким мусором и я решил - пришло время чистой установки ОС с наведением порядка. Сделать я это задумал с помощью инструментов автоматизации - Ansible плейбуков и вспомогательных скриптов. Вдохновила меня на это коллекция из Ansible Galaxy от Джефа Гирлинга. Правда её использование мне показалось избыточной затеей, поэтому я решил сделать свой вариант (Да-да, разбираться в чужих огромных плейбуках никому не хочется).
Что будем делать
Мне хочется затронуть основные вопросы конфигурации macOS, поэтому попробуем решить следующие задачи:
- Установим пару приложений
 - Раскатаем дотфайлы
 - Поменяем системные параметры и параметры пользовательского окружения
 
Думаю этого хватит, чтобы понять суть и потом допилить решение под себя. Погнали.
Подготовительный этап
Если мак не только что достали из коробки, а уже давно используют в качестве рабочей лошади, конечно же сначала надо сделать бэкап данных и только потом заниматься экспериментами, поэтому:
- Делаем бэкап системы, файлов, фотографий, документов, паролей, ключей и т.п.
 - Убеждаемся, что есть доступ к своей учётной записи Apple ID
 - Убеждаемся, что есть доступ к бэкапу и всем учётным записям, которые потребуются для его восстановления (если бэкап делался в облако или на файловый сервер)
 - Начисто переустанавливаем macOS.
 
И теперь, когда в руках оказался чистый мак со свежей ОС, можно начинать развлекаться.
Волшебный репозиторий
Сперва - вот мой репозиторий для настройки своего Мака. Можно скачать его к себе и посмотреть содержимое.
Если коротко, то скрипт run.sh сначала устанавливает Xcode Command Line tools и Ansible, а потом запускает плейбук с дальнейшей конфигурацией системы.
Установка приложений
К счастью в моём арсенале практически все приложения можно установить с помощью HomeBrew (пакетного менеджера, которого так не хватало в macOS), поэтому особых танцев с бубном тут нет.
- name: Check if Homebrew is Installed
  ansible.builtin.command: brew -v
  register: homebrew
  
- name: Download HomeBrew Installation Script
  ansible.builtin.get_url:
    url: https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh
    dest: /tmp/brew-install.sh
    mode: '0755'
  when: homebrew.rc != 0
- name: Install HomeBrew
  ansible.builtin.script: /tmp/brew-install.sh
  when: homebrew.rc != 0
- name: Delete HomeBrew Installation Script
  ansible.builtin.file:
    path: /tmp/brew-install.sh
    state: absent
  when: homebrew.rc != 0Теперь можно установить какое-нибудь приложение. Я поставлю сразу несколько:
- name: Install Formulas
  community.general.homebrew:
    name:
      - ranger
      - tmux
    state: present
- name: Install Casks
  community.general.homebrew_cask:
    name:
      - iterm2
      - visual-studio-code
    state: presentСледует заметить, что для homebrew написано несколько модулей ansible. Для консольных приложений (formula) используется модуль homebrew, для графических (cask) - homebrew_cask.
Однако несколько пакетов придётся установить и из Mac App Store. Для этого придётся воспользоваться утилитой mas. С её помощью мы можем найти интересующий нас пакет и узнать его ID, после чего установить.
Раскатка дотфайлов
После установки приложений можно заняться деплоем их конфигураций. Для примера я включу превью изображений в ranger, установлю Oh My Zsh и подключу к нему тему powerlevel10k.
ranger
С дотфайлами есть несколько подходов - можно подготовить файл и просто скопировать его, а можно менять или добавлять строки в исходной конфигурации. Поскольку для включения изображений нужно включить всего две опции, можно воспользоваться вторым способом
- name: Ranger - enable image preview
  ansible.builtin.lineinfile:
    path: ~/.config/ranger/rc.conf
    regexp: "{{ item.regexp }}"
    line:   "{{ item.line }}"
  loop:
    - { regexp: '^set\ preview_images', line: 'set preview_images true' }
    - { regexp: '^set\ preview_images_method', line: 'set preview_images_method iterm2' }Теперь можно запустить Ranger и проверить работоспособность
zsh
С установкой Oh My Zsh проблем быть не должно - Фреймворк ставится так же, как и homebrew - скачивается установочный скрипт, запускается и удаляется. А вот с применением темы Powerlevel10k уже интереснее. Если файла конфига нет, то придётся выполнить первые два шага, после чего запустить терминал, ответить на вопросы визарда Powerlevel10k - он всё сделает сам, останется лишь забрать себе сгенерированный конфиг.
- name: Powerlevel10k - get source code
  ansible.builtin.git:
    repo: https://github.com/romkatv/powerlevel10k.git
    dest: ~/.powerlevel10k
    update: yes
- name: Powerlevel10k - update .zshrc
  ansible.builtin.lineinfile:
    path: ~/.zshrc
    regexp: '^source ~\/.powerlevel10k'
    line: source ~/.powerlevel10k/powerlevel10k.zsh-theme
    
- name: Powerlevel10k - copy config
  ansible.builtin.copy:
    src: zsh/.p10k.zsh
    dest: ~/.p10k.zsh
    mode: '0644'
    
- name: Powerlevel10k - Update .zshrc
  ansible.builtin.blockinfile:
    path: ~/.zshrc
    block: |
      if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then
        source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh"
      fi
    insertbefore: BOF
    marker_begin: "BEGIN Enable Powerlevel10k instant prompt"
    marker_end: "END Powerlevel10k instant prompt"
    backup: yes
    create: yes
- name: Powerlevel10k - Update .zshrc
  ansible.builtin.blockinfile:
    path: ~/.zshrc_test
    block: |
      [[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh
    insertafter: EOF
    marker_begin: "BEGIN Powerlevel10k config"
    marker_end: "END Powerlevel10k config"
    backup: yesИзменение параметров системы
Основной инструмент для управления системой из командной строки - утилита defaults. С её помощью можно прочитать или установить параметры практически для всего, что можно щёлкнуть мышью в графическом интерфейсе. Это могут быть как настройки ОС, так и установленных приложений.
Например, я хочу использовать клавишу Caps Lock для переключения раскладки клавиатуры. За это отвечает параметр TISRomanSwitchState и я могу его поменять следующей командой:
defaults write -g TISRomanSwitchState 1
К счастью в Ansible есть готовый модуль osx_defaults, позволяющий описывать конфигурацию привычным образом:
- name: Use CapsLock to switch keyboard layout
  community.general.osx_defaults:
    key: TISRomanSwitchState
    type: int
    value: 1Кстати, чтобы определить тип значения, можно воспользоваться командой:
defaults read-type -g TISRomanSwitchState Type is integer
А сейчас я хочу настроить Finder на отображение текущего пути стиле Unix. Для этого включу скрытую опцию _FXShowPosixPathInTitle, а заодно включу интерактивную панель пути - ShowPathbar:
- name: Set Option - Show Current Path
  community.general.osx_defaults:
    domain: com.apple.finder
    key:   "{{ item.key }}"
    type:  "{{ item.type }}"
    value: "{{ item.value }}"
  loop:
    - { key: ShowPathbar, type: boolean, value: 1 }
    - { key: _FXShowPosixPathInTitle, type: boolean, value: 1 }
  register: finder
- name: Finder - kill
  ansible.builtin.command: killall Finder
  when: finder is changedГотово, теперь Finder перезапустился и стал выглядеть крутым.
Итоги
Я постарался показать базовые возможности Ansible для хранения конфигурации своего мака в виде кода. Теперь остаётся только сделать над собой усилие и не трогать ничего руками. Да, это будет сложно, потому что искать нужные параметры через defaults тот ещё квест. Да, не все приложения могут работать с defaults, например, тот же iTerm2, использующий собственный API. Но всё равно этот опыт будет полезным. Как минимум, это просто способ получить академические знания об устройстве MacOS, как максимум - облегчить боль обслуживания яблочного парка из десятков и сотен машин.