使用 GitHub Actions 自動備份程式碼倉

K8sCat發表於2021-06-13

這是我參與更文挑戰的第12天,活動詳情檢視:更文挑戰

任何東西都有丟失的可能性,除非你在丟失前做好了備份!本文將講解如何使用 GitHub Actions 實現自動備份程式碼倉。

目標

本文的目標是將 GitHub 使用者 user-a 下的所有程式碼倉同步到 GitHub 使用者 user-b 下。

準備

新建一個程式碼倉,程式碼倉的目錄結構下面這樣:

.
├── .github
│   └── workflows
│       └── mirror.yaml
複製程式碼

並在程式碼倉中設定三個 Secret,分別是:

  • GH_TOKEN_A 對應 GitHub 使用者 user-a 的 AccessToken
  • GH_TOKEN_B 對應 GitHub 使用者 user-b 的 AccessToken
  • SLACK_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 }}
複製程式碼

相關文章