相互同步 VPS 和 GitHub 上的 git 仓库

Posted by Eric Jin on 2023-04-04

背景

由于公司或者某些地方对 GitHub 的网络非常的不友好,所以我希望这时候把代码直接推送到我的服务器上,由服务器同步到 GitHub 上去。

而家里的网络我会直接推送到 GitHub,所以这个时候也需要 GitHub 能把代码同步到服务器上,便于其他客户端从服务器获取到最新的代码。

步骤

1. 在服务器上创建空仓库

在服务器上创建一个新的空仓库作为中转站,该仓库将不储存代码,只保存提交记录(即git log)。例如,你可以使用以下命令在中转服务器上创建一个新的仓库:

mkdir myrepo.git
cd myrepo.git
git init --bare

2. 在中转服务器上添加 Git hook

在中转服务器上,创建一个名为 post-receive 的 Git hook,并添加以下脚本:

#!/bin/bash

while read oldrev newrev refname
do
  # 只在主分支上的提交才触发
  if [[ $refname = "refs/heads/main" ]]; then
    # 将代码推送到 GitHub
    git push origin main
  fi
done

post-receive hook 将会在有新的代码提交时被触发,并自动将代码推送到 GitHub 的特定仓库中。

3. 为 post-receive 文件添加可执行权限

保存并退出 post-receive 文件。然后,为 post-receive 文件添加可执行权限。

chmod +x post-receive

4. 在本地计算机上添加远程仓库

在本地计算机上,在你的代码仓库中添加一个名为 origin 的远程仓库,该仓库指向你在中转服务器上创建的空仓库。可以使用以下命令将此远程仓库添加到你的代码仓库中:

git remote add origin username@transit-server:/path/to/myrepo.git

其中,username 是中转服务器的用户名,transit-server 是中转服务器的主机名或 IP 地址,/path/to/myrepo.git 是在中转服务器上创建的空仓库的路径。

5. 将代码推送到中转服务器

在本地,可以将你的代码推送到中转服务器。

git push origin main

现在,当你从本地推送代码到中转服务器时,post-receive hook 将自动将代码推送到 GitHub 上的相应仓库。

6. 将 GitHub 上的代码同步到中转服务器

当你直接将代码推送到 GitHub 时,你需要确保中转服务器上的代码也是最新的。为此,可以创建一个 GitHub Actions workflow 来实现这个功能。下面是一个简单的工作流程,可以在 GitHub 接收到 push 事件时将代码推送到中转服务器仓库的对应分支:

name: Sync to Transfer Server

on:
  push:
    branches:
      - main

jobs:
  push-to-transfer-server:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
          
      - name: Configure SSH
        uses: webfactory/ssh-agent@v0.5.0
        with:
          ssh-private-key: ${{ secrets.SERVER_PRIVATE_KEY }}

      - name: Push to Server
        uses: appleboy/ssh-action@master
        with:
          host: ${{ vars.SERVER_HOST }}
          username: ${{ vars.SERVER_USERNAME }}
          key: ${{ secrets.SERVER_PRIVATE_KEY }}
          script: |
            cd ${{ vars.SERVER_PATH }}
            git fetch
            latest_commit=$(git rev-parse FETCH_HEAD)
            git update-ref refs/heads/main $latest_commit            

这个workflow会在接收到main分支上的push事件时触发。它会先将代码检出到工作目录中,使用 SSH 连接到远程服务器,并将代码推送到服务器上。

要想让你的GitHub连接你的服务器,你需要在服务器上生成一个SSH密钥对, 你可以使用ssh-keygen命令来生成密钥对。

将公钥添加到服务器上的authorized_keys文件中,该文件通常位于服务器上的~/.ssh/目录中。你可以使用文本编辑器或ssh-copy-id命令将公钥添加进去。

将私钥复制到GitHub仓库的secrets中。你可以进入GitHub仓库,选择“Settings” > “Secrets” > “New repository secret”,然后将私钥复制粘贴到值字段中。 这就是${{ secrets.SERVER_PRIVATE_KEY }} 这个变量了。

${{ vars.SERVER_USERNAME }} 这类变量则是在刚刚的Variables 里面创建的。