关于 authoring-github-workflows
帮助开发者安全编写和审查 GitHub Actions 工作流 YAML 的技能,重点处理 YAML 与 Actions 语法之间的差异。它强调为含表达式的特殊标量加引号,并用 actionlint 验证工作流结构。
主要功能
- 01识别表达式中的 YAML 注释陷阱
- 02判断哪些标量需要加引号
- 03使用 actionlint 验证工作流
- 04区分 YAML 解析与 Actions 校验
- 05提供合并前检查清单
适用场景
- 01编辑或审查 .github/workflows/ 文件
- 02排查没有启动任何作业的工作流错误
- 03验证包含 `${{ }}` 表达式的字段
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dotnet/ skills --skill authoring-github-workflows安装后在 Claude Code 中输入/authoring-github-workflows确认已加载。
详细介绍
安全编写 GitHub Actions 工作流
GitHub Actions 工作流文件使用 YAML,但有效的 YAML 并不等于有效的工作流。工作流可能通过 yaml.safe_load 解析(或在随意审查中看起来没有问题),却仍会在加载时被 GitHub Actions 拒绝,产生含糊不清的错误 “This run likely failed because of a workflow file issue”,并且没有任何作业启动。本技能将介绍 YAML 与 Actions 之间的陷阱(尤其是 # 被当作注释的陷阱)、如何正确为表达式标量加引号,以及如何在合并前使用 actionlint 验证。
范围:语法与语义。 本技能关注工作流 YAML 在语法和结构上的正确性,包括引号、解析,以及决定 GitHub Actions 能否加载并运行文件的
actionlint级别有效性。本技能不涉及工作流应该做什么,也不涉及 Agent 工作流应该如何运行。有关语义和功能方面的指导(设计工作流逻辑、Agent 工作流模式、编写 gh-aw),请参阅.github/agents/agentic-workflows.agent.md。两者相辅相成:使用 Agent 确保行为正确,使用本技能确保 YAML 正确。
适用场景
- 编辑、添加或审查
.github/workflows/下的任何文件。 - 编写包含
${{ }}表达式的run-name、name、if、env、with或run值。 - 工作流运行失败,并提示 “This run likely failed because of a workflow file issue”,且没有任何作业运行。
- 合并工作流编辑后,
main上的 Eval/CI 突然在每次运行时都失败,尽管更改“看起来没问题”。 - 判断 YAML 标量是否需要加引号。
不适用场景
- 编写非 Actions 的 YAML(应用配置、Kubernetes、Compose、Azure Pipelines、GitLab CI)。
- 仅编写有效
run:块中的 shell/脚本逻辑(这是脚本编写任务,不是工作流语法任务)。
头号陷阱:未加引号的表达式中的 # 会被当作 YAML 注释
在 YAML 中,空格后跟 # 表示注释开始。对于未加引号的普通标量,从空格和 # 开始直到行尾的所有内容都会被静默丢弃:
# BAD — the run-name is silently truncated at " #"
run-name: ${{ inputs.pr_number != '' && format('Evaluate PR #{0} @ {1}', inputs.pr_number, inputs.head_sha) || '' }}
YAML 会将其解析为 run-name: ${{ inputs.pr_number != '' && format('Evaluate PR——一个未终止的 ${{ 表达式。yaml.safe_load 会成功(它只会看到一个带有尾随注释的截断字符串),所以这个错误会通过简单的验证,但 GitHub Actions 会拒绝格式错误的表达式,并拒绝启动任何运行。
# GOOD — wrap the whole value in double quotes so '#' stays inside the scalar
run-name: "${{ inputs.pr_number != '' && format('Evaluate PR #{0} @ {1}', inputs.pr_number, inputs.head_sha) || '' }}"
内部表达式已经使用单引号,因此给标量加双引号是安全的。这正是导致 dotnet/skills 在 main 上的评估失败的错误(PR #746 → 通过加引号修复)。
其他会导致普通标量必须加引号的字符
| 字符 / 模式 | 导致问题的原因 | 修复方法 |
|---|---|---|
空格后跟 #(空格-井号) | 开始 YAML 注释;截断值 | 为整个值加引号 |
开头为 *、&、!、?、|、>、@、` | YAML 锚点/别名/标签/块标量 | 为值加引号 |
开头为 { 或 [ | 会被解析为流式映射/序列(裸写的 ${{ }} 以 $ 开头,因此是安全的,但在开头字符之后出现 {{ 有风险) | 为值加引号 |
值中出现 : 后跟空格(冒号-空格) | 会被解析为嵌套映射键 | 为值加引号 |
| 有意义的首尾空格 | 普通标量会移除这些空格 | 为值加引号 |
值为 true/false/yes/no/on/off/数字,但必须保持为字符串 | YAML 类型强制转换 | 为值加引号 |
经验法则: 如果 name、run-name、if、env 或 with 的值包含 ${{ }} 表达式,并且包含字面量 #、: 或开头的特殊字符,就要用双引号括起整个标量。
工作流程
步骤 1:识别已更改或编写的工作流文件
git diff --name-only origin/main... -- .github/workflows/
对于每个文件,扫描所有同时包含 ${{ 和 #、冒号-空格或开头特殊字符的行。
步骤 2:为有风险的表达式标量加引号
当值中嵌入表达式且包含 # 或其他特殊字符时(见上表),请将整个值用双引号括起来。若内部表达式使用单引号,优先使用双引号;反之亦然。不要转义 ${{ }} 大括号——为标量加引号即可。
步骤 3:使用 actionlint 验证(权威方式)
actionlint 理解 GitHub Actions 架构和表达式语法,因此可以准确捕获这类普通 YAML 检查器会漏掉的错误。下载一个固定版本并运行:
ACTIONLINT_VERSION=1.7.7
ACTIONLINT_SHA256=023070a287cd8cccd71515fedc843f1985bf96c436b7effaecce67290e7e0757
curl -fsSLo actionlint.tar.gz \
"https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz"
# Verify the download against the pinned checksum before extracting/executing it:
echo "${ACTIONLINT_SHA256} actionlint.tar.gz" | sha256sum -c -
tar -xzf actionlint.tar.gz actionlint
# Focus on workflow/expression correctness; silence shell/py style noise:
./actionlint -shellcheck= -pyflakes= -color .github/workflows/*.yml
在 Windows PowerShell 中,请使用 actionlint_<ver>_windows_amd64.zip 文件并运行 Expand-Archive。
截断表达式错误会显示为:
got unexpected EOF while lexing end of string literal, expecting ''' [expression]
退出代码为 0 表示工作流结构有效。
步骤 4:确认仅检查 YAML 并不足够
不要依赖 yaml.safe_load、yamllint 或“能解析”来作为有效证明。它们会接受被截断为注释的形式。只有 actionlint(或推送更改并观察 GitHub Actions 是否能解析)才能验证 Actions 层。
步骤 5:保持 CI 检查通过
本仓库会自动运行 actionlint(见 .github/workflows/actionlint.yml),检查任何涉及 .github/workflows/ 的 PR。请求审查前,请确保你的更改通过此检查。如果你新增工作流,检查会自动覆盖它。
验证
- 每个包含
#、冒号加空格或特殊字符开头的${{ }}值都已加引号。 -
actionlint -shellcheck= -pyflakes= .github/workflows/*.yml的退出代码为0。 - 没有工作流运行报告 "This run likely failed because of a workflow file issue"。
- PR 中的
actionlintCI 检查已通过。
常见陷阱
| 陷阱 | 解决方案 |
|---|---|
表达式中含有 # 的 run-name/name 未加引号 | 将整个值用双引号括起来 |
相信 yaml.safe_load/yamllint/代码审查能够发现问题 | 运行 actionlint;仅检查 YAML 会接受被截断的形式 |
转义 ${{ 大括号来“修复”问题 | 不要这样做——改为给标量加引号;转义会破坏表达式 |
| 对包含单引号的值使用单引号 | 外层标量请使用双引号 |
启用 shellcheck 添加 actionlint,结果被已有的 shell 风格警告淹没 | 使用 -shellcheck= -pyflakes= 运行,以专注检查工作流和表达式错误 |
| 认为 YAML 检查通过就意味着工作流会运行 | 推送更改并确认作业确实启动,或依赖 actionlint 检查 |
参考资料
- actionlint — GitHub Actions 工作流的静态检查器。
- GitHub Actions:工作流语法
- YAML 1.2 规范 — 注释
- 仓库技能编写指南:
.agents/skills/create-skill/SKILL.md