devops
November 27, 2021

Автоматизируем настройку macOS с помощью Ansible

В какой-то момент мой мак "зарос" всяким мусором и я решил - пришло время чистой установки ОС с наведением порядка. Сделать я это задумал с помощью инструментов автоматизации - Ansible плейбуков и вспомогательных скриптов. Вдохновила меня на это коллекция из Ansible Galaxy от Джефа Гирлинга. Правда её использование мне показалось избыточной затеей, поэтому я решил сделать свой вариант (Да-да, разбираться в чужих огромных плейбуках никому не хочется).

Что будем делать

Мне хочется затронуть основные вопросы конфигурации macOS, поэтому попробуем решить следующие задачи:

  1. Установим пару приложений
  2. Раскатаем дотфайлы
  3. Поменяем системные параметры и параметры пользовательского окружения

Думаю этого хватит, чтобы понять суть и потом допилить решение под себя. Погнали.

Подготовительный этап

Если мак не только что достали из коробки, а уже давно используют в качестве рабочей лошади, конечно же сначала надо сделать бэкап данных и только потом заниматься экспериментами, поэтому:

  1. Делаем бэкап системы, файлов, фотографий, документов, паролей, ключей и т.п.
  2. Убеждаемся, что есть доступ к своей учётной записи Apple ID
  3. Убеждаемся, что есть доступ к бэкапу и всем учётным записям, которые потребуются для его восстановления (если бэкап делался в облако или на файловый сервер)
  4. Начисто переустанавливаем macOS.

И теперь, когда в руках оказался чистый мак со свежей ОС, можно начинать развлекаться.

Волшебный репозиторий

Сперва - вот мой репозиторий для настройки своего Мака. Можно скачать его к себе и посмотреть содержимое.

Если коротко, то скрипт run.sh сначала устанавливает Xcode Command Line tools и Ansible, а потом запускает плейбук с дальнейшей конфигурацией системы.

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

К счастью в моём арсенале практически все приложения можно установить с помощью HomeBrew (пакетного менеджера, которого так не хватало в macOS), поэтому особых танцев с бубном тут нет.

Сначала установим HomeBrew:

- 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 и проверить работоспособность

Ansible изменил конфигурацию 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 изменил внешний вид Finder

Итоги

Я постарался показать базовые возможности Ansible для хранения конфигурации своего мака в виде кода. Теперь остаётся только сделать над собой усилие и не трогать ничего руками. Да, это будет сложно, потому что искать нужные параметры через defaults тот ещё квест. Да, не все приложения могут работать с defaults, например, тот же iTerm2, использующий собственный API. Но всё равно этот опыт будет полезным. Как минимум, это просто способ получить академические знания об устройстве MacOS, как максимум - облегчить боль обслуживания яблочного парка из десятков и сотен машин.