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。

编辑器出现新文件路径后,粘贴下面的最小公共流程:
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。

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、测试包输入是否正确,以及默认分支名是否与触发配置一致。

验收时看三个可见状态:运行标题为 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。
-
455 收藏
-
Golang · Go教程 | 3个月前 | CI/CD · gitHub actions · Go教程 · 持续集成 · Go 持续集成 CI Go test GitHub Actions self-hosted runner 自托管 runner340 收藏
-
124 收藏
-
311 收藏
-
462 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习