banner
wuhang2003

Zwh's Blog

ZH/EN | 一个人不在计算机专业却时不时就想折腾计算机相关东西的咸鱼

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

前言#

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

套轮子#

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

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

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

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

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_repoupstream_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 那就可以取消打勾正式跑一次了。

image

踩坑日记#

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

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

  1. fatal: refusing to merge unrelated histories

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

  1. 上游仓库修改 workflow 导致的同步失败,报错示例:
 ! [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 权限:

image

可以看到上面是有 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 的步骤。配置如下:

- 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 测试效果。

image

没啥问题,可以跑路了。

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

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


加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。