Hexo + GitHub Actions + OSS 博客系统搭建

1,580 阅读6分钟

又搭博客系统?

Hexo 博客大家都应该比较熟了,使用也比较简单,本地生成下静态文件往 Github 一推就好了,需要的话还可以给 GitHub Pages 配置自定义域名。

但 Github Pages 的访问速度甚是感人,而且 github.io DNS 经常被污染,总之就是不太稳。每次本地构建后推送也显得繁琐。之前刷 GitHub Actions 时发现这玩意做 Github 项目的部署还是很方便的,甚至都不要需要自己的服务器,想着能不能通过 GitHub Actions 将 Hexo 部署到其他地方。

简单来说我们要实现一个通过 CI 自动将 Hexo 博客内容部署到 OSS 的发布系统,其实很简单,往下看~

Hexo 略过

没接触过的同学戳这: hexo.io/zh-cn/

讲下 OSS

姑且贴一下 OSS 的链接 阿里云对象存储服务

日常搬砖中 OSS 使用的比较多,这里以 OSS 作为示例,当然你也可以换成对应的 AWS、七牛云、腾讯云啥的,功能上大差不差。OSS 可以简单的理解成一个网盘,你可在直接在面上传各种媒体与数据文件,然后可以就可以通过生成的资源链接地址访问。

一个静态网站的就是一大堆 html css js 资源整合,正常将这些文件按目录结构上传到 OSS 其实就能访问了,看个最简单的 index.html 文件示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello, World!</title>
</head>
<body>
    <h1>Hello, World!</h1>
</body>
</html>

在 OSS 新建一个 Bucket(自定义,名称唯一),然后将 index.html 上传到 OSS 上吧,这里直接上图了:

不过 OSS 有个限制,OSS 本身有提供静态站点的支持需要配置下,否则 html 文件并不能直接在浏览器打开,而是触发了下载,详情可以看这设置静态网站托管

可以戳这里试试: erii-blog.oss-cn-hangzhou.aliyuncs.com/index.html

出于安全考虑,中国区域自2018年8月13日起,中国以外区域自2019年9月25日起,通过浏览器访问OSS上网页类型文件(mimetype为text/html,扩展名包括HTM、HTML、JSP、PLG、HTX、STM):

  • 使用OSS默认域名访问时,Response Header中会自动加上 Content-Disposition:'attachment=filename;'。即从浏览器访问网页类型文件时,不会显示文件内容,而是以附件形式进行下载。
  • 使用绑定的自定义域名访问OSS时,Response Header中不会加上 Content-Disposition:'attachment=filename;',只要您的浏览器支持该类型文件的预览,可以直接预览文件内容。

所以咱们需要配置下给 OSS 设置的 Bucket 配置下自定义域名:

设置首页:

设置自定义域名,我这里使用的是 blog.ierii.com,这个域名是在腾讯云上买的(因为便宜!),所以还需要到腾讯云上配置下 CNAM

blog.ierii.com cname erii-blog.oss-cn-hangzhou.aliyuncs.com

然后 OSS 上配置下自定义域名

可以看到首页了~

将 Hexo 资源上传到 OSS

接下来我们看看如何将 Hexo 生成的静态资源上传到 OSS,先初始化个 Hexo 博客项目:

hexo init hexo-oss-action

hexo 默认使用的 yarn 进行依赖安装,不过后面走 GitHub Actions 时我们就直接使用 npm 了,建议初始化项目也使用 npm 安装依赖。

安装完依赖 npm run build 下,看看生产的静态文件:

