Ваш новый разработчик только что закончил подписывать трудовой договор и с горящими глазами готов закрывать по 15 задач в день. На его пути стоит лишь одно препятствие новый ноутбук, который пока что не настроен должным образом. Чаще всего процесс настройки окружения описывается в документе, который выдается новому разработчику. Мы не сильно далеко ушли и тоже составили такой список.
-
Установка Xcode
-
Настройка локального git репозитория
-
Настройка окружения
-
Настройка проекта
-
Ознакомление с документацией
-
Настройка таск-трекера (Jira/Youtrack)
Чтобы не тратить драгоценное время разработчика на ручную настройку нового ноутбука, мы автоматизировали 3 и 4 пункты, т.к. они являются самыми трудозатратными.
Итак, давайте сперва рассмотрим настройку окружения.
Настройка окружения
В нашем случае настройка окружения состоит из нескольких пунктов:
1. Установка brew зависимостей
2. Установка mint зависимостей
3. Установка и настройка ruby
4. Установка и настройка python
Чтобы упростить настройку вышеперечисленных пунктов, мы воспользовались Ansible. Он помогает решать множество задач, таких как управление сетями, операционными системами, развертка серверной инфраструктуры, мы же ограничились простой настройкой локальной машины через playbooks.
Но и тут мы не остановились и решили еще больше облегчить жизнь нового разработчика и написали скрипт, который устанавливает ansible и производит настройку вышеперечисленных пунктов.
В конечном итоге скрипт получился вот таким:
#!/usr/bin/env bashcurl https://bootstrap.pypa.io/get-pip.py -o get-pip.py && sudo python get-pip.pysudo pip install ansiblecd $(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 installedstat: path=~/.rvmregister: rvmdirchangedwhen: false- name: Install RVM for a usercommand: bash -c "\curl -sSL https://get.rvm.io | bash -s -- --ignore-dotfiles"when: rvmdir.stat.exists == False- name: Add RVM to profilecommand: bash -c "~/.rvm/bin/rvm get stable --auto-dotfiles"when: rvmdir.stat.exists == False- name: Add RVM to zshrccommand: 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 Defaultcommand: bash -c "~/.rvm/bin/rvm --default use {{ rubyversion }}"when: rvmdir.stat.exists == False- name: Install Ruby Gems required for iOS app developementgem: 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/rubyrequire 'FileUtils'require 'colorize'RESOURCES_DIRECTORY = './Scripts/Resources'.freezeXCODE_DIRECTORY = '~/Library/Developer/Xcode'.freezedef 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)}") endenddef 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)}") endenddef 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 = trueFileUtils.cp_r(file, path) } return unless need_to_reboot_xcode puts 'Quiting Xcode'.blue system('killall Xcode')enddef setup_gems puts "\nSetting up gems".blue.bold system('bundle install')enddef setup_mocks puts "\nSetting up mocks".blue.bold system('cd $(pwd) && swiftymocky generate')enddef setup_projects puts "\nSetting up projects".blue.bold system('bundle exec rake update_projects')end# StepsDir.chdir("#{File.expand_path(File.dirname(__FILE__))}/..")setup_git_hookssetup_file_templatessetup_xcode_snippetssetup_gemssetup_mockssetup_projects
Что тут вообще происходит:
-
Настройка гит-хуков. В нашем случае к коммиту всегда прибавляется номер задачи, который берется из названия ветки.
-
Установка темплейтов для генерации модулей.
-
Установка полезных сниппетов.
-
Установка гемов.
-
Генерация моков.
-
Настройка проекта.
Чуть подробнее, пожалуй, стоит остановиться на последнем шаге. В нем мы подтягиваем и кэшируем все нужные Carthage зависимости и генерируем файлы проектов с помощью XcodeGen.
Заключение
Настройка проекта и установка всех зависимостей занимает в нашем случае около 1 часа, который новый разработчик может потратить на чтение других вводных документов и общение с наставником. Благодаря этим скриптам мы точно знаем, что окружения всех наших разработчиков синхронизированы, тем самым мы можем избежать многих непонятных ошибок.
В итоге мы сэкономили время нового разработчика при онбординге и позволили ему быстрее приступить к задачам. Более того, этими же скриптами мы настраиваем билд-агенты, получая от скриптов двойную пользу.