破事水 | Actions 同步上游项目及合并到自己分支

前言

本人在前几天将博客的 Shiro 换成了加了更多特性的赞助版主题 Shiroi（闭源）。由于原仓库是私有仓库，且不允许直接 Fork。在这种情况下，需要让自己的仓库和原仓库保持定期同步，不想自己打命令行推，遂将目光转向了 GitHub Actions，一边踩坑一边写下此篇内容。

套轮子

一般情况下，这种简单的需求都会有写好的轮子和前人的踩坑经验，就看该怎么套了（

类似的 Apps 和 Actions 模板有挺多，比如 Pull、GitHub Sync 等，最后选了 aormsby/Fork-Sync-With-Upstream-action 作为轮子来写同步配置。支持用 Access Token 拉私有仓库，可以选择上游仓库和目标仓库的分支，有示例，非常的贴心。

轮子找好了，接下来新建一个专门与上游同步的 sync 分支，保证修改都在 main 分支上

好吧，那就新建个 workflow 文件写配置C/V 官方示例吧。这里我建了一个 sync 分支用来保证与上游仓库的一致性，至于同步到 main 分支那就是接下来的事了（

Copy
name: 'Upstream Sync'

on:
schedule:
- cron: '0 0 * * *'
# 每天8点（UTC+8）执行

workflow_dispatch: # click the button on Github repo!
inputs:
sync_test_mode: # Adds a boolean option that appears during manual workflow run for easy test mode config
description: 'Fork Sync Test Mode'
type: boolean
default: false

jobs:
sync_latest_from_upstream:
runs-on: ubuntu-latest
name: Sync latest commits from upstream repo

steps:
# REQUIRED step
# Step 1: run a standard checkout action, provided by github
- name: Checkout target repo
uses: actions/checkout@v3
with:
# optional: set the branch to checkout,
# sync action checks out your 'target_sync_branch' anyway
# 切到要同步的分支
ref: sync
# REQUIRED if your upstream repo is private (see wiki)
persist-credentials: false

# REQUIRED step
# Step 2: run the sync action
- name: Sync upstream changes
id: sync
uses: aormsby/Fork-Sync-With-Upstream-action@v3.4.1
with:
target_sync_branch: sync
# REQUIRED 'target_repo_token' exactly like this!
target_repo_token: ${{ secrets.GITHUB_TOKEN }}
upstream_sync_branch: main
upstream_sync_repo: upstream/Shiroi
upstream_repo_access_token: ${{ secrets.UPSTREAM_REPO_SECRET }}

# Set test_mode true during manual dispatch to run tests instead of the true action!!
test_mode: ${{ inputs.sync_test_mode }}

# Step 3: Display a sample message based on the sync output var 'has_new_commits'
- name: New commits found
if: steps.sync.outputs.has_new_commits == 'true'
run: echo "New commits were found to sync."

- name: No new commits
if: steps.sync.outputs.has_new_commits == 'false'
run: echo "There were no new commits."

- name: Show value of 'has_new_commits'
run: echo ${{ steps.sync.outputs.has_new_commits }}

实际上需要修改的地方就几点，定时触发时间 (cron)，Step 1 中的 ref 和 2 中的 target_sync_branch 需一致且指向自己项目的同步分支（这里为 sync），上游仓库的信息和源分支 (upstream_sync_repo、upstream_sync_branch)。

写完工作流之后进行一下相关变量的配置。先准备一个能读取私有上游的 Access tokens，这里选 Classic，因为 Fine-grained tokens 似乎读不了所有权不属于自己的私有项目（存疑），权限给个 repo 大类即可。复制生成后的 token，来到仓库设置 -> Secrets and Variables -> Actions 添加个 Repository secrets。变量名写 UPSTREAM_REPO_SECRET，值就是刚才生成的 token，保存。再来到 Actions Setting，把 Workflow permissions 改成 Read and write 保存。

测试

来到自己配置的 Workflow 页面，把 Test Mode 勾上跑一次配置是否有问题（此时仅测试配置是否正确，不涉及对仓库的操作），看到所有配置都是 PASSED 那就可以取消打勾正式跑一次了。

踩坑日记

CI 日志显示fatal: could not read Username for 'https://github.com': No such device or address

后来看了一下原 issues 这算是不影响使用的已知 bug，就放着它吧

fatal: refusing to merge unrelated histories

这个问题出现在我手动解决冲突合完 PR 后的第二天，猜测是同步分支含私货（自己修改的提交）导致的无法同步。没办法，只能手动拉取仓库源代码进行 force-push 了（

上游仓库修改 workflow 导致的同步失败，报错示例：

Copy
! [remote rejected] sync -> sync (refusing to allow a GitHub App to create or update workflow `.github/workflows/build.yml` without `workflows` permission)
error: failed to push some refs to 'https://github.com/wuhang2003/Shiroi.git'

在聊解决这个问题的方法之前，先大骂 GitHub Actions 所生成的 Token 就是个 **。为什么要这么说呢？

这是 Actions 所生成的一次性 Token 权限：

可以看到上面是有 Actions 写权限的，再看一下日志说的没有 workflow 权限，相信各位应该察觉到不对劲了，生成的 Token 权限和显示权限咋还不一致呢？这就是我想吐槽的地方。在官方的讨论区目前只搜到一个讨论与此有关，而且还没回复......

当然权限问题解决方法也很简单暴力，自己建一个带 workflow 和 repo 权限的 PAT 加入 sercets 代替已有的 ${{ secrets.GITHUB_TOKEN }} 就行了。

在这之后...

最开始我是仅使用上述工作流同步之后自己提 PR 合并到 main 来实现手动更新。但折腾几天下来，一来感觉太过繁琐不够懒，二来有冲突了还要自己动手解决也费劲。所以也在研究怎么自动提 PR / 自动 merge 到 main 并 push。后来根据实际情况选择了 rebase（变基）的方式合并更改（变基介绍），好处是不会让自己的更改串整个 commit 流，而是统一堆在上游的 commit 之后。这里所使用的 workflow 是 tiacsys/git-rebase。直接在原 CI 配置 sync.yml 后增加 rebase 的步骤。配置如下：

Copy
- name: git-rebase
uses: tiacsys/git-rebase@v1
with:
repo: "git@github.com:wuhang2003/Shiroi.git"
source_branch: "main" # 待修改的主分支
destination_branch: "sync" # 上游同步分支
ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}

完事之后生成个提交的 SSH Key，公钥丢仓库的 deploy ssh 给写权限，私钥丢 Actions secret 设置为 SSH_PRIVATE_KEY 的值。完工！接下来可以点击按钮触发一下 Workflow 测试效果。

没啥问题，可以跑路了。

这样折腾下来，一个能保证定期同步（且支持手动触发）上游私有仓库并进行 rebase 后同步到 main 的工作流就写好了。当然，额外增加的 rebase 工作流也可以根据需要修改成 Merge 方式合并或者自动提 PR 并在评估没问题后合并，玩法还是挺多的。只能说偷懒 / 折腾是第一生产力（确信

此文由 Mix Space 同步更新至 xLog
原始链接为 https://zwh.moe/posts/technology/actions-sync