目前在非工作场景下,我经常会在手头的 MBP 16 和 MBA M1 间切换,切换的时机大概就是按照就近原则,哪台离我物理距离近就用哪台。但由于我的博客是基于 Hexo 搭建的静态博客,所以在写博客时,两台设备间博客配置和文章内容的同步问题就尤为让人头疼。本文就分享下我是如何解决静态博客的多设备同步和备份问题的。

配置的同步与备份

整站

最近我的 MBP 被大雨淋坏后刚刚修好(除了 CD 面外壳,其他部分全部被换新了),这台设备之前被用来作为在公司工作时的主力设备,所以为了满足公司的数据合规要求就没开启 TimeMachine 。现在从工作中退役重新成为我的个人办公电脑后,从零搭建一套和原配置完全一致的本地博客配置会带来不小的工作量。好在我之前对博客本地内容做了定期的整站备份,因此直接拷贝一份稍加初始化即可。所以从经验来看,新建立的备份机制也应该是对本地整站备份才更加方便和安全。另一方面,不同于以前简单的定期备份,我还需要设计一套方便的同步方案,来做到近乎实时的同步,保证跨设备使用的体验。

Hexo 是基于 Node 实现的静态博客系统,这就导致如果直接使用 Dropbox 或 Onedrive 等网盘进行整站同步,那么 node_modules 这类目录的同步成本会非常高,有时甚至会阻塞正常文件的同步。虽然 Onedrive 可以在同步设置中排除指定内容,但由于莫名的原因,我在使用 Onedrive 同步博客的整站目录时出现过冲突导致的同步失败进而需要手动解决冲突甚至丢失文件的问题,因此直接使用网盘的自动同步机制进行同步是不太可行的。

而配置类内容的修改频率不高,同时几乎不会有多台设备并发或并行处理的情况,因此只需要在每次博客发布时能够触发同步即可。所以在这个问题上我决定遵循 KISS 原则,直接使用 GitHub 的私有仓库进行备份和同步。这里选择私有仓库,主要是考虑到有些配置还涉及了一些 appid 和 appsecret 相关内容,不适合公开。

主题

Hexo 的主题有两种安装方式,一种是通过 npm 以依赖的方式安装到 node_modules,另一种是直接 clone 主题内容到 themes 目录下,由于我总会对主题进行微调,所以通常会选用后一种方式安装。

但由于主题目录中本身就包含 git 信息,对整站进行 git 同步就会涉及 submobule 的麻烦问题,主题的静态依赖众多也导致同步量大增,但这些其实与博客的领域并不是直接相关的。因此对于主题,我选择 fork 原主题仓库,将对主题的修改在这个 fork 仓库中独立维护,有修改再提交代码,整站备份时则将主题目录配置在 .gitignore 中,不与整站内容耦合。

需要注意的是,通常主题的配置文件都在主题目录下,但大部分主题的 .gitignore 中都会将 _config.yml 这个配置文件排除以避免误提交,因此我们需要将主题配置文件拷贝到博客根目录并重命名,比如我将现在正在使用的 even 主题的配置文件命名为了 _config.even.yml ,Hexo 5.0 以上的版本会自动按高优先级加载这个格式命名的主题配置文件,算是约定大于配置。如果你使用了其他名称,也可以通过指定 Hexo 配置中的 theme_config 的值为文件名来达到同样的效果。

文章的同步与备份

iCloud同步

博客的核心是内容,跨设备修改最频繁的也是内容,因此将博客内容所在的 source 目录设置为实时同步也就非常有必要。

原本我们使用各类同步网盘就可以很轻松地实现实时同步,然而各类网盘只能同步其绑定目录下的内容,或对 home 目录进行全量同步,而不能灵活选择其他自定义目录。那么软链其他目录到网盘绑定目录是否可行呢?很遗憾,或许是源于操作系统的限制,目前的网盘均无法同步外部链接到网盘目录的软链,直接同步的话他们真的就只会同步软链本身,有点像早年不了解 Windows 的用户拷贝文件只拷贝了快捷方式导致在其他设备无法使用的老梗。

不过我们可以逆向思维,将 Hexo 的 source 目录转移到网盘目录中保存以实现实时同步,随后在博客根目录中再通过软链的方式将网盘中的 source 目录链接到博客目录,这样一来,文件系统在我们预览和发布博客时仍会检索软链指向的文件从而不影响博客的构建:

1
2
3
mkdir ~/Library/Mobile\ Documents/com\~apple\~CloudDocs/Blogs/
mv ./source ~/Library/Mobile\ Documents/com\~apple\~CloudDocs/Blogs/
ln -s ~/Library/Mobile\ Documents/com\~apple\~CloudDocs/Blogs/source source

