如何在GitHub Actions中实现目标分支更新时触发合并代码构建?
1. 先搞懂base ref是什么
base ref就是你PR的目标分支(比如你的master),也就是最终要合并代码的分支。对应的head ref是发起PR的源分支(比如你的分支X)。GitHub处理PR时,都是基于base ref的最新状态来计算合并后的结果,说白了就是你之前理解的“目标分支”,只是GitHub文档用了这个术语而已。
2. 先确保当前PR构建的是合并后的代码
你之前的配置里,pull_request事件其实默认会构建合并后的代码——GitHub会自动生成一个临时的合并提交(把你的PR分支和当前master的最新代码合并),github.sha指向这个临时提交。但如果你的actions/checkout步骤加了ref: ${{ github.head_ref }}这类参数,就会只拉PR分支的代码,把这个参数删掉就行:
steps: - name: 拉取代码 uses: actions/checkout@v4 # 别加ref参数,默认拉取合并后的临时提交
3. 实现master变更时自动重新触发PR构建
默认情况下,master推新代码后,指向它的PR不会自动重新构建。要实现这个,有两种靠谱的方法:
方法一:用GitHub API自动触发PR构建(推荐)
保留原主工作流(比如叫ci.yml)的基础触发配置:
on: push: branches: - master pull_request: branches: - master types: [opened, synchronize, reopened]
然后新建一个专门的触发工作流(比如trigger-pr-builds.yml),当master有推送时,自动触发所有打开的PR的构建:
name: master更新时触发PR构建 on: push: branches: - master jobs: trigger: runs-on: ubuntu-latest steps: - name: 安装GitHub CLI run: sudo apt install gh -y - name: 触发所有指向master的PR构建 run: | # 获取所有打开的、目标分支是master的PR编号 PR_LIST=$(gh pr list --base master --state open --json number --jq '.[].number') # 逐个触发主工作流,构建合并后的代码 for PR_NUM in $PR_LIST; do echo "正在触发PR #$PR_NUM的构建" gh workflow run ci.yml --ref refs/pull/$PR_NUM/merge done env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
方法二:用pull_request_target事件(注意安全)
pull_request_target会在master(base ref)变更时触发,但它是在master的上下文里运行的,权限更高——别用它执行PR分支里的未信任脚本(比如PR里的自定义shell脚本)。
把原主工作流的pull_request换成pull_request_target:
on: push: branches: - master pull_request_target: branches: - master types: [opened, synchronize, reopened]
然后手动合并PR分支到master来构建合并后的代码:
steps: - name: 拉取master分支 uses: actions/checkout@v4 with: ref: ${{ github.base_ref }} - name: 合并PR分支到master run: | git fetch origin ${{ github.head_ref }}:${{ github.head_ref }} git merge ${{ github.head_ref }} --no-ff
4. 解决重复构建的问题
你现在同时开了pull_request和pull_request_target,两个事件都会触发工作流,所以出现重复任务。按上面的方案二选一,保留对应的触发事件就行。
5. 验证效果
配置完成后:
- 打开或更新PR时,工作流会构建合并后的代码
- master推新代码时,所有指向它的PR会自动重新构建合并后的状态
- 合并PR前,你能100%确认合并后的代码能通过所有测试,不会搞挂master分支
内容的提问来源于stack exchange,提问作者The Quantum Physicist