public
├── 2020
│   └── 12
│       └── 27
│           └── hello-world
│               └── index.html
├── archives
│   ├── 2020
│   │   ├── 12
│   │   │   └── index.html
│   │   └── index.html
│   └── index.html
├── css
│   ├── fonts
│   │   ├── FontAwesome.otf
│   │   ├── fontawesome-webfont.eot
│   │   ├── fontawesome-webfont.svg
│   │   ├── fontawesome-webfont.ttf
│   │   ├── fontawesome-webfont.woff
│   │   └── fontawesome-webfont.woff2
│   ├── images
│   │   └── banner.jpg
│   └── style.css
├── fancybox
│   ├── jquery.fancybox.min.css
│   └── jquery.fancybox.min.js
├── index.html
└── js
    ├── jquery-3.4.1.min.js
    └── script.js

这么多文件都要一个个手动往 OSS 上怼也是不现实的,OSS 有提供各种环境的 SDK,这里这里咱们直接上 node sdk,安装依赖:

npm i ali-oss recursive-readdir -D

recursive-readdir 是个工具库用于获取目标文件夹下的所有文件路径。

ali-oss 的基本用法也很简单:

const OSS = require('ali-oss');

const client = new OSS({
    region: 'oss-cn-hangzhou', // 创建 bucket 选择的区域
    bucket: 'erii-blog', // 创建 bucket 名称
    accessKeyId: '***', // 你的 accessKeyId
    accessKeySecret: '***', // 你的 accessKeySecret
});

// 将 public/index.html 上传到 erii-blog bucket 根目录下的 hexo-index.html
client.put('hexo-index.html', './public/index.html').then(res => {
    console.log(res.url);
    // http://erii-blog.oss-cn-hangzhou.aliyuncs.com/hexo-index.html
});

accessKeyIdaccessKeyId 可以在这获取:

上传完成可以在 OSS 控制台看到 hexo-index.html

接下只要写个脚本将 publish 目录下的所有文件都按照对应的路径上传到 OSS 就行了,实现也比较简单这里直接贴代码了:

// upload.js
const OSS = require('ali-oss');
const recursive = require('recursive-readdir');
const ORIGIN = 'http://blog.ierii.com';
const PUBLISH_PATH = './public';

const client = new OSS({
    region: 'oss-cn-hangzhou', // 创建 bucket 选择的区域
    bucket: 'erii-blog', // bucket name
    accessKeyId: '***', // 你的 accessKeyId
    accessKeySecret: '***', // 你的 accessKeySecret
});

function getFiles() {
    return new Promise((resolve, reject) => {
        recursive(PUBLISH_PATH, (err, files) => {
            if (!err) {
                resolve(files);
            } else {
                reject(err);
            }
        });
    });
}

function upload(file) {
    return client.put(file.replace('public/', ''), `./${file}`).then(res => {
        const url = `${ORIGIN}/${res.name}`;
        console.log(`SYNC SUCCESS: ${url}`);
        return url;
    });
}


(async function main() {
    const files = await getFiles();
    const uploadTasks = files.map(file => upload(file));
    await Promise.all(uploadTasks);
})();

再打开 blog.ierii.com 发现 hexo 生产的静态文件都已经同步到 OSS 了,接下我们只需要考虑如何将这些操作通过 CI 实现自动化部署。

通过 GitHub Actions 实现自动部署

总之先把之前创建的 Hexo 项目先推送到 Github 上去吧。

关于 GitHub Actions 的教程建议可以看下官方文档和阮一峰的老师文章 GitHub Actions 入门教程

与 GitLab CI、Travis CI 不同的是 GitHub Actions 功能是建立在一个个 action 之上的,官方已经提供需要方便好用的 action,针对环境的部署与配置会比 GitLab CI 和 Travis CI 方便很多。下面直接上手好理解点。

在当前目录底下新建一个 .github/workflows/main.yml 的配置文件:

touch .github/workflows/main.yml

这是 GitHub Actions 默认读取的配置文件目录,main.yml 配置如下:

name: hexo-oss-action

on: [push] # 当有新 push 时触发

