任何东西都有丢失的可能性,除非你在丢失前做好了备份!本文将讲解如何使用 GitHub Actions 实现自动备份代码仓。
目标
本文的目标是将 GitHub 用户 user-a
下的所有代码仓同步到 GitHub 用户 user-b
下。
准备
新建一个代码仓,代码仓的目录结构下面这样:
.
├── .github
│ └── workflows
│ └── mirror.yaml
并在代码仓中设置三个 Secret,分别是:
GH_TOKEN_A
对应 GitHub 用户user-a
的 AccessTokenGH_TOKEN_B
对应 GitHub 用户user-b
的 AccessTokenSLACK_WEBHOOK
对应 Slack WebHook 地址(可选)
工作流
获取代码仓列表
使用 GitHub CLI gh
获取 GitHub 用户 user-a
下的所有代码仓
echo ${GH_TOKEN_A} > gh_token_a # 将 user-a 的 GitHub AccessToken 写入文件
gh auth login --with-token < gh_token_a # GitHub CLI 登录
gh repo list user-a -L 1000 > a_repos # 获取 user-a 的所有代码仓并写入文件
cat a_repos # 显示代码仓列表
cat a_repos | wc -l # 显示代码仓总数
创建同名代码仓
在 GitHub 用户 user-b
下创建同名代码仓。
gh repo create user-b/${repo_name} --private --description "${repo}" -y || true # 忽略错误,代码仓可能已经存在
克隆代码仓
使用 --bare
参数克隆 GitHub 用户 user-a
下的代码仓。
git clone --bare https://${GH_TOKEN_A}@github.com/user-a/${repo_name}.git ${repo_name}
推送代码
使用 --all
和 --mirror
参数分别进行代码推送,防止推送的数据过大导致推送失败。
--all
表示推送所有的分支--mirror
表示推送refs/
下的所有引用,包括分支、标签等
cd ${repo_name}
mirror_repo="https://${GH_TOKEN_B}@github.com/user-b/${repo_name}.git"
git push --all -f ${mirror_repo} || true
git push --mirror -f ${mirror_repo} || true
优化
- 忽略体积过大或者不需要备份的代码仓
# 为了保证代码仓名称判断的准确性,在定义和判断时,在每个代码仓名称的左右添加斜杆
IGNORE_REPOS="/repo_a/repo_b/"
[[ ${IGNORE_REPOS} =~ "/${repo_name}/" ]] && continue || true
- 对于
tag
数量太多的代码仓,仅备份分支
ONLY_BRANCH_REPOS="/repo_c/repo_d/"
[[ ${ONLY_BRANCH_REPOS} =~ "/${repo_name}/" ]] && continue || true
- 由于 GitHub Actions 的时区为
UTC
,定时任务的时间需要-8h
周一到周五的凌晨2点:
- 一般配置:
0 2 * * 1-5
- GitHub Actions 定时任务:
0 18 * * 0-4
完整的工作流配置
.github/workflows/mirror.yaml
文件的内容:
name: Mirror repos
on:
schedule:
- cron: "0 18 * * 0-4" # 设置定时任务,周一到周五的凌晨2点进行备份
workflow_dispatch: # 手动触发构建
jobs:
mirror:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: GitHub CLI version
run: gh --version
- name: List repos
env:
GH_TOKEN_A: ${{ secrets.GH_TOKEN_A }}
run: |
echo ${GH_TOKEN_A} > gh_token_a
gh auth login --with-token < gh_token_a
gh repo list user-a -L 1000 > a_repos
cat a_repos
cat a_repos | wc -l
- name: Mirror repos
env:
GH_TOKEN_A: ${{ secrets.GH_TOKEN_A }}
GH_TOKEN_B: ${{ secrets.GH_TOKEN_B }}
IGNORE_REPOS: "/repo_a/repo_b/"
ONLY_BRANCH_REPOS: "/repo_c/repo_d/"
run: |
echo ${GH_TOKEN_B} > gh_token_b
gh auth login --with-token < gh_token_b
mkdir repos
cd repos
set -x
cat ${GITHUB_WORKSPACE}/a_repos | while read repo; do
repo_name=$(echo ${repo} | awk '{print $1}' | awk -F/ '{print $2}')
[[ ${IGNORE_REPOS} =~ "/${repo_name}/" ]] && continue || true
gh repo create user-b/${repo_name} --private --description "${repo}" -y || true
rm -rf ${repo_name}
git clone --bare https://${GH_TOKEN_A}@github.com/user-a/${repo_name}.git ${repo_name}
cd ${repo_name}
mirror_repo="https://${GH_TOKEN_B}@github.com/user-b/${repo_name}.git"
git push --all -f ${mirror_repo} || true
[[ ${ONLY_BRANCH_REPOS} =~ "/${repo_name}/" ]] && continue || true
git push --mirror -f ${mirror_repo} || true
cd -
done
- name: Slack Notification
uses: rtCamp/action-slack-notify@v2
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}