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

推荐订阅源

博客园 - Franky
N
Netflix TechBlog - Medium
Google Online Security Blog
Google Online Security Blog
月光博客
月光博客
量子位
酷 壳 – CoolShell
酷 壳 – CoolShell
V
V2EX
腾讯CDC
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 聂微东
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
M
MIT News - Artificial intelligence
Vercel News
Vercel News
The GitHub Blog
The GitHub Blog
Hugging Face - Blog
Hugging Face - Blog
博客园 - 【当耐特】
Apple Machine Learning Research
Apple Machine Learning Research
aimingoo的专栏
aimingoo的专栏
博客园 - 三生石上(FineUI控件)
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
MongoDB | Blog
MongoDB | Blog
H
Help Net Security
The Cloudflare Blog
Blog — PlanetScale
Blog — PlanetScale
F
Full Disclosure
G
Google Developers Blog
罗磊的独立博客
Jina AI
Jina AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Y
Y Combinator Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
J
Java Code Geeks
A
About on SuperTechFans
IT之家
IT之家
大猫的无限游戏
大猫的无限游戏
S
SegmentFault 最新的问题
有赞技术团队
有赞技术团队
GbyAI
GbyAI
雷峰网
雷峰网
T
The Blog of Author Tim Ferriss
The Register - Security
The Register - Security
U
Unit 42
D
Docker
Martin Fowler
Martin Fowler
L
LINUX DO - 热门话题
NISL@THU
NISL@THU
阮一峰的网络日志
阮一峰的网络日志
C
Cybersecurity and Infrastructure Security Agency CISA
博客园_首页
Google DeepMind News
Google DeepMind News

半方池水半方田

小球称重问题及其引申的思考 蓝色的结构色 病毒是不是生物? 省外居民身份证的补换领(苏州) 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 用户进行文件提交,所以阿里云安全中心会报告异地登录警告。如果觉得烦的话可以前往安全中心集中添加白名单。

本文参考