背景

我的博客是使用 Hexo 自动生成的静态网站。此前都是通过在本地使用 Node SDK 将生成的网页推送到七牛云的。非常麻烦,而且不利于在多端同步。为了学习 GitHub Actions 的使用方法,我决定使用它将我的博客自动推送到七牛云的存储空间中。

七牛云存储提供 http 协议下免费的 10G 空间和免费的流量,完全可以满足我的博客的需要。所以我推荐适用它存储博客。 GitHub pages 服务使用固然方便,但是在国内的访问速度太慢,不推荐使用。

我需要解决的主要问题是,因为我使用七牛云的免费容量,所以对它的推送越少越好。目前已有的工具是全量推送的,每次都要推送整个仓库,耗费流量。同时, CDN 的刷新次数也有限值。因此,我希望实现每一次推送都为增量推送,只把改变和新增的内容推送到云上。

解决这一问题的方法是,在运行 hexo generate 时,程序会自动计算文件的 MD5 值,如果与现有文件相同,则不重新生成。而如果与现有文件不同,则生成。而且生成的文件列表会在控制台输出。因此,只要读取这一次推送生成的文件,再向七牛云推送即可。那么我可以将仓库的版本回退一次,生成静态文件,再把版本回到最新,再生成一次,两个生成文件的不同会在控制台输出,读取这一输出,则可以找到这一次推送的文件。

我把上述方法写成了一个 GitHub Action ,可以供和我有相似需求的用户直接调用。

下面介绍这个 Action 的使用方法。在使用这个 Action 之前,要先在七牛云中申请一个对象存储仓库。

建立仓库

七牛云 登录账户。然后在控制台中,找到“对象存储Kodo” > “空间管理”,点击“新建”按钮。 为仓库取一个名字,然后选择存储的区域,将“私有”改为“公开”。

到仓库设置中,将“默认首页”开启。这样,可以把 index.html 文件作为首页。

获取密钥

这个 Action 使用 SDK 向七牛云中推送。因此,需要获得密钥。密钥可以在七牛云的个人账户设置界面中“密钥管理”部分找到。里面有 AccessKeySecretKey 两个。一会儿要把他们添加到仓库中。

设置域名

七牛的域名只能使用 30 天。因此需要尽快绑定域名。在控制台中,找到 “CDN” > “CDN管理”,点击“添加”。然后,将你的域名添加到七牛云中。源站选择新建的仓库。七牛会分配一个 CNAME ,到域名供应商处完成解析。

现在七牛云配制好了。我们来配置 Git 仓库。

你的网站

找到 Hexo.io 的使用说明,简历你的个人网站。请使用命令行工具 hexo-cli 。例如:

1
hexo init myblog

上面的命令会生成一个网站,包含在 myblog 文件夹中。在这个文件夹中,加入您的网站内容。

创建 Git 仓库

在 GitHub 中,新建一个空仓库,公开或私密都可以。把这一仓库克隆到本地。你可以创建一个 README 文件,并添加 .gitignore 文件。一个样例为:

1
2
3
4
5
6
7
.DS_Store
Thumbs.db
db.json
*.log
node_modules/
public/
.deploy*/

在仓库中加入密钥

仓库的 Secrets 是存放私密信息的地方。在仓库的 Settings 选项卡中,找到 Secrets ,并且点击 “New repository secret”。使用 QINIU_ACCESS_KEYQINIU_SECRET_KEY 作为键,把值相应地复制进去。

添加 workflow

现在,我们给 GitHub Actions 添加一个 workflow ,用于驱动 Actions 的运行。

在仓库中创建文件 .github/workflows/deploy.yml ,加入以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
name: deploy hexo to qiniu
on: [push]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 2
path: master
- uses: actions/setup-node@v2
with:
node-version: 14.x
- uses: Hanlin-Dong/hexo-to-qiniu-action@v1
with:
bucket: your-bucket
access-key: ${{ secrets.QINIU_ACCESS_KEY }}
secret-key: ${{ secrets.QINIU_SECRET_KEY }}
base-folder: myblog
domain: "http://abc.io"
sub-domain: ""

