Ваш новый разработчик только что закончил подписывать трудовой договор и с горящими глазами готов закрывать по 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: bundler
version: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 часа, который новый разработчик может потратить на чтение других вводных документов и общение с наставником. Благодаря этим скриптам мы точно знаем, что окружения всех наших разработчиков синхронизированы, тем самым мы можем избежать многих непонятных ошибок.
В итоге мы сэкономили время нового разработчика при онбординге и позволили ему быстрее приступить к задачам. Более того, этими же скриптами мы настраиваем билд-агенты, получая от скриптов двойную пользу.