Гайд · TNWS AI

Как отменять устаревшие AI-тесты pull request через concurrency GitHub Actions

6 мин—

Настраиваем concurrency для проверки AI-контракта: новые коммиты отменяют устаревшие запуски одного PR без пересечения разных веток.

Задача и границы применения

Не тратить время CI на старую ревизию промпта, если автор PR уже отправил следующий коммит. Регрессионный набор фикстур может выполняться долго; прошлый результат на старом SHA не нужен для слияния нового. Группа concurrency должна различать ветки и не отменять проверку другого PR. При этом отмена не равна успешному тесту: branch protection ориентируется на итоговые checks актуального коммита. Ниже — сценарий для собственного репозитория, а не обещание универсальной точности генеративной модели. Для демонстрации используются вымышленные обращения, которые не содержат персональных данных.

Актуальность названий полей, событий и версий actions проверена 19.09.2026 по официальным материалам GitHub. Если у вашего проекта другая структура каталогов или другой протокол AI API, адаптируйте пути и прикладной клиент после проверки его документации. Пример не содержит утверждений о тарифах, ценах или скорости.

Что подготовить до изменения workflow

Нужен репозиторий GitHub, доступ к его Actions и возможность добавить файл в каталог .github/workflows. Подготовьте тестовую ветку и одно синтетическое обращение с известным ожидаемым результатом. Не берите production-заявки: даже если runner временный, ввод и диагностика оказываются в истории запуска. Для Python-части используйте unittest из стандартной библиотеки; если в вашем проекте есть дополнительные импорты, установите их отдельно из контролируемого файла зависимостей.

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

Пошаговая настройка

  1. Создайте нужные каталоги и файлы из блоков ниже; имена путей в YAML должны совпадать с именами файлов в репозитории. Файл workflow хранится внутри .github/workflows, а тестовый код — в tests или scripts согласно примеру.
  2. Проверьте три секции YAML: on задаёт событие, permissions ограничивает встроенный токен, jobs содержит runner и последовательность шагов. Не вставляйте ключи или закрытые тексты в YAML.
  3. Сохраните YAML в .github/workflows/ai-pr-concurrency.yml, а тест из блока ниже — в tests/test_prompt_contract.py. Выражение github.workflow отделяет этот workflow от других, github.ref разделяет PR между собой. Значение cancel-in-progress: true отменяет уже выполняемый запуск той же группы, когда поступает новый. Создайте PR и внесите два изменения подряд в одну ветку, затем откройте историю Actions: старый запуск может стать cancelled, новый должен завершиться. Не применяйте эту схему к процессу, где побочные эффекты должны обязательно завершиться, например миграции базы. Для ночного запуска продумайте другую группу, если один файл обслуживает несколько событий.
  4. Сначала воспроизведите успешный вход, затем намеренно измените одно ожидаемое значение и убедитесь, что job падает. Проверка без негативного сценария не доказывает, что тест вообще способен обнаружить регрессию. После этого верните правильное значение и повторите запуск.
  5. При сбое откройте конкретный job и шаг, а не ориентируйтесь только на общий статус workflow. Проверьте событие запуска, выбранную ветку, наличие файлов, код завершения скрипта и последние безопасные диагностические сообщения.

Готовый шаблон workflow

Скопируйте YAML; если в блоке указаны два имени файла, разделите его на два соответствующих файла. Ссылки на внешние actions и параметры проверены по их официальным README на 19.09.2026.

name: AI contract PR
on:
  pull_request:
permissions:
  contents: read
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
jobs:
  regression:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-python@v7
        with:
          python-version: '3.13'
      - run: python -m unittest discover -s tests -v

Код или тестовая фикстура для копирования

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

# tests/test_prompt_contract.py
import unittest

def render_ticket(text):
    # Замените на детерминированную часть своего приложения.
    # Сетевой вызов модели здесь намеренно отсутствует.
    return {"priority": "high" if "срочно" in text.lower() else "normal",
            "summary": text.strip()}

