Генератор CI/CD: промпты и YAML для GitHub Actions

Автор: Обновлено

Что такое AI-генерация CI/CD пайплайнов?

AI-генерация CI/CD использует LLM для черновика GitHub Actions YAML по промпту со стеком, триггерами, стадиями, целевой инфраструктурой и ограничениями. Черновик экономит время на настройке, но перед коммитом его всё равно нужно проверить на безопасность и корректность.

TL;DR

  • -Полезный промпт для CI/CD охватывает 5 элементов: стек и версии, триггеры, стадии, инфраструктуру и ограничения вроде таймаутов и concurrency.
  • -Каждый сгенерированный workflow нужно проверить по 4 пунктам: полные SHA для actions, минимальные permissions, узкий scope секретов и маскирование чувствительного вывода.
  • -Ревью в четыре прохода (база → безопасность → оптимизация → расширение) упрощает поиск ошибок по сравнению с одним перегруженным промптом.
  • -Reusable workflows через workflow_call устраняют дублирование конфигов между репозиториями - одно обновление распространяется на все проекты организации.
  • -Без timeout-minutes на каждый job зависший runner GitHub Actions может блокировать очередь до 6 часов (дефолтный лимит платформы).

Опасность AI-сгенерированного workflow обычно не в невалидном YAML. Чаще модель забывает permissions, оставляет шестичасовой таймаут или подключает сторонний action по изменяемому тегу. Черновик появится за минуту, но границы доверия модель не угадает, если их не указать в промпте.

Ниже — шаблоны для Node.js, Python, Docker и поэтапного деплоя. Для читаемости в примерах используются актуальные мажорные теги. Перед публикацией workflow закрепите каждый внешний action проверенным полным commit SHA и перепроверьте permissions.

Анатомия промпта для CI/CD генерации

AI-модель генерирует YAML тем точнее, чем конкретнее описан контекст - тот же принцип, что в context engineering для LLM: структурированный вход даёт структурированный выход. Промпт для GitHub Actions должен содержать пять элементов:

  1. Стек и версии - язык, runtime, package manager
  2. Триггеры - на какие события запускать pipeline
  3. Стадии - что именно проверять и в каком порядке
  4. Инфраструктура - где деплоить, какие секреты нужны
  5. Ограничения - таймауты, concurrency, условия пропуска

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

Базовый шаблон промпта:

Generate a GitHub Actions workflow for:
- Stack: [language] [version], [package manager]
- Triggers: push to main, pull_request to main
- Steps: install deps, lint, test, build
- Cache: [package manager] cache
- Node version matrix: [versions]
- Fail fast: true
- Timeout: 15 minutes per job

Первый промпт: Node.js с матричным тестированием

Начнём с типовой задачи. Нужен pipeline для TypeScript-проекта: линтинг, тесты на нескольких версиях Node.js, билд.

Промпт:

Generate a GitHub Actions workflow for a TypeScript project:
- Runtime: Node.js 22, 24, 26 (matrix strategy)
- Package manager: pnpm 11 with caching
- Triggers: push to main, pull_request to main
- Jobs: lint (single node version), test (matrix), build (single, needs test)
- Lint: eslint + prettier check
- Test: vitest with coverage, upload coverage artifact
- Build: tsc --noEmit, then vite build
- Fail fast: false (run all matrix combinations)
- Timeout: 10 minutes per job
- Concurrency: cancel in-progress runs for same PR

Модель сгенерирует следующий черновик. Node 24 — LTS-база, Node 22 проверяет старую поддерживаемую ветку, а Node 26 заранее ловит несовместимости с Current. Перед копированием матрицы в долгоживущий репозиторий сверьтесь с расписанием релизов Node.js.

name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  lint:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v7

      - uses: pnpm/action-setup@v4
        with:
          version: 11

      - uses: actions/setup-node@v7
        with:
          node-version: 24
          cache: pnpm

      - run: pnpm install --frozen-lockfile
      - run: pnpm lint
      - run: pnpm prettier --check .

  test:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    strategy:
      fail-fast: false
      matrix:
        node-version: [22, 24, 26]
    steps:
      - uses: actions/checkout@v7

      - uses: pnpm/action-setup@v4
        with:
          version: 11

      - uses: actions/setup-node@v7
        with:
          node-version: ${{ matrix.node-version }}
          cache: pnpm

      - run: pnpm install --frozen-lockfile
      - run: pnpm test -- --coverage

      - uses: actions/upload-artifact@v7
        if: matrix.node-version == 24
        with:
          name: coverage-report
          path: coverage/

  build:
    needs: [lint, test]
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v7

      - uses: pnpm/action-setup@v4
        with:
          version: 11

      - uses: actions/setup-node@v7
        with:
          node-version: 24
          cache: pnpm

      - run: pnpm install --frozen-lockfile
      - run: pnpm tsc --noEmit
      - run: pnpm build

