当你第 10 次在不同仓库中复制粘贴「安装 Node.js → 安装依赖 → 运行 Lint → 运行测试」这组步骤时,就该考虑将重复逻辑提取为可复用组件了。GitHub Actions 提供了两种复用机制:Composite Action(动作组合)和 Reusable Workflow(可复用工作流)。本文聚焦于 Composite Action,从语法基础到组织级市场建设,系统讲解如何构建高质量的 CI/CD 组件化体系。
一、Composite Action vs Reusable Workflow:选型决策
在深入 Composite Action 之前,必须先澄清它与 Reusable Workflow 的根本区别:
| 维度 | Composite Action | Reusable Workflow |
|---|---|---|
| 定义位置 | 仓库内 .github/actions/my-action/action.yml | 仓库内 .github/workflows/reusable.yml |
| 调用方式 | uses: ./.github/actions/my-action | uses: owner/repo/.github/workflows/reusable.yml@main |
| 输出能力 | 支持 outputs | 支持 outputs + secrets 传递 |
| 嵌套调用 | ✅ 内部可调用其他 action | ❌ 不能嵌套调用 workflow |
| job 定义 | ❌ 不能定义 job,只能封装 steps | ✅ 可以定义完整的 jobs 矩阵 |
| 运行器控制 | 继承调用方的 runner | 可指定自己的 runs-on |
| 权限传播 | 继承调用方 permissions | 独立设置 permissions |
| 适用场景 | 步骤级复用(安装依赖、部署脚本) | 工作流级复用(完整 CI/CD 流水线) |
选择决策树:
需要复用的是?
├── 一组 steps(如安装+测试+上传报告)
│ └── Composite Action
├── 完整 workflow(包含多个 jobs)
│ └── Reusable Workflow
└── 需要跨仓库 secrets 传递?
├── 是 → Reusable Workflow(secrets: inherit)
└── 否 → Composite Action(更轻量)
二、Composite Action 基础语法
2.1 最小可运行示例
创建文件 .github/actions/setup-node-deps/action.yml:
name: 'Setup Node.js and Dependencies'
description: 'Setup Node.js, cache npm, install dependencies'
inputs:
node-version:
description: 'Node.js version to use'
required: false
default: '20'
working-directory:
description: 'Directory containing package.json'
required: false
default: '.'
outputs:
cache-hit:
description: 'Whether npm cache was hit'
value: ${{ steps.cache.outputs.cache-hit }}
runs:
using: composite
steps:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
- name: Cache dependencies
id: cache
uses: actions/cache@v4
with:
path: ${{ inputs.working-directory }}/node_modules
key: ${{ runner.os }}-node-${{ inputs.node-version }}-${{ hashFiles(format('{0}/package-lock.json', inputs.working-directory)) }}
restore-keys: |
${{ runner.os }}-node-${{ inputs.node-version }}-
- name: Install dependencies
run: npm ci
shell: bash
working-directory: ${{ inputs.working-directory }}
在工作流中使用:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-node-deps
with:
node-version: '18'
working-directory: './frontend'
- run: npm test
working-directory: './frontend'
2.2 输入与输出定义规范
输入类型:
| 属性 | 说明 | 示例 |
|---|---|---|
description | 人类可读的说明 | 必需 |
required | 是否必需 | true / false |
default | 默认值 | '20'、'./' |
type | 数据类型(v2.0+) | string、boolean、number |
输出定义:
Composite Action 中 outputs 的值不能直接写,必须通过 value: ${{ steps.step-id.outputs.some-output }} 引用内部 step 的输出。
三、高级模式:嵌套调用与条件逻辑
3.1 嵌套调用其他 Action
Composite Action 内部可以调用其他 Composite Action,形成组件层级:
# .github/actions/setup-and-test/action.yml
name: 'Setup, Build and Test'
description: 'Full CI pipeline for a Node.js package'
inputs:
package-dir:
required: true
runs:
using: composite
steps:
# 调用底层 Composite Action
- uses: ./.github/actions/setup-node-deps
with:
working-directory: ${{ inputs.package-dir }}
- name: Lint
run: npm run lint
shell: bash
working-directory: ${{ inputs.package-dir }}
- name: Test
run: npm test -- --coverage
shell: bash
working-directory: ${{ inputs.package-dir }}
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
directory: ${{ inputs.package-dir }}/coverage
3.2 条件执行
在 Composite Action 中使用 if 条件:
runs:
using: composite
steps:
- name: Install dependencies
run: npm ci
shell: bash
- name: Run security audit
if: inputs.run-audit == 'true'
run: npm audit --audit-level=high
shell: bash
注意:Composite Action 中的 if 条件不支持 ${{ }} 表达式以外的上下文,如 github.event_name 可用,但 env.MY_VAR 需要显式传递为 input。
3.3 多 Shell 支持
对于跨平台项目,Composite Action 需要显式指定 shell:
runs:
using: composite
steps:
- name: Run script (cross-platform)
run: |
if [ "${{ runner.os }}" = "Windows" ]; then
./scripts/build.ps1
else
./scripts/build.sh
fi
shell: bash
四、跨仓库复用:组织级 Action 市场
4.1 发布到独立仓库
将 Composite Action 发布到独立仓库(如 my-org/actions-setup-node),供所有组织内仓库使用:
# 仓库:my-org/actions-setup-node
# 文件:action.yml(根目录)
name: 'Org Standard Node.js Setup'
description: 'Organization-standard Node.js environment setup'
inputs:
node-version:
default: '20'
runs:
using: composite
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
- run: npm ci
shell: bash
调用方式:
- uses: my-org/actions-setup-node@v1
with:
node-version: '18'
4.2 版本管理策略
| 策略 | 标签 | 适用场景 |
|---|---|---|
| 语义化版本 | v1.2.3 | 明确指定,可复现 |
| 浮动主版本 | v1 | 自动接收向后兼容更新 |
| 分支引用 | @main | 开发阶段,不推荐生产使用 |
| Commit SHA | @abc1234 | 最高安全性,审计友好 |
推荐:生产环境使用 v1 或精确版本号,安全敏感场景使用 SHA。
4.3 组织级 Action 市场目录
在组织内维护一个「官方 Action 目录」仓库:
# my-org/.github/actions-marketplace/README.md
## 认证 Actions
| Action | 用途 | 最新版本 |
|--------|------|---------|
| [actions-setup-node](https://github.com/my-org/actions-setup-node) | Node.js 环境标准化 | v2.1.0 |
| [actions-deploy-aws](https://github.com/my-org/actions-deploy-aws) | OIDC 方式部署到 AWS | v1.3.0 |
| [actions-notify-slack](https://github.com/my-org/actions-notify-slack) | CI 结果通知 | v1.0.5 |
## 使用规范
1. 优先使用认证 Actions,避免重复造轮子
2. Action 输入必须提供 `default` 值,降低使用门槛
3. 每个 Action 必须包含 `README.md` 和使用示例
五、Composite Action 的工程化最佳实践
5.1 目录结构规范
.github/
├── actions/
│ ├── setup-node-deps/
│ │ ├── action.yml # 核心定义
│ │ ├── README.md # 使用文档
│ │ └── scripts/
│ │ └── post-install.sh # 辅助脚本
│ ├── deploy-aws/
│ │ ├── action.yml
│ │ └── README.md
│ └── notify-slack/
│ ├── action.yml
│ └── README.md
└── workflows/
└── main.yml # 主工作流
5.2 输入验证与默认值
inputs:
environment:
description: 'Target environment'
required: true
# 使用 type: choice(调用方限制)
timeout:
description: 'Timeout in minutes'
required: false
default: '30'
# 在 step 中做数值验证
runs:
using: composite
steps:
- name: Validate inputs
run: |
if [[ ! "${{ inputs.environment }}" =~ ^(staging|production)$ ]]; then
echo "Invalid environment: ${{ inputs.environment }}"
exit 1
fi
if [[ "${{ inputs.timeout }}" -gt 120 ]]; then
echo "Timeout cannot exceed 120 minutes"
exit 1
fi
shell: bash
5.3 测试 Composite Action
Composite Action 本身没有官方测试框架,但可以通过「引用自身的测试 workflow」验证:
# .github/workflows/test-action.yml
name: Test Composite Action
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Test setup-node-deps
uses: ./.github/actions/setup-node-deps
with:
node-version: '18'
- name: Verify Node version
run: node --version | grep "v18"
六、Composite Action 与 Reusable Workflow 的联合使用
在复杂项目中,两者不是互斥的,而是分层协作:
┌─────────────────────────────────────────┐
│ Reusable Workflow │
│ ┌─────────────────────────────────┐ │
│ │ Job: build │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ Composite Action │ │ │
│ │ │ (setup + test + lint) │ │ │
│ │ └─────────────────────────┘ │ │
│ └─────────────────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ Job: deploy │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ Composite Action │ │ │
│ │ │ (build image + push) │ │ │
│ │ └─────────────────────────┘ │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
示例:
# reusable-ci.yml(Reusable Workflow)
name: Standard CI
on:
workflow_call:
inputs:
node-version:
default: '20'
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: my-org/actions-setup-node@v1
with:
node-version: ${{ inputs.node-version }}
- uses: ./.github/actions/run-tests # Composite Action
deploy:
needs: quality
runs-on: ubuntu-latest
steps:
- uses: ./.github/actions/deploy-aws # Composite Action
with:
environment: staging
七、常见问题解答(FAQ)
Q1: Composite Action 可以使用 uses: docker:// 吗?
不支持。Composite Action 的 runs 只能是 composite 类型,内部 step 只能使用 run 或 uses 调用其他 action。如果需要 Docker 执行,可以使用 Reusable Workflow 或直接在工作流中调用 docker://。
Q2: Composite Action 中如何访问调用方的 secrets?
直接访问调用方 secrets 需要在调用时显式传递:
# 调用方
- uses: ./.github/actions/my-action
with:
api-token: ${{ secrets.API_TOKEN }}
# action.yml
inputs:
api-token:
required: true
runs:
steps:
- run: echo "${{ inputs.api-token }}"
Q3: 如何调试 Composite Action 的内部步骤?
在调用方启用 debug 日志:
env:
ACTIONS_STEP_DEBUG: true
然后在 Actions 日志中可以看到每个内部 step 的详细执行过程。
Q4: Composite Action 可以定义环境变量吗?
可以在内部 step 中设置 env,但不能像 workflow 那样在 job 级别定义全局环境变量:
runs:
using: composite
steps:
- run: npm test
shell: bash
env:
NODE_ENV: test
CI: true
总结
Composite Action 是 GitHub Actions 生态中最被低估的复用机制。它填补了「单个 step 太细」与「整个 workflow 太粗」之间的空白,让团队能够将高频重复的 CI/CD 逻辑封装为标准组件。
实施路径:
| 阶段 | 行动 | 效果 |
|---|---|---|
| 识别重复 | 统计各仓库 workflow 中的重复 steps | 找到复用机会 |
| 提取组件 | 将重复步骤提取为 Composite Action | 首次复用 |
| 发布市场 | 建立组织级 Action 仓库和目录 | 跨仓库复用 |
| 版本管理 | 引入语义化版本和变更日志 | 可维护性 |
| 联合 Workflow | 高压场景组合 Reusable Workflow | 完整流水线复用 |
最终目标是让每位开发者像调用标准库函数一样调用组织的 CI/CD 组件,将精力集中在业务逻辑而非 pipeline plumbing 上。
延伸阅读:
- GitHub Actions 自托管 Runner — 组织级 Action 与私有 Runner 的协同
- GitHub Actions 缓存优化 — Composite Action 内部的缓存键设计
- 前端 Monorepo 工程化 — Composite Action 在 monorepo 中的大规模应用
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。