惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Apple Machine Learning Research
Apple Machine Learning Research
J
Java Code Geeks
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Last Week in AI
Last Week in AI
雷峰网
雷峰网
博客园_首页
小众软件
小众软件
美团技术团队
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
腾讯CDC
P
Proofpoint News Feed
MongoDB | Blog
MongoDB | Blog
Google DeepMind News
Google DeepMind News
MyScale Blog
MyScale Blog
U
Unit 42
The Cloudflare Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Microsoft Security Blog
Microsoft Security Blog
大猫的无限游戏
大猫的无限游戏
Engineering at Meta
Engineering at Meta
N
Netflix TechBlog - Medium
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - 叶小钗

半方池水半方田

小球称重问题及其引申的思考 蓝色的结构色 病毒是不是生物? 省外居民身份证的补换领(苏州) hexo-filter-titlebased-link:构建你的数字花园 Vercel 应用实践学习 通过与 Keycloak 配合实现博客文章的受限访问功能 非公开的文章 Soft Mode 非公开的文章 Strict Mode 试试用代码块在 Hexo 中插入实况照片 动态图片测试 Hexo-Butterfly 主题 Preloader 加载页定制 Keycloak 的部署以及 Hexo-Butterfly 网页应用的接入 随机访问文章的实现(Sitemap+本地缓存优化) Authentik 的部署记录 Waline 自建 Auth 认证 记一次行李托运理赔流程(南航) Docker 镜像的制作、拉取与运行 Hexo-Butterfly 主题中对 Algolia 搜索框 Power By 的定制 Hexo 动态加载配置(钩子函数) 设计原则 VSCode 中的配置 Spring AI 中使用 PGVector 实现向量存储 提示词工程速查手册 2025 WePlay 上海两日游 和随机数生成相关的题目 一起撸串(字符串) 树状数组上手了就十分简单 缓存置换算法的实现与 Java 中相关的数据结构 关于海量数据的若干问题
把博客发布交给 GitHub Actions
wuanqin · 2026-05-11 · via 半方池水半方田

站内文章3 年前,我将博客框架从 WordPress 切换到了 Hexo,并通过服务器进行托管,其机制如下图:

preview

这期间,我有尝试过将目前的发布方案交给 GitHub Action 去处理。但是当时我的博客发布的工作流特别复杂,这检查那检查,这生成那生成…通过对博客 站内文章关系图谱 方案改造后,我的博客工作流就变得简洁了很多。所以今天我想尝试能不能抽出一点时间一劳永逸解决这个问题。(结果今天的单休就泡汤了)

本文是对服务器托管静态文件的情况下,对本地发布步骤的改造。当然,如果你的公共目录就放在 GitHub,也许会简单多。不过那是另外一套架构的做法,估计也大差不差,这里就不再讨论。

GitHub Actions 为博客的发布流程增加了另一种可能:

sequenceDiagram
    autonumber
    participant Local as 本地计算机 (Git/PC)
    participant GH_Repo as GitHub 仓库 (Source)
    participant GHA as GitHub Actions (CI/CD)
    participant Server as 云服务器 (Nginx)
    
    Local->>GH_Repo: git push
    GH_Repo->>GHA: 触发 Workflow
    
    rect rgba(240, 248, 255, 0.5)
        Note over GHA, Server: 「泥最耗时」的部分
        GHA->>GHA: 环境准备 & 依赖安装
        GHA->>GHA: 执行发布前检查
        GHA->>GHA: hexo generate
        GHA->>Server: 传输 Public 文件
    end

    Server->>Server: Git Hooks
    Server->>Server: Nginx 静态托管
    
    actor Visitor as 访客
    Visitor->>Server: 访问域名

GitHub 上有博客项目和主题的私有仓库

在开始之前,我们确保我们的博客项目的源代码已交给 GitHub 托管,后续操作将基于这个仓库进行 GitHub Actions 的构建:当仓库 main 分支变动时,执行 GitHub Action,将 public 目录发送到服务器。如果我们的博客配置中包含有敏感密钥,或者我们不想将文章 Markdown 内容直接公开,那么建议仓库保持私有仓库,除非我们已经处理好这些问题。

我们的主题也应该是交给 GitHub 管理的(主题开发者的仓库,或自己的私仓)。主题和博客项目的关系应当形成一种 Git Submodule 的关系。在博客项目中,首先删掉 theme 文件夹下我们使用的主题(注意备份,确保已经保存到远仓)。然后通过以下命令重新导入到博客项目中:

1
2
# 把主题仓库作为子模块添加 
git submodule add https://github.com/xxx/hexo-theme-xxx themes/xxx

