Например, успешная команда создания проекта подтверждает, что сервер принял запрос. Сохранность проекта после перезапуска подтверждает повторное чтение той же записи. Нужная проверка зависит от требования задачи.
Поэтому о готовности договариваются заранее. Задача готова, когда выполнены условия, которые можно проверить. Проверка идёт в три слоя. Здесь разберём первый слой, который включает команду проверки, запуск и CI. CI автоматически запускает проверки при изменениях проекта. Второй и третий слои включают ревью отдельным агентом и твоё решение.
Команда проверки и код возврата
Автоматические проверки обычно включают линтер, проверку типов и тесты. Проверка типов находит места, где коду передано значение не того вида, который он ожидает. Какие проверки нужны, зависит от проекта. Важно, чтобы агент, ты и CI запускали один и тот же набор. Удобнее всего собрать проверки в одну команду проверки, например npm run check или make check. Так весь выбранный набор запускается одним вызовом.
Команда сообщает результат кодом возврата. Так называют число, которое программа отдаёт системе при завершении. Ноль означает успех, любое другое число означает ошибку. Например, один тест не прошёл:
$ npm run check
lint ok
typecheck ok
test 12 passed, 1 failed
$ echo $?
1
Агент и CI судят о результате именно по этому числу. Отсюда главное требование к команде. Она должна возвращать ошибку, когда хоть одна проверка не прошла. Команду легко случайно испортить. Достаточно собрать проверки в скрипт, который продолжает работу после ошибки и в конце возвращает ноль. Все строки вывода при этом выглядят как обычно, а «готово» перестаёт что-либо значить.
Линтер и проверка типов прошли, а один тест не прошёл. CI запускает npm run check и показывает результат в pull request зелёной отметкой при успехе или красной при ошибке. Что он покажет?
1.
Красную отметку из-за упавшего теста.
2.
Зелёную отметку, хотя тест упал.
3.
Красную отметку у тестов и зелёную у остальных проверок.
Что подтверждает успешная команда
Успешная команда подтверждает только то, что в неё записано. Она не знает, какой сценарий ты считаешь главным. Если тест проверяет создание записи, а требование говорит о сохранении после перезапуска, команда пройдёт и при несохранённых данных.
Поэтому главный сценарий задачи проверяют, выполняя его. Агент выполняет действие и записывает, что увидел. В записи три поля: коммит, действие и наблюдение. Например, задача добавила в README инструкцию по установке. Запись проверки может выглядеть так:
Коммит: 4f2a91c
Действие: репозиторий склонирован в новую папку, зависимости установлены по README,
запущена команда проверки.
Наблюдение: установка прошла без ошибок, команда проверки завершилась с кодом 0.
Когда в проекте появится приложение, тем же способом проверяют его запуск: открыли страницу, выполнили действие, записали, что увидели.
По такой записи следующая сессия и ты отличаете выполненную проверку от намерения её выполнить. Если проверку выполнить не удалось, в записи так и сказано, с причиной. «Не запускал, потому что база недоступна» полезнее, чем «готово».
Что добавляет CI
Команду проверки агент запускает на твоём компьютере. Результат зависит от его состояния. Могла остаться старая база, установленная вручную библиотека или файл, не попавший в коммит.
Для этого проекта можно настроить GitHub Actions, сервис автоматического запуска команд в GitHub. Он запускает общую команду проверки при открытии и обновлении pull request. В примере используем GitHub-hosted runner, машину для проверок, которую предоставляет GitHub. Обычный Linux runner ubuntu-latest получает новую виртуальную машину для каждого запуска.
Простой workflow, то есть описание автоматического запуска, помещается в один файл в репозитории. В нём записано примерно следующее:
Когда: открыт или обновлён pull request
Что сделать: получить код, установить зависимости, выполнить команду проверки
Результат: зелёная или красная отметка в pull request
Шаг actions/checkout получает код из репозитория. При событии pull_request и без явно заданного ref, то есть версии для загрузки, он получает результат объединения ветки задачи с основной веткой. Другое событие или явно заданный ref меняет то, какой код проверяется. Свой runner тоже может сохранять состояние прошлых запусков. Чистое окружение относится к выбранной настройке, а не к любому CI.
У такой проверки два преимущества перед локальной. Во-первых, код проверяется в том виде, в каком он сохранён в репозитории. Во-вторых, результат виден прямо в pull request зелёной или красной отметкой, без чтения отчёта агента.
Проверьте понимание
Агент запустил команду проверки на твоём компьютере, и она прошла. GitHub Actions запускает её для события pull_request на GitHub-hosted runner. Шаг actions/checkout используется без параметра ref. Версии языка, тестового фреймворка и зависимостей совпадают с локальными. Команда запускается из корня репозитория. В журнале CI тест сообщает, что не находит файл с тестовыми данными. Какое объяснение подходит лучше всего?
1.
На машине CI установлена другая версия тестового фреймворка.
2.
CI проверяет код основной ветки вместо кода pull request.
3.
Нужный тесту файл есть только на твоём компьютере.
Решения, которые придётся принять
Что входит в команду проверки. Чем больше проверок, тем дольше ждать. Команду, которая идёт десять минут, перестают запускать.
Мой совет
В команду проверки я включаю линтер, проверку типов и тесты, которые проходят быстро. Долгие проверки запускаю отдельно, перед релизом.
Какой CI-сервис взять. Для репозитория на GitHub проще всего GitHub Actions, потому что настройка лежит в том же репозитории, а результат виден в pull request. Другой сервис подойдёт, если он показывает результат проверки в pull request.