Workflow 文件结构
这一课你将能做到
- 在仓库创建
.github/workflows/ci.yml,用 on 配置 push / pull_request / workflow_dispatch 三种触发器
- 读懂最小 workflow 的骨架:
name、on、permissions、jobs、runs-on、steps 各管什么
- 在 GitHub Actions 页手动触发一次 run,并对照日志确认
echo 输出
为什么要学
概念课说过「自动工头」;工头的说明书就是 workflow YAML。路径约定死在 .github/workflows/*.yml(或 .yaml)——放错目录等于没装,GitHub 根本不扫描。
触发器写错时,最常见的懵是:「我 push 了为什么没跑?」或「只想手动跑一次,结果每次 push 都在烧分钟数。」这些都不是玄学,是 on 的语义没吃透。
本课把文件骨架钉牢:何时跑、权限多大、里面有哪些 job。Job 并行/串行、step 细节下一课再拆。
先建立图像
仓库根目录
└─ .github/
└─ workflows/
├─ ci.yml ← 名称随意,扩展名必须是 yml/yaml
├─ docker.yml
└─ …
一个 workflow 文件 ≈
name: 页面上显示的名字
on: 何时启动(触发器)
permissions: 这次 run 的 GITHUB_TOKEN 能做什么
jobs:
job-id:
runs-on: 哪种虚拟机
steps: 具体命令 / action
人话:workflow 像一份「外卖单」——写清什么时候下单(on)、厨房在哪(runs-on)、做哪几道菜(jobs/steps)。单子本身也进 Git,改单子要 PR。
| 键 | 人话 | 初学必填? |
|---|
name | Actions 列表里的标题 | 建议填 |
on | 触发条件 | 必须 |
permissions | token 能力上限 | 强烈建议显式写 |
jobs | 要跑的任务集合 | 必须 |
concurrency | 同名 run 互斥/取消旧的 | 进阶可选 |
跟着做
步骤 1:创建目录与文件
在本地仓库(或 GitHub 网页 Add file)新建:
.github/workflows/ci.yml
注意是 workflows 复数,不是 workflow。若目录不存在就整条路径新建。提交到一个 feature 分支试水——YAML 语法严重错误时,Actions 页会直接出现醒目的解析失败。
步骤 2:粘贴最小可运行示例
写入:
# 在 Actions 列表里显示的名字
name: CI
# 何时启动这份流水线
on:
push:
branches: [main]
pull_request:
branches: [main]
# 允许在网页上点「Run workflow」手动跑
workflow_dispatch:
# 默认只读代码,符合最小权限
permissions:
contents: read
jobs:
hello:
# GitHub 托管的 Ubuntu 虚拟机
runs-on: ubuntu-latest
steps:
- name: 打个招呼
run: echo "Hello CodeRoadMap"
推上去后:对 main 的 push/PR 应出现 run;也可在 Actions 页点 Run workflow 手动运行。
步骤 3:在 UI 里核对一次 run
Actions → 点进本次 CI → 点 job hello → 展开 step「打个招呼」。日志里应有 Hello CodeRoadMap。
若没出现 run:检查分支名是否真是 main(有的仓库是 master)、文件路径是否在 .github/workflows/、是否 push 到了远端。
步骤 4:改触发器做对照实验
再 push 一次普通提交——不应自动跑;回到 Actions 页点 Run workflow 才跑。改回 push + pull_request 后,确认 PR 会再次触发。笔记写:「on 决定烧不烧分钟数。」
步骤 5:认识常用触发器表
on | 场景 | 备注 |
|---|
push | 分支收到提交 | 可按 branches / paths 过滤 |
pull_request | PR 打开/同步/重开 | 面试最常问的场景 |
workflow_dispatch | 人手点运行或 API 触发 | 排错利器 |
schedule | cron 定时 | 注意时区是 UTC |
workflow_call | 被其它 workflow 复用 | 可复用流水线(进阶) |
本站主路径常用:push + pull_request 到 main,外加 workflow_dispatch 方便排错。
步骤 6:把流水线当代码审一遍
- 触发是否过宽(是否在所有分支狂跑制造噪音)
permissions 是否过大(无必要的 write)
- job 名是否与日后分支保护里要勾的 check 名一致(第 11 课)
讲清楚要点
- 因为 GitHub 只扫描固定目录,所以文件必须放在
.github/workflows/——放 scripts/ci.yml 不会自动变成 Actions。
- 因为
on 决定何时占用 runner,所以先写准触发器再堆步骤——省分钟数从「少跑冤枉流水线」开始。
- 因为
GITHUB_TOKEN 默认能力随 permissions 变,所以显式写最小权限——纯 CI 常用 contents: read;推 GHCR 才加 packages: write。
- 因为 workflow 版本化在 Git,所以改流水线与改业务一样走 review——避免「某天 main 上的 CD 被悄悄改掉」。
- 因为一个仓库可有多个 workflow 文件,所以按职责拆分更清晰——
ci.yml 测代码、docker.yml 推镜像,失败域分离。
- 因为 YAML 对缩进敏感,所以用空格对齐、先跑通最小 echo 再加复杂步骤——Tab 与空格混用是解析失败的常见根因。
你可能会懵的地方
-
「name 和 job 的 id 哪个出现在分支保护里?」
分支保护勾选的是 job 级 check(默认用 job 的 key,或 job 上的 name: 字段)。workflow 顶层 name 只是列表标题。第 11 课会再钉一次。
-
「push 和 pull_request 会不会跑两次?」
开 PR 时跑 pull_request;往 PR 分支再 push 会再触发一轮;合并进 main 的 push 再跑一次——这是预期行为。可用 paths 过滤减轻(第 11 课)。
-
「permissions 不写行不行?」
能跑,但默认权限随仓库/组织设置变化,可读性差、可审计性差。显式写出「这份 CI 只需读代码」更安全也更好审。
-
「workflow_dispatch 按钮为什么有时不出现?」
该触发器必须存在于默认分支上的 workflow 文件里,按钮才会出现。在 feature 分支加了 workflow_dispatch 但没合进默认分支时,Actions 列表里看不到「Run workflow」入口。
-
「on: 后面直接跟数组是什么写法?」
on: [push, pull_request] 是简写,等价于对两个事件都用默认配置(所有分支)。需要 branches/paths 过滤时必须用映射写法。
错法对照
| 错法 | 问题 | 正解 |
|---|
文件放在 .github/ci.yml | GitHub 不加载 | .github/workflows/ci.yml |
on: [push] 无分支过滤,所有分支狂跑 | 浪费分钟、噪音大 | 限制 branches,需要时再加 paths |
默认给 contents: write「方便以后用」 | token 能力过大 | CI 只读;写权限按 job 需要再开 |
| 用 Tab 缩进 YAML | 解析失败或报错难懂 | 统一空格,编辑器配置 show invisibles |
| 只在网页点 Generate 从不提交 YAML | 无法复现、无法 review | YAML 进仓库,PR 合并 |
卡点急救
- workflow 完全不触发:按顺序查四点——① 文件在不在
.github/workflows/;② 触发的分支名与 on.push.branches 是否匹配;③ 该文件是否已存在于默认分支(新加在 feature 分支的 workflow 不响应本分支的 push);④ 仓库 Actions 是否被 Settings 禁用。
- YAML 报错
Unexpected value 或整个文件不解析:九成是缩进错误或 Tab 字符。用编辑器「显示空白字符」逐行查;也可本地用 yamllint 或 VS Code 的 YAML 插件先校验再 push。
- 手动 Run workflow 按钮不见了:确认默认分支上的文件仍含
workflow_dispatch:;确认你在 Actions 左侧列表选中了正确的 workflow;还要注意按钮只在页面右上角,容易看漏。
面试常问
-
GitHub Actions 的 workflow 文件放在哪?有哪些常见触发事件?
放在仓库 .github/workflows/ 目录,扩展名 yml/yaml。常见触发:push、pull_request、workflow_dispatch(手动)、schedule(cron)、release、workflow_call(被复用)。可以按 branches、paths 精细过滤,比如 monorepo 里只改 backend/** 才跑 Java 检查。
-
permissions 字段为什么重要?
它控制这次 run 里 GITHUB_TOKEN 的能力上限。GitHub 默认 token 权限可能偏宽(取决于仓库设置),显式声明最小权限(如 contents: read)能缩小「workflow 被注入恶意命令后能造成的破坏面」。这是 Actions 安全加固的第一课,也是供应链安全面试题的得分点。
-
一个仓库多个 workflow 文件怎么组织?
按职责拆:CI 检查一个文件(高频、PR 触发)、镜像构建一个文件(main 触发)、定时任务一个文件。好处是失败域分离、权限各配、日志各看;坏处是触发条件要写清楚,避免同一改动重复跑多个流水线。
自测三问
- 写出最小 workflow 必须有的两个顶层键(除
name 外)。
- 如何做到「代码 push 不自动跑、只在网页手动跑」?
- 为什么建议写
permissions: contents: read?
小结一句话
一个 workflow = 触发器 +(可选)权限声明 + jobs 集合;文件必须在 .github/workflows/。
延伸