Hexo 框架与内容仓库分离架构

1. 核心架构

  • 框架仓库 (hexo-main): 存放 Hexo 核心源码、主题及插件。
  • 内容仓库 (hexo-content): 仅存放 Markdown 文章及静态资源。

2. 内容仓库配置

2.1 自动化工作流 (.github/workflows/deploy.yml)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
name: Content Sync
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Content
uses: actions/checkout@v4
with:
token: ${{ secrets.PAT_TOKEN }} # 敏感数据已隐藏

- name: Process Images
run: |
# 自动转换图片为 webp
echo "Processing images..."

- name: Trigger Framework Repo
uses: peter-evans/repository-dispatch@v2
with:
token: ${{ secrets.PAT_TOKEN }}
repository: your-username/hexo-main
event-type: content_updated

2.2 格式检查脚本 (.github/scripts/format_checker.py)

该脚本会自动补全 Front-matter 并维护 updated 时间。主人,这是脚本源码:

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import os
import re
import sys
from datetime import datetime

def process_markdown(file_path):
if not os.path.exists(file_path):
return

with open(file_path, 'r', encoding='utf-8') as f:
original_content = f.read()

# 1. 匹配并提取 Front-matter
fm_match = re.match(r'^---\n(.*?)\n---', original_content, re.DOTALL)
if not fm_match:
return

fm_text = fm_match.group(1)
body_content = original_content[fm_match.end():]
now_str = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

# 2. 解析 Front-matter
lines = fm_text.split('\n')
fm_dict = {}
current_key = None
for line in lines:
clean_line = line.strip()
if not clean_line: continue
if ':' in line and not clean_line.startswith('-'):
parts = line.split(':', 1)
fm_dict[parts[0].strip()] = parts[1].strip()
current_key = parts[0].strip()
elif clean_line.startswith('-') and current_key:
if not isinstance(fm_dict[current_key], list):
fm_dict[current_key] = [fm_dict[current_key]] if fm_dict[current_key] else []
fm_dict[current_key].append(clean_line)

# 3. 补全与修正
if 'title' not in fm_dict: fm_dict['title'] = os.path.basename(file_path).replace('.md', '')
if 'date' not in fm_dict: fm_dict['date'] = now_str
if 'cover' not in fm_dict: fm_dict['cover'] = "/static/img/cover/1.webp"
if 'description' not in fm_dict: fm_dict['description'] = ""

# 图片后缀修正
new_body = re.sub(r'\.(png|jpg|jpeg)', '.webp', body_content, flags=re.IGNORECASE)

# 4. 脏检查与 Updated 更新
content_changed = (new_body != body_content)
if content_changed:
fm_dict['updated'] = now_str

# 5. 重组 Front-matter
order = ['title', 'date', 'updated', 'cover', 'description', 'tags', 'categories']
new_fm_lines = []
for k in order:
if k in fm_dict:
v = fm_dict[k]
if isinstance(v, list):
new_fm_lines.append(f"{k}:")
for item in v: new_fm_lines.append(f" {item}")
else:
new_fm_lines.append(f"{k}: {v}")

final_content = f"---\n{chr(10).join(new_fm_lines)}\n---{new_body}"
with open(file_path, 'w', encoding='utf-8') as f:
f.write(final_content)

if __name__ == "__main__":
if len(sys.argv) > 1:
for f in sys.argv[1:]:
if f.endswith('.md'): process_markdown(f)
else:
for root, dirs, files in os.walk('.'):
if any(x in root for x in ['.github', '.git', 'node_modules']): continue
for file in files:
if file.endswith('.md'): process_markdown(os.path.join(root, file))

3. 框架仓库配置

hexo-main 中创建监听 content_updated 事件的 Action,执行部署逻辑。


提示:请确保在 GitHub Secrets 中配置好 PAT_TOKEN