jobs:
  build: # 一项叫做 build 的任务

    runs-on: ubuntu-latest # 在最新版的 Ubuntu 系统下运行
    
    steps:
    - name: Checkout # 将仓库内 master 分支的内容下载到工作目录
      uses: actions/checkout@v2 # 脚本来自 https://github.com/actions/checkout
      
    - name: run node # 配置 Node 环境
      uses: actions/setup-node@v1 # 配置脚本来自 https://github.com/actions/setup-node
      with:
        node-version: '12'
    - run: npm install
    - run: npm run build
    - run: node sync.js
      env:
        OSS_REGION: ${{ secrets.OSS_REGION }}
        OSS_BUCKET: ${{ secrets.OSS_BUCKET }}
        OSS_ACCESS_KEY_ID: ${{ secrets.OSS_ACCESS_KEY_ID }}
        OSS_ACCESS_KEY_SECRET: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
        ORIGIN: ${{ secrets.ORIGIN }}

配置文件非常好理解,push 触发 CI 任务,runs-on: ubuntu-latest 指定系统环境,steps 的步骤清楚的描述了构建流程:

  • 使用 actions/checkout@v2 将仓库内容下载到工作目录
  • 使用 actions/setup-node@v1 配置 node 环境
  • npm install 安装依赖
  • npm run build hexo build
  • node sync.js 同步 publish 目录下的静态内容到 OSS

这里需要关注下 env 中的环境变量配置,日常开发中有许多私密的配置是不能直接在代码中硬编码的,像上是面的 OSS 的 accessKeyIdaccessKeySecret 一类的配置直接暴露出来是很危险的。我们需要一个更加安全方法将配置信息传递给 CI 环境,GitHub Actions 中我们可以直接在 CI 中通过 env 项获取当前项目的 secrets 配置,secrets 直接在项目 Github Setting 中配置,十分方便:

然后看下关键的 sync.js:

// sync.js
const OSS = require('ali-oss');
const recursive = require('recursive-readdir');
const PUBLISH_PATH = './public';

// 从 Github Secrets 中获取配置
const {
    OSS_REGION,
    OSS_BUCKET,
    OSS_ACCESS_KEY_ID,
    OSS_ACCESS_KEY_SECRET,
    ORIGIN,
} = process.env;

const client = new OSS({
    region: OSS_REGION,
    bucket: OSS_BUCKET,
    accessKeyId: OSS_ACCESS_KEY_ID,
    accessKeySecret: OSS_ACCESS_KEY_SECRET,
});

function getFiles() {
    return new Promise((resolve, reject) => {
        recursive(PUBLISH_PATH, (err, files) => {
            if (!err) {
                resolve(files);
            } else {
                reject(err);
            }
        });
    });
}

function upload(file) {
    return client.put(file.replace('public/', ''), `./${file}`)
        .then(res => {
            const url = `${ORIGIN}/${res.name}`
            console.log(`SYNC SUCCESS: ${url}`);
            return url;
        });
}

(async function main() {
    const files = await getFiles();
    await Promise.all(files.map(file => upload(file)));
    console.log('SYNC DONE !');
})();

和上面的 upload 没啥差别只是将一些配置挪到了 Github Secrets 中,这里需要强调下 CI 运行的目录就是当前项目的根目录sync.js 不需要做啥目录切换的操作,现在我们需要将本地修改推送到 Github 就可以看到 Github Actions 的构建了。

至此一个简单的 Hexo 博客发布系统就完成了~

当然 Github Actions 还可以实现更多强大的功能,有兴趣的小伙伴可以深入研究下,这里只是抛个小小实践思路。

其他

OSS 好像不能白嫖,需要花个 4 块还是 7 块大洋开通下,自己玩玩还是很便宜的~

OSS 配置完自定义域名后如果未配置证书默认走的 http 协议,有需要的同学可以通过配置证书托管为自定义域名加上 https 小绿标:

我的域名证书是直接通过腾讯云的免费证书生成的,有兴趣的同学也可以捣鼓下 Let's Encrypt

演示的仓库地址在这: github.com/kinglisky/h…

最后贴个喜欢的漫画:

嘻嘻~