momo's Blog.

使用gitee自动部署博客

字数统计: 722阅读时长: 3 min
2022/06/17 Share

前言

国内的git page或者静态网站托管的服务都需要备案。 没办法只能把博客迁移到香港机器。 不过迁移香港节点, 自动部署就有点问题。

方案

使用主机进行部署, hexo有几种方式.

  1. sftp
  2. sshd
  3. rsync

能用也是能用,不过sshd和sftp同步大量文件着实有点慢, 但是你说用rsnyc吧, 你主机上面必须要有这个命令。 电脑有mac有Windows, 如果用这三种方式肯定要有对应的二进制文件,这样配置起来实在是麻烦。

最后考虑了一下,还是正常推送到gitee,然后走gitee的webhook触发本机更新。

代码思路

既然我们已经用了云主机,那就可以直接把代码部署到本地。

整体部署过程是: gitee –> webhook –> deploy

Webhook 签名

gitee的webhook需要计算签名。

  • Step1:把timestamp+”\n”+密钥当做签名字符串,使用HmacSHA256算法计算签名。
  • Setp2:对上述得到的结果进行 Base64 encode。
  • Setp3:对上述得到的结果进行 urlEncode,得到最终的签名(需要使用UTF-8字符集)

官方签名计算示例(Python2.7)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#python 2.7
import time
import hmac
import hashlib
import base64
import urllib

timestamp = long(round(time.time() * 1000))
secret = 'this is secret'
secret_enc = bytes(secret).encode('utf-8')
string_to_sign = '{}\n{}'.format(timestamp, secret)
string_to_sign_enc = bytes(string_to_sign).encode('utf-8')
hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
sign = urllib.quote_plus(base64.b64encode(hmac_code))
print(timestamp)
print(sign)

服务端代码

这里我们就实现2个步骤就行

  1. 鉴权
  2. 部署

代码很简单, 如下:

鉴权

sign.py

1
2
3
4
5
6
7
8
import hmac
import hashlib


def get_sha256_sign(key: str, data: str) -> bytes:
data = data.encode('utf-8')
key = key.encode('utf-8')
return hmac.new(key, data, digestmod=hashlib.sha256).digest()

部署

部署的具体步骤如下:

  1. git clone 项目
  2. rsync 项目到网站根目录

git clone 走的是ssh, 而我们用的是docker环境, 所以需要关闭add known 的时候的提示。
添加了如下环境变量

os.environ["GIT_SSH_COMMAND"] = "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"

后续的操作就简单了, 直接部署即可.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import base64
from typing import Union
from fastapi import FastAPI, Header, status, Body, HTTPException
import subprocess
from sign import get_sha256_sign
import os
import git

app = FastAPI()


secret_key = os.getenv('secret_key', '')
html_path = os.getenv('html_path', '/data/html/')
os.environ["GIT_SSH_COMMAND"] = "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"


def deploy(git_ssh: str, html_path: str) -> bool:
subprocess.Popen(f'rm -r ./tmp/', shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
git.Repo.clone_from(git_ssh, './tmp/', branch='master')
r = subprocess.Popen(f'rm -rf ./tmp/.git/ && rsync -rtvu --delete ./tmp/ {html_path}',
shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
if r.wait() != 0:
return False
return True


@app.post("/deploy/")
async def deploy_server(
X_Gitee_Token: Union[str, None] = Header(default=None),
X_Gitee_Timestamp: Union[int, None] = Header(default=None),
user_agent: Union[str, None] = Header(default=None),
body: dict = Body(),
):

git_url = body['project']['git_ssh_url']

if user_agent not in ['git-oschina-hook']:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail='403 FORBIDDEN')
token = base64.b64encode(get_sha256_sign(secret_key, '{}\n{}'.format(X_Gitee_Timestamp, secret_key))).decode()
if X_Gitee_Token != token:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail='403 FORBIDDEN')

if not deploy(git_url, html_path):
raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail='部署错误')
return {'status': 'ok'}

参考

  1. WebHook 密钥验证和验证算法
CATALOG
  1. 1. 前言
  2. 2. 方案
  3. 3. 代码思路
    1. 3.1. Webhook 签名
    2. 3.2. 服务端代码
      1. 3.2.1. 鉴权
      2. 3.2.2. 部署
  4. 4. 参考