Learn (Almost) Anything
КаталогСкачать курс

Автоматизация в GitHub

Как запускать варианты по OS, runtime version, package manager или feature flag; `include`, `exclude`, `fail-fast`, `max-parallel`, naming jobs и контроль стоимости.

ruЭнциклопедия33 урока6 модулей9
GitHub actionsАвтоматизация в GitHubМодель workflowсостояние и артефактыматрицы и runtimeрелизы и environmentsПереиспользование и собственные actionsgovernance и эксплуатацияgithubactions

Matrix strategy

Как запускать варианты по OS, runtime version, package manager или feature flag; `include`, `exclude`, `fail-fast`, `max-parallel`, naming jobs и контроль стоимости.

Matrix strategy

Matrix strategy в GitHub Actions — это способ описать один job как набор вариантов: разные OS, версии runtime, package managers, feature flags или режимы тестов. Вместо трёх почти одинаковых jobs вы пишете один job с strategy.matrix, а GitHub создаёт отдельный job run для каждой комбинации значений.

В CI pipeline базовый контракт был один: checkout → setup → install → lint/typecheck/test/build. Matrix добавляет к нему вопрос: «в каких средах этот контракт должен быть правдой?» Если библиотека должна работать на Node 20 и 22, на Ubuntu и Windows, это уже не один ответ, а четыре.

flowchart TD A[Один job definition] --> B[Оси matrix] B --> C[os] B --> D[runtime] B --> E[package manager] C --> F[Полный набор комбинаций] D --> F E --> F F --> G[exclude убирает бессмысленные варианты] G --> H[include добавляет поля или точечные jobs] H --> I[Отдельные job runs] I --> J[fail-fast / continue-on-error] I --> K[max-parallel]
flowchart TD
  A[Один job definition] --> B[Оси matrix]
  B --> C[os]
  B --> D[runtime]
  B --> E[package manager]
  C --> F[Полный набор комбинаций]
  D --> F
  E --> F
  F --> G[exclude убирает бессмысленные варианты]
  G --> H[include добавляет поля или точечные jobs]
  H --> I[Отдельные job runs]
  I --> J[fail-fast / continue-on-error]
  I --> K[max-parallel]
Matrix strategy разворачивает один job в набор job runs, а затем `exclude`, `include`, `fail-fast` и `max-parallel` управляют формой и поведением этого набора.

Как матрица разворачивается

Минимальная матрица выглядит так:

jobs:
  test:
    name: test / ${{ matrix.os }} / node ${{ matrix.node }}
    runs-on: ${{ matrix.os }}

    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]
        node: [20, 22]

    steps:
      - uses: actions/checkout@v4

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

      - run: npm ci
      - run: npm test

GitHub создаст четыре job run: Ubuntu+Node 20, Ubuntu+Node 22, Windows+Node 20, Windows+Node 22. Значения доступны через matrix.os, matrix.node и другие ключи, которые вы сами задали. runs-on часто берут прямо из матрицы, поэтому выбор runner связан с GitHub-hosted runners и, если используются свои машины, с Self-hosted runners.

Есть важный практический лимит: одна матрица может породить максимум 256 jobs за workflow run. До этого лимита легко добраться случайно: 4 OS × 5 версий runtime × 3 package managers × 4 feature flags = 240 jobs. Формально красиво, но почти всегда слишком дорого и шумно.

ИнтерактивСколько jobs создаст матрица#int-1
GitHub Actions создаёт job для каждой комбинации осей. Реальную матрицу часто надо pruning-ить через exclude/include.
Jobs в матрице4 jobs
Минут runner при 5 мин/job20 мин
Поменяйте число OS, runtime-версий и package managers: это показывает, как быстро растёт матрица до дорогого fan-out.

include и exclude

exclude убирает комбинации из полного декартова произведения. Это нужно, когда вариант бессмысленен или нестабилен: например, старый runtime не поддерживается на Windows, macOS нужен только для одной версии, а experimental-флаг не должен запускаться везде.

strategy:
  matrix:
    os: [ubuntu-latest, windows-latest, macos-latest]
    node: [20, 22]
    package-manager: [npm, pnpm]
    exclude:
      - os: macos-latest
        package-manager: pnpm

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

jobs:
  test:
    name: test / ${{ matrix.os }} / node ${{ matrix.node }} / ${{ matrix.package-manager }}
    runs-on: ${{ matrix.os }}

    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, windows-latest]
        node: [20, 22]
        package-manager: [npm]
        include:
          - os: ubuntu-latest
            node: 22
            package-manager: pnpm
            experimental: true

    continue-on-error: ${{ matrix.experimental == true }}

    steps:
      - uses: actions/checkout@v4

      - name: Set up pnpm
        if: ${{ matrix.package-manager == 'pnpm' }}
        uses: pnpm/action-setup@v4
        with:
          version: 10
          run_install: false

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

      - name: Install dependencies
        shell: bash
        run: |
          if [ "${{ matrix.package-manager }}" = "pnpm" ]; then
            pnpm install --frozen-lockfile
          else
            npm ci
          fi

      - run: ${{ matrix.package-manager }} test