根目录生成的 .gitmodules 记得妥善管理。子模块文件夹本身就是独立仓库,Git 会自动忽略。

SSH 的公钥和私钥

注意到,我们之前部署的主要流程是将本地电脑生成好的 public 文件夹发送的服务器中的仓库,中间是通过 SSH 链接的。现在我们想要 GitHub Actions 做这件事,那么我们必须生成另一对 SSH 公钥和私钥:

  • 公钥放在服务器的 /home/git/.ssh/authorized_keys 中;
  • 私钥放在 Actions secrets and variables 中。
1
2

ssh-keygen -t rsa -b 4096 -f deploy_key

image.png

其他环境变量的配置

如果你在本地发布工作流中使用到了一些脚本,这些脚本依赖了一些本地计算机的环境变量,如 JavaScript 的类似这样的代码 process.env.TENCENT_API_SECRETID,那么也放进 Secrets 中。

GitHub 的 PAT 我也放进了 Secrets,这是为了保证子模块拉取的顺利进行。

另外,我们还需要补充 Git 的一些基础参数,这部分可以放在 VariablesVariablesSecrets 区别在于变量的值是否明文。

image.png

新增 Workflows

在博客根目录下新增相应文件夹和工作流文件:

image.png

内容参考如下:

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
name: Hexo Deploy  

on:
push:
branches:
- main
workflow_dispatch:

jobs:
build-and-deploy:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
token: ${{ secrets.MY_GITHUB_TOKEN }}
submodules: true
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Cache node modules
id: cache-node-modules
uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-

- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: npm install

- name: Setup SSH Key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
# 扫描服务器指纹,防止部署时被拦截
ssh-keyscan -t rsa 106.15.38.218 >> ~/.ssh/known_hosts

- name: Publish
env:

TENCENT_API_SECRETID: ${{ secrets.TENCENT_API_SECRETID }}
TENCENT_API_SECRETKEY: ${{ secrets.TENCENT_API_SECRETKEY }}


GIT_NAME: ${{ vars.GIT_USER_NAME }}
GIT_EMAIL: ${{ vars.GIT_USER_EMAIL }}
run: |
git config --global user.name "$GIT_NAME"
git config --global user.email "$GIT_EMAIL"
npm run publish

重新审视之前的工作流

基本步骤已经弄得差不多了,现在开始检查。检查 package.json 中,npm run publishpostpublishprepublish 工作流是否都正确。检查无误后,我们就可以进行 push 测试了。

push 的过程中,观察 Actions 中的日志,逐步排查问题。不断试错,不断解决即可。

问题解决

采用 GitHub Actions 方法发布时文章的更新时间错误

如果使用 GitHub Actions 进行发布,所有文章的更新时间可能会全部设定为当前时间。为了解决这个问题,我们需要为我们的文章提供正确的更新时间。

直接想到的办法是为所有文章的 Front-Matter 增加 updated 字段,但是这么做未免太麻烦。我们可以使用插件/脚本解决。

首先,关掉 Hexo 默认的「更新时间」的逻辑。在 _config.yaml 中,修改以下设置:

1
updated_option: "empty"

然后,在项目根目录的 scripts 文件夹下新增以下脚本:

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
const { exec } = require('child_process');  
const { promisify } = require('util');
const path = require('path');

const execAsync = promisify(exec);

hexo.extend.filter.register('before_post_render', async function (post) {


if (post.updated) {
return post;
}

try {

const fullPath = path.resolve(hexo.source_dir, post.source);



const { stdout } = await execAsync(
`git log -1 --format=%at -- "${fullPath}"`
);

const timestamp = stdout.trim();

if (timestamp) {


const gitDate = new Date(parseInt(timestamp, 10) * 1000);

if (!isNaN(gitDate.getTime())) {
post.updated = gitDate;
}
}
} catch (err) {

hexo.log.debug(`[GitTime] Could not get git log for ${post.source}. Reason: ${err.message}`);
}

return post;
});

这个脚本借助 Hexo 过滤器,在文章渲染前自动处理时间:

  • 如果文章已手动设置 updated 时间,则直接跳过处理;
  • 通过 Git 命令获取文件最后一次提交的时间,自动赋值给文章 updated 字段。

使用 Obsidian 管理博客的同学还可以使用 Linter,它能顺手管理文章的 Front-Matter 字段。

处理 ECS 的报警

由于在这个过程中 GitHub Action 会登录我们服务器的 git 用户进行文件提交,所以阿里云安全中心会报告异地登录警告。如果觉得烦的话可以前往安全中心集中添加白名单。

本文参考