class PromptContract(unittest.TestCase):
    def test_urgent_ticket(self):
        actual = render_ticket("Срочно: платеж не прошёл")
        self.assertEqual(actual["priority"], "high")
        self.assertEqual(actual["summary"], "Срочно: платеж не прошёл")

if __name__ == "__main__":
    unittest.main()

Реалистичный вход и ожидаемый результат

Первый коммит PR меняет описание правила, второй исправляет priority. Для одного PR старый запуск отменён, новый выполняет test_urgent_ticket и получает high. Одновременный PR из другой ветки не должен попасть в ту же группу и отмениться. Перед тем как трактовать красный job как баг модели, убедитесь, что сравниваются значения одной и той же ревизии кода и одной и той же фикстуры. Не подменяйте ожидаемое значение фактически полученным: такое «исправление» отключит сигнал регрессии.

Разбор ошибок и ограничения

Если workflow не появляется в Actions, сперва проверьте расположение YAML и событие on; затем проверьте ограничения по путям и веткам. Если job есть, но падает на checkout или setup-python, отличайте инфраструктурную ошибку от провала прикладного утверждения. Если Python завершился с ошибкой импорта, проверьте реальные пути и зависимости. Если проблема только в содержании ответа, сохраните минимальный безопасный вход и точное различие ожидаемого и фактического полей.

В CI тестируется выбранный контракт, а не «качество ИИ вообще». Он не доказывает отсутствие галлюцинаций, безопасность всех возможных промптов или корректность каждой интеграции. Добавляйте отдельные фикстуры для отрицательных кейсов, пустого ввода, Unicode и граничных форматов, когда эти ситуации есть в вашем продукте. Для live API ограничивайте доступ к секрету и используйте отдельные тестовые данные. Не выводите заголовки Authorization, тело закрытого ответа или переменные окружения целиком.

Чек-лист перед слиянием

  • Workflow лежит в .github/workflows, а событие запуска соответствует задаче.
  • Скрипт работает локально на синтетическом входе и падает при намеренно нарушенном контракте.
  • Имена файлов, переменные окружения и пути в YAML совпадают с репозиторием.
  • Секреты не записаны в код, входные параметры, summary или диагностический файл.
  • Проверен успешный запуск и понятный неуспешный запуск; просмотрен конкретный job.
  • Код и тесты соответствуют ограничениям доверия для внешнего PR и правам токена.
  • Результат относится к актуальному коммиту, а не к предыдущей ревизии.

FAQ

Почему canceled не означает ошибку теста?

Это решение оркестрации: новый запуск вытеснил старый. Оценивайте тестовый результат актуального SHA, не отменённой ревизии.

Почему в группе два выражения?

Имя workflow отделяет независимые процессы, ref отделяет ветки или PR; без ref события разных PR могут конфликтовать.

Можно ли отменять интеграционный тест?

Только если тест безопасно прерывать и он не оставляет внешних побочных эффектов; иначе нужна уборка ресурсов или отдельная стратегия.

Что если текущий запуск так и не стартовал?

Проверьте фильтры события и историю Actions: отмена concurrency не запускает workflow самостоятельно.

Как зафиксировать проверенный результат

После успешного запуска сохраните ссылку на конкретный run рядом с pull request: так ревьюер увидит проверенную ревизию. При следующем сбое сравнивайте не только цвет check, но и фикстуру, входные параметры, изменённые файлы и точный шаг ошибки. Если ослабили утверждение теста, объясните в PR, какой риск остался непокрытым, и добавьте отдельный сценарий. При переносе шаблона в другой репозиторий повторно проверьте структуру каталогов и разрешения: одинаковый YAML сам по себе не гарантирует одинаковую модель доверия. Не называйте демонстрационные результаты измерением качества или производительности AI-системы.

Официальные первоисточники

Проверено 19.09.2026. Описание синтаксиса и поведения: 1, 2, 3. Читайте первоисточники при переносе шаблона в другой репозиторий: доступность версии action или поддерживаемый синтаксис могут измениться.

Читайте также

Комментарии

Пока тихо. Скажите первое слово