我的方案直接选用了 iCloud ,尽管显然 Dropbox 的同步能力更强大而且还附有版本管理功能,但目前其免费版只支持三台设备登录(多年不用的我现在才发现这项能力缩水了,之前免费版也是可以同时登录很多台的),同时我的主力写作工具 iA Writer 在 iOS 上对 Dropbox 的支持也不太完整,所以在写作时只使用 Apple 生态产品的我就转为使用 iCloud 了。实际使用体验也还可以,有时会有几秒的同步延迟,至于版本管理功能我们可以交给下文的 GitHub 备份来处理。另一方面,iOS 上的写作工具普遍对 iCloud 支持很好,这也让我们间接实现了跨平台的写作工作流。

GitHub备份

长期以来,我都会把我的文章原文放置到 GitHub 的公共仓库中,备份之余也可供有需要的朋友直接使用,因此这一项备份能力也要加上。另外,上文提到原文内容移动到了 iCloud ,那么数据的一致性和可靠性就要完全依赖这个偶尔出现同步问题、没有版本控制能力的「知名 Apple 网络产品」了,为了避免 iCloud 出问题后的抱头痛哭,加上 GitHub 备份也是极有必要的。

同样,由于只是备份手段,所以我们只需要在部署博客的时候触发就好,触发时将 iCloud 中的博客内容拷贝到仓库目录,自动添加个时间相关 commit 直接 push:

1
2
3
4
5
6
7
8
rm -rf ./github.backup/source
mkdir ./github.backup/source
cp -r source/* ./github.backup/source/
cd ./github.backup
git pull
git add .
git commit -m "$(date)"
git push

另外有的朋友会问,前面不是已经有了整站备份吗,对内容进行额外备份岂不是多此一举?然而我们都知道 Git 对软链的处理也很不好,同时我也有公开原始文章内容的需要,所以这个处理步骤仍然是有必要的。

建立一键脚本

以上就是对静态博客进行同步和备份的方案,但我们每次部署博客或更换新设备都要执行一堆命令,这可太不友好了,所以我们还要写几个简单的一键脚本来简化我们的同步和备份操作。

一键发布

相信每个 Hexo 用户都有这样的一个脚本来快速构建和部署博客,所以这里就不再赘述逻辑了。需要注意的是,与直接构建部署不同,我们还要在部署结束后触发下文的一键同步和一键备份脚本,来实现上文提到的整站和内容的最终一致性备份:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#! /bin/bash
# deploy.sh

# Update site data and configs.
git pull

# Deploy the blog to server.
hexo clean --silent
hexo g --silent
hexo d
hexo clean --silent
hexo g --silent
hexo d --config _config.yml

# Invoke the backup and push logic.
./backup.sh
./push.sh

一键同步

这里的同步指的是上文提到整站备份能力,将备份除了主题和文章内容的所有数据:

1
2
3
4
5
6
7
8
#! /bin/bash
# push.sh

# Push to repo with date.
git pull
git add .
git commit -m "$(date)"
git push

另外为了提交的内容能符合我们的要求,我们还要在博客根目录的 .gitignore 中额外排除相应的内容:

1
2
3
4
5
6
7
8
.deploy_git
themes
public
github.backup
.DS_Store
*/.DS_Store
source
db.json

如果希望不执行部署操作也能在其他设备同步最新配置,则仍需要我们仿照代码开发流程,手动执行 git pull

一键备份

即将文章内容备份到公开的 GitHub 仓库中:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#! /bin/bash
# backup.sh

# Backup source from iCloud.
rm -rf ./github.backup/source
mkdir ./github.backup/source
cp -r source/* ./github.backup/source/
cd ./github.backup
git pull
git add .
git commit -m "$(date)"
git push

# Backup the entire blog project.
cd ..
git pull
git add .
git commit -m "$(date)"
git push

一键初始化

有了以上脚本,我们就可以一键进行博客的发布、备份和同步了,不过对于新设备,我们还要整合下以上内容提供个一键初始化脚本,实现「git clone」+「执行一次脚本」即可完成博客配置,减轻我们配置新设备的工作量:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#! /bin/bash

# Init source backup.
git clone {public.git.repo.path} github.backup

# Install themes.
git clone {theme-foo.git.repo.path} themes/foo

# Install Hexo and dependencies.
npm install hexo-cli -g
npm install -S

# Soft link real source from iCloud.
ln -s ~/Library/Mobile\ Documents/com\~apple\~CloudDocs/Blogs/source source

# Make shell scirpts executable.
chmod +x pull.sh
chmod +x push.sh
chmod +x deploy.sh
chmod +x backup.sh