Ключевые детали, которые AI корректно обработал: --frozen-lockfile для воспроизводимости, кеш pnpm через встроенную опцию actions/setup-node, загрузка coverage только с одной версии Node.js, concurrency для отмены устаревших запусков.

Промпт для Python: тесты + линтинг + публикация пакета

Python-проекты требуют другой структуры. Виртуальное окружение, зависимости через pip или poetry, специфика кеширования. Матрица начинается с Python 3.12 и включает актуальную на момент обновления ветку 3.14. При следующем обновлении workflow сверьтесь с официальным списком релизов Python.

Промпт:

Generate GitHub Actions workflow for a Python library:
- Python versions: 3.12, 3.13, 3.14 (matrix)
- Dependency manager: poetry with caching
- Triggers: push to main, pull_request, release published
- Jobs:
  1. quality: ruff check + ruff format --check + mypy (single python version)
  2. test: pytest with matrix, upload junit xml
  3. publish: build wheel, publish to PyPI (only on release event)
- Publish uses trusted publishing (OIDC), no API tokens
- Timeout: 15 minutes
name: CI/CD

on:
  push:
    branches: [main]
  pull_request:
  release:
    types: [published]

jobs:
  quality:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v7

      - uses: actions/setup-python@v7
        with:
          python-version: "3.14"

      - name: Install poetry
        run: pipx install poetry

      - name: Cache poetry
        uses: actions/cache@v6
        with:
          path: ~/.cache/pypoetry
          key: poetry-${{ hashFiles('poetry.lock') }}

      - run: poetry install --no-interaction
      - run: poetry run ruff check .
      - run: poetry run ruff format --check .
      - run: poetry run mypy src/

  test:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.12", "3.13", "3.14"]
    steps:
      - uses: actions/checkout@v7

      - uses: actions/setup-python@v7
        with:
          python-version: ${{ matrix.python-version }}

      - name: Install poetry
        run: pipx install poetry

      - name: Cache poetry
        uses: actions/cache@v6
        with:
          path: ~/.cache/pypoetry
          key: poetry-${{ matrix.python-version }}-${{ hashFiles('poetry.lock') }}

      - run: poetry install --no-interaction
      - run: poetry run pytest --junitxml=results.xml

      - uses: actions/upload-artifact@v7
        if: always()
        with:
          name: test-results-${{ matrix.python-version }}
          path: results.xml

  publish:
    if: github.event_name == 'release'
    needs: [quality, test]
    runs-on: ubuntu-latest
    timeout-minutes: 15
    permissions:
      id-token: write
    environment: pypi
    steps:
      - uses: actions/checkout@v7

      - uses: actions/setup-python@v7
        with:
          python-version: "3.14"

      - name: Install poetry
        run: pipx install poetry

      - run: poetry build
      - uses: pypa/gh-action-pypi-publish@release/v1

Обратите внимание на trusted publishing через OIDC. Нет API-токенов в секретах, PyPI верифицирует публикацию через GitHub identity. Это текущий стандарт для open-source пакетов.

Docker: multi-stage build + registry push

Контейнерные проекты требуют работы с Docker registry, кеширования слоёв и multi-platform builds.

Промпт:

Generate GitHub Actions for Docker image:
- Triggers: push to main (build+push), pull_request (build only)
- Registry: GitHub Container Registry (ghcr.io)
- Multi-platform: linux/amd64, linux/arm64
- Cache: GitHub Actions cache backend
- Tags: sha, branch name, latest (only on main), semver from git tag
- Scan image with trivy before push
- Timeout: 20 minutes
name: Docker

