这是一篇为您整理的完整技术博客文章,涵盖了从 Deploy from Branch 迁移到 GitHub Actions 过程中遇到的所有关键问题及解决方案。
前言
背景:本站 https://blog.dazhongma.top/ 原使用 GitHub Pages 的 “Deploy from branch” 模式部署。随着站点复杂度增加,该模式在构建环境隔离、Secrets 管理和多平台同步上逐渐捉襟见肘。本文记录了将 Hugo + LoveIt 主题项目完整迁移至 GitHub Actions 过程中踩过的 7 个坑及对应解法,希望帮你少走弯路。
一、为什么从 Deploy from Branch 切换到 GitHub Actions?
| 对比维度 | Deploy from Branch | GitHub Actions |
|---|
| 构建环境 | GitHub 托管的 Jekyll/静态渲染器 | 自定义 Ubuntu 容器,可装任意工具 |
| Secrets 管理 | ❌ 不支持,敏感信息易泄露 | ✅ 原生 Secrets + Variables 支持 |
| 多仓库同步 | ❌ 仅支持单仓库 | ✅ 可同时推送 GitHub + Gitee |
| Hugo 版本控制 | ⚠️ 受限于平台内置版本 | ✅ 精确指定版本(如 0.126.0) |
| 构建日志 | ❌ 几乎不可见 | ✅ 完整日志,便于排查 |
二、7 个核心问题与解决方案
1. 双仓库(GitHub + Gitee)推送管理
很多博主同时维护 GitHub(主站)和 Gitee(国内镜像)。通过命名 remote 来区分:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # 查看当前所有远程仓库
git remote -v
# 添加/修改远程仓库
git remote add github git@github.com:username/repo.git
git remote add gitee git@gitee.com:username/repo.git
# 设置默认推送(push 不带参数时的目标)
git remote set-url origin git@github.com:username/repo.git
# 分别推送
git push github main
git push gitee main
# 一键推送到两个仓库(可选)
git remote add all git@github.com:username/repo.git
git remote set-url --add --push all git@gitee.com:username/repo.git
git push all main
|
💡 建议:GitHub Actions 的 workflow 中也可以加一个 step 同步到 Gitee,实现 push 一次、双端自动更新。
2. Git Push 连接拒绝:代理配置
国内直连 GitHub 经常超时或连接被拒。如果你本地有代理工具,可以为 GitHub 单独配置 Git 代理:
1
2
3
4
5
6
7
8
9
10
11
| # HTTP 代理
git config http.https://github.com.proxy http://127.0.0.1:你的端口
# SOCKS5 代理(推荐,兼容性更好)
git config http.https://github.com.proxy socks5://127.0.0.1:你的端口
# 验证配置
git config --get http.https://github.com.proxy
# 强制推送测试
git push github main --force
|
💡 注意:此配置仅对 https://github.com 生效,不影响其他 Git 仓库。取消代理用 git config --unset http.https://github.com.proxy。
3. 私有仓库 + GitHub Actions = 💰 $48/年
这是一个容易被忽视的计费陷阱:
- Public 仓库:GitHub Actions 完全免费,无限分钟数
- Private 仓库:免费账户每月仅 2,000 分钟;超出或需要更多并发需升级 Pro($4/月)或 Team($4/月/user)
- 2026 年现状:私有仓库持续 CI 构建,年成本至少 $48
✅ 解决方案:将仓库设为 Public。博客源码本身不含敏感信息(Secrets 通过 CI 注入),公开仓库是社区主流做法。如果确实需要私有,考虑使用免费的 CI 替代方案(如 Cloudflare Pages、Vercel)。
4. LoveIt 主题 Submodule 离线 vs 在线:deploy.yml 配置差异
这是迁移中最常见的构建失败原因。报错如下:
1
2
| fatal: No url found for submodule path 'themes/LoveIt' in .gitmodules
Error: The process '/usr/bin/git' failed with exit code 128
|
根因:LoveIt 主题的引入方式决定了 checkout 步骤的配置。
方式 A:在线 Submodule(推荐)
.gitmodules 中有 URL 记录:
1
2
3
| [submodule "themes/LoveIt"]
path = themes/LoveIt
url = https://github.com/dillonzq/LoveIt.git
|
deploy.yml 中 必须 启用 submodules:
1
2
3
4
| - uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: recursive # ← 关键!
|
方式 B:离线拷贝(无 .gitmodules)
直接将 LoveIt 文件夹复制到 themes/ 下,没有 submodule 关系。
deploy.yml 中 不要 启用 submodules:
1
2
3
4
| - uses: actions/checkout@v4
with:
fetch-depth: 0
# 不写 submodules 字段
|
⚠️ 检查方法:在项目根目录运行 cat .gitmodules,有内容就是方式 A,文件不存在或为空就是方式 B。两者配置不能混用。
5. themes/LoveIt 变成 Gitlink(160000 模式)无法打开
在 GitHub 网页上看到 themes/LoveIt 显示为一个灰色图标、无法点击进入,这就是 gitlink 问题——Git 将其记录为 submodule 指针而非实际文件。
诊断:
1
2
| git ls-files --stage themes/LoveIt
# 输出: 160000 xxxx 0 themes/LoveIt ← 160000 = gitlink
|
修复步骤:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # 1. 移除 gitlink 记录(不删除本地文件)
git rm --cached themes/LoveIt
# 2. 确认已清除
git ls-files --stage themes/LoveIt # 应无输出
# 3. 作为普通目录重新添加
git add themes/LoveIt/
# 4. 验证(应为大量 100644 文件)
git status | wc -l # 应显示数百/数千个新增文件
# 5. 提交
git commit -m "fix: convert LoveIt gitlink to regular directory"
|
💡 选择建议:如果你不需要跟随上游更新主题,修复后保持普通目录即可,deploy.yml 也无需配置 submodules,反而更简单稳定。如果需要跟进上游更新,则应正确初始化为 submodule(git submodule add <url> themes/LoveIt)。
6. baseURL 未指定导致页面乱码 / 资源加载失败
Hugo 构建时如果缺少 --baseURL,生成的 HTML 中所有链接都是相对路径,部署到自定义域名后 CSS/JS/图片全部 404。
✅ 解决方案:使用 Repository Variables 管理 baseURL
仓库 Settings → Secrets and variables → Actions → Variables 标签页
新建变量:
| Name | Value |
|---|
SITE_BASEURL | https://blog.dazhongma.top/ |
deploy.yml 中引用:
1
2
| - name: Build
run: hugo --gc --minify --baseURL "${{ vars.SITE_BASEURL }}"
|
💡 为什么用 Variables 而不是 Secrets? baseURL 是非敏感配置,Variables 可在 API 中读取、不会脱敏,更适合此类场景。同时可在本地开发时用不同值覆盖。
7. 公开仓库下的 Secrets 安全注入
仓库设为 Public 后,绝不能将 API Key 硬编码在 config.toml 中。正确做法是在 CI 中动态注入。
Step 1:添加 Secrets
仓库 Settings → Secrets and variables → Actions → Secrets 标签页:
| Secret Name | 用途 |
|---|
GITALK_CLIENT_ID | Gitalk OAuth Client ID |
GITALK_CLIENT_SECRET | Gitalk OAuth Client Secret |
VALINE_APP_ID | Valine LeanCloud App ID |
VALINE_APP_KEY | Valine LeanCloud App Key |
Step 2:config.toml 中使用占位符
1
2
3
4
5
6
7
| [params.page.comment.gitalk]
clientId = "PLACEHOLDER_GITALK_CLIENT_ID"
clientSecret = "PLACEHOLDER_GITALK_CLIENT_SECRET"
[params.page.comment.valine]
appId = "PLACEHOLDER_VALINE_APP_ID"
appKey = "PLACEHOLDER_VALINE_APP_KEY"
|
Step 3:deploy.yml 中 sed 注入(必须在 Checkout 之后)
1
2
3
4
5
6
7
8
9
10
11
| - name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Inject secrets into config
run: |
sed -i "s|PLACEHOLDER_GITALK_CLIENT_ID|${{ secrets.GITALK_CLIENT_ID }}|g" config.toml
sed -i "s|PLACEHOLDER_GITALK_CLIENT_SECRET|${{ secrets.GITALK_CLIENT_SECRET }}|g" config.toml
sed -i "s|PLACEHOLDER_VALINE_APP_ID|${{ secrets.VALINE_APP_ID }}|g" config.toml
sed -i "s|PLACEHOLDER_VALINE_APP_KEY|${{ secrets.VALINE_APP_KEY }}|g" config.toml
|
🔒 安全保障:${{ secrets.XXX }} 在服务端展开,即使有人 fork 你的仓库也无法获取你的 Secrets。GitHub 还会自动对日志中的 Secret 值脱敏为 ***。
三、完整的 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
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: Deploy Hugo to GitHub Pages
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
env:
HUGO_VERSION: "0.126.0"
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: recursive # ← 根据第4点实际情况决定是否保留
- name: Inject secrets into config
run: |
sed -i "s|PLACEHOLDER_GITALK_CLIENT_ID|${{ secrets.GITALK_CLIENT_ID }}|g" config.toml
sed -i "s|PLACEHOLDER_GITALK_CLIENT_SECRET|${{ secrets.GITALK_CLIENT_SECRET }}|g" config.toml
sed -i "s|PLACEHOLDER_VALINE_APP_ID|${{ secrets.VALINE_APP_ID }}|g" config.toml
sed -i "s|PLACEHOLDER_VALINE_APP_KEY|${{ secrets.VALINE_APP_KEY }}|g" config.toml
- name: Setup Hugo
uses: peaceiris/actions-hugo@v3
with:
hugo-version: ${{ env.HUGO_VERSION }}
extended: true
- name: Build
run: hugo --gc --minify --baseURL "${{ vars.SITE_BASEURL }}"
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./public
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
|
四、补充建议
- 本地预览与 CI 一致性:确保本地
hugo version 与 HUGO_VERSION 环境变量一致,避免 “本地正常、CI 报错” 的经典问题 - VSCode GitHub Actions 扩展:安装后主要用于 YAML Schema 校验和自动补全,Secrets 管理仍建议在网页端操作
- 首次迁移验证:Inject 步骤后可临时加
grep -c "PLACEHOLDER" config.toml 断言占位符已全部替换,确认无误后删除 - 缓存优化:后续可加入
actions/cache 缓存 Hugo modules 和 npm 依赖,将构建时间从 2-3 分钟缩短至 30 秒内
本文记录的踩坑经验基于 2026 年 8 月的 GitHub 平台策略,如有变更请以官方文档为准。欢迎在评论区交流你的 Hugo 部署经验!