登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  软件教程

GitHub Actions 抽取可复用工作流并传递最小权限

来源:17golang原创

时间:2026-10-08 14:39:39 161浏览 收藏

把重复的 GitHub Actions job 抽成可复用工作流,核心是两份 YAML:公共文件用 on.workflow_call 声明输入,调用文件在 job 级用 uses 引用它。最小权限应在调用 job 明确写出,例如只做检出和测试时使用 permissions: contents: read。被调用工作流可以继续收紧权限,但不能把调用方授予的权限提升。

官方地址:https://github.com/

下面以同一仓库内复用 Go 测试 job 为例,全程从 GitHub 网页界面创建文件并在 Actions 页面确认结果。界面名称可能随产品更新微调,但文件位置、workflow_call 和 job 级 uses 是配置关键。

确认抽取边界与官方入口

可复用工作流适合抽取一个或多个完整 job,例如统一测试、构建、扫描或部署。它与复合 Action 的区别是:可复用工作流在 job 级调用,能够包含多个 job;复合 Action 则在 step 中运行。

本例准备两个文件:

  • .github/workflows/reusable-ci.yml:公共测试流程,入口是 workflow_call。
  • .github/workflows/ci.yml:调用方,负责触发条件、最小权限和传参。

GitHub 官方要求可复用工作流直接放在 .github/workflows 中,不支持再放到这个目录的子目录。调用同仓库文件时可以使用 ./.github/workflows/文件名,不需要追加分支或标签。

创建 workflow_call 可复用工作流

操作路径:仓库主页 → Code → Add file → Create new file。在文件名输入框填写 .github/workflows/reusable-ci.yml。

在代码仓库网页中创建 reusable-ci.yml 的原创操作界面说明图
图1:操作示意图。在仓库 Code 页面使用 Add file → Create new file,新文件路径填写 .github/workflows/reusable-ci.yml;画面为原创界面说明,不是网页截图。

编辑器出现新文件路径后,粘贴下面的最小公共流程:

name: Reusable CI

on:
  workflow_call:
    inputs:
      package:
        # 调用方可指定测试包范围,默认覆盖当前模块。
        required: false
        type: string
        default: ./...

# 公共流程自身也保持只读,避免未来新增步骤意外获得写权限。
permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout caller repository
        uses: actions/checkout@v4
      - name: Setup Go from go.mod
        uses: actions/setup-go@v5
        with:
          # 直接读取调用仓库的 go.mod,避免在公共文件中硬编码版本。
          go-version-file: go.mod
          cache: true
      - name: Run tests
        # input 来自受控工作流配置,不接收外部事件文本。
        run: go test "${{ inputs.package }}"

点击右上方或页面下方的 Commit changes...,填写提交说明并提交。成功标志是文件列表中出现 .github/workflows/reusable-ci.yml,内容顶部包含 workflow_call。

在调用工作流中引用公共流程

再次走 Code → Add file → Create new file,创建 .github/workflows/ci.yml。调用可复用工作流的 uses 必须直接写在 job 下,不能放进 steps。

name: CI

on:
  # 保留手动入口,便于在网页上立即验收。
  workflow_dispatch:
  push:
    branches: [main]

jobs:
  reusable-test:
    # 同仓库调用使用相对路径,不附加 @branch。
    uses: ./.github/workflows/reusable-ci.yml
    with:
      package: ./...
    # 该 job 只需读取代码,不授予 issues、packages 或 contents 写权限。
    permissions:
      contents: read

这里不要同时添加 runs-on 或 steps。调用可复用工作流的 job 支持的是 uses、with、secrets、permissions、needs、if 等调用相关关键字,实际运行器由被调用工作流中的 job 决定。

把 GITHUB_TOKEN 收紧到最小权限

操作位置:ci.yml 编辑页 → jobs.reusable-test。把 permissions 与 uses 保持同级,写入 contents: read。

调用工作流中配置 uses 和 contents read 最小权限的原创编辑界面说明图
图2:操作示意图。调用 job 使用 uses 引用 reusable-ci.yml,并把 permissions 设置为 contents: read;画面是精简后的原创编辑状态。

