把 GitHub Action 抽成跨项目复用

1346 字
7 分钟
把 GitHub Action 抽成跨项目复用

背景#

有些 GitHub Actions 一开始只是为某个项目临时写的。比如构建镜像时发一条飞书通知,开始构建发一次,结束时根据结果再发一次。

这种 action 如果只服务一个项目,放在当前仓库的 .github/actions 里没有问题。但一旦第二个、第三个项目也想复用,同一份脚本就会开始被复制。复制几次以后,问题很快就出来了:

  • 一个项目改了通知卡片,另一个项目忘了同步;
  • 参数名字和行为慢慢变得不一致;
  • 修一个小 bug,要在多个仓库里重复改;
  • 新项目接入时,还要去旧项目里翻实现细节。

所以我单独建了一个 forge-actions 仓库,用来放跨项目共用的 GitHub Actions。

这个仓库的目的很明确:把已经稳定下来的 CI 小工具沉淀成共享 action。业务项目只负责调用,不再关心 action 内部怎么拼消息、怎么签名、怎么测试。

第一个抽出来的 action#

这次先迁移的是 feishu-notify

它做的事情很简单:在 GitHub Actions 运行过程中,给飞书自定义机器人发送一张通知卡片。常见场景是:

  • workflow 开始时提醒一次;
  • workflow 成功时发成功通知;
  • workflow 失败或取消时发异常通知;
  • 卡片里带上仓库、分支、提交、触发人、运行链接等信息。

迁移后,action 放在共享仓库的这个位置:

.github/actions/feishu-notify/

项目里包含三类文件:

  • action.yml:定义 action 的输入参数和运行方式;
  • send-card.js:真正发送飞书卡片的 Node.js 脚本;
  • send-card.test.js:用 Node 内置测试框架覆盖消息构造逻辑。

这里没有引入额外 npm 依赖。对一个通知 action 来说,这样更省心:少一层安装、少一份 lockfile,也少一点 CI 环境里的不确定性。

其他项目如何使用#

其他仓库不需要复制 send-card.js,直接在 workflow 里引用共享仓库即可。

最小示例如下:

- name: Send Feishu notification
uses: sephymartin-lab/forge-actions/.github/actions/feishu-notify@main
with:
webhook: ${{ secrets.FEISHU_WEBHOOK_URL }}
secret: ${{ secrets.FEISHU_BOT_SECRET }}
status: success
title: Build success
summary: Image has been built and published.
run-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
ref-name: ${{ github.ref_name }}
sha: ${{ github.sha }}
actor: ${{ github.actor }}
repository: ${{ github.repository }}
workflow: ${{ github.workflow }}
run-number: ${{ github.run_number }}
event-name: ${{ github.event_name }}

如果需要额外展示一些业务字段,可以用 extra-fields 传一个 JSON 对象:

- name: Send Feishu notification
uses: sephymartin-lab/forge-actions/.github/actions/feishu-notify@main
with:
webhook: ${{ secrets.FEISHU_WEBHOOK_URL }}
secret: ${{ secrets.FEISHU_BOT_SECRET }}
status: success
title: Publish success
summary: Artifact has been published.
run-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
ref-name: ${{ github.ref_name }}
sha: ${{ github.sha }}
actor: ${{ github.actor }}
repository: ${{ github.repository }}
workflow: ${{ github.workflow }}
run-number: ${{ github.run_number }}
event-name: ${{ github.event_name }}
extra-fields: '{"Environment":"production","Artifact":"docker image"}'

webhooksecret 都应该放在 GitHub Secrets 里,不要写进 workflow 文件。secret 是可选的,如果飞书机器人没有开启签名校验,可以不传。

一个完整一点的用法#

我更习惯在 workflow 里发两类通知:开始通知和结束通知。

开始通知直接写在构建任务前面:

- name: Notify build started
uses: sephymartin-lab/forge-actions/.github/actions/feishu-notify@main
with:
webhook: ${{ secrets.FEISHU_WEBHOOK_URL }}
secret: ${{ secrets.FEISHU_BOT_SECRET }}
status: started
title: Build started
summary: GitHub Actions workflow has started.
run-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
ref-name: ${{ github.ref_name }}
sha: ${{ github.sha }}
actor: ${{ github.actor }}
repository: ${{ github.repository }}
workflow: ${{ github.workflow }}
run-number: ${{ github.run_number }}
event-name: ${{ github.event_name }}

结束通知通常要放在 if: always() 的步骤里,否则前面的构建失败后,通知步骤可能不会执行:

- name: Notify build finished
if: always()
uses: sephymartin-lab/forge-actions/.github/actions/feishu-notify@main
with:
webhook: ${{ secrets.FEISHU_WEBHOOK_URL }}
secret: ${{ secrets.FEISHU_BOT_SECRET }}
status: ${{ job.status }}
title: Build finished
summary: GitHub Actions workflow has finished.
run-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
ref-name: ${{ github.ref_name }}
sha: ${{ github.sha }}
actor: ${{ github.actor }}
repository: ${{ github.repository }}
workflow: ${{ github.workflow }}
run-number: ${{ github.run_number }}
event-name: ${{ github.event_name }}

feishu-notify 支持的状态是:

  • started
  • success
  • failure
  • cancelled

job.status 在结束步骤里正好能覆盖常见结果,适合拿来做最终通知。

关于版本引用#

上面的示例用的是 @main

uses: sephymartin-lab/forge-actions/.github/actions/feishu-notify@main

这适合刚迁移、还没有正式发版本的时候用。等 action 稳定以后,更推荐改成 tag 或 commit SHA:

uses: sephymartin-lab/forge-actions/.github/actions/feishu-notify@v1

这样业务项目不会因为共享仓库的最新提交而突然改变行为。共享 action 仓库继续演进,业务项目按自己的节奏升级版本。

本地验证#

共享 action 最怕的是“看起来只是改了一点通知文案,结果所有项目都受影响”。所以迁移时我保留了测试文件,并用 Node.js 20 直接跑测试:

Terminal window
rtk mise exec node@20 -- node --test .github/actions/feishu-notify/send-card.test.js

除了测试,还要顺手检查几件事:

  • action.yml 能被 YAML 解析;
  • send-card.js 语法没有问题;
  • 旧项目里不再引用本地 action;
  • 新项目 README 里有输入参数、Secrets 说明和调用示例。

这些检查不复杂,但能避免共享工具变成新的不确定因素。

小结#

这类 action 抽出来以后,收益主要不在“少写几行 YAML”,而是在边界变清楚了。

业务项目只声明自己什么时候通知、传什么上下文;共享仓库负责通知格式、签名逻辑、字段展示和测试。后面如果要接入更多项目,也不用再复制脚本,只要引用同一个 action,再把 webhook 和 secret 配好就行。

对团队内部的 CI 工具来说,这种小仓库很实用。它不需要做得很大,但应该把接口、示例和验证方式写清楚。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

把 GitHub Action 抽成跨项目复用
https://blog.sephy.top/posts/shared-github-actions/
作者
虾米
发布于
2026-07-18
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
虾米
coder
分类
标签
站点统计
文章
66
分类
9
标签
67
总字数
72,767
运行时长
0
最后活动
0 天前
站点信息
构建平台
GitHub Actions
博客版本
Firefly v6.14.2
文章许可
CC BY-NC-SA 4.0