语雀云端写作实现了 Markdown 格式的博文与博客构建目录的分离,这样的话,我想同时使用多少个主题都是没有问题的了,更重要的事,可以完全实现静态博客在线编辑。

本文对于文件(代码)变动的描述方式为-指删掉该行,+指增加改行(如果你是直接复制此行,请注意删掉+和上下文对齐),...表示省略上下文。另外,本文对于文件(代码)变动的描述只针对当时的文件(代码)版本,请根据实际情况决定对自己的相应文件(代码)进行变动。

博文迁移

首先是注册语雀账号,登陆上后,新建知识库,名称就为个人博客,再点击右上方的新建,选择导入,选择 Markdown 格式,将本地博文(即 _post 下的 Markdown 文件)批量(按住 Ctrl 可多选)导入至知识库个人博客。

导入成功后会发现每篇博文的开头,即 front-matter 部分,看起来有点乱,可以参看下图说明更正一下。

更正 front-matter

本地调试

安装 Elog

Elog,一个开放式跨端博客解决方案,可以随意组合写作平台(语雀/Notion/FlowUs)和博客平台(Hexo/Vitepress/Confluence/WordPress)等,是本次实现语雀云端写作的关键。

本着非必要不升级的原则,我使用的 Node.js 一直以来都是 v12.14.0,本次安装使用的 Elog 要求至少 14 版本的 Node.js,故而我升级 Node.js 到最新的 LTS 版本,即 18 版本。至于如何升级,如果是 Windows,就卸载然后安装,如果是 Linux,推荐使用 NVM 安装。

全局安装 @elog/cli:

1
npm install -g @elog/cli

进入<博客构建目录>,执行以下命令进行初始化,会在根目录生成一份配置文件 elog.config.js 和本地调试用的环境变量文件 .elog.env。

1
elog init

将 .elog.env 加入 .gitignore,防止隐私数据提交:

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

配置

编辑 .elog.env,配置语雀认证的环境变量:

1
2
3
4
5
6
7
# 语雀(Token方式)
YUQUE_TOKEN=
# 语雀(帐号密码方式)
YUQUE_USERNAME=
YUQUE_PASSWORD=
YUQUE_LOGIN=
YUQUE_REPO=

Token 方式和帐号密码方式二选一,现在使用 Token 需要开会员,没有会员的就使用帐号密码方式。YUQUE_LOGIN 和 YUQUE_REPO 的获取方式见 elog 官方文档的关键信息获取

再配置图床认证的环境变量,平台可在腾讯云、阿里云、又拍云、七牛云和 GitHub 中选择一个,详细配置方式见 elog 官方文档的关键信息获取

编辑 elog.config.js,设置写作平台为 yuque-pwd(即帐号密码方式认证的语雀):

