关于 make-github-actions-workflow
用于按照仓库约定创建 GitHub Actions 工作流的技能,指导选择触发事件、设置权限、安全编写脚本、添加条件、处理错误并验证,适用于 CI 和仓库自动化。
主要功能
- 01选择工作流触发事件
- 02声明明确且最小化的权限
- 03添加执行条件和幂等检查
- 04复杂逻辑使用 `actions/github-script@v8`
- 05处理 API 错误和分页
- 06验证工作流安全性与行为
适用场景
- 01创建 CI 验证工作流
- 02自动化管理拉取请求
- 03添加问题标签或通知
- 04检查工作流权限与安全性
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ dotnet/ efcore --skill make-github-actions-workflow安装后在 Claude Code 中输入/make-github-actions-workflow确认已加载。
详细介绍
创建 GitHub Actions 工作流
创建并配置符合此仓库约定和模式的 GitHub Actions 工作流。工作流可自动执行 CI 验证、PR 管理、问题标记和通知等任务。
仓库约定
此仓库的所有工作流都遵循特定模式。创建新工作流时,请遵守以下约定:
文件位置和命名
- 所有工作流都存放在
.github/workflows/ - 使用 kebab-case 命名:
label-and-milestone-issues.yml、validate-pr-target-branch.yml - 开头添加描述性注释块,说明工作流的用途
Runner
- 通用任务使用
ubuntu-latest - 需要指定 OS 版本时(例如 Copilot 设置步骤),使用
ubuntu-24.04
权限
- 始终声明明确的权限,切勿依赖默认权限
- 使用最小权限:仅请求工作流所需的权限
- 常见模式:
- 只读:
permissions: {} - PR 评论:
pull-requests: write - 问题管理:
issues: write - 读取代码:
contents: read
- 只读:
- 对于需要对外部贡献者提交的 PR 进行写入操作的工作流,请使用
pull_request_target,而不是pull_request- 安全警告:
pull_request_target工作流会在基础仓库的上下文中运行,并使用该仓库的权限。不要在这些工作流中检出或执行 PR 分支中的代码。应改用事件载荷和/或 GitHub API(actions/github-script、github.rest.*等)安全地检查和处理 PR。
- 安全警告:
脚本编写
- 对于复杂逻辑,使用
actions/github-script@v8,而不是 shell 脚本 - 使用
async/await编写 JavaScript;可使用github对象(Octokit)和context - REST API 调用使用
github.rest.*,GraphQL 查询使用github.graphql() - 通过
context.payload.pull_request、context.repo.owner、context.repo.repo访问 PR/问题数据 - 使用
console.log()记录调试信息;发生错误时使用core.setFailed()
错误处理
- 使用 try/catch 块包装 API 调用
- 收集错误并在最后统一报告,而不是遇到第一个错误就失败
- 跳过操作时记录原因
工作流
步骤 1:了解需求
编写工作流前,请明确:
- 哪些事件应触发工作流?(push、pull_request、check_suite、schedule 等)
- 需要哪些权限?
- 哪些条件应作为执行门槛?(作者、分支、标签等)
- 需要调用哪些 API?
- 是否存在速率限制或幂等性方面的顾虑?
步骤 2:选择触发事件
| 用例 | 事件 | 说明 |
|---|---|---|
| PR 已打开/已更新 | pull_request | 对 PR 的只读访问权限 |
| 需要写入权限的 PR | pull_request_target | 可评论、添加标签、关闭 |
| PR 已合并 | pull_request_target: [closed] | 检查 github.event.pull_request.merged == true |
| CI 检查已完成 | check_suite: [completed] | 过滤 app.slug != 'github-actions',跳过自身 |
| 代码已推送 | push | 按分支/路径过滤 |
| 手动触发 | workflow_dispatch | 添加参数用的 inputs: |
| 定时触发 | schedule | 使用 cron 语法 |
步骤 3:编写工作流
按照以下模板创建 YAML 文件:
# Description of what this workflow does and why.
name: Descriptive Workflow Name
on:
<trigger_event>:
types: [<event_types>]
permissions:
<resource>: <read|write>
jobs:
<job-name>:
if: <guard_condition> # Optional: skip when not needed
runs-on: ubuntu-latest
steps:
- name: Descriptive step name
uses: actions/github-script@v8
with:
script: |
// Your logic here
第 4 步:实现守卫条件
添加条件,避免不必要的运行:
- 使用作业级
if:检查事件负载(例如,github.event.check_suite.app.slug != 'github-actions') - 在脚本中提前
return,以检查 PR 作者、草稿状态等 - 检查幂等性(例如,使用隐藏的 HTML 注释作为标记,防止重复操作)
对于响应 opened、edited 和 reopened 的 pull_request_target 策略工作流:
- 在
edited时,除非存在context.payload.changes?.base,否则返回;标题或正文编辑不得重新应用目标分支策略。 - 检查
context.actor的权限,而不只是 PR 作者的权限。维护者可能会编辑或重新打开外部 PR,此时操作必须使用维护者的权限。 - 在
opened时,操作者和作者相同,因此只需查询一次权限,并将结果用于策略和标签决策。 - 如果操作者或作者是机器人,则排除自动化处理。
- 直接指定禁止的分支类别(例如
release/*),而不是只允许某个分支,例如main。 - 如果权限查询失败,则停止操作,不执行破坏性操作;无法确认权限并不能证明贡献者是外部用户。
第 5 步:验证
创建工作流后:
- 权限已最小化并显式声明
- 复杂逻辑使用
actions/github-script@v8 - 守卫条件可避免不必要的运行
- API 调用会在需要时处理分页(使用
per_page: 100) - 错误处理不会悄悄吞掉失败
- 未硬编码任何机密信息或令牌
- 工作流不会创建无限循环(例如,触发自身的工作流)
此仓库中的现有工作流
请将以下工作流作为参考:
| 工作流 | 触发条件 | 用途 |
|---|---|---|
validate-pr-target-branch.yml | pull_request_target: [opened, edited, reopened] | 关闭目标为发布分支的外部 PR,并添加社区标签 |
label-and-milestone-issues.yml | pull_request_target: [closed] | 合并修复 PR 后,为相关议题添加标签和里程碑 |
copilot-setup-steps.yml | workflow_dispatch、push、pull_request | 为 Copilot 设置开发环境 |
inter-branch-merge-flow.yml | 推送到 release/** | 通过 arcade 触发分支间合并 |
常见模式
在 PR 上发表评论
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: 'Comment text'
});
检查用户权限
const { data: permissions } = await github.rest.repos.getCollaboratorPermissionLevel({
owner: context.repo.owner,
repo: context.repo.repo,
username: context.actor
});
const hasWriteAccess = ['admin', 'write'].includes(permissions.permission);
幂等地更新由机器人拥有的评论
隐藏标记可标识工作流的评论,但不能证明所有权:贡献者和其他应用都可以在其评论中放置相同的标记。请同时匹配标记和确切的 github-actions[bot] 登录名,然后更新该评论或创建新评论。通用的 user.type === 'Bot' 检查无法证明评论归工作流所有。请保留工作流固定的操作 SHA,且切勿在 pull_request_target 作业中执行来自拉取请求 fork 的代码。
const marker = '';
const comments = await github.paginate(github.rest.issues.listComments, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
per_page: 100
});
const body = `${marker}\nComment text`;
const existing = comments.find(comment =>
comment.user?.login === 'github-actions[bot]' && comment.body?.includes(marker));
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body
});
}
在特定提交中读取仓库文件
const { data: fileData } = await github.rest.repos.getContent({
owner, repo,
path: 'eng/Versions.props',
ref: commitSha
});
const content = Buffer.from(fileData.content, 'base64').toString('utf-8');
参考资料
评论
支持 Markdown