Полный пример: lint, test, build, deploy

Собираем всё изученное в один настоящий пайплайн от пуша до прода.

Цель курса — автоматизировать всё от пуша до прода: один файл проверяет код, собирает артефакт и катит его на сервер по правилам.

Что должен делать пайплайн

  • на каждый PR и push в main — прогнать lint и test (барьер качества);
  • если проверки прошли — собрать артефакт (build);
  • деплоить deploy только из main, с защищённым окружением production;
  • не запускать два деплоя разом и отменять устаревшие прогоны CI.

Полный workflow

name: CI/CD
on:
  push:
    branches: [main]
  pull_request:

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

permissions:
  contents: read

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"
      - run: npm ci
      - run: npm run lint

  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node: [18, 20]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
          cache: "npm"
      - run: npm ci
      - run: npm test

  build:
    needs: [lint, test]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"
      - run: npm ci
      - run: npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist/

  deploy:
    needs: build
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://example.com
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: dist
          path: dist/
      - name: Деплой
        run: |
          echo "Выкатываю dist/ на прод"
          # здесь реальная команда деплоя (ssh/облачный экшен)
        env:
          DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

Как это читается

  • lint и test идут параллельно; test ещё и размножен матрицей по версиям Node.
  • build ждёт оба (needs: [lint, test]) и выгружает артефакт dist.
  • deploy зависит от build, выполняется только из main (if) и привязан к production — здесь сработают required reviewers и секреты окружения.
  • concurrency отменяет устаревшие прогоны; permissions: contents: read задаёт минимальные права по умолчанию.

Граф выполнения

Вывод:

lint  ─┐
       ├─> build ─> deploy (только main, с апрувом)
test ─┘

Итог

  • Один файл реализует полный путь: проверка → сборка → условный деплой.
  • Ключевые приёмы курса работают вместе: matrix, needs, if, environment, concurrency, permissions, артефакты.
  • Это и есть автоматизация «от пуша до прода» — её можно адаптировать почти под любой проект.
Проверьте себя
1. Почему job deploy в примере имеет условие if: github.ref == 'refs/heads/main'?
AЧтобы деплоить из любой ветки
BЧтобы выкатывать на прод только изменения из основной ветки main, а не из веток PR
CЧтобы ускорить сборку
DЧтобы отключить тесты
2. Почему build объявлен с needs: [lint, test]?
AЧтобы build стартовал раньше них
BЧтобы build начинался только после успешных lint и test (барьер качества перед сборкой)
CЧтобы отключить матрицу
DЭто случайная настройка
3. Что обеспечивает блок concurrency в этом пайплайне?
AШифрует артефакт
BОтменяет устаревшие прогоны той же ветки, чтобы не тратить минуты на неактуальные сборки
CСоздаёт несколько окружений
DЗапускает deploy без апрува
Поддержать проект