1
2
3
4
5
...
write: {
- platform: "yuque"
+ platform: "yuque-pwd",
...

配置本地部署方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
...
deploy: {
platform: "local",
local: {
- outputDir: "./docs",
+ outputDir: "source/_posts",
filename: "title",
- format: "markdown",
+ format: "matter-markdown",
catalog: false,
formatExt: "",
},
...

开启图床,设置图床平台为腾讯云、阿里云、又拍云、七牛云或 GitHub:

1
2
3
4
5
6
7
...
image: {
- enable: false,
+ enable: true
- platform: "local",
+ platform: "<图床平台>",
...

最后配置图床,配置方式见 elog 官方文档的配置详情

同步

1
elog sync -e .elog.env

如果日志显示同步成功,本地调试即成功。同时,注意到当前目录生成了一个缓存文件 elog.cache.json,其中是我们文章的原始数据。

持续集成

本地安装 @elog/cli:

1
npm install @elog/cli

为了防止 hexo 在生成博客源文件时报错,新建目录 cache,将 elog.cache.json 移动过去。

编辑 package.json,新建一个 NPM 命令用于同步:

1
2
3
4
5
6
7
8
9
...
"scripts": {
"build": "hexo generate",
"clean": "hexo clean",
"deploy": "hexo deploy",
"server": "hexo server",
+ "sync": "elog sync -a cache/elog.cache.json"
},
...

前往博客构建仓库添加密钥,如下图,添加 YUQUE_TOKEN(YUQUE_USERNAME、YUQUE_PASSWORD)、YUQUE_LOGIN、YUQUE_REPO 的值,再添加图床的密钥,如使用腾讯云作为图床平台,需添加 COS_SECRET_ID、COS_SECRET_KEY、COS_BUCKET、COS_REGION 和 COS_HOST 的值。

添加密钥

编辑 GitHub Actions 工作流:

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
name: hexo-build
# 仓库分支main有推送时执行jobs下定义的任务
on:
push:
branches:
- main
# 设置时区为上海
env:
TZ: Asia/Shanghai
jobs:
# 定义名为blog-build的任务
blog-build:
# 定义运行的操作系统
runs-on: ubuntu-latest
# 定义步骤
steps:
# 签出仓库的默认分支,此处即为main,同时迁出子项目,此处即为主题仓库
- name: Checkout
uses: actions/checkout@v3
with:
submodules: true
# 安装别名为lts/Hydrogen的Node.js,即18版本,同时进行全局缓存
- - name: Install Node.js v12.14.0
+ - name: Install Node.js lts/Hydrogen
uses: actions/setup-node@v3
with:
- node-version: "12.14.0"
+ node-version: "lts/Hydrogen"
cache: "npm"
# 缓存文件夹node_modules并生成唯一码
- name: Cache dependencies
uses: actions/cache@v3
id: cache-dependencies
with:
path: node_modules
key: ${{runner.OS}}-${{hashFiles('**/package-lock.json')}}
# 如果唯一码比对不成功,重新安装依赖,成功则沿用缓存
- name: Install dependencies
if: steps.cache-dependencies.outputs.cache-hit != 'true'
run: npm ci
+ - name: Pull posts from Yuque
+ env:
+ YUQUE_TOKEN: ${{ secrets.YUQUE_TOKEN }}
+ YUQUE_LOGIN: ${{ secrets.YUQUE_LOGIN }}
+ YUQUE_REPO: ${{ secrets.YUQUE_REPO }}
+ COS_SECRET_ID: ${{ secrets.COS_SECRET_ID }}
+ COS_SECRET_KEY: ${{ secrets.COS_SECRET_KEY }}
+ COS_BUCKET: ${{ secrets.COS_BUCKET }}
+ COS_REGION: ${{ secrets.COS_REGION }}
+ COS_HOST: ${{ secrets.COS_HOST }}
+ run: |
+ # elog.cache.json被指定在子目录cache下,需要先创建,先判断有没有,没有才创建
+ [ ! -d "cache" ] && mkdir cache
+ npm run sync
- name: Setup private rsa key
env:
DEPLOY_KEY: ${{secrets.DEPLOY_KEY}}
run: |
mkdir -p ~/.ssh/
echo "$DEPLOY_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan github.com >> ~/.ssh/known_hosts
ssh-keyscan gitee.com >> ~/.ssh/known_hosts
- name: Download and clone PUBLIC
run: |
# 下载自己的GitHub用户仓库,下载的文件为master.zip
curl -LsO https://github.com/ql-isaac/<自己的Github用户名>.github.io/archive/refs/heads/master.zip
# 解压
unzip master.zip -d .
# 重命名为public
mv <自己的Github用户名>.github.io-master/ public
# 克隆自己的GitHub用户仓库,重命令为.deploy_git
git clone git@github.com:ql-isaac/<自己的Github用户名>.github.io.git .deploy_git
- name: Download DB
uses: dawidd6/action-download-artifact@v2
continue-on-error: true # 构件最多只能保存90天,90天过后这一步会报错,设置成报错继续执行
with:
github_token: ${{secrets.GITHUB_TOKEN}}
name: "DB"
- name: Generate and deploy
run: |
git config --global user.name "${{secrets.GIT_NAME}}"
git config --global user.email "${{secrets.GIT_EMAIL}}"
npm run build && npm run deploy
- name: Upload DB for next workflow to download
uses: actions/upload-artifact@v3
with:
name: "DB"
path: db.json
retention-days: 90
+ - name: Upload Cache for next workflow to download
+ uses: actions/upload-artifact@v3
+ with:
+ name: "Cache"
+ path: cache
+ retention-days: 90
+ - name: Upload Posts for next workflow to download
+ uses: actions/upload-artifact@v3
+ with:
+ name: "Posts"
+ path: "source/_posts"
+ retention-days: 90
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
name: hexo-build
on:
push:
branches:
- main
env:
TZ: Asia/Shanghai
jobs:
blog-build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: true
- - name: Install Node.js v12.14.0
+ - name: Install Node.js lts/Hydrogen
uses: actions/setup-node@v3
with:
- node-version: "12.14.0"
+ node-version: "lts/Hydrogen"
cache: "npm"
- name: Cache dependencies
uses: actions/cache@v3
id: cache-dependencies
with:
path: node_modules
key: ${{runner.OS}}-${{hashFiles('**/package-lock.json')}}
- name: Install dependencies
if: steps.cache-dependencies.outputs.cache-hit != 'true'
run: npm ci
+ - name: Pull posts from Yuque
+ env:
+ YUQUE_TOKEN: ${{ secrets.YUQUE_TOKEN }}
+ YUQUE_LOGIN: ${{ secrets.YUQUE_LOGIN }}
+ YUQUE_REPO: ${{ secrets.YUQUE_REPO }}
+ COS_SECRET_ID: ${{ secrets.COS_SECRET_ID }}
+ COS_SECRET_KEY: ${{ secrets.COS_SECRET_KEY }}
+ COS_BUCKET: ${{ secrets.COS_BUCKET }}
+ COS_REGION: ${{ secrets.COS_REGION }}
+ COS_HOST: ${{ secrets.COS_HOST }}
+ run: |
+ [ ! -d "cache" ] && mkdir cache
+ npm run sync
- name: Setup private rsa key
env:
DEPLOY_KEY: ${{secrets.DEPLOY_KEY}}
run: |
mkdir -p ~/.ssh/
echo "$DEPLOY_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan github.com >> ~/.ssh/known_hosts
ssh-keyscan gitee.com >> ~/.ssh/known_hosts
- name: Download and clone PUBLIC
run: |
curl -LsO https://github.com/ql-isaac/<自己的Github用户名>.github.io/archive/refs/heads/master.zip
unzip master.zip -d .
mv <自己的Github用户名>.github.io-master/ public
git clone git@github.com:ql-isaac/<自己的Github用户名>.github.io.git .deploy_git
- name: Download DB
uses: dawidd6/action-download-artifact@v2
continue-on-error: true
with:
github_token: ${{secrets.GITHUB_TOKEN}}
name: "DB"
- name: Generate and deploy
run: |
git config --global user.name "${{secrets.GIT_NAME}}"
git config --global user.email "${{secrets.GIT_EMAIL}}"
npm run build && npm run deploy
- name: Upload DB for next workflow to download
uses: actions/upload-artifact@v3
with:
name: "DB"
path: db.json
retention-days: 90
+ - name: Upload Cache for next workflow to use
+ uses: actions/upload-artifact@v3
+ with:
+ name: "Cache"
+ path: cache
+ retention-days: 90
+ - name: Upload Posts for next workflow to use
+ uses: actions/upload-artifact@v3
+ with:
+ name: "Posts"
+ path: "source/_posts"
+ retention-days: 90

提交并推送:

1
2
3
git add .
git commit -m ":green_heart: 使用elog实现语雀云端写作"
git push

再次编辑 GitHub Actions 工作流:

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
name: hexo-build
on:
# 将执行jobs下定义的任务的充分条件改为有类型为publish的外部事件触发
- push:
- branches:
- - main
+ repository_dispatch:
+ types:
+ - publish
# 设置时区为上海
env:
TZ: Asia/Shanghai
jobs:
# 定义名为blog-build的任务
blog-build:
# 定义运行的操作系统
runs-on: ubuntu-latest
# 定义步骤
steps:
# 签出仓库的默认分支,此处即为main,同时迁出子项目,此处即为主题仓库
- name: Checkout
uses: actions/checkout@v3
with:
submodules: true
# 安装别名为lts/Hydrogen的Node.js,即18版本,同时进行全局缓存
- name: Install Node.js lts/Hydrogen
uses: actions/setup-node@v3
with:
node-version: "lts/Hydrogen"
cache: "npm"
# 缓存文件夹node_modules并生成唯一码
- name: Cache dependencies
uses: actions/cache@v3
id: cache-dependencies
with:
path: node_modules
key: ${{runner.OS}}-${{hashFiles('**/package-lock.json')}}
# 如果唯一码比对不成功,重新安装依赖,成功则沿用缓存
- name: Install dependencies
if: steps.cache-dependencies.outputs.cache-hit != 'true'
run: npm ci
+ - name: Download Posts from the last workflow
+ uses: dawidd6/action-download-artifact@v2
+ continue-on-error: true # 构件最多只能保存90天,90天过后这一步会报错,设置成报错继续执行
+ with:
+ github_token: ${{secrets.GITHUB_TOKEN}}
+ name: "Posts"
+ path: "source/_posts"
+ - name: Download Cache from the last workflow
+ continue-on-error: true
+ uses: dawidd6/action-download-artifact@v2
+ continue-on-error: true
+ with:
+ github_token: ${{secrets.GITHUB_TOKEN}}
+ name: "Cache"
+ path: "cache"
- name: Pull posts from Yuque
env:
YUQUE_TOKEN: ${{ secrets.YUQUE_TOKEN }}
YUQUE_LOGIN: ${{ secrets.YUQUE_LOGIN }}
YUQUE_REPO: ${{ secrets.YUQUE_REPO }}
COS_SECRET_ID: ${{ secrets.COS_SECRET_ID }}
COS_SECRET_KEY: ${{ secrets.COS_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
COS_HOST: ${{ secrets.COS_HOST }}
run: |
[ ! -d "cache" ] && mkdir cache
npm run sync
- name: Setup private rsa key
env:
DEPLOY_KEY: ${{secrets.DEPLOY_KEY}}
run: |
mkdir -p ~/.ssh/
echo "$DEPLOY_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan github.com >> ~/.ssh/known_hosts
ssh-keyscan gitee.com >> ~/.ssh/known_hosts
- name: Download and clone PUBLIC
run: |
# 下载自己的GitHub用户仓库,下载的文件为master.zip
curl -LsO https://github.com/ql-isaac/<自己的Github用户名>.github.io/archive/refs/heads/master.zip
# 解压
unzip master.zip -d .
# 重命名为public
mv <自己的Github用户名>.github.io-master/ public
# 克隆自己的GitHub用户仓库,重命令为.deploy_git
git clone git@github.com:ql-isaac/<自己的Github用户名>.github.io.git .deploy_git
- name: Download DB
uses: dawidd6/action-download-artifact@v2
continue-on-error: true # 构件最多只能保存90天,90天过后这一步会报错,设置成报错继续执行
with:
github_token: ${{secrets.GITHUB_TOKEN}}
name: "DB"
- name: Generate and deploy
run: |
git config --global user.name "${{secrets.GIT_NAME}}"
git config --global user.email "${{secrets.GIT_EMAIL}}"
npm run build && npm run deploy
- name: Upload DB for next workflow to download
uses: actions/upload-artifact@v3
with:
name: "DB"
path: db.json
retention-days: 90
- name: Upload Cache for next workflow to download
uses: actions/upload-artifact@v3
with:
name: "Cache"
path: cache
retention-days: 90
- name: Upload Posts for next workflow to download
uses: actions/upload-artifact@v3
with:
name: "Posts"
path: "source/_posts"
retention-days: 90
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
name: hexo-build
on:
- push:
- branches:
- - main
+ repository_dispatch:
+ types:
+ - publish
env:
TZ: Asia/Shanghai
jobs:
blog-build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: true
- name: Install Node.js lts/Hydrogen
uses: actions/setup-node@v3
with:
node-version: "lts/Hydrogen"
cache: "npm"
- name: Cache dependencies
uses: actions/cache@v3
id: cache-dependencies
with:
path: node_modules
key: ${{runner.OS}}-${{hashFiles('**/package-lock.json')}}
- name: Install dependencies
if: steps.cache-dependencies.outputs.cache-hit != 'true'
run: npm ci
+ - name: Download Posts from the last workflow
+ uses: dawidd6/action-download-artifact@v2
+ continue-on-error: true
+ with:
+ github_token: ${{secrets.GITHUB_TOKEN}}
+ name: "Posts"
+ path: "source/_posts"
+ - name: Download Cache from the last workflow
+ uses: dawidd6/action-download-artifact@v2
+ continue-on-error: true
+ with:
+ github_token: ${{secrets.GITHUB_TOKEN}}
+ name: "Cache"
+ path: "cache"
- name: Pull posts from Yuque
env:
YUQUE_TOKEN: ${{ secrets.YUQUE_TOKEN }}
YUQUE_LOGIN: ${{ secrets.YUQUE_LOGIN }}
YUQUE_REPO: ${{ secrets.YUQUE_REPO }}
COS_SECRET_ID: ${{ secrets.COS_SECRET_ID }}
COS_SECRET_KEY: ${{ secrets.COS_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
COS_HOST: ${{ secrets.COS_HOST }}
run: |
[ ! -d "cache" ] && mkdir cache
npm run sync
- name: Setup private rsa key
env:
DEPLOY_KEY: ${{secrets.DEPLOY_KEY}}
run: |
mkdir -p ~/.ssh/
echo "$DEPLOY_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan github.com >> ~/.ssh/known_hosts
ssh-keyscan gitee.com >> ~/.ssh/known_hosts
- name: Download and clone PUBLIC
run: |
curl -LsO https://github.com/ql-isaac/<自己的Github用户名>.github.io/archive/refs/heads/master.zip
unzip master.zip -d .
mv <自己的Github用户名>.github.io-master/ public
git clone git@github.com:ql-isaac/<自己的Github用户名>.github.io.git .deploy_git
- name: Download DB
uses: dawidd6/action-download-artifact@v2
continue-on-error: true
with:
github_token: ${{secrets.GITHUB_TOKEN}}
name: "DB"
- name: Generate and deploy
run: |
git config --global user.name "${{secrets.GIT_NAME}}"
git config --global user.email "${{secrets.GIT_EMAIL}}"
npm run build && npm run deploy
- name: Upload DB for next workflow to download
uses: actions/upload-artifact@v3
with:
name: "DB"
path: db.json
retention-days: 90
- name: Upload Cache for next workflow to download
uses: actions/upload-artifact@v3
with:
name: "Cache"
path: cache
retention-days: 90
- name: Upload Posts for next workflow to download
uses: actions/upload-artifact@v3
with:
name: "Posts"
path: "source/_posts"
retention-days: 90

将 source 下 _posts 和 cache 下的 elog.cache.json 加入 .gitignore 并删除掉,这两个地方的文件已经不需要。

最终提交并推送:

1
2
3
git add .
git commit -m ":green_heart: 使用elog实现语雀云端写作(终)"
git push

这样,在语雀上写好博文并更新,只需要地址栏输入以下地址(外部事件触发 API),即可触发持续集成,实现云端写作。

1
https://serverless-api-elog.vercel.app/api/github?user=<自己GitHub用户名>&repo=<博客构建仓库名>&event_type=publish&token=<自己生成的Token>

生成 Token 的方式(勾选 repo 的权限):

image.png

以上 API 是 elog 官方在 Vercel 上搭建的,使用的 Vercel 默认域名,会经常被墙,我们可以自己搭建一个并且使用上自己的域名(未备案也行,因为 Vercel 是国外服务)。

fork serverless-api登录 Vercel,新建,导入刚刚 fork 的项目,部署。到域名设置那里,添加自己的域名,如 publish.xxx.xx,再到域名供应商那里添加一下 cname 解析,最后回到域名设置那里,确认解析生效,这样,将以上地址中域名替换为自己的域名得到自己的 API。