如何实现 gitlab 的跨仓库同步

687 阅读4分钟

大家好,我是馋嘴的猫。最近的工作中,遇到了 gitlab 中,两个仓库需要代码同步的场景。经过一番摸索,成功使用 gitlab-ci 实现了此需求,下面请跟着我,一起来看看如何实现吧~

背景

假设我们现在有一个场景,有两个 Gitlab 仓库,分别叫 A 和 B。

我们需要在 A 仓库每次 main 分支的 src 目录有变化,则执行 build 操作来生成产物,并将产物同步到 B 仓库的 src 目录下。

B 仓库 src 目录检测到更新后,也需要执行它的 build 操作,生成产物并发布到 npm 包。

问题

在上面的流程中,我们需要注意到的是,有几个难点:

  1. 如何保证全流程自动,不需要手动执行
  2. 如何保证准确监听到合适的目录、分支
  3. 如何可以跨仓库提交增量修改的代码

仓库地址

解决方案

  1. 在 B 仓库的 gitlab 页面,申请一个 token,用于后续的提交代码操作。

    新增 token 路径:

    1. 确定当前登录用户拥有 B 仓库的 maintainer 权限
    2. 打开 B 仓库的 gitlab 页面,点击左侧菜单栏的 Settings -> Access Tokens
    3. 填入 token name,请注意,这个 token name 与后面提交 MR 的用户名一致,此处为了方便辨别,我们选择命名为 ci-bot
    4. 根据自己需求,选择 expiration daterolescope
    5. 点击 Create personal access token,生成 token。
    6. 页面此时会刷新,并在页面的 Your new project access token 下显示新生成的 token。请复制下来。
    7. 回到仓库 A 的 gitlab 页面,点击左侧菜单栏的 Settings -> CI/CD -> Variables,添加一个名为 REPO_B_GITLAB_TOKEN 的变量,值为刚刚复制的 token。
  2. 查看A仓库的 .gitlab-ci.yml, 有两个 stage,分别为 build(构建产物)和 update(同步产物)操作。

  3. 在 build 操作中,我们通过 rules 规则,来加上 build 的条件,只有在 main 分支 的 src 目录下有变更时,才自动触发 build 操作。

rules:
  - if: '$CI_COMMIT_REF_NAME == "main"'
    changes:
      - 'src/**/*'
    when: on_success

然后,通过 artifacts 规则,将产物保存下来,供 update 环节使用。

artifacts:
paths:
  - dist/**/*
  1. 在 update 操作中,我们通过 needs 规则,确定前置条件要先执行 build 操作。
needs: [ "build" ]

通过 before_script 规则,添加淘宝源并安装jq,用于后续的 json 解析。

before_script:
  # 省略添加淘宝源的操作
  - apt-get update -y && apt-get install -y jq
  1. 在 update 操作中的 script 部分,就是我们的重头戏了。大概有以下重点,可对照 .gitlab-ci.yml 查看
  • 克隆仓库 B,使用刚刚配置的 REPO_B_GITLAB_TOKEN,并将 build 步骤生成的产物拷贝到仓库 B 的 src 目录下。注意:需要将下面的<your gitlab host>替换为你的 gitlab 域名。
- git clone -b main https://oauth2:$REPO_B_GITLAB_TOKEN@<your gitlab host>/rickiezheng/gitlab-sync-demo-b.git b
- cp -r dist/* b/src/dist-a
  • 执行常规的 git add、commit、push 操作,并且定义好提交的分支名,用户名,邮箱等信息

注意,这里由于是先 clone 的 B 仓库代码再覆盖的代码,所以不会全量提交,只会提交有更新的代码

  • 生成 MR 并提交到 B 仓库。 在这里,我们调用了gitlab提供的 API, 并传入刚刚设置的 REPO_B_GITLAB_TOKEN,即可完成 MR 新建操作。
  • 注意:需要将下面的<your gitlab host>替换为你的 gitlab 域名。
  - "merge_request_response=$(curl --silent --header 'PRIVATE-TOKEN: '$REPO_B_GITLAB_TOKEN -X POST 'https://<your gitlab host>/api/v4/projects/'$repo_id'/merge_requests' -F 'source_branch='$branch_name -F 'target_branch=main' -F 'title=Update dist files from demo-a' -F 'description=Automated MR for updating dist files' -F 'remove_source_branch=true' -F 'squash=true'); if [ $? -eq 0 ]; then echo $merge_request_response | jq -r '.web_url'; else echo $merge_request_response | jq -r '.message'; fi"

注: repo_id 可以在仓库 B 的 gitlab 主页上的仓库名下找到,格式类似:ProjectID: 12345

在 MR 提交成功后,jq会解析出 MR 的地址,并显示在 gitlab job 的控制台,方便用户查看。

  1. 至此,我们已经完成仓库 A 的 gitlab-ci 设置了。让我们来尝试在 src 目录做些修改,并提交到 main 分支吧。
  2. 可以看到,仓库 A 的 main 分支在 src 目录代码有更新后,自动执行了 pipeline,且成功运行了其下的 build 和 update 操作。
image.png

此时查看 update stage 的控制台日志,ci成功将仓库 A 的改动,提交到了仓库 B, 并输出了仓库 B 的 MR 地址。

image.png
  1. 点击 MR 地址,进入仓库 B 的 Merge request 页面,可以看到 PR 已经顺利提过来了,查看 Changes 面板,可以看到仓库 A 修改好的文件,已经增量提交过来了,而并不是全量提交。
image.png
  1. 在合入这笔提交前,先让我们来看下仓库 B 的 .gitlab-ci.yml

在这个文件里,我们定义了 build 和 update 两个 stage。

stages:
  - build
  - publish
  1. 在 build 操作中,与仓库 A 类似,我们通过 rules 规则,来加上 build 的条件,只有在 main 分支 的 src 目录下有变更时,才自动触发 build 操作。
rules:
  - if: '$CI_COMMIT_REF_NAME == "main"'
    changes:
      - 'src/**/*'
    when: on_success

然后,通过 artifacts 规则,将产物保存下来,供 publish 环节使用。

artifacts:
paths:
  - dist/**/*
  1. 在 publish 操作中,我们通过 needs 规则,确定前置条件要先执行 build 操作。
needs: [ "build" ]

在正常情况下,这里将执行 npm 发布的完整操作,但是因为这是个示例项目,我们暂且不实现具体逻辑,只用 ls 查看发包的目录是否正常~

- echo "Publishing to npm now..."
# - pnpm publish
- ls dist
  1. 现在,让我们来尝试合入此笔 MR。在合入 MR 后,pipeline 也会自动执行,且成功运行了其下的 build 和 publish 操作。
image.png
  1. 至此,gitlab 跨仓库同步的全流程演示完成。

总结

我们今天通过两个 gitlab 仓库的联合演示,讲述了如何通过 gitlab 提供的 ci 与 pipeline 功能,实现跨仓库的同步。其中,涉及到了如 token 的设置,对指定目录的监听,MR 的自动生成等等知识点,最终,我们实现了本文一开始定下的目标:

  1. 保证全流程自动,不需要手动执行
  2. 保证准确监听到合适的目录、分支
  3. 实现跨仓库提交增量修改的代码

好的,以上就是对如何实现 gitlab 的跨仓库同步的全部介绍啦!

欢迎多多点赞,有问题也欢迎在评论区交流~