Ваш новый разработчик только что закончил подписывать трудовой договор и с горящими глазами готов закрывать по 15 задач в день. На его пути стоит лишь одно препятствие — новый ноутбук, который пока что не настроен должным образом. Чаще всего процесс настройки окружения описывается в документе, который выдается новому разработчику. Мы не сильно далеко ушли и тоже составили такой список.
Установка Xcode
Настройка локального git репозитория
Настройка окружения
Настройка проекта
Ознакомление с документацией
Настройка таск-трекера (Jira/Youtrack)
Чтобы не тратить драгоценное время разработчика на ручную настройку нового ноутбука, мы автоматизировали 3 и 4 пункты, т.к. они являются самыми трудозатратными.
Итак, давайте сперва рассмотрим настройку окружения.
Настройка окружения
В нашем случае настройка окружения состоит из нескольких пунктов:
1. Установка brew зависимостей
2. Установка mint зависимостей
3. Установка и настройка ruby
4. Установка и настройка python
Чтобы упростить настройку вышеперечисленных пунктов, мы воспользовались Ansible. Он помогает решать множество задач, таких как управление сетями, операционными системами, развертка серверной инфраструктуры, – мы же ограничились простой настройкой локальной машины через playbooks.
Но и тут мы не остановились и решили еще больше облегчить жизнь нового разработчика и написали скрипт, который устанавливает ansible и производит настройку вышеперечисленных пунктов.
В конечном итоге скрипт получился вот таким:
#!/usr/bin/env bash curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py && sudo python get-pip.py sudo pip install ansible cd $(dirname $0) && ansible-playbook main.yml
Тут происходит следующее:
Установка менеджера пакетов pip
Установка Ansible
Запуск наших задач из
main.yml
Осталось разобраться, что находится в main.yml. А там всего лишь
- hosts: localhost roles: - common_setup
Здесь hosts отвечает за те машины, на которых мы хотим запустить наши задачи, в нашем случае это локальная машина, а roles содержит информацию о том, какие задачи нужно выполнить, переменные и метаинформацию.
roles ожидает, что файлы будут находиться в определенных папках, в нашем случае иерархия выглядит вот так
Каждая папка (meta, tasks и vars) должна содержать файл main.yml.
Все команды можно уместить в главном main.yml, но подход через roles обеспечивает большую читаемость и модульность.
Больше всего нас интересует папка tasks, в которой собраны задачи, выполняющие настройку. Рассмотрим структуру задачи, которая выполняет установку и настройку Ruby с помощью rvm.
--- - name: Check if RVM already installed stat: path=~/.rvm register: rvmdir changedwhen: false - name: Install RVM for a user command: bash -c "\curl -sSL https://get.rvm.io | bash -s -- --ignore-dotfiles" when: rvmdir.stat.exists == False - name: Add RVM to profile command: bash -c "~/.rvm/bin/rvm get stable --auto-dotfiles" when: rvmdir.stat.exists == False - name: Add RVM to zshrc command: bash -c "echo '\n[ -s ${HOME}/.rvm/scripts/rvm ] && source ${HOME}/.rvm/scripts/rvm' >> ~/.zshrc" when: rvmdir.stat.exists == False - name: Install {{ rubyversion }} command: bash -c "~/.rvm/bin/rvm install {{ rubyversion }}" when: rvmdir.stat.exists == False - name: Set Ruby Default command: bash -c "~/.rvm/bin/rvm --default use {{ rubyversion }}" when: rvmdir.stat.exists == False - name: Install Ruby Gems required for iOS app developement gem: name={{ item.name }} version={{ item.version }} state={{ if item.version is defined nil else latest }} withitems: "{{ rubygems_packages_to_install }}" when: rvmdir.stat.exists == False
Проверяем, установлен ли rvm.
name- название задачиstat- путь до rvmregisterсохраняет переменную для последующего использованияchanged_whenпереопределяет поведение смены состояния машины после исполненияМы точно знаем, что после исполнения этой задачи состояние не изменится, поэтому просто ставим false. Более подробно использование этой команды описано здесь.
Устанавливаем rvm.
name- название задачиcommand- команда для выполненияwhen- условие для выполненияУсловием для выполнения здесь является отсутствие установленного ранее rvm, мы можем узнать это из сохраненной на предыдущем шаге переменной
rvmdir.Настраиваем rvm. Параметры аналогичны.
Добавляем строки в .zshrc конфиг. На MacOS Catalina этот шаг обязателен, так как zsh теперь используется по умолчанию.
Устанавливаем нужную нам для разработки версию Ruby. Тут параметры тоже аналогичны, единственным отличием будет использование нашей переменной из папки vars. Объявлена она в
main.ymlследующим образом:rubyversion: ruby-2.6.5Назначаем свежеустановленную версию Ruby версией по умолчанию
Устанавливаем нужные гемы, в нашем случае это просто bundler. Тут также используется переменная из папки vars, объявленная таким образом:
rubygems_packages_to_install:- name: bundlerversion:2.1.4
На этом настройка окружения закончена, можем переходить к настройке самого проекта.
Настройка проекта
Под настройкой проекта мы подразумеваем полную установку всех зависимостей и других необходимых вещей для успешного билда.
Скрипт для настройки проекта выглядит следующим образом:
#!/usr/bin/ruby require 'FileUtils' require 'colorize' RESOURCES_DIRECTORY = './Scripts/Resources'.freeze XCODE_DIRECTORY = '~/Library/Developer/Xcode'.freeze def setup_git_hooks puts "Setting up git hooks".blue.bold git_hooks_path = '.git/hooks' FileUtils.mkdir_p(git_hooks_path) Dir["#{RESOURCES_DIRECTORY}/Git hooks/*"].each do |file| FileUtils.cp_r(file, "#{git_hooks_path}/#{File.basename(file)}") end end def setup_file_templates puts "\nSetting up file templates".blue.bold file_templates_path = File.expand_path("#{XCODE_DIRECTORY}/Templates/File Templates/Tests") FileUtils.mkdir_p(file_templates_path) Dir["#{RESOURCES_DIRECTORY}/Templates/*.xctemplate"].each do |file| FileUtils.cp_r(file, "#{file_templates_path}/#{File.basename(file)}") end end def setup_xcode_snippets puts "\nSetting up xcode snippets".blue.bold need_to_reboot_xcode = false code_snippets_path = File.expand_path("#{XCODE_DIRECTORY}/UserData/CodeSnippets") FileUtils.mkdir_p(code_snippets_path) Dir["#{RESOURCES_DIRECTORY}/Snippets/*.codesnippet"].each { |file| path = "#{code_snippets_path}/#{File.basename(file)}" next if File.file?(path) need_to_reboot_xcode = true FileUtils.cp_r(file, path) } return unless need_to_reboot_xcode puts 'Quiting Xcode'.blue system('killall Xcode') end def setup_gems puts "\nSetting up gems".blue.bold system('bundle install') end def setup_mocks puts "\nSetting up mocks".blue.bold system('cd $(pwd) && swiftymocky generate') end def setup_projects puts "\nSetting up projects".blue.bold system('bundle exec rake update_projects') end # Steps Dir.chdir("#{File.expand_path(File.dirname(__FILE__))}/..") setup_git_hooks setup_file_templates setup_xcode_snippets setup_gems setup_mocks setup_projects
Что тут вообще происходит:
Настройка гит-хуков. В нашем случае к коммиту всегда прибавляется номер задачи, который берется из названия ветки.
Установка темплейтов для генерации модулей.
Установка полезных сниппетов.
Установка гемов.
Генерация моков.
Настройка проекта.
Чуть подробнее, пожалуй, стоит остановиться на последнем шаге. В нем мы подтягиваем и кэшируем все нужные Carthage зависимости и генерируем файлы проектов с помощью XcodeGen.
Заключение
Настройка проекта и установка всех зависимостей занимает в нашем случае около 1 часа, который новый разработчик может потратить на чтение других вводных документов и общение с наставником. Благодаря этим скриптам мы точно знаем, что окружения всех наших разработчиков синхронизированы, тем самым мы можем избежать многих непонятных ошибок.
В итоге мы сэкономили время нового разработчика при онбординге и позволили ему быстрее приступить к задачам. Более того, этими же скриптами мы настраиваем билд-агенты, получая от скриптов двойную пользу.