GitHub 会为每个 job 创建 GITHUB_TOKEN。即使某个 Action 没有显式接收这个 secret,也可能通过 github.token 上下文访问它,所以权限应按 job 主动收紧。对于只检出代码并运行测试的流程,contents: read 通常就是所需权限。

权限传递遵循“只能相同或更严格”的规则。假设调用链是 A → B → C,而 A 只授予 packages: read,B 和 C 都不能提升到 packages: write。因此最小权限应该从最外层调用方开始限制,而不是期待最内层自行纠正。

编辑完成后点击 Commit changes...。可见成功状态是 ci.yml 同时包含 uses、with 和 job 级 permissions,且提交已进入准备运行的分支。

从 Actions 页面运行并确认调用关系

操作路径:仓库顶部 Actions → 左侧选择 CI → Run workflow → 选择分支 → 再次点击 Run workflow。如果看不到按钮,先确认 workflow_dispatch 已提交到默认分支。

刷新运行列表并打开最新记录。页面应显示调用方工作流和公共测试 job 的关联,最终状态为绿色完成。如果测试失败,先展开 reusable-test / test,检查仓库是否存在 go.mod、测试包输入是否正确,以及默认分支名是否与触发配置一致。

GitHub Actions 调用方与可复用测试作业均成功的原创结果界面说明图
图3:结果示意图。工作流运行图显示 caller 调用了 reusable-test,两个节点均为绿色完成状态;这是原创结果说明图,不是实际运行截图。

验收时看三个可见状态:运行标题为 CI、reusable-test job 已展开、所有步骤为绿色。这样可以确认调用路径生效,而不是误跑了旧的重复 job。

处理跨仓库、密钥与版本固定

同仓库复用完成后,再按需要扩展:

  • 跨仓库引用:使用 OWNER/REPOSITORY/.github/workflows/FILE@REF。私有仓库还要在被调用仓库的 Settings → Actions → General → Access 中允许目标仓库访问。
  • 固定版本:公共仓库可使用提交 SHA、标签或分支;安全敏感流程优先固定完整提交 SHA,避免引用内容被移动。
  • 传递密钥:只在 workflow_call.secrets 声明真正需要的密钥,并在调用 job 的 secrets 中逐一映射。不要为了省事默认继承全部密钥。
  • 组织内继承:同一组织或企业内可以使用 secrets: inherit,但它扩大了可见密钥集合,不符合最小权限时应改为显式映射。
  • 嵌套调用:当前官方限制支持最多十层工作流连接;所有嵌套工作流都必须对最初调用方可访问,权限仍不能向下提升。
jobs:
  deploy:
    # 跨仓库复用时固定完整提交 SHA,降低引用漂移风险。
    uses: example-org/automation/.github/workflows/deploy.yml@0123456789abcdef0123456789abcdef01234567
    permissions:
      contents: read
      # 只有采用 OIDC 换取云端短期凭据时才授予 id-token: write。
      id-token: write
    secrets:
      # 仅映射被调用流程声明且确实需要的单个密钥。
      deployment_key: ${{ secrets.DEPLOYMENT_KEY }}

如果流程只做测试,不要照抄部署权限。先列出 job 实际调用的 API 和资源,再为每个权限项写出理由;无法解释的写权限应删除。

常见问题

为什么调用 job 不能写 runs-on?

因为这个 job 的职责是调用整份可复用工作流,运行器由被调用工作流内部的 job 决定。调用 job 不是普通 steps job。

为什么使用相对路径时不能写 @main?

同仓库调用采用 ./.github/workflows/file.yml,GitHub 会使用与调用方一致的提交上下文。@ref 用于跨仓库引用。

permissions 写在公共工作流里还不够吗?

不够。最外层调用方决定可传入的权限上限,被调用工作流只能保持或继续降低。把最小权限写在调用 job,边界更清楚。

什么时候可以使用 secrets: inherit?

只有同一组织或企业内调用支持这种便利写法,而且它会让更多密钥对被调用流程可见。生产环境优先显式映射所需密钥。

参考资料:GitHub Docs 的 Reuse workflows、Reusing workflow configurations 和 Use GITHUB_TOKEN for authentication。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>