on:
  push:
    branches: [main]
    tags: ["v*"]
  pull_request:
    branches: [main]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build:
    runs-on: ubuntu-latest
    timeout-minutes: 20
    permissions:
      contents: read
      packages: write
      security-events: write
    steps:
      - uses: actions/checkout@v7

      - uses: docker/setup-qemu-action@v4

      - uses: docker/setup-buildx-action@v4

      - name: Log in to GHCR
        if: github.event_name != 'pull_request'
        uses: docker/login-action@v4
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v6
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=sha
            type=ref,event=branch
            type=semver,pattern={{version}}
            type=raw,value=latest,enable={{is_default_branch}}

      - name: Build and push
        uses: docker/build-push-action@v7
        with:
          context: .
          platforms: linux/amd64,linux/arm64
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Scan with Trivy
        uses: aquasecurity/[email protected]
        with:
          image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ github.sha }}
          format: sarif
          output: trivy-results.sarif

      - name: Upload scan results
        if: always()
        uses: github/codeql-action/upload-sarif@v4
        with:
          sarif_file: trivy-results.sarif

docker/metadata-action автоматически формирует теги по git context. cache-from: type=gha использует встроенный кеш GitHub Actions без внешнего registry для кеш-слоёв.

Multi-stage pipeline: от PR до production

Полноценный деплой-пайплайн включает стадии staging и production с ручным подтверждением. Здесь промпт становится длиннее, потому что описывает бизнес-логику деплоя.

Промпт:

Generate a multi-stage deployment pipeline:
- Trigger: push to main deploys to staging automatically
- Production deploy: manual workflow_dispatch with environment approval
- Stages: test → build → deploy-staging → deploy-production
- Deploy target: Cloudflare Pages (staging and production)
- Environment secrets: CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID
- Build artifact shared between deploy jobs
- Slack notification on deploy success/failure
- Production requires GitHub environment protection rules
name: Deploy

on:
  push:
    branches: [main]
  workflow_dispatch:
    inputs:
      environment:
        description: "Deploy target"
        required: true
        type: choice
        options:
          - staging
          - production

jobs:
  test:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 24
          cache: npm
      - run: npm ci
      - run: npm test

  build:
    needs: test
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 24
          cache: npm
      - run: npm ci
      - run: npm run build

      - uses: actions/upload-artifact@v7
        with:
          name: build-output
          path: dist/
          retention-days: 3

  deploy-staging:
    if: github.event_name == 'push' || inputs.environment == 'staging'
    needs: build
    runs-on: ubuntu-latest
    timeout-minutes: 10
    environment:
      name: staging
      url: https://staging.example.com
    steps:
      - uses: actions/download-artifact@v8
        with:
          name: build-output
          path: dist/

      - name: Deploy to Cloudflare Pages
        uses: cloudflare/wrangler-action@v4
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          command: pages deploy dist/ --project-name=my-project --branch=staging

      - name: Notify Slack
        if: always()
        uses: slackapi/slack-github-action@v3
        with:
          webhook: ${{ secrets.SLACK_WEBHOOK }}
          webhook-type: incoming-webhook
          payload: |
            {
              "text": "Staging deploy ${{ job.status }}: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
            }

  deploy-production:
    if: inputs.environment == 'production'
    needs: build
    runs-on: ubuntu-latest
    timeout-minutes: 10
    environment:
      name: production
      url: https://example.com
    steps:
      - uses: actions/download-artifact@v8
        with:
          name: build-output
          path: dist/

      - name: Deploy to Cloudflare Pages
        uses: cloudflare/wrangler-action@v4
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          command: pages deploy dist/ --project-name=my-project --branch=main

      - name: Notify Slack
        if: always()
        uses: slackapi/slack-github-action@v3
        with:
          webhook: ${{ secrets.SLACK_WEBHOOK }}
          webhook-type: incoming-webhook
          payload: |
            {
              "text": "Production deploy ${{ job.status }}: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
            }

Билд-артефакт создаётся один раз и переиспользуется в обоих деплоях. Staging деплоится автоматически на push в main. Production требует ручного запуска через workflow_dispatch и подтверждения через GitHub environment protection rules.

Best practices: что AI пропускает

AI-модели генерируют структурно корректные workflows, но систематически пропускают несколько вещей. Чеклист для ревью сгенерированного пайплайна.

Безопасность

Pinning actions по SHA. Стандартный uses: actions/checkout@v7 ссылается на изменяемый тег. В рекомендациях GitHub по безопасности полный commit SHA назван единственным неизменяемым способом подключить action. Перед пиннингом проверьте commit в официальном репозитории:

# Вместо этого
- uses: actions/checkout@v7

# Используйте это
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

Добавьте в промпт: Pin all third-party actions to full SHA commit hash with version comment.

Минимальные permissions. Дефолт зависит от настроек репозитория и организации. В новом личном репозитории токен получает чтение contents и packages, но у организации может быть другая политика. Задавайте права в workflow явно и не полагайтесь на текущую настройку администратора:

