基于 sandcastle 和 course-video-manager 提取的常用模式。 适用于快速查阅,不替代完整教程。配合 第 1 课 和 第 2 课 使用。
最常用模式:当 Issue 被添加标签时触发。
on:
issues:
types: [labeled]
jobs:
my-job:
if: github.event.label.name == 'agent:hello'
runs-on: ubuntu-latest
sandcastle 使用 pull_request_target 而非 pull_request,因为后者在 PR 过时时可能无法生成 merge commit。
on:
pull_request_target:
types: [labeled]
jobs:
my-job:
if: github.event.label.name == 'agent:review'
runs-on: ubuntu-latest
pull_request vs pull_request_target:
pull_request_target 在 PR 的基础分支上下文中运行,因此可以访问 secrets。但它不会自动合并 PR 代码——你需要手动 checkout PR 的 head SHA。这两个仓库都在 checkout 步骤中显式使用 ref: ${{ github.event.pull_request.head.sha }}。
触发条件还可以组合使用。
# Issue 关闭时(用于 queued promotion 模式)
on:
issues:
types: [closed]
# 定时触发(cron)+ 手动触发
on:
schedule:
- cron: '0 9 * * 1-5' # 工作日每天 9:00 UTC
workflow_dispatch: # 允许手动触发
| 表达式 | 类型 | 说明 |
|---|---|---|
github.event.label.name | Issue / PR | 触发此工作流的标签名称 |
github.event.issue.number | Issue | Issue 编号 |
github.event.issue.title | Issue | Issue 标题 |
github.event.issue.body | Issue | Issue 正文(Markdown) |
github.event.issue.labels.*.name | Issue | Issue 上所有标签的名称数组 |
github.event.pull_request.number | PR | PR 编号 |
github.event.pull_request.head.sha | PR | PR 分支的最新 commit SHA |
github.event.pull_request.head.ref | PR | PR 分支名称 |
github.event.pull_request.state | PR | PR 状态:open 或 closed |
github.event.action | 通用 | 触发事件的动作类型(如 labeled、opened) |
gh 命令都要求 gh CLI 能识别当前仓库。在 GitHub Actions 中,要么先在之前的 step 中 uses: actions/checkout@v4(提供 .git 目录),要么设置环境变量 GH_REPO: ${{ github.repository }}。详见 第 1 课。
gh issue edit "$ISSUE_NUMBER" --add-label "agent:in-progress"
gh issue edit "$ISSUE_NUMBER" --remove-label "agent:hello" || true
# ↑
# || true 防止标签不存在时命令报错导致 workflow 失败
gh issue edit "$ISSUE_NUMBER" \
--remove-label "agent:implement" \
--remove-label "agent:blocked"
# 语法与 issue 完全一致
gh pr edit "$PR_NUMBER" --add-label "agent:review"
gh pr edit "$PR_NUMBER" --remove-label "agent:in-progress"
gh issue comment "$ISSUE_NUMBER" --body "任务已完成。"
gh issue comment "$ISSUE_NUMBER" --body-file "$RUNNER_TEMP/comment.md"
这两个仓库使用一组标签来追踪工作流的状态。这是标准转换模板:
jobs:
my-job:
# ...
steps:
- name: "Transition: start"
run: |
gh issue edit "$N" --remove-label "agent:hello" || true
gh issue edit "$N" --remove-label "agent:blocked" || true
gh issue edit "$N" --add-label "agent:in-progress"
- name: "Do work"
id: work
run: ./do-the-thing.sh
- name: "Transition: fail"
if: failure() && steps.work.outcome == 'failure'
run: |
gh issue edit "$N" --add-label "agent:blocked"
echo "FAILED" | gh issue comment "$N" --body-file -
- name: "Transition: complete"
if: always()
run: gh issue edit "$N" --remove-label "agent:in-progress" || true
防止同一个 Issue/PR 同时运行多个工作流实例:
concurrency:
group: agent-my-job-issue-${{ github.event.issue.number }}
cancel-in-progress: false # 不取消正在运行的,而是排队等待
关键点:sandcastle 和 course-video-manager 总是使用 cancel-in-progress: false。因为 AI 代理工作流可能运行数十分钟,取消它比等待更危险。
多个 PR 写入工作流共享一个并发组(防止同时推送):
concurrency:
group: agent-mutate-pr-${{ github.event.pull_request.number }}
cancel-in-progress: false
| 令牌 | 来源 | 用途 |
|---|---|---|
secrets.GITHUB_TOKEN |
GitHub 自动提供 | 标准 API 操作。权限在 permissions: 块中声明 |
secrets.AGENT_PAT |
手动添加的 Personal Access Token | 触发下游工作流。用 GITHUB_TOKEN 添加的标签不会触发其他工作流 |
GITHUB_TOKEN 触发的事件来防止递归。因此,如果工作流 A 想在完成时触发工作流 B,它必须使用 PAT 来操作标签。sandcastle 中实现为:先尝试 PAT,如果 PAT 不可用则回退到 GITHUB_TOKEN(此时下游不自动触发,需要手动帮助)。
# PAT 优先模式
- name: "Trigger downstream"
run: |
if [ -n "$AGENT_PAT" ]; then
GH_TOKEN="$AGENT_PAT" gh pr edit "$PR" --add-label "agent:review"
else
gh pr edit "$PR" --add-label "agent:review"
echo "::warning::No AGENT_PAT, agent:review must be applied manually"
fi
env:
AGENT_PAT: ${{ secrets.AGENT_PAT }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# 标签匹配(最常用)
if: github.event.label.name == 'agent:hello'
# 与 GITHUB_TOKEN 触发的下游抑制无关——只是筛选标签
# 检查 PR 是否仍开放
if: github.event.pull_request.state == 'open'
# 检查 PR 是否关闭(拒绝关闭的 PR)
if: github.event.pull_request.state != 'open'
# 组合条件
if: |
github.event.label.name == 'agent:implement' &&
github.event.issue.state == 'open'
# 步骤条件函数
if: failure() # 前面任何步骤失败时运行
if: success() # 前面所有步骤成功时运行
if: always() # 无论成功或失败都运行
if: cancelled() # 工作流被取消时运行
工作流的 run: 步骤在 Ubuntu 虚拟机中执行 shell 命令。以下是最常出现的语法:
| 语法 | 含义 | 示例 |
|---|---|---|
|| true |
前一个命令失败时假装成功,继续执行 | gh issue edit --remove-label "x" || true |
$VAR |
读取 shell 环境变量 | echo "Issue #$N" |
$(cmd) |
命令替换:把 cmd 的输出嵌入字符串 | echo "时间:$(date)" |
${{ expr }} |
GitHub Actions 表达式,非 shell 语法 | ${{ github.event.issue.number }} |
env: |
把 GitHub 上下文变量传给 shell | N: ${{ github.event.issue.number }} |
if [ "$X" = "Y" ]; then |
shell 条件判断(不是 Actions 的 if:) |
if [ "$PROCEED" = "true" ]; then |
echo "k=v" >> "$GITHUB_OUTPUT" |
写入 step 输出,供后续 step/job 读取 | echo "proceed=true" >> "$GITHUB_OUTPUT" |
$RUNNER_TEMP |
临时目录路径,工作流结束后自动清理 | cat > "$RUNNER_TEMP/comment.md" |
gh issue edit --add-label 只能添加仓库中已存在的标签。如果标签不存在,命令会失败。解决:先手动在仓库 Labels 页面创建所有需要的标签。
issues: write 权限,gh 命令会静默失败或报 403。始终在 jobs.<job>.permissions 中显式声明所需权限。
|| true。当标签可能已经被移除时,--remove-label 会因标签不存在而报错。始终添加 || true 除非你 100% 确定标签存在。