在这个名为 deploy 的 workflow 中,一共有三步。第一步,使用 checkout@v2 把仓库拉取到 GitHub 的云虚拟机中。然后,使用 setup-node@v2 安装 Node 环境。最后,使用我的仓库中的 “Hanlin-Dong/hexo-to-qiniu-action@v1” 来向七牛云推送。

你需要把里面的 “your-bucket” 替换为七牛仓库名。把“base-folder”替换为你的博客文件夹 myblog ,把“domain”替换成你的个人域名。

有时由于一些原因,你希望全量推送一次仓库。这时,可以同时设置一个手动激发的 workflow ,建立文件 .github/workflows/pushall.yml ,加入以下内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
name: manually deploy hexo to qiniu
on: workflow_dispatch
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 2
path: master
- uses: actions/setup-node@v2
with:
node-version: 14.x
- uses: Hanlin-Dong/hexo-to-qiniu-action@v1-full
with:
bucket: your-bucket
access-key: ${{ secrets.QINIU_ACCESS_KEY }}
secret-key: ${{ secrets.QINIU_SECRET_KEY }}
base-folder: myblog
domain: "http://abc.io"
sub-domain: ""

在这里, workflow_dispatch 告诉 GitHub ,在 Actions 页面中加入一个手动激发的按钮,点击按钮即可执行这一个 workflow 。后面的唯一变化是使用了 v1-full 这一标签,表示全量推送。

如果你有两个博客需要推送到一个域名下,比如中文博客包含在文件夹 cn 中,英文包含在 en 中,则可以设置两个并行的 jobs 。仓库的目录结构为

1
2
3
4
5
6
.
├── .github
├── cn/
├── en/
├── .gitignore
└── README.md

这时的 workflow 如下:

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
name: deploy two hexo sites to qiniu
on: [push]
jobs:
deploy-cn:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 2
path: master
- uses: actions/setup-node@v2
with:
node-version: 14.x
- uses: Hanlin-Dong/hexo-to-qiniu-action@v1
with:
bucket: your-bucket
access-key: ${{ secrets.QINIU_ACCESS_KEY }}
secret-key: ${{ secrets.QINIU_SECRET_KEY }}
base-folder: cn
domain: "http://abc.io"
sub-domain: ""
deploy-en:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 2
path: master
- uses: actions/setup-node@v2
with:
node-version: 14.x
- uses: Hanlin-Dong/hexo-to-qiniu-action@v1
with:
bucket: your-bucket
access-key: ${{ secrets.QINIU_ACCESS_KEY }}
secret-key: ${{ secrets.QINIU_SECRET_KEY }}
base-folder: en
domain: "http://abc.io"
sub-domain: en

在这种情况下,在 cn 文件夹中的网站可以直接从根域名访问。因为 sub-domain 为空。然而, en 文件夹中的网站需要从子域获取,因为其 sub-domainen 。这时,主页地址为 “http://abc.io/en/”。需要注意,在 en 网站的 _config.yml 文件中,要设置 root: /en/

提交分支

由于是增量推送,需要回退一个版本,如果只有一个版本时会报错。因此,首次推送时,需要分两次推送。

第一次提交分支时,只创建 myblog 文件夹,而不添加内容,只添加一个 .gitkeep 空文件以提交这个目录。 .gitignore.github 文件要同时提交上去。这时仓库如下:

1
2
3
4
5
6
7
8
9
.
├── .github
│   └── workflows
│   ├── deploy.yml
│   └── pushall.yml
├── myblog
│   └── .gitkeep
├── .gitignore
└── README.md

推送之后, Actions 会报错。但是由于没有推送博客内容,所以没有影响。然后,再把 myblog 里面的博客内容复制进来,再次提交。这时, Actions 可以自动成功运行了。

如果在初期配置的过程中操作有误,可以使用 pushall 中的 workflow ,把整站推送一遍。在配置正确之后,就可以使用后面的增量推送了。