permissions:
  contents: read
  packages: write

Секреты. AI иногда предлагает хардкодить значения или использовать ${{ secrets.GITHUB_TOKEN }} там, где нужен отдельный токен. Всегда проверяйте, какие секреты использует workflow. Подробнее о дырах безопасности в AI-коде - в чеклисте AI security audit.

Производительность

Кеширование. Встроенный кеш actions/setup-node подходит, когда граф зависимостей определяет один lockfile. В монорепо укажите правильный lockfile через cache-dependency-path или используйте actions/cache, если нужен собственный scope и порядок восстановления.

Параллельные jobs. AI часто выстраивает все jobs в линейную цепочку через needs. Lint и test можно запускать параллельно:

build:
  needs: [lint, test]  # lint и test запускаются параллельно

Conditional steps. Пропускайте тяжёлые шаги, если изменились только документы:

- name: Check for code changes
  id: changes
  uses: dorny/paths-filter@v3
  with:
    filters: |
      code:
        - 'src/**'
        - 'package.json'

- name: Run tests
  if: steps.changes.outputs.code == 'true'
  run: npm test

Надёжность

Retry для flaky шагов. Сетевые операции (npm install, docker push) падают из-за таймаутов. Добавьте retry:

- name: Install dependencies
  uses: nick-fields/retry@v3
  with:
    timeout_minutes: 5
    max_attempts: 3
    command: npm ci

Таймауты. Без timeout-minutes job может висеть до 6 часов (дефолт GitHub). Всегда задавайте явный таймаут.

Concurrency. Без concurrency группы два push подряд запустят два одинаковых пайплайна. Для PR-чеков отменяйте предыдущие запуски:

concurrency:
  group: ci-${{ github.ref }}
  cancel-in-progress: ${{ github.event_name == 'pull_request' }}

Продвинутые промпты: reusable workflows и composite actions

Когда проектов много и каждый хранит копию одного CI/CD конфига, обновление превращается в рутину. GitHub поддерживает reusable workflows и composite actions для переиспользования.

Промпт для reusable workflow:

Generate a reusable GitHub Actions workflow (.github/workflows/reusable-node-ci.yml):
- Callable via workflow_call
- Inputs: node-version (string, default "24"), package-manager (string, default "pnpm"), run-lint (boolean, default true)
- Secrets: inherited
- Jobs: install, lint (conditional on input), test, build
- Cache based on package manager input
name: Node.js CI (Reusable)

on:
  workflow_call:
    inputs:
      node-version:
        type: string
        default: "24"
      package-manager:
        type: string
        default: "pnpm"
      run-lint:
        type: boolean
        default: true

jobs:
  ci:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v7

      - if: inputs.package-manager == 'pnpm'
        uses: pnpm/action-setup@v4
        with:
          version: 11

      - uses: actions/setup-node@v7
        with:
          node-version: ${{ inputs.node-version }}
          cache: ${{ inputs.package-manager }}

      - name: Install (npm)
        if: inputs.package-manager == 'npm'
        run: npm ci

      - name: Install (pnpm)
        if: inputs.package-manager == 'pnpm'
        run: pnpm install --frozen-lockfile

      - name: Lint
        if: inputs.run-lint
        run: ${{ inputs.package-manager }} run lint

      - run: ${{ inputs.package-manager }} test
      - run: ${{ inputs.package-manager }} run build

Вызов из другого репозитория:

jobs:
  ci:
    uses: my-org/.github/.github/workflows/reusable-node-ci.yml@main
    with:
      node-version: "24"
      package-manager: pnpm
      run-lint: true
    secrets: inherit

Один конфиг обновляется в центральном репозитории и применяется ко всем проектам организации.

Итеративная доработка: prompt chaining

Сложные пайплайны редко получаются с первого промпта. Цепочка промптов:

  1. Базовый промпт - генерирует скелет workflow
  2. Промпт-ревью - “Review this workflow for security issues, missing caches, and unnecessary steps”
  3. Промпт-оптимизация - “Optimize this workflow to run under 5 minutes by parallelizing jobs”
  4. Промпт-расширение - “Add Slack notifications, artifact uploads, and deployment to staging”

Каждый шаг уточняет результат предыдущего. Модель видит контекст и вносит точечные правки вместо перегенерации. Если таких промптов накапливается много по разным проектам, наводить порядок помогает система prompt engineering.

Пример промпта-ревью:

