mcpField
M
make-github-actions-workflow

make-github-actions-workflow

github.com/dotnet云与 DevOps14.8k46 次安装今天更新
代码仓库

关于 make-github-actions-workflow

用于按照仓库约定创建 GitHub Actions 工作流的技能,指导选择触发事件、设置权限、安全编写脚本、添加条件、处理错误并验证,适用于 CI 和仓库自动化。

主要功能

  1. 01选择工作流触发事件
  2. 02声明明确且最小化的权限
  3. 03添加执行条件和幂等检查
  4. 04复杂逻辑使用 `actions/github-script@v8`
  5. 05处理 API 错误和分页
  6. 06验证工作流安全性与行为

适用场景

  1. 01创建 CI 验证工作流
  2. 02自动化管理拉取请求
  3. 03添加问题标签或通知
  4. 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 的只读访问权限
需要写入权限的 PRpull_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.ymlpull_request_target: [opened, edited, reopened]关闭目标为发布分支的外部 PR,并添加社区标签
label-and-milestone-issues.ymlpull_request_target: [closed]合并修复 PR 后,为相关议题添加标签和里程碑
copilot-setup-steps.ymlworkflow_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