Reusable workflows
`workflow_call`, inputs, secrets, outputs, nested workflows, matrix with reusable workflows, limitations с environment secrets и мониторинг использования.
Reusable workflows
Reusable workflow — это workflow-файл, который другой workflow вызывает как целый job. Он лежит в .github/workflows, как обычный workflow, но объявляет специальный trigger workflow_call. Это не Composite actions: composite action собирает несколько steps в один action, а reusable workflow может содержать jobs, runs-on, permissions, environment, outputs и собственную структуру pipeline.
Хороший use case: у организации есть десять репозиториев с одинаковым Node.js CI, Docker build или deploy flow. Вместо copy-paste YAML в каждом repo можно держать один workflow в platform-actions и вызывать его из product repo. Так проще обновлять setup, вводить единые Permissions и GITHUB_TOKEN, менять runner images и проверять deploy-политику.
flowchart TD A[Caller workflow: ci.yml] -->|jobs.node-ci.uses| B[Reusable workflow: node-ci.yml] A -->|with: node-version, run-build| B A -->|secrets: explicit или inherit| B B --> C[Job: test] C --> D[Steps: checkout, setup, test, build] D --> E[Step output через GITHUB_OUTPUT] E --> F[Job output] F --> G[workflow_call output] G -->|needs.node-ci.outputs.report-name| H[Downstream job в caller]
Минимальный контракт workflow_call
Reusable workflow начинается с контракта: какие inputs принимает, какие secrets ожидает и какие outputs возвращает. Пример файла .github/workflows/node-ci.yml в общем repo:
name: reusable-node-ci
on:
workflow_call:
inputs:
node-version:
required: true
type: string
run-build:
required: false
type: boolean
default: true
secrets:
npm-token:
required: false
outputs:
report-name:
description: "Name of the uploaded test report"
value: ${{ jobs.test.outputs.report-name }}
jobs:
test:
runs-on: ubuntu-24.04
permissions:
contents: read
outputs:
report-name: ${{ steps.meta.outputs.report-name }}
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: ${{ inputs.node-version }}
cache: npm
- run: npm ci
- run: npm test
- if: ${{ inputs.run-build }}
run: npm run build
- id: meta
run: echo "report-name=node-${{ inputs.node-version }}-report" >> "$GITHUB_OUTPUT"Здесь важно три уровня output: step пишет в $GITHUB_OUTPUT, job мапит step output в jobs.test.outputs, а on.workflow_call.outputs поднимает его наружу для caller workflow. Это та же модель данных, что в Step outputs и job outputs, только добавлен workflow-level слой.
Как caller вызывает workflow
Reusable workflow вызывается через jobs.<job_id>.uses, не внутри steps. Поэтому caller job не пишет runs-on и steps: runner и steps находятся в called workflow.
name: ci
on:
pull_request:
push:
branches: [main]
jobs:
node-ci:
permissions:
contents: read
uses: octo-org/platform-actions/.github/workflows/node-ci.yml@8b7f2c0a4f0b3a9e6d1c2b5a1234567890abcdef
with:
node-version: "22"
run-build: true
secrets:
npm-token: ${{ secrets.NPM_TOKEN }}
print-report:
needs: node-ci
runs-on: ubuntu-24.04
steps:
- run: echo "Report: ${{ needs.node-ci.outputs.report-name }}"Синтаксис бывает внешний и локальный. Для другого repo используется owner/repo/.github/workflows/file.yml@ref; для того же repo — ./.github/workflows/file.yml. Внешний ref может быть branch, tag или commit SHA. По тем же причинам, что в Marketplace actions и pinning, для production-sensitive workflow лучше pin по SHA или по строго контролируемому release tag. Локальный вызов без @ref берёт workflow из того же commit, что и caller.
Есть деталь, которая часто удивляет: если called workflow делает actions/checkout, по умолчанию он checkout-ит репозиторий caller workflow, а не repo, где лежит reusable workflow. Это обычно именно то, что нужно для общего CI: reusable YAML живёт отдельно, но тестирует код вызывающего проекта.
Inputs, secrets и inherit
Inputs в workflow_call типизированы: string, number или boolean. Они доступны через inputs.*, а не как обычные env-переменные. Если нужен env, задайте его явно внутри called workflow:
env:
NODE_VERSION: ${{ inputs.node-version }}Secrets передаются отдельно. Можно явно перечислять secrets, как в примере выше, или использовать secrets: inherit, если caller и called workflow находятся в допустимой области доступа — например, в одной organization или enterprise. inherit удобно для внутренних стандартных workflows, но это сильное разрешение: called workflow получает все secrets, к которым имеет доступ caller job. Для deploy и package publish лучше начинать с явного списка.
Во вложенных reusable workflows secrets не «текут» автоматически через всю цепочку. Если A вызывает B, а B вызывает C, то C увидит secret из A только если A передал его B, а B затем передал его C. Это полезное ограничение для Secure use и threat model: каждый переход в цепочке должен быть осознанным.
Matrix with reusable workflows
Caller job может использовать Matrix strategy и вызывать reusable workflow несколько раз с разными inputs:
jobs:
deploy:
strategy:
fail-fast: false
max-parallel: 1
matrix:
target: [dev, stage, prod]
uses: octo-org/platform-actions/.github/workflows/deploy.yml@v2
with:
environment: ${{ matrix.target }}
secrets: inheritТакой pattern хорошо подходит для однотипных deployments, browser matrix, language-version matrix и package builds. Но matrix не бесплатна: она создаёт несколько job runs, ест minutes и усложняет outputs. Если reusable workflow с matrix возвращает один output, caller получит значение от последнего успешного reusable workflow, который реально установил этот output. Для отчётов и build artifacts часто лучше использовать Artifacts и reports, а не пытаться склеивать всё в один string output.
Environments и protection rules
Главное ограничение: environment secrets нельзя передать из caller через on.workflow_call, потому что workflow_call не поддерживает keyword environment на уровне контракта. Если called workflow сам указывает environment на job level, он использует secrets именно этого environment внутри called workflow, а не «environment secret, переданный caller».
Практический вариант для deploy reusable workflow — передавать имя environment как input и назначать environment внутри called job:
on:
workflow_call:
inputs:
environment:
required: true
type: string
jobs:
deploy:
runs-on: ubuntu-24.04
environment: ${{ inputs.environment }}
permissions:
contents: read
id-token: write
steps:
- run: ./scripts/deploy.sh "${{ inputs.environment }}"Тогда approval, wait timer, branch/tag restrictions и environment secrets работают в месте, где реально выполняется deploy. Это напрямую связано с Environments и protection rules и Deployment workflows: reusable workflow не должен обходить release governance, он должен централизовать его.
Nested workflows, OIDC и мониторинг
Reusable workflow может вызывать другой reusable workflow. GitHub документирует цепочку до десяти уровней: top-level caller плюс до девяти called workflows. Циклы запрещены, все workflows в цепочке должны быть доступны caller, а permissions по цепочке можно только сохранить или сузить, но не повысить. Если reusable deploy получает contents: read, вложенный workflow не должен внезапно стать contents: write.
Для Cloud deploy через OIDC reusable workflows особенно полезны: можно держать один hardened deploy workflow и на стороне cloud role проверять, что OIDC token пришёл именно из этого called workflow. В OIDC token для такого job есть claim job_workflow_ref, который указывает на reusable workflow. Это позволяет строить trust policy не только вокруг caller repo, но и вокруг утверждённого deployment workflow.
{
"repository": "octo-org/product-api",
"workflow_ref": "octo-org/product-api/.github/workflows/release.yml@refs/heads/main",
"job_workflow_ref": "octo-org/platform-actions/.github/workflows/deploy.yml@refs/tags/v2.4.1",
"environment": "prod"
}Эксплуатационно reusable workflows нужно мониторить как shared dependency. В GitHub Enterprise Cloud использование можно отслеживать через audit log API; в репозитории caller reused workflows также видны как dependencies в dependency graph. На практике добавляют CODEOWNERS на .github/workflows, changelog для shared workflow repo, pinned refs в caller repos и регулярный review через Monitoring и troubleshooting и Enterprise policies и migration.
Когда это не тот инструмент
Reusable workflow не заменяет всё. Если нужно просто спрятать пять shell steps и вызывать их как один step — берите Composite actions. Если нужна переносимая логика на Node API GitHub — это зона JavaScript actions. Если нужен свой Linux image с зависимостями — смотрите Docker container actions. Reusable workflow выбирают, когда единицей переиспользования является job или pipeline: CI template, deploy policy, release flow, package publish, security scan.
See also
- Marketplace actions и pinning — почему reusable workflow из другого repo тоже стоит pin-ить осознанно.
- Composite actions — когда переиспользовать steps, а не jobs.
- Matrix strategy — как контролировать
include,exclude,fail-fastиmax-parallel. - Environments и protection rules — approval, wait timers и environment secrets для deploy jobs.
- Cloud deploy через OIDC — short-lived cloud credentials и trust conditions.
- Versioning и maintenance actions — release discipline для shared automation.
Внешние справки: GitHub Docs по reuse workflows, workflow syntax, reusing workflow configurations и OIDC with reusable workflows.