Review this GitHub Actions workflow for:
1. Security: pinned actions, minimal permissions, secret handling
2. Performance: caching, parallelism, conditional execution
3. Reliability: timeouts, retry, concurrency groups
4. Maintainability: DRY (reusable workflows), clear naming

List issues as: [SEVERITY] description → fix

AI выдаст структурированный список проблем с конкретными исправлениями. Этот паттерн работает и как code review через AI-агентов, только применённый к инфраструктурному коду.

Защита от каскадных сбоев в pipeline

CI/CD пайплайн зависит от внешних сервисов: npm registry, Docker Hub, облачные провайдеры. Один упавший registry блокирует все деплои. Принципы circuit breaker применимы и здесь:

  • Fallback registry. Настройте .npmrc с резервным registry
  • Retry с backoff. Сетевые шаги должны повторяться с увеличивающейся задержкой
  • Timeout на каждый шаг. Не только на job, но и на отдельные step’ы через timeout-minutes
  • Cache как circuit breaker. Если registry недоступен, закешированные зависимости позволяют пайплайну пройти (для тестов, не для деплоя)

Шаблон промпта: универсальный генератор

Финальный промпт, покрывающий большинство сценариев:

Generate a production-ready GitHub Actions workflow:

PROJECT:
- Language: [X], version: [Y]
- Package manager: [Z]
- Monorepo: yes/no

TRIGGERS:
- push: [branches]
- pull_request: [branches]
- release: published
- schedule: [cron]
- workflow_dispatch: [inputs]

JOBS (in dependency order):
1. [job-name]: [description] (runs on: [os], timeout: [min])
2. ...

REQUIREMENTS:
- Pin all third-party actions to SHA
- Minimal permissions per job
- Cache: [strategy]
- Concurrency: cancel in-progress for PRs
- Artifacts: [what to upload]
- Notifications: [Slack/email/none]
- Environments: [staging/production with protection rules]

CONSTRAINTS:
- Total pipeline time: under [X] minutes
- Runner: [ubuntu-latest / self-hosted]
- No secrets in logs (mask sensitive outputs)

Заполните квадратные скобки и проверяйте результат как инфраструктурный код. Если workflow собирает контейнер, сверяйте build context, порядок слоёв, runtime-содержимое и сканирование с гайдом по Docker multi-stage builds.

Результат

Поручайте модели первый черновик, но не финальное решение. Промпт со стеком, триггерами, стадиями, инфраструктурой и ограничениями даёт ревьюеру конкретный YAML. Перед merge проверьте permissions, границы секретов, SHA внешних actions и таймауты. Когда этот процесс стабилизируется, вынесите повторяющиеся части в reusable workflow, а не генерируйте один и тот же конфиг заново.

Ценный результат — не пятиминутная демка, а проверяемый workflow, в diff которого видны границы доверия и поведение при сбоях.


Нужна помощь с настройкой CI/CD? Я помогаю стартапам внедрять AI-решения и строить продукты - belov.works.

Часто задаваемые вопросы

Пиннить GitHub Actions по полному SHA или достаточно тега версии типа @v7?
В production-пайплайнах используйте полный SHA. Мажорный тег может измениться, а полный commit SHA оставляет action неизменным до явного обновления. Проверьте, что SHA принадлежит официальному репозиторию action, а обновления поручите Dependabot или Renovate с обязательным ревью. В примерах ниже мажорные теги оставлены ради читаемости; production-вариант показан в разделе безопасности.
Как организовать CI/CD для монорепозитория, где изменились только некоторые сервисы?
Использовать path-based фильтрацию через dorny/paths-filter для определения изменившихся сервисов, затем закрывать тяжёлые jobs (тесты, билды, деплои) за conditional steps. Каждый сервис получает собственные правила фильтрации; jobs запускаются только при изменении релевантных путей. Это предотвращает ситуацию, когда правка документации в одном сервисе запускает полный билд и деплой несвязанных сервисов. Нативная фильтрация GitHub по on.push.paths снижает частоту триггеров, но не даёт гранулярности для условного запуска конкретных jobs внутри workflow - paths-filter решает эту задачу.
Какая стратегия кеширования зависимостей правильная для GitHub Actions?
Начните со встроенного кеша actions/setup-node или actions/setup-python. В монорепо укажите нужный lockfile через cache-dependency-path; actions/cache нужен при собственном scope или порядке восстановления. Для Docker-слоёв cache-from: type=gha использует кеш GitHub Actions. Включайте хеш lockfile в ключ кеша зависимостей, чтобы изменения инвалидировали старые записи.