GitHub Workflows 全面解析:从核心概念到开源实现
目录
- [GitHub Workflows 是什么](#GitHub Workflows 是什么)
- 核心概念全景
- 工作流执行流程
- 关键特性深度解析
- [实战:一个完整的 CI/CD 工作流](#实战:一个完整的 CI/CD 工作流)
- 开源实现全景对比
- 选型建议与总结
1. GitHub Workflows 是什么
GitHub Actions 是 GitHub 内置的持续集成与持续交付(CI/CD)平台。开发者通过在仓库的 .github/workflows/ 目录下放置 YAML 文件来定义自动化流程,当仓库中发生特定事件(推送代码、创建 Pull Request、定时任务等)时,GitHub 会自动触发对应的工作流执行构建、测试、部署等任务。
GitHub Actions 的定位远超传统 CI/CD 工具。除了 DevOps 场景,它还可以用于仓库管理的自动化------例如自动给 Issue 添加标签、关闭陈旧的 Pull Request、发布 Release 时自动生成变更日志等。这种"事件驱动 + 可编程"的设计,使得 GitHub Workflows 成为一个通用的仓库自动化引擎。
核心数据 :GitHub 提供 Linux、Windows 和 macOS 三种平台的托管运行器,每次工作流运行都在全新的虚拟机中执行,用完即弃。用户也可以在自己的数据中心或云基础设施中部署自托管运行器(self-hosted runner),以满足合规、安全或性能需求。
2. 核心概念全景
理解 GitHub Workflows 需要掌握几个层次分明的核心概念。下图展示了从事件触发到最终执行的完整架构层次:
#mermaid-svg-Q9aBB4HpbxOuLBId{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Q9aBB4HpbxOuLBId .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Q9aBB4HpbxOuLBId .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Q9aBB4HpbxOuLBId .error-icon{fill:#552222;}#mermaid-svg-Q9aBB4HpbxOuLBId .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Q9aBB4HpbxOuLBId .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Q9aBB4HpbxOuLBId .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Q9aBB4HpbxOuLBId .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Q9aBB4HpbxOuLBId .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Q9aBB4HpbxOuLBId .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Q9aBB4HpbxOuLBId .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Q9aBB4HpbxOuLBId .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Q9aBB4HpbxOuLBId .marker.cross{stroke:#333333;}#mermaid-svg-Q9aBB4HpbxOuLBId svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Q9aBB4HpbxOuLBId p{margin:0;}#mermaid-svg-Q9aBB4HpbxOuLBId .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Q9aBB4HpbxOuLBId .cluster-label text{fill:#333;}#mermaid-svg-Q9aBB4HpbxOuLBId .cluster-label span{color:#333;}#mermaid-svg-Q9aBB4HpbxOuLBId .cluster-label span p{background-color:transparent;}#mermaid-svg-Q9aBB4HpbxOuLBId .label text,#mermaid-svg-Q9aBB4HpbxOuLBId span{fill:#333;color:#333;}#mermaid-svg-Q9aBB4HpbxOuLBId .node rect,#mermaid-svg-Q9aBB4HpbxOuLBId .node circle,#mermaid-svg-Q9aBB4HpbxOuLBId .node ellipse,#mermaid-svg-Q9aBB4HpbxOuLBId .node polygon,#mermaid-svg-Q9aBB4HpbxOuLBId .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Q9aBB4HpbxOuLBId .rough-node .label text,#mermaid-svg-Q9aBB4HpbxOuLBId .node .label text,#mermaid-svg-Q9aBB4HpbxOuLBId .image-shape .label,#mermaid-svg-Q9aBB4HpbxOuLBId .icon-shape .label{text-anchor:middle;}#mermaid-svg-Q9aBB4HpbxOuLBId .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Q9aBB4HpbxOuLBId .rough-node .label,#mermaid-svg-Q9aBB4HpbxOuLBId .node .label,#mermaid-svg-Q9aBB4HpbxOuLBId .image-shape .label,#mermaid-svg-Q9aBB4HpbxOuLBId .icon-shape .label{text-align:center;}#mermaid-svg-Q9aBB4HpbxOuLBId .node.clickable{cursor:pointer;}#mermaid-svg-Q9aBB4HpbxOuLBId .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Q9aBB4HpbxOuLBId .arrowheadPath{fill:#333333;}#mermaid-svg-Q9aBB4HpbxOuLBId .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Q9aBB4HpbxOuLBId .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Q9aBB4HpbxOuLBId .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Q9aBB4HpbxOuLBId .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Q9aBB4HpbxOuLBId .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Q9aBB4HpbxOuLBId .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Q9aBB4HpbxOuLBId .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Q9aBB4HpbxOuLBId .cluster text{fill:#333;}#mermaid-svg-Q9aBB4HpbxOuLBId .cluster span{color:#333;}#mermaid-svg-Q9aBB4HpbxOuLBId div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Q9aBB4HpbxOuLBId .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Q9aBB4HpbxOuLBId rect.text{fill:none;stroke-width:0;}#mermaid-svg-Q9aBB4HpbxOuLBId .icon-shape,#mermaid-svg-Q9aBB4HpbxOuLBId .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Q9aBB4HpbxOuLBId .icon-shape p,#mermaid-svg-Q9aBB4HpbxOuLBId .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Q9aBB4HpbxOuLBId .icon-shape .label rect,#mermaid-svg-Q9aBB4HpbxOuLBId .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Q9aBB4HpbxOuLBId .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Q9aBB4HpbxOuLBId .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Q9aBB4HpbxOuLBId :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 运行器 (Runner)
GitHub 托管
(ubuntu/windows/macos)
自托管
(self-hosted runner)
步骤 (Steps)
uses: checkout
run: npm install
run: npm test
uses: upload-artifact
作业 (Jobs)
needs
needs
Job: build
Job: test
Job: deploy
工作流 (Workflow)
.github/workflows/*.yml
包含全局配置与作业定义
触发器 (on:)
定义哪些事件、分支、路径、标签会触发工作流
事件层 (Events)
push
pull_request
schedule (cron)
workflow_dispatch
issues / release ...
图 1:GitHub Actions 从事件触发到运行器执行的五层架构
2.1 工作流(Workflow)
工作流是可配置的自动化流程,由一个或多个作业组成,通过 YAML 文件定义。工作流文件必须存放在仓库的 .github/workflows/ 目录下,文件扩展名为 .yml 或 .yaml。一个仓库可以包含多个工作流文件,每个负责不同的任务。工作流之间还可以通过 workflow_call 触发器互相调用,实现复用。
2.2 作业(Job)
作业是工作流中在同一运行器上执行的一组步骤。每个作业都在自己的虚拟机或容器中运行,作业之间默认并行执行。通过 needs 关键字可以定义作业间的依赖关系,被依赖的作业完成后,依赖的作业才开始执行。作业还支持矩阵策略,让同一作业使用不同的变量组合多次运行。
2.3 步骤(Step)
步骤是作业中的最小执行单元,可以是自定义的 shell 命令(run),也可以是引用一个可复用的动作(uses)。同一作业内的步骤按顺序执行,共享同一个文件系统。步骤支持条件判断(if)、超时控制(timeout-minutes)、环境变量(env)等配置。
2.4 动作(Action)
动作是预定义的、可复用的代码单元,用于在工作流中执行特定任务。动作可以是 GitHub 官方提供的(如 actions/checkout、actions/setup-node),也可以是社区开发者在 GitHub Marketplace 上发布的。动作通过 uses: owner/repo@version 格式引用,版本支持标签、分支或 commit SHA。
2.5 运行器(Runner)
运行器是执行工作流作业的服务器。GitHub 提供 Ubuntu Linux、Windows 和 macOS 三种平台的托管运行器,每次运行都在全新虚拟机中执行。用户也可以部署自托管运行器到自己的基础设施中,支持 x64、ARM64、ARM32 等架构。GitHub 还提供 Actions Runner Controller(ARC)方案,在 Kubernetes 上实现运行器的自动扩缩容。
2.6 触发器(Triggers)
触发器通过 on 关键字定义,指定哪些事件会触发工作流。常见触发事件如下表所示:
| 触发事件 | 说明 | 典型场景 |
|---|---|---|
push |
推送代码到仓库时触发 | 提交后自动构建 |
pull_request |
创建或更新 PR 时触发 | PR 合并前自动测试 |
schedule |
按 Cron 表达式定时触发 | nightly 构建、定期巡检 |
workflow_dispatch |
手动触发,支持参数化输入 | 按需部署到指定环境 |
workflow_run |
另一个工作流完成后触发 | 构建完成后触发部署 |
workflow_call |
被其他工作流作为可重用工作流调用 | 跨仓库复用 CI 逻辑 |
issues |
创建或更新 Issue 时触发 | 自动分配标签、回复 |
release |
发布 Release 时触发 | 自动发布到 npm/PyPI |
触发器还支持高级过滤:通过 branches/branches-ignore 指定触发分支,通过 paths/paths-ignore 指定触发文件路径,通过 types 限制事件活动类型。同一事件内的多个过滤条件为 AND 关系,不同事件之间为 OR 关系。
3. 工作流执行流程
当开发者推送代码到 GitHub 仓库时,一个完整的工作流执行流程如下:
#mermaid-svg-1muDgHG2yDwRu9zz{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1muDgHG2yDwRu9zz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1muDgHG2yDwRu9zz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1muDgHG2yDwRu9zz .error-icon{fill:#552222;}#mermaid-svg-1muDgHG2yDwRu9zz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1muDgHG2yDwRu9zz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1muDgHG2yDwRu9zz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1muDgHG2yDwRu9zz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1muDgHG2yDwRu9zz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1muDgHG2yDwRu9zz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1muDgHG2yDwRu9zz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1muDgHG2yDwRu9zz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1muDgHG2yDwRu9zz .marker.cross{stroke:#333333;}#mermaid-svg-1muDgHG2yDwRu9zz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1muDgHG2yDwRu9zz p{margin:0;}#mermaid-svg-1muDgHG2yDwRu9zz .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-1muDgHG2yDwRu9zz .cluster-label text{fill:#333;}#mermaid-svg-1muDgHG2yDwRu9zz .cluster-label span{color:#333;}#mermaid-svg-1muDgHG2yDwRu9zz .cluster-label span p{background-color:transparent;}#mermaid-svg-1muDgHG2yDwRu9zz .label text,#mermaid-svg-1muDgHG2yDwRu9zz span{fill:#333;color:#333;}#mermaid-svg-1muDgHG2yDwRu9zz .node rect,#mermaid-svg-1muDgHG2yDwRu9zz .node circle,#mermaid-svg-1muDgHG2yDwRu9zz .node ellipse,#mermaid-svg-1muDgHG2yDwRu9zz .node polygon,#mermaid-svg-1muDgHG2yDwRu9zz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1muDgHG2yDwRu9zz .rough-node .label text,#mermaid-svg-1muDgHG2yDwRu9zz .node .label text,#mermaid-svg-1muDgHG2yDwRu9zz .image-shape .label,#mermaid-svg-1muDgHG2yDwRu9zz .icon-shape .label{text-anchor:middle;}#mermaid-svg-1muDgHG2yDwRu9zz .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1muDgHG2yDwRu9zz .rough-node .label,#mermaid-svg-1muDgHG2yDwRu9zz .node .label,#mermaid-svg-1muDgHG2yDwRu9zz .image-shape .label,#mermaid-svg-1muDgHG2yDwRu9zz .icon-shape .label{text-align:center;}#mermaid-svg-1muDgHG2yDwRu9zz .node.clickable{cursor:pointer;}#mermaid-svg-1muDgHG2yDwRu9zz .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1muDgHG2yDwRu9zz .arrowheadPath{fill:#333333;}#mermaid-svg-1muDgHG2yDwRu9zz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1muDgHG2yDwRu9zz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1muDgHG2yDwRu9zz .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1muDgHG2yDwRu9zz .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1muDgHG2yDwRu9zz .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1muDgHG2yDwRu9zz .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1muDgHG2yDwRu9zz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1muDgHG2yDwRu9zz .cluster text{fill:#333;}#mermaid-svg-1muDgHG2yDwRu9zz .cluster span{color:#333;}#mermaid-svg-1muDgHG2yDwRu9zz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1muDgHG2yDwRu9zz .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1muDgHG2yDwRu9zz rect.text{fill:none;stroke-width:0;}#mermaid-svg-1muDgHG2yDwRu9zz .icon-shape,#mermaid-svg-1muDgHG2yDwRu9zz .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1muDgHG2yDwRu9zz .icon-shape p,#mermaid-svg-1muDgHG2yDwRu9zz .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1muDgHG2yDwRu9zz .icon-shape .label rect,#mermaid-svg-1muDgHG2yDwRu9zz .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1muDgHG2yDwRu9zz .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1muDgHG2yDwRu9zz .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1muDgHG2yDwRu9zz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1️⃣ 事件产生
git push / PR
schedule / 手动
2️⃣ 触发匹配
on: 事件类型
分支/路径过滤
3️⃣ 工作流解析
读取 YAML
展开上下文变量
4️⃣ 作业调度
needs 依赖排序
矩阵展开
5️⃣ 步骤执行
顺序运行
run / uses
6️⃣ 结果输出
工件上传
日志/状态/通知
图 2:GitHub Actions 工作流从事件到结果的六步执行流程
在这个过程中,有几个值得关注的细节:
- 作业调度 :GitHub 解析
needs依赖关系后生成有向无环图(DAG),按拓扑排序决定作业执行顺序。没有依赖关系的作业会被并行调度到不同的运行器上。 - 矩阵展开 :如果作业配置了
strategy.matrix,GitHub 会在调度阶段将矩阵展开为多个作业实例,每个实例使用不同的变量组合,各自独立调度执行。 - 上下文注入 :工作流解析阶段会将
github、secrets、env、inputs等上下文变量注入到 YAML 模板中,通过${``{ }}表达式完成变量替换。 - 数据传递 :作业之间通过工件 (artifact)传递构建产物,通过
outputs传递简单值(如版本号、镜像 tag)。跨工作流运行则通过缓存(cache)复用依赖文件。
4. 关键特性深度解析
4.1 矩阵策略(Matrix)
矩阵策略允许同一作业使用不同的变量组合多次运行。这在跨平台测试场景中特别有用------例如同时在 Ubuntu、Windows、macOS 上测试多个 Node.js 版本。矩阵在调度阶段展开,每个组合成为一个独立的作业实例。
#mermaid-svg-vAnkgMQ4raeLZpHd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vAnkgMQ4raeLZpHd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vAnkgMQ4raeLZpHd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vAnkgMQ4raeLZpHd .error-icon{fill:#552222;}#mermaid-svg-vAnkgMQ4raeLZpHd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vAnkgMQ4raeLZpHd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vAnkgMQ4raeLZpHd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vAnkgMQ4raeLZpHd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vAnkgMQ4raeLZpHd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vAnkgMQ4raeLZpHd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vAnkgMQ4raeLZpHd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vAnkgMQ4raeLZpHd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vAnkgMQ4raeLZpHd .marker.cross{stroke:#333333;}#mermaid-svg-vAnkgMQ4raeLZpHd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vAnkgMQ4raeLZpHd p{margin:0;}#mermaid-svg-vAnkgMQ4raeLZpHd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vAnkgMQ4raeLZpHd .cluster-label text{fill:#333;}#mermaid-svg-vAnkgMQ4raeLZpHd .cluster-label span{color:#333;}#mermaid-svg-vAnkgMQ4raeLZpHd .cluster-label span p{background-color:transparent;}#mermaid-svg-vAnkgMQ4raeLZpHd .label text,#mermaid-svg-vAnkgMQ4raeLZpHd span{fill:#333;color:#333;}#mermaid-svg-vAnkgMQ4raeLZpHd .node rect,#mermaid-svg-vAnkgMQ4raeLZpHd .node circle,#mermaid-svg-vAnkgMQ4raeLZpHd .node ellipse,#mermaid-svg-vAnkgMQ4raeLZpHd .node polygon,#mermaid-svg-vAnkgMQ4raeLZpHd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vAnkgMQ4raeLZpHd .rough-node .label text,#mermaid-svg-vAnkgMQ4raeLZpHd .node .label text,#mermaid-svg-vAnkgMQ4raeLZpHd .image-shape .label,#mermaid-svg-vAnkgMQ4raeLZpHd .icon-shape .label{text-anchor:middle;}#mermaid-svg-vAnkgMQ4raeLZpHd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vAnkgMQ4raeLZpHd .rough-node .label,#mermaid-svg-vAnkgMQ4raeLZpHd .node .label,#mermaid-svg-vAnkgMQ4raeLZpHd .image-shape .label,#mermaid-svg-vAnkgMQ4raeLZpHd .icon-shape .label{text-align:center;}#mermaid-svg-vAnkgMQ4raeLZpHd .node.clickable{cursor:pointer;}#mermaid-svg-vAnkgMQ4raeLZpHd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vAnkgMQ4raeLZpHd .arrowheadPath{fill:#333333;}#mermaid-svg-vAnkgMQ4raeLZpHd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vAnkgMQ4raeLZpHd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vAnkgMQ4raeLZpHd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vAnkgMQ4raeLZpHd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vAnkgMQ4raeLZpHd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vAnkgMQ4raeLZpHd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vAnkgMQ4raeLZpHd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vAnkgMQ4raeLZpHd .cluster text{fill:#333;}#mermaid-svg-vAnkgMQ4raeLZpHd .cluster span{color:#333;}#mermaid-svg-vAnkgMQ4raeLZpHd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vAnkgMQ4raeLZpHd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vAnkgMQ4raeLZpHd rect.text{fill:none;stroke-width:0;}#mermaid-svg-vAnkgMQ4raeLZpHd .icon-shape,#mermaid-svg-vAnkgMQ4raeLZpHd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vAnkgMQ4raeLZpHd .icon-shape p,#mermaid-svg-vAnkgMQ4raeLZpHd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vAnkgMQ4raeLZpHd .icon-shape .label rect,#mermaid-svg-vAnkgMQ4raeLZpHd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vAnkgMQ4raeLZpHd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vAnkgMQ4raeLZpHd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vAnkgMQ4raeLZpHd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 展开
🔄 矩阵展开结果
Ubuntu+Node20
Job #1
macOS+Node20
Job #4
Win+Node20
Job #7
Ubuntu+Node22
Job #2
macOS+Node22
Job #5
Win+Node22
Job #8
Ubuntu+Node24
Job #3
macOS+Node24
Job #6
Win+Node24
Job #9
📋 YAML 定义
strategy:
matrix:
os: ubuntu, macos, windows
node: 20, 22, 24
fail-fast: false
runs-on: {{ matrix.os }}
node-version: {{ matrix.node }}
✅ 3 种 OS × 3 个 Node 版本 = 9 个并行作业
每个作业在独立运行器上执行,互不干扰
图 3:矩阵策略将 3×3 变量组合展开为 9 个独立作业
矩阵还支持 include(额外添加组合)和 exclude(排除特定组合),fail-fast 控制某个组合失败时是否终止全部(默认 true),max-parallel 限制最大并行度。
4.2 机密管理(Secrets)
机密用于存储敏感信息(API 密钥、部署凭证等),在 GitHub 的安全存储中管理,通过 ${``{ secrets.NAME }} 引用。机密支持仓库级、环境级和组织级三个层级。在日志中机密值会被自动遮蔽为 ***。从 fork 仓库触发的工作流不会传递机密(GITHUB_TOKEN 除外),避免供应链攻击。
4.3 工件与缓存
工件 用于在作业之间传递数据(如构建产物),默认保留 90 天。通过 actions/upload-artifact@v4 上传,actions/download-artifact@v4 下载,v4 版本还提供 SHA256 校验确保完整性。
缓存 用于在不同工作流运行之间复用依赖文件(如 npm 包、Gradle 依赖),加速构建。通过 actions/cache@v4 管理,以 key 唯一标识。缓存按分支隔离,仓库级总容量限制为 10 GB,超过 7 天未访问的缓存会被自动清理。
4.4 并发控制(Concurrency)
通过 concurrency 关键字,可以对共享相同并发键的工作流或作业进行排队控制。典型应用场景是:同一分支的新推送应该取消正在运行的旧工作流,避免资源浪费。
yaml
# 取消同一分支上正在进行的旧运行
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
5. 实战:一个完整的 CI/CD 工作流
下面是一个接近真实项目的 CI/CD 工作流,涵盖了代码检出、依赖缓存、矩阵测试、条件部署等核心特性:
yaml
name: CI/CD Pipeline
on:
push:
branches: [main, 'release/**']
paths-ignore: ['**.md', 'docs/**']
pull_request:
branches: [main]
workflow_dispatch:
inputs:
environment:
type: choice
options: [staging, production]
required: true
permissions:
contents: read
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run lint
test:
needs: lint
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node: [20, 22, 24]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: npm
- run: npm ci
- run: npm test -- --coverage
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: coverage-${{ matrix.os }}-${{ matrix.node }}
path: coverage/
retention-days: 7
build:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment: ${{ inputs.environment || 'staging' }}
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: ./dist
- name: Deploy
run: ./deploy.sh
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
✅ 这个工作流体现了几个关键设计
- 阶段化执行:lint → test → build → deploy 形成流水线,前序阶段失败不会触发后续
- 矩阵覆盖:test 作业在 3 种 OS × 3 个 Node 版本上运行
- 条件部署:deploy 作业仅在 main 分支触发
- 并发取消:新推送会取消同分支正在进行的旧运行
- 工件传递:build 产物通过 artifact 传递给 deploy
- 依赖缓存 :setup-node 的
cache: npm自动缓存 npm 依赖
6. 开源实现全景对比
GitHub Actions 的工作流规范(YAML 语法、核心概念)已逐渐成为一种事实标准。围绕这一标准,开源社区形成了丰富的实现生态。下图展示了主要开源实现的定位与关系:
#mermaid-svg-abeLibGdvcQ7VFxi{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-abeLibGdvcQ7VFxi .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-abeLibGdvcQ7VFxi .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-abeLibGdvcQ7VFxi .error-icon{fill:#552222;}#mermaid-svg-abeLibGdvcQ7VFxi .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-abeLibGdvcQ7VFxi .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-abeLibGdvcQ7VFxi .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-abeLibGdvcQ7VFxi .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-abeLibGdvcQ7VFxi .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-abeLibGdvcQ7VFxi .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-abeLibGdvcQ7VFxi .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-abeLibGdvcQ7VFxi .marker{fill:#333333;stroke:#333333;}#mermaid-svg-abeLibGdvcQ7VFxi .marker.cross{stroke:#333333;}#mermaid-svg-abeLibGdvcQ7VFxi svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-abeLibGdvcQ7VFxi p{margin:0;}#mermaid-svg-abeLibGdvcQ7VFxi .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-abeLibGdvcQ7VFxi .cluster-label text{fill:#333;}#mermaid-svg-abeLibGdvcQ7VFxi .cluster-label span{color:#333;}#mermaid-svg-abeLibGdvcQ7VFxi .cluster-label span p{background-color:transparent;}#mermaid-svg-abeLibGdvcQ7VFxi .label text,#mermaid-svg-abeLibGdvcQ7VFxi span{fill:#333;color:#333;}#mermaid-svg-abeLibGdvcQ7VFxi .node rect,#mermaid-svg-abeLibGdvcQ7VFxi .node circle,#mermaid-svg-abeLibGdvcQ7VFxi .node ellipse,#mermaid-svg-abeLibGdvcQ7VFxi .node polygon,#mermaid-svg-abeLibGdvcQ7VFxi .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-abeLibGdvcQ7VFxi .rough-node .label text,#mermaid-svg-abeLibGdvcQ7VFxi .node .label text,#mermaid-svg-abeLibGdvcQ7VFxi .image-shape .label,#mermaid-svg-abeLibGdvcQ7VFxi .icon-shape .label{text-anchor:middle;}#mermaid-svg-abeLibGdvcQ7VFxi .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-abeLibGdvcQ7VFxi .rough-node .label,#mermaid-svg-abeLibGdvcQ7VFxi .node .label,#mermaid-svg-abeLibGdvcQ7VFxi .image-shape .label,#mermaid-svg-abeLibGdvcQ7VFxi .icon-shape .label{text-align:center;}#mermaid-svg-abeLibGdvcQ7VFxi .node.clickable{cursor:pointer;}#mermaid-svg-abeLibGdvcQ7VFxi .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-abeLibGdvcQ7VFxi .arrowheadPath{fill:#333333;}#mermaid-svg-abeLibGdvcQ7VFxi .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-abeLibGdvcQ7VFxi .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-abeLibGdvcQ7VFxi .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-abeLibGdvcQ7VFxi .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-abeLibGdvcQ7VFxi .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-abeLibGdvcQ7VFxi .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-abeLibGdvcQ7VFxi .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-abeLibGdvcQ7VFxi .cluster text{fill:#333;}#mermaid-svg-abeLibGdvcQ7VFxi .cluster span{color:#333;}#mermaid-svg-abeLibGdvcQ7VFxi div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-abeLibGdvcQ7VFxi .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-abeLibGdvcQ7VFxi rect.text{fill:none;stroke-width:0;}#mermaid-svg-abeLibGdvcQ7VFxi .icon-shape,#mermaid-svg-abeLibGdvcQ7VFxi .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-abeLibGdvcQ7VFxi .icon-shape p,#mermaid-svg-abeLibGdvcQ7VFxi .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-abeLibGdvcQ7VFxi .icon-shape .label rect,#mermaid-svg-abeLibGdvcQ7VFxi .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-abeLibGdvcQ7VFxi .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-abeLibGdvcQ7VFxi .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-abeLibGdvcQ7VFxi :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 📘 GitHub Actions
工作流规范
YAML 语法 + 核心概念
🟠 act
nektos/act
本地运行器
Docker 模拟,开发调试用
🟢 Gitea Actions
Gitea 内置 CI/CD
高度兼容 GitHub Actions 语法
🟣 Forgejo Actions
Forgejo 内置 CI/CD
熟悉感优先,非 100% 兼容
🟢 GitHub Actions Runner
actions/runner
官方开源运行器
执行 GitHub 上的工作流
⚪ Woodpecker CI
独立 CI/CD 引擎
.woodpecker.yml,语法不同
⚪ Drone CI
Docker 原生 CI/CD
.drone.yml,语法不同
图 4:围绕 GitHub Actions 工作流规范的开源实现生态
图例:🟢 高兼容 | 🟣 部分兼容 | 🟠 本地工具 | ⚪ 独立方案
6.1 act --- 本地运行器 社区工具
仓库 :github.com/nektos/act
act 是一个命令行工具,通过 Docker 容器在本地模拟 GitHub 托管运行器环境,让开发者无需推送代码即可测试和调试工作流。它读取 .github/workflows/ 目录中的 YAML 文件,拉取或构建对应的 Docker 镜像,按照依赖关系在容器中执行步骤。环境变量和文件系统都配置为匹配 GitHub 托管运行器。
act 适合工作流开发阶段的快速反馈:修改 YAML 后立即在本地运行,查看输出和错误,减少反复推送代码到 GitHub 的时间。它还有 VS Code 扩展支持。需要注意的是,act 是本地运行器,不是完整的 GitHub Actions 服务------对 Marketplace Actions 的支持取决于该 action 是否依赖 GitHub 特有的服务端能力。
bash
# 安装 act
$ brew install act
# 本地运行 push 事件触发的工作流
$ act push
# 只运行指定作业
$ act -j build
# 使用更大的镜像(更接近 GitHub 环境)
$ act -P ubuntu-latest=catthehacker/ubuntu:act-latest
6.2 Gitea Actions --- 高度兼容的内置 CI/CD 官方内置
仓库 :Gitea 内置功能,Runner 在 gitea/runner
Gitea Actions 是 Gitea 内置的 CI/CD 系统,官方定位为"closely compatible with GitHub Actions"。它支持 GitHub Actions 兼容的 workflow/action 语法,可以复用大量现有的 GitHub Actions。工作流由 Gitea 触发并分发给 Runner 执行,Runner 在 Linux、macOS、Windows 上均可运行。
Gitea Actions 的一个特色是支持自定义 Actions URL 前缀------可以同时引用 https://github.com/xxx/xxx@xxx 和 https://gitea.com/xxx/xxx@xxx 的动作。但也存在已知限制:concurrency 组、环境保护规则、continue-on-error、problem matchers 等功能受限或不被支持;多标签 Runner 路由机制与 GitHub 存在差异。
6.3 Forgejo Actions --- 熟悉感优先 社区驱动
仓库 :code.forgejo.org/forgejo/runner
Forgejo Actions 是 Forgejo(Gitea 的社区分支)内置的 CI/CD 系统。官方明确说明其设计目标是"familiarity instead of compatibility"------熟悉感优先,而非 100% 兼容。如果仓库中没有 .forgejo/workflows/ 目录,会回退查找 .github/workflows/ 目录,降低了迁移成本。
Forgejo Runner 底层使用 nektos/act 执行工作流,支持 LXC 容器执行、持久化缓存。已知差异包括:默认环境使用 Debian bookworm 而非 Ubuntu;github 上下文中部分子键缺失;permissions 和 continue-on-error 等子键会被忽略;OIDC ID Token 生成方式不同。Forgejo 在 2024 年底接受了渗透测试,相关问题已在 2025 年修复。
6.4 GitHub Actions Runner --- 官方开源运行器 官方
仓库 :github.com/actions/runner
这是 GitHub Actions 的官方运行器应用程序,用 C# 编写。它不是 GitHub Actions 的"替代实现",而是 GitHub Actions 服务的一部分------在自托管机器上安装运行后,连接 GitHub Actions 服务接收作业分配并执行。Runner 支持自动更新,也支持通过 --ephemeral 注册为临时运行器(处理完一个作业后自动注销)。
GitHub 还提供 Actions Runner Controller(ARC)作为 Kubernetes 上自托管运行器的推荐方案,以及 Runner Scale Set Client 用于构建自定义扩缩容方案。通过 workflow_job webhook 可以实现基于事件的自定义扩缩容。
6.5 Woodpecker CI 与 Drone CI --- 独立方案
Woodpecker CI 是 Drone CI 的社区分支,是一个独立的 CI/CD 引擎。它使用 .woodpecker.yml 作为配置格式,语法与 GitHub Actions 不同------每个步骤通过 image 和 commands 显式定义执行环境,步骤之间共享同一工作区卷。支持 Docker 和 Kubernetes 后端、并行执行、DAG 依赖、Matrix、Cron 等。从 GitHub Actions 迁移到 Woodpecker 需要重写工作流文件。
Drone CI 是早期的 Docker 原生 CI/CD 系统,使用 .drone.yml,每个 Pipeline Step 在独立 Docker 容器中执行。Drone 与 Gitea、Forgejo 等代码托管平台有集成。两者均与 GitHub Actions 语法不兼容。
6.6 横向对比
| 维度 | act | Gitea Actions | Forgejo Actions | actions/runner | Woodpecker | Drone |
|---|---|---|---|---|---|---|
| 类型 | 本地运行器 | 内置 CI/CD | 内置 CI/CD | 官方运行器 | 独立 CI/CD | 独立 CI/CD |
| YAML 兼容 | 较高 | 接近兼容 | 熟悉但不完全 | 原生 | 不兼容 | 不兼容 |
| 执行后端 | Docker | Docker | Docker/LXC | 虚拟机/容器 | Docker/K8s | Docker |
| Marketplace | 取决于 action | GitHub+自定义URL | Forgejo Catalog | GitHub Marketplace | 插件市场 | 插件市场 |
| K8s 支持 | - | 非官方方向 | - | ARC | 原生 | 原生 |
| 安全审计 | 无 | 无 | 2024 渗透测试 | GitHub 流程 | - | - |
| 适用场景 | 本地调试 | Gitea 自托管 | Forgejo 自托管 | GitHub 自托管 | 轻量自托管 | 轻量自托管 |
7. 选型建议与总结
GitHub Actions 工作流的核心价值在于"事件驱动 + YAML 定义 + 可复用动作 + 弹性运行器"的组合,它把 CI/CD 从基础设施问题变成了代码仓库的一部分。理解其五层架构(事件 → 触发器 → 工作流 → 作业 → 步骤)和核心概念(矩阵、机密、工件、缓存、并发),是设计高质量自动化流程的基础。
在开源实现的选择上,取决于具体需求:
| 你的需求 | 推荐方案 | 理由 |
|---|---|---|
| 本地调试 GitHub Actions 工作流 | act | Docker 模拟,快速反馈,无需推送代码 |
| GitHub 上运行自托管 Runner | actions/runner | 官方运行器,功能完整,支持 ARC 扩缩容 |
| 自托管平台,保留 GitHub Actions 语法 | Gitea Actions | 兼容性最高,支持复用 Marketplace 动作 |
| Forgejo 自托管 CI/CD | Forgejo Actions | 原生集成,支持 LXC,安全性经过审计 |
| 不依赖 GitHub Actions 语法的轻量 CI | Woodpecker CI | 现代设计,Docker/K8s 原生,插件生态 |
迁移注意事项
从 GitHub Actions 迁移到 Gitea Actions 或 Forgejo Actions 时,需要注意
concurrency、环境保护、continue-on-error、problem matchers 等功能的支持差异。迁移到 Woodpecker 或 Drone 则需要完全重写工作流文件。建议先用 act 在本地验证工作流兼容性,再做平台迁移决策。
本文由 AI 辅助撰写,基于 GitHub 官方文档及开源项目仓库的最新信息整理。配图均为原创 Mermaid 图表设计。