Здесь основная матрица проверяет npm на двух OS и двух версиях Node. Через include добавлен один отдельный экспериментальный job: Ubuntu + Node 22 + pnpm. Такой job не должен ломать весь workflow, поэтому continue-on-error привязан к matrix.experimental.

fail-fast, experimental jobs и шум

По умолчанию fail-fast для matrix strategy включён. Если один job в матрице падает, GitHub отменяет остальные queued и in-progress jobs этой матрицы. Это хорошо для быстрых PR-проверок: если Ubuntu+Node 22 уже показал грубую ошибку компиляции, ждать остальные варианты часто бессмысленно.

Но для диагностики совместимости fail-fast: false часто полезнее. Например, если вы поддерживаете Windows, macOS и Linux, вам может быть важно увидеть все падения сразу, а не чинить их по одному rerun за rerun.

strategy:
  fail-fast: false
  matrix:
    os: [ubuntu-latest, windows-latest, macos-latest]
    node: [20, 22]

Не путайте fail-fast и continue-on-error. fail-fast управляет всей матрицей: отменять ли соседние jobs после падения. continue-on-error относится к конкретному job run. Хорошая схема для experimental runtime такая: стабильные варианты обязаны проходить, experimental-вариант может упасть без красного workflow, но при этом виден в checks.

max-parallel и контроль стоимости

Matrix не всегда ускоряет pipeline. Она ускоряет wall-clock time только если есть доступные runners и если параллельный запуск не упирается во внешние ресурсы: rate limits, базу данных, staging API, shared test account. На платных private repos ещё появляется прямой денежный эффект: GitHub Actions usage считается по времени runner jobs, а Windows и macOS runners стоят дороже Linux.

max-parallel ограничивает число одновременно выполняющихся jobs внутри матрицы:

strategy:
  max-parallel: 2
  matrix:
    os: [ubuntu-latest, windows-latest, macos-latest]
    node: [20, 22]

Это не уменьшает общее число jobs, но снижает пиковую нагрузку. Полезно, если тесты бьют в один внешний sandbox, если self-hosted runners ограничены железом или если macOS jobs не должны занимать всю очередь. Для отмены устаревших запусков на новых pushes используйте concurrency, как в CI pipeline; это другая ось оптимизации, ближе к Оптимизация времени CI.

Имена jobs и required checks

У matrix jobs должны быть понятные name. Если оставить один общий name: test, в интерфейсе checks будет сложнее понять, что именно упало. Лучше включать ключевые значения:

name: test / ${{ matrix.os }} / node ${{ matrix.node }}

Для branch protection это тоже важно. Required check должен быть стабильным и предсказуемым. Если вы меняете имя job или структуру матрицы, protected branch может начать ждать старый check или перестать требовать новый. Поэтому матрицу лучше проектировать как публичный контракт репозитория, а не как временную YAML-игрушку.

Что класть в оси матрицы

Хорошая ось матрицы отвечает на реальный compatibility-риск. Для app-кода обычно достаточно одной основной OS и одной актуальной версии runtime на быстрых PR-проверках, плюс расширенная матрица на push в main, nightly schedule или release branch. Для библиотек матрица шире: поддерживаемые версии runtime, OS, иногда package manager или backend.

Плохая ось матрицы просто размножает уверенность без новой информации. Например, гонять lint на трёх OS обычно не нужно: lint почти всегда зависит от runtime и lock-файла, а не от Windows/macOS. E2E на всех runtime-версиях тоже часто избыточны: smoke/e2e можно оставить на одном «каноническом» runtime, а unit tests прогнать шире.

Cache в матрице требует аккуратных ключей. Если OS, runtime или package manager влияют на зависимости, включайте их в cache key или используйте setup-action cache, который учитывает lock-файлы. Иначе один вариант матрицы может пытаться переиспользовать неподходящий cache. Подробная граница между cache и артефактами разобрана в Dependency cache, а сохранение отчётов для каждого варианта — в Artifacts и reports.

Рабочая эвристика

Начинайте с минимальной матрицы, которая покрывает обещанную поддержку. Для PR: самая быстрая и репрезентативная проверка. Для main и релизов: расширенная совместимость. Используйте exclude, чтобы убрать бессмысленные комбинации, include — чтобы добавить точечные варианты, fail-fast — осознанно под режим диагностики, max-parallel — когда параллельность дороже пользы.

Matrix strategy хороша не потому, что делает YAML «мощнее». Она делает поддержку явной: какие среды проект считает важными, какие варианты экспериментальны и сколько команда готова платить за уверенность.

See also

Быстрое повторение #rc-1

Что именно GitHub Actions делает с strategy.matrix, если заданы оси os: [ubuntu-latest, windows-latest] и node: [20, 22]?

Показать ответ

Он создаёт отдельный job run для каждой комбинации: Ubuntu+20, Ubuntu+22, Windows+20, Windows+22. Значения доступны как matrix.os и matrix.node.

Источники

  1. GitHub Docs — Workflow syntax for GitHub Actions: matrix, include, exclude, fail-fast, max-parallel
  2. GitHub Docs — Running variations of jobs in a workflow
  3. GitHub Docs — GitHub Actions billing