Зачем это необходимо
Когда в твоей команде работают больше одного человека, так или иначе все сталкиваются с проблемой разных стилей кодирования каждого члена команды. Кто-то пишет скобки для блоков if...else
, кто-то нет. Когда проект становится больше, то такой код труднее читать и еще сложнее проводить код-ревью.
Чтобы код-ревью и прочие командные митинги не превратились в обсуждение tab vs spaces на повышенных тонах, лучше настроить репозиторий таким образом, чтобы сам проект не допускал написание невалидного и нестандартного для команды кода.
С одной стороны, использование разных стилей кодирования может показаться вкусовщиной, недостойной внимания. Ну не оборачивает джун единственную строку кода после условия if
, а кто-то пишет, что с того? Если оставить код из под пера джуна "как есть", то это может стать "бомбой замедленного действия": ту строку кода после if
могут удалить, и тогда под условие попадет следующая строка. Конечно, эта ситуация обычно отлавливается на код-ревью, однако бывает так, что этот потенциальный баг проходит проверку, и вот две основных причины:
- Мы все люди, а люди ошибаются.
- Люди социальны, а значит вступать "в очередной раз" в конфликт, связанный со стилями, не захочется. И тут возможны два варианта:
- "Лучше поправлю сам", думает проверяющий, и правит код.
- Проверяющий срывается на джуна и высказывает свои сомнения в его адекватности и необходимости существования.
Как можно добиться того, чтобы каждый писал в соответствии с командным стилем? Бить по рукам на код-ревью каждый раз демотивирует и автора кода, и самого проверяющего. К счастью, эта проблема будоражит умы не одного программиста не первый год, и в нашем распоряжении сейчас есть множество инструментов.
Цель этой статьи — рассказать другим и себе будущему, как я настраиваю репозиторий проекта таким образом, чтобы он сам обезопасил себя от невалидного кода с точки зрения стандартов команды.
Что мы имеем
В качестве примера возьмем демонстрационный проект, код которого будет выложен на GitHub. Так как я занимаюсь разработкой на .NET Core, то и проект будет написан на нем. Что я буду использовать:
- .NET Core 3.1
- Angular 8+
- Github аккаунт
- Travis CI
Я уже создал демонстрационный репозиторий проекта и настроил в нем Travis-CI. Далее в статье разберем, что необходимо было сделать для этого и почему.
Пайплайн репозитория
Пайплайн репозитория — механизм, предотвращающий попадаение невалидного кода с второстепенных веток в основную master branch
.
"Из коробки" пайплайны доступны в Gitlab и Azure DevOps, а в Github — через Travis CI.
Настраиваем репозиторий
Мне нравится подход к разработке софта Егора Бугаенко. Я законспектировал несколько его докладов в своем блоге. Если кратко, то вот основные принципы, которым я следую при настройке репозитория:
- Ограничение прав на пуш. Я ограничиваю права на пуш в develop и master всем, кроме мейнтейнеров (maintainer).
- Пайплайн сборки. Я прописываю пайплайн для сборки проекта в CICD и прогона юниттестов как для бэкенда, так и фронта.
- Repository is a king. В репозитории я прописываю правила работы с кодом и gitflow, а также другие связанные с подходами в разработке документы.
- Fail fast. Если код написан невалидно с точки зрения стандартного стиля, то разработчик получит ошибку компиляции.
- Git pre-commits hoocks. Чтобы не занимать агенты CI гитлаба лишком часто, я добавляю прогон тестов и иные полезные операции на пре-коммит хуки гита.
Что мы получаем в итоге? Во-первых, в master и develop смогут залить код только мейнтейнеры проекта. В идеале, конечно, и им нужно ограничить доступ, чтобы только "автомат" мог сливать ветки. Я оставил реализацию этого принципа "на потом". Ограничение прав настраивается через интерфейс гитлаба, поэтому я не буду описывать этот этап здесь.
Валидация бэкенда
Я настраиваю solution-файл (*.sln) проекта так, чтобы он выдавал несоответствия написанного кода стандартам стайл-гайда .NET как ошибки компиляции. Чтобы сделать это, мне понадобится файл с перечислением кодов ошибок, пара nuget-пакетов и немного терпения.
Я использую stylecop в проектах для .NET Core. Чтобы его верно настроить, прежде всего мы создаем несколько файлов в корне проекта рядом с solution-файлом (ссылки ведут на gist.github.com):
Directory.build.props
— ссылка на файл.standard.ruleset
— ссылка на файл.stylecop.json
— ссылка на файл.
После этих действий наш проект не будет собираться, пока в нем будут ошибки стиля кодирования.
Валидация фронтенда
Фронтенд-приложение тоже необходимо валидировать. Здесь настройки пайплайна менее критичны к нарушениям стиля кода: если мы пропустим где-то точку с запятой, то проект все равно будет работать. На страже репозитория здесь будет стоять агент пайплайна. Я автоматизирую следующие команды:
# Проверка линта
ng lint
# Сборка в режиме продакшна, чтобы провалидировать и html-файлы
ng build --prod
# Прогон тестов
ng test
Есть небольшой нюанс работы агентов репозитория с тестами. Дело в том, что для прогона тестов необходим движок Хрома (Chrome / Chromium), а он чаще всего отсутствует в контейнерах CI-систем. Чтобы агент мог запускать тесты фронта, я добавляю npm-пакет puppeteer
в проект, который подтянет с собой и хромиум.
Таким образом, чтобы и корректность фронтенда валидировалась пайплайном, нам необходимо проделать следующие шаги:
- Добавить новую команду
"test-headless-ci-only": "ng test --browsers ChromiumNoSandbox"
в блокscripts
файлаpackages.json
:
"scripts": {
"ng": "ng",
"start": "ng serve -o",
"build": "ng build",
"build-stage": "ng build --configuration=staging",
"build-prod": "ng build --prod",
"test": "ng test",
"test-headless-ci-only": "ng test --browsers ChromiumNoSandbox",
"lint": "ng lint",
"e2e": "ng e2e"
},
- Установить пакет
npm install puppeteer
и прописать его в файлеkarma.conf.js
в самое начало файла:
const process = require("process");
process.env.CHROME_BIN = require("puppeteer").executablePath();
module.exports = function(config) {
...
};
- Добавить кастомный лаунчер тестов в файле
karma.conf.js
в секциюcustomLaunchers
:
config.set({
....,
customLaunchers: {
ChromiumNoSandbox: {
base: "ChromeHeadless",
flags: [
"--no-sandbox",
"--headless",
"--disable-gpu",
"--disable-translate",
"--disable-extensions"
]
}
},
singleRun: true
});
Теперь в скриптах пайплайна можно запускать тесты командой npm run est-headless-ci-only
.
Стандартизируем код фронтенда
Чтобы код-ревью тикетов для фронтенда не превратились в обсуждение предпочтений форматирования, лучше всего стандартизировать его. Я пользуюсь инструментом prettierrc, потому что репозиторий проекта имеет много звезд и документация написана подробно. Эта библиотека помогает подкорректировать форматирование автоматически. Чтобы добавить prettierrc в проект, необходимо:
- Установить пакеты prettier и pretty-quick глобально:
npm install -g prettier
npm install -g pretty-quick
- Добавить файл конфигурации с именем
.prettierrc
в корень фронтенд-приложения:
{
"useTabs": false,
"printWidth": 120,
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "none",
"semi": true
}
- Добавить список файлов для игнорирования prettier-ом в файл с именем
.prettierignore
в корень фронтенд-приложения:
package.json
package-lock.json
tslint.json
tsconfig.json
browserslist
.gitkeep
favicon.ico
tsconfig.lib.json
tsconfig.app.json
tsconfig.spec.json
karma.conf.js
protractor.conf.js
ng-package.json
*.html
Теперь можно "привести в порядок" код фронтенда командой pretty-quick --staged
.
Использование прекоммит-хуков
Запуск агента пайплайна в CI/CD системах — это потребление ресурсов, и зачастую небесплатных. Можно и нужно запускать валидацию проекта локально, но делать это на каждый коммит надоедает. В итоге люди перестают запускать скрипты так часто. Чтобы автоматизировать этот процесс, я пользуюсь прекоммит-хуками, которые позволяют запускать полезные скрипты при коммитах и пушах.
Для фронтенда лучше всего подойдет библиотека husky. Чтобы настроить хук, необходимо:
- Установить библиотеку husky
npm install -g husky
- Добавить хук husk в файл
package.json
в конец:
"devDependencies": {
...
},
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged",
"pre-push": "ng lint && ng test --browsers ChromiumNoSandbox"
}
}
Здесь я разделил команды: нет необходимости проверять тесты фронтенда на каждый коммит, но мы не дадим залить изменения в удаленный репозиторий, пока тесты не будут "зелеными".
Итог
После того, как сделаны описанные в статье шаги, я получаю проект, который "защищает сам себя" от невалидного кода. Понятное дело, что одной проверкой синтаксиса и стайл-гайда не уберечь продукт от багов, однако даже эти незначительные вещи помогают в достижении большего качества кода и позволяют обсуждать архитектурные решения на код-ревью, а не вопросы форматирования.
EvgeniiR
Увидев валидацию подумал что будут тесты/статический анализ.
Но их нет, потому добавлю от себя, и ещё эту пикчу:
maximgorbatyuk Автор
Статья действительно не о тестах, а о том, как подготовить репозиторий для команды, чтобы она стала писать код в одном стиле без просьб и пряников с кнутами. Сама система не даёт написать нестандартный код.
А юниттесты — понятное дело, тоже нужны, и об этом написано много. В том числе и поэтому касаться этого я не стал в статье
KickingBear
Что порекомендуете повесить на препуш-хук для .net бэкенда?
maximgorbatyuk Автор
Была идея запускать прогон тестов бэкенда тоже, однако решил отказаться от нее по нескольким причинам:
Держать в течение нескольких дней отрефаченный код без возможности его запушить — рискованно тоже по нескольким причинам:
KickingBear
Немного не в тему, но почему бы и да
Какой на ваш взгляд туллинг на test coverage юнит тестов хорош? Интересны варианты и с интеграцией в VisualStudio и 3rd party на CI например
maximgorbatyuk Автор
Пока что я не занимался настройкой графиков покрытия тестами, однако есть в планах настроить репозиторий так, чтобы при агенты пайплайнов сканировал test coverage проекта, и если он стал ниже определенного уровня, то пайплайн фейлился