Skip to content

Commit dbbd3e4

Browse files
lujun9972claude
andcommitted
blog: 用GitHub Actions自动构建EGO博客
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 369bc74 commit dbbd3e4

1 file changed

Lines changed: 210 additions & 0 deletions

File tree

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
#+TITLE: 用GitHub Actions自动构建EGO博客
2+
#+AUTHOR: lujun9972
3+
#+CATEGORY: Emacs之怒
4+
#+DATE: [2026-04-11 周五]
5+
#+OPTIONS: ^:{}
6+
7+
* 背景
8+
9+
我的博客 [[https://lujun9972.github.io/][暗无天日]] 使用 [[https://github.com/lujun9972/EGO][EGO]](Emacs + Git + Org-mode)作为静态站点生成器。源文件是 ~.org~ 格式,存放在 ~source~ 分支;生成的 HTML 存放在 ~master~ 分支,通过 GitHub Pages 托管。
10+
11+
一直以来,我都是在本机执行 ~auto_publish.el~ 来发布博客。但既然用了 GitHub,为什么不把构建过程也自动化呢?于是我开始折腾用 GitHub Actions 来自动构建博客。
12+
13+
* 最终效果
14+
15+
每次 ~push~ 到 ~source~ 分支时,GitHub Actions 会自动:
16+
1. 用 EGO 将 ~.org~ 文件转换为 HTML
17+
2. 将生成的 HTML 推送到 ~master~ 分支
18+
3. GitHub Pages 自动部署
19+
20+
顺带还会在 Habitica 上完成"写博客"任务打个卡(游戏化激励)。
21+
22+
* 实现过程
23+
24+
** 第一步:改造 auto_publish.el
25+
26+
原来 ~auto_publish.el~ 里的路径都是硬编码的:
27+
28+
#+begin_src emacs-lisp
29+
(setq load-path (cons "~/EGO/" load-path))
30+
(setq load-path (cons "~/csdn-publish/" load-path))
31+
(setq load-path (cons "~/toutiao/" load-path))
32+
#+end_src
33+
34+
这在本地没问题,但在 CI 环境里路径完全不同。所以我把所有硬编码路径改为从环境变量读取,保留默认值确保本地兼容:
35+
36+
#+begin_src emacs-lisp
37+
(setq load-path (cons (or (getenv "EGO_DIR") "~/EGO/") load-path))
38+
(setq load-path (cons (or (getenv "CSDN_DIR") "~/csdn-publish/") load-path))
39+
(setq load-path (cons (or (getenv "TOUTIAO_DIR") "~/toutiao/") load-path))
40+
#+end_src
41+
42+
~ego-project-config-alist~ 中的 ~:repository-directory~ 和 ~:store-dir~ 也做了类似处理:
43+
44+
#+begin_src emacs-lisp
45+
`(("blog" :repository-directory ,(or (getenv "REPO_DIR") "~/source")
46+
...
47+
:store-dir ,(or (getenv "STORE_DIR") "~/web")))
48+
#+end_src
49+
50+
同时删除了已废弃的 CSDN 和头条发布代码(对应的包在本机也不存在了),精简了脚本。
51+
52+
** 第二步:编写 GitHub Actions Workflow
53+
54+
最终的 workflow 文件 ~.github/workflows/github-action.yml~:
55+
56+
#+begin_src yaml
57+
name: 提交 habitica 任务 + 构建博客
58+
on:
59+
push:
60+
branches: [source]
61+
62+
permissions:
63+
contents: write
64+
65+
jobs:
66+
Finish-Habitica-Tasks:
67+
runs-on: ubuntu-latest
68+
steps:
69+
- name: Check out habash code
70+
uses: actions/checkout@v4
71+
with:
72+
fetch-depth: 0
73+
repository: nasfarley88/habash
74+
- name: 确保 habash 可执行
75+
run: chmod +x ./habash
76+
- name: 提交 habitica 任务
77+
run: |
78+
set -x
79+
./habash up "写博客"
80+
env:
81+
HABITICA_TOKEN: ${{ secrets.HABITICA_TOKEN }}
82+
HABITICA_UUID: ${{ secrets.HABITICA_UUID }}
83+
84+
Build-Blog:
85+
runs-on: ubuntu-latest
86+
env:
87+
EGO_DIR: ${{ github.workspace }}/EGO
88+
REPO_DIR: ${{ github.workspace }}
89+
STORE_DIR: ${{ github.workspace }}/web
90+
REPO: https://github.com/lujun9972/${{ github.event.repository.name }}
91+
steps:
92+
- name: Checkout 博客源文件
93+
uses: actions/checkout@v4
94+
with:
95+
fetch-depth: 0
96+
ref: source
97+
98+
- name: Checkout EGO
99+
uses: actions/checkout@v4
100+
with:
101+
repository: lujun9972/EGO
102+
path: EGO
103+
104+
- name: Checkout master 分支到 store-dir
105+
run: |
106+
git clone --branch master --single-branch \
107+
https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git \
108+
"$STORE_DIR"
109+
cd "$STORE_DIR"
110+
git config user.name "github-actions[bot]"
111+
git config user.email "github-actions[bot]@users.noreply.github.com"
112+
113+
- name: 安装 Emacs 和系统依赖
114+
run: sudo apt-get update && sudo apt-get install -y emacs-nox
115+
116+
- name: 确保 repo 干净并配置 git 身份
117+
run: |
118+
git config user.name "github-actions[bot]"
119+
git config user.email "github-actions[bot]@users.noreply.github.com"
120+
git clean -fd
121+
git checkout -- .
122+
123+
- name: 执行 auto_publish.el 构建博客
124+
run: emacs --batch -l auto_publish.el
125+
126+
- name: 推送 HTML 到 master 分支
127+
run: |
128+
cd "$STORE_DIR"
129+
git config user.name "github-actions[bot]"
130+
git config user.email "github-actions[bot]@users.noreply.github.com"
131+
git add -A
132+
git diff --staged --quiet || git commit -m "auto publish: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
133+
git push origin master
134+
#+end_src
135+
136+
两个 Job 并行运行,互不影响。
137+
138+
** 第三步:踩过的坑
139+
140+
整个过程踩了不少坑,记录一下:
141+
142+
*** 坑1:EGO 缺少依赖
143+
144+
~auto_publish.el~ 在本地运行时,~ht~ 和 ~dash~ 两个包早就安装过了。但 CI 是全新环境,~package-install~ 列表里没有它们,导致 EGO 加载失败:
145+
146+
#+begin_quote
147+
Cannot open load file: No such file or directory, ht
148+
#+end_quote
149+
150+
解决:在 ~package-install~ 列表中加上 ~ht~ 和 ~dash~。
151+
152+
*** 坑2:本地包在 CI 中不存在
153+
154+
~toutiao~ 和 ~csdn-publish~ 是我本地的自定义包,不在任何包管理器上。CI 里根本没有:
155+
156+
#+begin_quote
157+
Cannot open load file: No such file or directory, toutiao
158+
#+end_quote
159+
160+
解决:用 ~(require 'toutiao nil t)~ 的 ~noerror~ 模式,找不到就静默跳过。后来发现本机上这两个包也早已不用了,干脆直接删除了相关代码。
161+
162+
*** 坑3:括号不匹配
163+
164+
删代码时一不小心把 ~let*~ 的关闭括号也删了,导致 Emacs 解析到文件末尾时报错:
165+
166+
#+begin_quote
167+
End of file during parsing: auto_publish.el
168+
#+end_quote
169+
170+
解决:补回括号,并用 ~emacs --batch --eval '(check-parens)'~ 验证。
171+
172+
*** 坑4:EGO 内部 git commit 缺少身份
173+
174+
EGO 构建完后会用 ~vc-git-commit~ 提交 store-dir 的变更,但 store-dir 里没配 git user:
175+
176+
#+begin_quote
177+
Failed (status 128): git --no-pager commit -m Update published html files...
178+
#+end_quote
179+
180+
解决:clone master 分支后立即配置 ~git config user.name/email~。
181+
182+
*** 坑5:EGO 的 stash/pop 在 CI 中失败
183+
184+
这是最难搞的一个。EGO 检测到 repo 有未提交变更时,会先 ~git stash~,构建完成后再 ~git stash pop~。但在 CI 中 stash pop 总是失败:
185+
186+
#+begin_quote
187+
Failed (status 1): git --no-pager stash pop -q 0 .
188+
#+end_quote
189+
190+
试了 ~git clean -fd~、~git checkout -- .~ 确保构建前 repo 干净,都没用——EGO 内部还是会触发 stash。
191+
192+
最终解决方案:用 ~ego-do-publication~ 的 ~checkin-all~ 参数(第四个参数),让 EGO 用 ~git commit~ 代替 ~git stash~,从根本上避免 stash/pop:
193+
194+
#+begin_src emacs-lisp
195+
;; 之前:(ego-do-publication "blog" nil nil nil)
196+
;; 之后:(ego-do-publication "blog" t nil "CI auto publish")
197+
#+end_src
198+
199+
第二个参数 ~t~ 表示全量构建,第四个参数非 nil 时 EGO 会 commit 而不是 stash。
200+
201+
* 总结
202+
203+
整个折腾过程的核心就是让 ~auto_publish.el~ 在 CI 环境和本地环境都能正常运行。关键改动:
204+
205+
1. 路径可配置化(环境变量 + 默认值)
206+
2. 删除废弃的第三方发布代码
207+
3. 补全缺失的依赖包
208+
4. 用 ~checkin-all~ 绕过 stash/pop 兼容性问题
209+
210+
虽然中间踩了不少坑,但最终效果还是很满意的——以后 push 博客源文件就能自动构建发布,再也不用手动跑 ~auto_publish.el~ 了。

0 commit comments

Comments
 (0)