Рабочие процессы, триггеры и задачи

Что это такое

Рабочий процесс — это файл в репозитории, который называет события, на которые он реагирует, задачи, которые нужно выполнить, и шаги внутри каждой задачи. Триггер — само событие: push в ветку, открытие pull request’а, создание тега, расписание или ручной запуск. Задача выполняется на свежей машине и состоит из шагов, каждый из которых — команда или переиспользуемое действие. Задачи по умолчанию независимы, и их можно сделать зависимыми — так сборку ставят перед развёртыванием.

Почему это важно

Именно правильно выбранный триггер решает, полезна автоматизация или шумна. Рабочий процесс, прогоняющий полный набор тестов на каждый push в каждую ветку, тратит раннеры и топит результаты; тот, что работает только на основной ветке, замечает проблему уже после слияния. Обычная форма — дешёвые проверки на каждом pull request’е и развёртывание только из основной ветки, а значит, файл должен кодировать политику, а не просто список команд.

Ключевые идеи

Триггер — это политика

События, которые слушает рабочий процесс, и ветки с путями, по которым он фильтрует, решают, что и когда запускается. Фильтры по путям важны в монорепозитории: изменение документации не должно заново собирать приложение. Ручной триггер полезен для операций, о которых должен решить человек, а не для тех, что происходят сами.

on:
  pull_request:
    paths: ["src/**"]
  push:
    branches: [main]
  workflow_dispatch:

Задача — свежая машина с собственной выгрузкой

Каждая задача стартует чистой, поэтому всё, что произвела предыдущая, нужно выгрузить и загрузить между ними. Вот почему задача сборки публикует артефакт, а задача развёртывания его забирает, вместо того чтобы рассчитывать, что файл всё ещё на месте. И поэтому же задача, зависящая от состояния предыдущей, — ошибка, а не медленный путь.

Шаги выполняются по порядку, и первый сбой останавливает задачу

Шаги последовательны внутри задачи, а упавший шаг валит задачу, если ему явно не разрешено продолжить. Этот порядок и делает конвейер читаемым: собрать, протестировать, опубликовать. Шаг, который проглатывает ошибку, прячет отказ от всех последующих проверок.

Задачи идут параллельно или зависят друг от друга — и это рычаг скорости

Независимые задачи выполняются одновременно; needs заставляет одну ждать другую. Набор тестов, разбитый на несколько параллельных задач, заканчивается куда раньше одной длинной, а матрица разворачивает одно определение задачи по нескольким версиям или платформам. Медленный конвейер обычно чинят именно здесь, а не ускорением отдельного шага.

jobs:
  test:
    strategy: { matrix: { node: [22, 24] } }
  deploy:
    needs: test

Переиспользуемые рабочие процессы и действия избавляют от копирования

Рабочий процесс может вызвать другой рабочий процесс с параметрами — так несколько репозиториев делят стандартный конвейер. Составное действие упаковывает последовательность шагов. И то и другое стоит выносить, когда одна и та же логика появляется в третьем месте; до этого они добавляют лишний слой.

Раннеры — ресурс с ценой и моделью безопасности

Хостинговые раннеры удобны, недолговечны и ограничены; собственные дешевле при большом объёме, но это машина, которую нужно защищать и обновлять. Собственный раннер, подключённый к публичному репозиторию, — серьёзный риск, потому что pull request может предложить код, который на нём выполнится.

Практика

Запустите нужную работу по нужному событию

Напишите рабочий процесс, который выполняет задачу тестов на pull request’ах, затрагивающих каталог исходников, и задачу развёртывания только на push’ах в основную ветку, пользуясь выгруженным артефактом, а не общим состоянием.

Готово, когда: pull request, меняющий только документацию, не запускает ни одной задачи, а задача развёртывания использует артефакт, который произвела задача тестов.

Распараллельте набор тестов

Разделите одну длинную задачу тестов на три, работающие одновременно, каждая со своим результатом.

Готово, когда: время выполнения всего рабочего процесса падает, а сбой в одной задаче всё равно помечает прогон как упавший.

Частые ошибки

  • Запускать полный конвейер на каждой ветке и каждом изменении — раннеры тратятся, а результаты игнорируются; исправить: фильтруйте триггеры по ветке и пути.
  • Полагать, что поздняя задача увидит файлы ранней — каждая задача стартует чистой; исправить: выгружайте и загружайте артефакты.
  • Передавать секреты в рабочий процесс, запущенный из форка — недоверенный код может ими воспользоваться; исправить: закрывайте прогоны с секретами проверкой.
  • Делать каждую задачу зависимой от каждой — прогон сериализуется и длится дольше самой работы; исправить: зависьте только там, где порядок действительно нужен.
  • Использовать собственный раннер в публичном репозитории — на вашей машине выполняется произвольный предложенный код; исправить: держите недоверенные прогоны на хостинговых недолговечных раннерах.

Что читать дальше