基于 Hugo + Git LFS + GitHub Actions 搭建稳定大容量博客架构

基于 Hugo + Git LFS + GitHub Actions 搭建稳定大容量博客架构

一、底层模板:Hugo Theme Stack 启动模板说明

当前整套博客基于官方脚手架仓库 hugo-theme-stack-starter 二次开发,该模板是 Stack 主题官方推荐快速启动模板,也是本架构的底层基础。本文所有改造、配置与自动化流程,均依托此模板实现。

1.1 模板仓库与核心特性

官方仓库地址:https://github.com/CaiJimmy/hugo-theme-stack-starter

模板原生能力:

  • 采用 Hugo Modules 加载主题,无需手动拷贝主题文件,版本管理规范
  • 预置完整站点目录与默认配置,开箱即用
  • 内置 GitHub Actions 工作流,自动构建并部署至 GitHub Pages
  • 配置定时任务,每日自动检测、更新主题版本
  • 同时支持本地运行、GitHub Codespaces 在线运行两种环境

1.2 前置运行依赖

本地部署模板需提前安装依赖(GitHub Codespaces 已预装,可跳过):

1
2
3
Git
Go
Hugo Extended 扩展版本

参考官方安装文档:https://gohugo.io/installation/

1.3 模板标准搭建流程

1.3.1 基于模板创建仓库
  1. 进入模板仓库主页,点击 Use this template 新建仓库
  2. 仓库命名规范

    • 个人博客主站:命名为 <用户名>.github.io,访问地址:https://<用户名>.github.io
    • 自定义仓库名:访问地址:https://<用户名>.github.io/仓库名/
1.3.2 配置 GitHub Pages 部署源
  1. 进入仓库 → Settings → Pages
  2. 找到 Build and deployment 模块,将 Source 设置为 GitHub Actions
1.3.3 站点本地预览
方式一:GitHub Codespaces 在线预览
  1. 仓库页面点击 Create codespace ,等待环境初始化
  2. 终端执行预览命令:
1
hugo server
方式二:本地克隆运行
1
2
3
git clone 你的公开仓库地址
cd 仓库目录
hugo server
1.3.4 站点基础配置

所有配置文件统一存放于 config/_default/ 目录,核心配置项: 编辑 config/_default/config.toml,修改 baseurl 为博客实际域名。

示例配置:

1
2
;; 仓库名为 用户名.github.io
baseurl = "https://xxx.github.io/"
1.3.4.1 配置文件格式说明

本主题及官方模板默认采用 TOML 作为配置文件格式,而非 YAML。 全站 config/ 目录下所有配置均使用 TOML 语法编写,请勿混用格式,避免编译报错。

1.3.4.2 Giscus 评论配置(私有仓库隔离方案)

Giscus 是一款基于 GitHub Discussions 的开源静态评论系统,非常适配 Hugo 静态博客。

Giscus 核心优势:

  • ✅ 基于 GitHub Discussions 原生实现,数据完全自留、安全可控
  • ✅ 支持表情反应、多级回复、点赞互动
  • ✅ 完全免费、无广告、开源轻量
  • ✅ 支持明暗双主题自动适配
  • ✅ 部署简单、零服务器、零运维

出于安全考虑,Giscus 评论相关配置(仓库 ID、分类 ID 等敏感信息)不存放在公开站点仓库,统一放置在 私有内容仓库 中。 部署流程中通过 GitHub Actions 脚本动态读取并合并配置,实现敏感信息隔离,彻底避免隐私配置泄露。

前置配置步骤(必须提前完成)

  1. 评论承载仓库必须为 GitHub 公开仓库
  2. 进入仓库 Settings,开启仓库的 Discussions 功能
  3. 安装 Giscus 官方应用到当前仓库:https://github.com/apps/giscus
  4. 访问 Giscus 官网生成配置:https://giscus.app
  5. 填入你的公开仓库信息,获取完整配置参数

1. 私有仓库目录结构giscus.toml 放置在私有仓库 content/post/ 目录下,与博文同级目录。

2. Actions 动态合并逻辑 构建阶段先清空原有评论配置,再将私有仓库内的 giscus.toml 追加至站点主配置文件,实现动态注入:

1
2
3
4
sed -i '/^\[comments\]/,/^$/d' config/_default/params.toml
if [ -f "content/post/giscus.toml" ]; then
  cat content/post/giscus.toml >> config/_default/params.toml
fi

方案优势:

  • 评论仓库 ID、分类 ID 等敏感配置仅留存私有仓库,零泄露风险
  • 公开仓库保持纯净,只保留站点通用配置
  • 依托现有双仓库架构,无需改造目录结构与权限体系
  • 更新评论配置仅需修改私有仓库,无需改动公开仓库代码

3. giscus.toml 内容格式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[comments]
  enabled = true
  provider = "giscus"
  [comments.giscus]
      repo = "ynhugo/ynhugo.github.io"
      repoID = "R_xxxxxxxxxx"
      category = "Announcements"
      categoryID = "DIC_xxxxxxxxxxxxxxx"
      mapping = "pathname"
      lightTheme = "light"
      darkTheme = "dark"
      reactionsEnabled = 1
      emitMetadata = 0
1.3.5 提交与自动发布

修改完成后执行常规 Git 推送,GitHub Actions 自动完成构建与部署:

1
2
3
git add .
git commit -m "更新站点内容/配置"
git push
1.3.6 一键自动化配置(分步执行版)

https://github.com/CaiJimmy/hugo-theme-stack-starter

https://github.com/ynhugo/ynhugo.github.io

git clone git@github.com:ynhugo/ynhugo.github.io.git ynhugo cd ynhugo

hugo mod get -u github.com/CaiJimmy/hugo-theme-stack/v4 hugo mod tidy

  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
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# 修改baseurl
sed -i 's|\(^baseurl = "\).*\("\)|\1https://ynhugo.github.io/\2|g' config/_default/config.toml
# 修改语言
sed -i 's|\(^locale[[:space:]]*= "\).*\("\)|\1zh-cn\2|g' config/_default/config.toml
# 修改标题
sed -i 's|\(^title[[:space:]]*= "\).*\("\)|\1ynhugo\2|g' config/_default/config.toml
# 修改默认博文语言
sed -i 's|\(^defaultContentLanguage[[:space:]]*= "\).*\("\)|\1zh-cn\2|g' config/_default/config.toml
# 修改每页显示博文数量
sed -i 's|\(^[[:space:]]*pagerSize[[:space:]]*= \).*|\120|g' config/_default/config.toml
# 修改子标题
sed -i 's|\(^[[:space:]]*subtitle = "\).*\("\)|\1欢迎来到我的博客\2|g' config/_default/params.toml

# 4. 创建中文页面(纯本地操作,批量复制执行)
# 添加中文归档
cat > content/page/archives/index.zh-cn.md << 'EOF'
---
title: "文章归档"
date: 2019-05-28
layout: "archives"
slug: "archives"
menu:
    main:
        weight: -70
        params:
            icon: archives
---
EOF

# 添加中文主页
cat > content/_index.zh-cn.md << 'EOF'
---
menu:
    main:
        name: "主页"
        weight: 1
        params:
            icon: home
---
EOF

# 添加中文搜索
cat > content/page/search/index.zh-cn.md << 'EOF'
---
title: "搜索"
slug: "search"
layout: "search"
outputs:
    - html
    - json
menu:
    main:
        weight: -60
        params:
            icon: search
---
EOF

# 添加中文友情链接
cat > content/page/links/index.zh-cn.md << 'EOF'
---
title: 友情链接
links:
  - title: naoemb
    website: https://naoemb.github.io
  - title: embnao
    website: https://embnao.github.io
  - title: hexgio
    website: https://hexgio.github.io
  - title: Gitee
    website: https://gitee.com/yenao
  - title: GitHub
    website: https://github.com/hexgio
    image: https://github.githubassets.com/assets/pinned-octocat-093da3e6fa40.svg
menu:
    main:
        weight: 4
        params:
            icon: link
comments: false
---
EOF

# 5. 生成短代码脚本(本地操作,批量复制执行)
mkdir -p layouts/shortcodes/
# 添加网易云短代码
cat - > layouts/shortcodes/netease.html <<'EOF'
<iframe
  frameborder="no"
  border="0"
  marginwidth="0"
  marginheight="0"
  width=330 height=86
  src="//music.163.com/outchain/player?type=2&id={{.Get 0}}&auto=0&height=66">
</iframe>
EOF
# 添加文字属性
cat - > layouts/shortcodes/align.html <<'EOF'
<p style="text-align:{{ index .Params 0 }}">{{ index .Params 1 | markdownify }}</p>
EOF
# 插入github仓库
cat - > layouts/shortcodes/github.html <<'EOF'
<div class="github">
    <div class="logo">
        {{ replace $.Site.Data.SVG.repository "icon" "icon github-icon" | safeHTML }}
        <a class="name" href={{ .Get "link" }} target="_blank">{{ .Get "name" }}</a>
    </div>
    <div class="description">{{ .Get "description" }}</div>
    <div class="language">
        <span class="language-color" style="background-color: {{ .Get "color" }}"></span>
        <span class="language-name">{{ .Get "language" }}</span>
    </div>
</div>
EOF
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
chmod +x autoconfig.sh

# 6. 注入 Actions 流程(本地操作,批量复制执行)
sed -i 's|\(^[[:space:]]*\)\(hugo \\\)|\1echo "添加短代码"\n\1bash autoconfig.sh\n\1\2|g' .github/workflows/deploy.yml

# 7. 提交推送(网络操作,网络异常可单独重试)
git add .
git commit -m "初始化博客配置"
git push
# 或使用你的封装脚本
# ~/git_push_pull.sh push

1.4 Stack 主题版本管理

模板默认使用 Stack 主题 v4 版本,通过 Hugo Modules 统一管理依赖。

1.4.1 手动更新主题

站点根目录执行命令,拉取最新主题并整理依赖:

1
2
hugo mod get -u github.com/CaiJimmy/hugo-theme-stack/v4
hugo mod tidy
1.4.2 版本说明

受 Go Modules 机制限制,若后续主题发布 v4 以上大版本,需要手动修改 config/module.toml 内的主题版本路径。

1.5 与现有架构兼容说明

  1. 目录与配置:公开仓库完全沿用模板原生目录结构、配置体系、Hugo Modules 规则,无需改动。
  2. 自动化任务:现有 update-theme.yml 主题定时更新工作流,与模板原生能力一致,可长期保留使用。
  3. 部署流程:自定义的 deploy.yml 融合了模板部署逻辑 + Hugo 编译缓存 + 私有仓库拉取 + Git LFS 资源加载,完全向下兼容。
  4. 写作规范:Stack 主题强制要求文章头部使用 YAML 数组格式标签,禁止纯空格分隔标签,避免渲染报错。

1.6 多平台部署参考

如需部署到 GitHub Pages 以外的静态托管平台,可参考 Hugo 官方文档: https://gohugo.io/host-and-deploy/

二、整体架构介绍

2.1 目录结构

采用「博文+配套资源同目录」结构,不拆分文件、不使用外部图床,贴合日常写作习惯,无需适配特殊路径。

1
2
3
4
5
6
7
8
blogs/
├── post/                ;; 所有博文目录
│   ├── 分类目录/
│   │   ├── 文章.org/.md
│   │   └── resources/   ;; 图片、PDF、压缩包、源码包等资源文件
├── auto-lfs-tracker.sh  ;; LFS 自动管理脚本
├── .gitattributes       ;; Git LFS 规则配置文件
└── .git/hooks/pre-push  ;; Git 前置钩子,自动触发扫描脚本

2.2 技术选型与设计思路

  • 核心载体:Git + GitHub 仓库统一存储博文与配套资源
  • 静态站点:使用 Hugo 作为静态博客生成器
  • 大文件方案:基于 Git LFS 托管图片、压缩包、PDF 等大容量资源,彻底避免 .git 目录无限膨胀
  • 自动化能力:Shell 脚本 + Git Hook 联动,推送代码前自动识别新资源文件类型,自动纳入 LFS 托管;搭配 GitHub Actions 实现全自动编译、发布
  • 部署方案:GitHub Actions 自动拉取私有仓库代码与 LFS 资源,借助 Hugo 编译后发布静态博客站点
方案优势
  1. 资源与博文同目录绑定,路径无需手动修改,完全贴合原生写作习惯
  2. 大文件统一 LFS 托管,仓库体积可控,长期更新不臃肿
  3. 全流程自动化配置,一次部署终身零维护
  4. 兼容私有仓库、公开部署、CI 编译、多设备克隆等全部场景

2.3 双仓库整体架构说明

采用**私有内容库 + 公开站点库**分离架构,兼顾原始数据安全与博客公开访问需求:

  • 私有仓库: blogs ,存储原始博文、资源文件、自动化脚本、配置文件,对外完全不可见,保障源码与资源安全
  • 公开仓库: ynhugo.github.io ,仅存储 Hugo 编译完成的静态网页文件,作为 GitHub Pages 对外访问入口

2.4 权限体系:细粒度 Token + 同名 Secrets

整套架构依靠 单个细粒度 Personal Access Token 实现双仓库跨仓权限互通,两个仓库统一使用同名 Secrets 存储密钥,配置极简、维护便捷。

2.4.1 创建细粒度访问令牌

令牌创建地址:https://github.com/settings/personal-access-tokens

  1. 点击 Generate new token,选择 Fine-grained token(细粒度令牌)
  2. 自定义令牌名称、有效期、用途备注,按需配置长期有效
  3. 仓库范围:精准勾选两个业务仓库

    • ynhugo/blogs (私有内容仓库)
    • ynhugo/ynhugo.github.io (公开部署仓库)
  4. 仓库最小权限配置

    • Metadata:Read(读取仓库基础信息,系统必选)
    • Code:Read and Write(支持代码拉取、推送、LFS 资源读写)
  5. 生成令牌后 立即复制备份 ,页面刷新后无法再次查看密钥。
2.4.2 仓库配置同名 Secrets

将同一 Token 密钥,分别配置到两个仓库的 Actions 密钥中, 密钥名称完全一致

  • 私有仓库 blogs:Settings → Secrets and variables → Actions 新建密钥

    • 名称: PRIVATE_BLOG_REPO_TOKEN
    • 值:粘贴统一 Token
  • 公开仓库 ynhugo.github.io:同路径新建同名密钥

    • 名称: PRIVATE_BLOG_REPO_TOKEN
    • 值:粘贴 完全相同的 Token
2.4.3 配置优势
  1. 单 Token 管控双仓库,密钥轮换、更新仅需操作一次,大幅降低维护成本
  2. 同名 Secrets 统一配置,GitHub Actions 调用语法一致,无需区分环境
  3. 细粒度权限精准限制仓库与操作范围,安全性远高于传统全局 Token
  4. 完美覆盖私有仓库推送、公开仓库跨仓克隆、LFS 资源拉取、静态文件推送等全部场景

2.5 全链路流转流程

完整链路:**本地编辑 → 钩子自动化检测 → 同步私有仓库 → Actions 拉取编译 → 发布至 gh-pages 分支 → 公网访问**

  1. 本地编辑阶段

    • 在本地 blogs 仓库新增、修改 .md/.org 博文,同步更新 resources 目录下的图片、PDF、压缩包等资源,保持原生目录结构。
  2. 本地提交与钩子触发

    • 执行常规 Git 提交推送命令:
1
2
3
git add .
git commit -m "更新博文/补充资源"
git push
  • 执行 git push 瞬间优先触发 pre-push 前置钩子
  • 自动调用 auto-lfs-tracker.sh --scan-only 脚本
  • 仅扫描新文件类型、更新 LFS 规则配置,**不执行任何提交操作**,不打断推送流程
  • 自动补全缺失的 LFS 追踪规则,确保新增资源全部纳入 LFS 托管
  1. 同步至私有 GitHub 仓库

    • 文本类文件(博文、脚本、配置)通过标准 Git 机制推送
    • 大体积资源文件,同步 LFS 指针文件与实体文件至 GitHub LFS 服务
    • 私有仓库完整留存原始博文、配置、LFS 资源,实现数据安全隔离
  2. 公开仓库 Actions 自动拉取

    • 公开仓库工作流自动触发,通过配置密钥鉴权执行操作:

      • 读取 PRIVATE_BLOG_REPO_TOKEN 密钥,鉴权克隆私有 blogs 仓库
      • 开启 LFS 自动下载,根据指针文件拉取完整实体资源,彻底规避页面资源 404 问题

*补充说明*:GitHub Actions 内置缓存**无法缓存 Git LFS 实体文件**,每次构建都会重新完整下载 LFS 资源,该现象为当前方案固有特性。

1
2
3
4
5
6
7
8
steps:
  - name: Clone private blogs repo
    uses: actions/checkout@v4
    with:
      repository: ynhugo/blogs
      token: ${{ secrets.PRIVATE_BLOG_REPO_TOKEN }}
      path: content
      lfs: true  # 开启 LFS 自动下载
  1. 编译与发布站点

    • 调用 Hugo 编译工具,基于原始博文与完整资源生成纯静态网页
    • 将编译完成的 public 静态资源,强制推送至公开仓库 gh-pages 分支
    • GitHub Pages 自动监听 gh-pages 分支,对外提供公网访问
  2. 最终访问链路

本地文件 → 私有仓库(Git代码+LFS资源)→ GitHub Actions + Hugo 编译 → 公开仓库 gh-pages 分支 → 终端用户访问

2.5.1 公开仓库 Actions 核心工作流
  1. 拉取私有仓库:通过统一密钥鉴权克隆私有内容仓库,自动下载全部 LFS 资源
  2. Hugo 编译:读取原始博文与资源,编译生成完整静态网站
  3. 分支部署:将静态文件推送至公开仓库 gh-pages 分支
  4. 站点上线:GitHub Pages 加载分支资源,自动更新公开博客站点

最终效果

  • 原始文章、私有资源仅存于私有仓库,安全不泄露
  • 静态页面统一托管公开仓库分支,公网正常访问
  • 全程自动化,无需人工干预,无部署冲突
2.5.2 Pre-Push 钩子时序与 .gitattributes 推送机制
执行时序(Git 官方固定规则)

git push 执行顺序:**优先运行 pre-push 钩子 → 钩子执行完毕 → 执行真正的远程推送** 所有 LFS 扫描、规则更新动作均在推送前完成,无规则遗漏、无执行时序错乱问题。

配置文件推送逻辑

脚本采用 --scan-only 纯扫描模式,核心特性: 仅本地修改 .gitattributes ,不自动提交

  1. 本次 git push:仅推送 *上一次已提交* 的代码和资源,不推送本次更新的 =.gitattributes
  2. 更新后的 .gitattributes 保留为 本地未提交修改
  3. 在下一次手动 git add && git commit 时统一提交
  4. 随下一次 git push 推送至远程私有仓库
机制优势
  • 不破坏原生 Git 推送流程,无冲突、无推送中断风险
  • 自动迭代 LFS 规则,无需手动维护配置
  • 规则滞后一次提交为安全兜底,不影响当前资源托管与站点部署

2.6 GitHub Actions 工作流(完整配置)

本架构采用「私有仓库触发 → 公开仓库构建」的自动化部署模式,共包含 3 个工作流文件:

  • 私有仓库:1 个触发部署工作流
  • 公开仓库:1 个构建部署工作流 + 1 个主题自动更新工作流
2.6.1 私有仓库工作流(触发部署)

路径: .github/workflows/trigger-deploy.yml 作用:私有仓库内容更新后,自动通知公开仓库重新构建博客

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
name: Trigger public repo deploy
on:
  push:
    branches:
      - main
      - master

jobs:
  trigger:
    runs-on: ubuntu-latest
    steps:
      - name: Trigger public site rebuild
        uses: peter-evans/repository-dispatch@v3
        with:
          token: ${{ secrets.PRIVATE_BLOG_REPO_TOKEN }}  # 这行必须加!
          repository: ynhugo/ynhugo.github.io
          event-type: blog-updated
2.6.2 公开仓库工作流(构建与部署)

作用:提供两套构建部署方案,可根据使用场景选择切换。

  • 传统 gh-pages 分支部署(兼容旧版)
  • GitHub 官方 Pages 官方部署(推荐,更稳定、规范)
  • 两种workflow的使用要根据部署方式配置公开仓库的pages的source为 Deploy from a branchGithub Actions
2.6.2.1 传统部署方式(gh-pages 分支)

适用场景:需要将编译产物推送到 gh-pages 分支的传统部署模式。 代码块仅展示,不自动导出文件 (:tangle no)

 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
name: Build and deploy

on:
  push:
    branches: [ main, master ]
  workflow_dispatch:
  repository_dispatch:
    types: [blog-updated]

env:
  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

permissions:
  contents: write

concurrency:
  group: pages
  cancel-in-progress: false

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout main
        uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Setup Hugo cache
        uses: actions/cache@v4
        with:
          path: ${{ runner.temp }}/hugo_cache
          key: ${{ runner.os }}-hugo-${{ github.sha }}
          restore-keys: |
            ${{ runner.os }}-hugo-

      - name: Clone private posts (shallow)
        run: |
          git clone --depth 1 --branch main \
          https://${{ secrets.PRIVATE_BLOG_REPO_TOKEN }}@github.com/ynhugo/blogs.git content/post

      - name: List posts
        run: ls -la content/post

      - name: Clean old public
        run: rm -rf public

      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v3
        with:
          hugo-version: 'latest'
          extended: true

      - name: Apply giscus config
        run: |
          sed -i '/^\[comments\]/,/^$/d' config/_default/params.toml
          if [ -f "content/post/giscus.toml" ]; then
            cat content/post/giscus.toml >> config/_default/params.toml
          fi

      - name: Build site
        run: |
          bash autoconfig.sh
          hugo --minify --gc --logLevel error --cacheDir "${{ runner.temp }}/hugo_cache"

      - name: Deploy to gh-pages
        uses: peaceiris/actions-gh-pages@v4
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./public
          publish_branch: gh-pages
          keep_files: true
2.6.2.2 官方部署方式(GitHub Pages 推荐模式)

适用场景:GitHub 官方标准部署流程,使用 artifact + deploy-pages 动作,更稳定、安全、官方推荐。 代码块自动继承父节点 tangle 配置,正常导出文件 (不写:tangle表示启用,:tangle no表示禁用)

对比之下,我觉得官方部署方式比传统方式略快,因此我也推荐官方部署方式

  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
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
name: Build and deploy (Official Pages + Private Repo + LFS + Giscus)

run-name: "Build and deploy (triggered by ${{ github.event_name }})"

on:
  push:
    branches: [main, master]
  workflow_dispatch:
  repository_dispatch:
    types: [blog-updated]

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: pages
  cancel-in-progress: false

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      TZ: Asia/Shanghai
    steps:
      - name: Checkout public repo
        uses: actions/checkout@v6
        with:
          submodules: recursive
          fetch-depth: 0

      - name: Delete default content/post
        run: rm -rf content/post

      - name: Setup Go
        uses: actions/setup-go@v6
        with:
          go-version: stable

      - name: Setup Node.js
        uses: actions/setup-node@v6
        with:
          node-version: 22

      - name: Setup Pages
        id: pages
        uses: actions/configure-pages@v6

      - name: Install Git LFS
        run: git lfs install

      - name: Clone private blog posts
        uses: actions/checkout@v6
        with:
          repository: ynhugo/blogs
          token: ${{ secrets.PRIVATE_BLOG_REPO_TOKEN }}
          path: content
          lfs: true

      - name: Show content
        run: ls -la content/post

      - name: Run autoconfig.sh
        run: |
          chmod +x autoconfig.sh
          bash autoconfig.sh

      - name: Inject giscus config
        run: |
          sed -i '/^\[comments\]/,/^$/d' config/_default/params.toml
          if [ -f "content/post/giscus.toml" ]; then
            cat content/post/giscus.toml >> config/_default/params.toml
          fi

      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v3
        with:
          hugo-version: "latest"
          extended: true

      - name: Build
        run: |
          hugo \
            --gc \
            --minify \
            --baseURL "${{ steps.pages.outputs.base_url }}/"

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v5
        with:
          path: ./public

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: github-pages
    steps:
      - name: Deploy to GitHub Pages
        uses: actions/deploy-pages@v5
2.6.3 不使用 LFS 的 Workflow 适配方案

GitHub LFS 存在免费存储额度与流量限制,长期使用易出现额度超限、构建失败、资源拉取异常等问题。为彻底规避 LFS 服务限制、提升博客构建部署速度、降低部署报错概率,本节提供完整的 **无 LFS 适配落地方案**。通过关闭本地 LFS 自动钩子、改造 GitHub Actions 工作流、清理仓库 LFS 配置、迁移大体积资源四步操作,可完全脱离 Git LFS 依赖,实现零额度限制、稳定高效的博客自动化部署。

2. 无 LFS 版本 GitHub Actions 工作流完整配置

基于官方推荐的 GitHub Pages 部署架构,改造工作流核心配置,关闭 LFS 初始化与 LFS 资源拉取能力,适配无 LFS 仓库部署场景。该配置兼容双仓库架构(公开仓库构建+私有仓库拉取博文),保留原有 Giscus 评论动态注入、短代码脚本执行、主题适配等核心能力,仅移除所有 LFS 依赖逻辑。

完整无 LFS 工作流配置(可直接替换原有 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
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
name: Build and deploy (No LFS Version)

run-name: "No LFS Build Deploy - ${{ github.event_name }}"

on:
  push:
    branches: [main, master]
  workflow_dispatch:
  repository_dispatch:
    types: [blog-updated]

# 官方Pages部署最小权限配置
permissions:
  contents: read
  pages: write
  id-token: write

# 并发控制,避免重复构建冲突
concurrency:
  group: pages
  cancel-in-progress: false

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      TZ: Asia/Shanghai
    steps:
      # 拉取公开仓库完整代码
      - name: Checkout public repo
        uses: actions/checkout@v6
        with:
          submodules: recursive
          fetch-depth: 0

      # 清空默认博文目录,准备拉取私有仓库博文
      - name: Delete default content/post
        run: rm -rf content/post

      # 安装环境依赖,适配Hugo编译
      - name: Setup Go
        uses: actions/setup-go@v6
        with:
          go-version: stable

      - name: Setup Node.js
        uses: actions/setup-node@v6
        with:
          node-version: 22

      # 初始化Pages部署配置
      - name: Setup Pages
        id: pages
        uses: actions/configure-pages@v6

      # 核心适配:关闭LFS拉取,纯Git模式拉取私有博文
      - name: Clone private blog posts (No LFS)
        uses: actions/checkout@v6
        with:
          repository: ynhugo/blogs
          token: ${{ secrets.PRIVATE_BLOG_REPO_TOKEN }}
          path: blogs
          lfs: false # 关键配置:禁用LFS下载,彻底规避LFS额度限制

      - name: Copy blog content
        run: cp -a blogs/content ./content

      # 校验博文文件拉取结果
      - name: Show content
        run: ls -la content/post

      # 执行自定义短代码脚本,适配主题样式
      - name: Run blogs/autoconfig.sh
        run: |
          chmod +x blogs/autoconfig.sh
          bash blogs/autoconfig.sh

      # 动态注入私有仓库Giscus评论配置,覆盖原有配置
      - name: Inject giscus config
        run: |
          sed -i '/^\[comments\]/,/^$/d' config/_default/params.toml
          if [ -f "blogs/giscus.toml" ]; then
            cat blogs/giscus.toml >> config/_default/params.toml
          fi

      # 加载最新Hugo扩展版本
      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v3
        with:
          hugo-version: "latest"
          extended: true

      # 优化编译:清理缓存、压缩静态资源、适配官方域名
      - name: Build
        run: |
          hugo \
            --gc \
            --minify \
            --baseURL "${{ steps.pages.outputs.base_url }}/"

      # 上传编译产物,用于Pages部署
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v5
        with:
          path: ./public

  # 官方标准部署流程
  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: github-pages
    steps:
      - name: Deploy to GitHub Pages
        uses: actions/deploy-pages@v5
3. 彻底脱离 LFS 完整落地方案

仅关闭工作流 LFS 拉取、清空钩子无法彻底根除 LFS 依赖,仓库历史配置、文件追踪规则、残留脚本仍会隐性依赖 LFS 服务。需通过 解除文件托管、清理配置脚本、提交生效、资源迁移适配 四步,完全剥离 LFS 依赖,实现纯普通 Git 仓库架构。

一键全自动恢复脚本(最终完美版)

直接复制保存为 restore_lfs_final.sh,一键执行:恢复所有文件 + 彻底卸载 LFS + 清理瘦身

 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
set +e
clear

# ===============================
# 主菜单(日常安全功能)
# ===============================
echo "====================================================="
echo "  🔒 LFS 安全恢复工具(可无限运行,绝对安全)"
echo "  ✅ 恢复 LFS 真实文件"
echo "  ✅ 删除 pre-push 钩子"
echo "  ✅ 删除 LFS 维护脚本"
echo "  ✅ 清理所有 LFS 残留"
echo "  ❌ 不提交、不修改仓库、.git 永远不会变大"
echo "====================================================="

echo -e "\n[1/5] 恢复文件..."
git lfs install --force >/dev/null 2>&1
git lfs checkout >/dev/null 2>&1

echo -e "\n[2/5] 卸载 LFS..."
git lfs uninstall >/dev/null 2>&1

echo -e "\n[3/5] 删除 pre-push..."
rm -f .git/hooks/pre-push

echo -e "\n[4/5] 删除 LFS 残留文件..."
rm -rf .git/lfs
rm -f .gitattributes
rm -f .lfsconfig
rm -f auto-lfs-tracker.sh
rm -f .git/hooks/*lfs*

echo -e "\n[5/5] 安全完成!"

echo -e "\n====================================================="
echo " 🎉 日常清理完成!"
echo " 🏆 .git 大小:$(du -sh .git | awk '{print $1}')"
echo "====================================================="

# ===============================
# 帮助说明(瘦身命令放在这里)
# ===============================
cat << HELP
=====================================================
ℹ️  永久瘦身命令(仅需要缩小时手动运行!)
=====================================================
# 作用:彻底清理历史垃圾,让 .git 永久变小
# 注意:只执行一次即可,不要频繁运行

git reflog expire --all --expire=now
git gc --prune=now --aggressive

=====================================================
HELP
大体积资源适配方案(核心落地关键)

普通 Git 仓库对单文件体积、仓库总容量存在严格限制。若直接将大量图片、PDF、压缩包等资源存入普通 Git,会导致仓库臃肿、克隆缓慢、编译超时、推送失败。因此彻底脱离 LFS 必须配套资源迁移方案:

将所有博文 resources 目录下的图片、PDF、压缩包、源码包等大体积静态资源,迁移至免费不限量的第三方对象存储 / 图床,并批量替换博文中的本地路径为在线 CDN 链接。

最终实现 仓库仅存储博文文本与站点配置,静态资源全托管云端 ,彻底达成零 LFS 依赖、零额度限制、极速克隆、快速构建、稳定部署的博客架构。

2.6.4 公开仓库工作流(主题自动更新)

路径: .github/workflows/update-theme.yml 作用:每日自动更新 Hugo 主题,无需手动维护

 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
name: Update theme

# Controls when the workflow will run
on:
  schedule:
    # Update theme automatically everyday at 00:00 UTC
    - cron: "0 0 * * *"
  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

jobs:
  update-theme:
    runs-on: ubuntu-latest

    permissions:
      # Give the default GITHUB_TOKEN write permission to commit and push the
      # added or changed files to the repository.
      contents: write
    
    steps:
      - uses: actions/checkout@v6

      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v3
        with:
          hugo-version: 'latest'
          extended: true

      - name: Update theme
        run: hugo mod get -u

      - name: Tidy go.mod, go.sum
        run: hugo mod tidy

      - name: Commit changes
        uses: stefanzweifel/git-auto-commit-action@v7
        with:
          commit_message: "CI: Update theme"

2.7 架构亮点总结

  • 单 Token 管控双仓库,同名 Secrets 统一配置,极简易维护
  • 细粒度最小权限设计,兼顾安全与实用性
  • 基于 Hugo 构建静态博客,搭配 Git LFS 管理大体积资源
  • 源码资源私有隔离,静态页面公开部署,权限边界清晰
  • 钩子+脚本全自动化,一次配置长期零维护
  • 执行时序安全可控,规则更新稳定无风险

2.8 部署耗时说明

结合实测结果,对当前架构部署耗时做客观说明:

Git LFS + 双仓库 架构下,部署耗时主要分为两部分:

  1. LFS 资源拉取:GitHub Actions 无法缓存 Git LFS 实体文件,每次构建都需要完整下载图片、PDF 等大体积资源,这是整体部署耗时(4~5 分钟)的核心来源,属于该方案固有限制。
  2. Hugo 编译:通过 --cacheDir 开启编译缓存后,仅首次编译耗时较长,后续增量编译可压缩至秒级。

优化选择:

  • 沿用现有架构:目录统一、写作习惯无改动、数据全托管在 GitHub,稳定性与易用性更强,接受 4~5 分钟部署耗时即可。
  • 极致提速方案:将大体积资源迁移至外部图床/对象存储,博文使用在线链接引用,彻底脱离 Git LFS,部署耗时可降至 30 秒内。

三、前置环境准备

3.1 安装 Git 与 Git LFS

主流 Linux / macOS 环境执行安装命令:

1
2
3
# 安装 Git(多数系统默认预装)
# 安装 Git LFS
git lfs install

验证安装:

1
git lfs version

3.2 初始化博客仓库

全新仓库可执行初始化,已有仓库可跳过:

1
2
3
4
cd ~/blogs/blogs
git init
git config user.name "你的用户名"
git config user.email "你的邮箱"

四、核心配置:Git LFS 自动化方案

4.1 智能扫描脚本(auto-lfs-tracker.sh)

仓库根目录创建自动化脚本,支持多运行模式、自动扫描规则、内置全套排错命令:

  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
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
set -e

SCAN_DIR="post"
CONFIG_FILE=".gitattributes"
SCAN_ONLY=false

if [[ "$1" == "--help" || "$1" == "-h" ]]; then
cat <<EOF
=============================================
        智能 Git LFS 自动追踪工具(加固终极版)
=============================================
【使用】
   ./auto-lfs-tracker.sh          # 自动扫描 + 提交配置
   ./auto-lfs-tracker.sh --scan-only # 仅扫描(钩子专用,不提交)
   ./auto-lfs-tracker.sh --help   # 查看帮助

=============================================
【一、历史大文件迁移到 LFS(仅执行一次)】
  作用:将仓库旧大文件转为 LFS 托管
=============================================
git lfs migrate import --everything --include="**/resources/**/*"
git push origin main --force

=============================================
【二、迁移后 .git 瘦身清理(必做)】
  作用:删除旧历史,从 GB → MB 级别
=============================================
git reflog expire --all --expire=now
git gc --prune=now --aggressive

=============================================
【三、pre-push 钩子安装(push 自动检查)】
  作用:每次 push 前自动扫描新文件类型
=============================================
cat > .git/hooks/pre-push << 'EOF_HOOK'
#!/bin/bash
echo "🔍 Git Push 前自动检查 LFS 未追踪文件(仅扫描)..."
./auto-lfs-tracker.sh --scan-only
EOF_HOOK
chmod +x .git/hooks/pre-push
chmod +x auto-lfs-tracker.sh

=============================================
【四、🔥 GitHub Actions / 克隆 404 / GH008 错误修复】
【报错:Object does not exist on the server / unknown Git LFS object】
  作用:把本地 LFS 大文件真正上传到 GitHub 服务器(必执行)
=============================================
git lfs push origin main --all
git push origin main --force

=============================================
【五、.sh 脚本误进 LFS 修复】
【报错:Smudge error 下载 sh 文件失败】
  作用:把脚本移出 LFS,恢复正常文本文件
=============================================
git lfs untrack "*.sh"
git add --renormalize auto-lfs-tracker.sh
git add .gitattributes
git commit -m "fix: 移除 .sh 从 LFS"
git push origin main --force

=============================================
【脚本作用】
1. 自动扫描 post/ 目录所有资源文件
2. 自动追加未追踪的类型到 Git LFS
3. 自动保护 **/resources/**/* 全部托管
4. 自动排除 .sh/.md/.org 等文本文件
5. 自动排除编辑器临时文件 *Minibuf* .DS_Store
6. 防止 Git 仓库爆炸、保证 CI 正常运行
7. 加固防 GH008 推送拒绝错误
=============================================
EOF
exit 0
fi

if [[ "$1" == "--scan-only" ]]; then
  SCAN_ONLY=true
  echo "============================================="
  echo "       智能 Git LFS 扫描模式(仅检查)"
  echo "============================================="
fi

cd "$(dirname "$0")"

echo -e "\n📂 扫描目录:$SCAN_DIR"

# 🔥 加固:排除文本、脚本、配置、编辑器临时文件
all_exts=$(find "$SCAN_DIR" -type f | \
  grep -vE '\.md$|\.org$|\.sh$|\.git|\.gitattributes|\.gitignore|README|LICENSE|^\./|/\.|Minibuf|\.DS_Store' | \
  sed -e 's/^.*\.//' | sort -u | grep -v '^/' | grep -v '^$' | grep -v ' ' | head -30)

echo -e "\n🔍 扫描到的文件类型:"
echo "$all_exts"

existing_patterns=$(git lfs track | awk '{print $1}')
added=0

for ext in $all_exts; do
    # 🔥 加固:过滤非法后缀、空后缀、带空格的垃圾文件
    if [[ -z "$ext" || "$ext" =~ " " || "$ext" =~ "/" || "$ext" =~ "*" ]]; then
        continue
    fi

    pattern="*.${ext}"
    if ! echo "$existing_patterns" | grep -qx "$pattern"; then
        echo "✅ 新增 LFS 追踪:$pattern"
        git lfs track "$pattern"
        added=$((added + 1))
    fi
done

# 强制全局 resources 规则
if ! grep -q "**/resources/**/\*" "$CONFIG_FILE"; then
    echo "✅ 启用全局规则:**/resources/**/*"
    git lfs track "**/resources/**/*"
    added=$((added + 1))
fi

# 钩子模式:不提交
if [[ "$SCAN_ONLY" == true ]]; then
  echo -e "\n✅ 扫描完成(钩子模式:不提交)"
  exit 0
fi

if [ $added -gt 0 ]; then
    echo -e "\n📝 提交配置..."
    git add "$CONFIG_FILE"
    git commit -m "auto: lfs 自动追踪新文件类型"
    echo -e "\n🎉 更新完成!"
else
    echo -e "\n🎉 所有类型已追踪,无需更新!"
fi

echo -e "\n✅ 全部完成!"

添加脚本执行权限:

1
chmod +x auto-lfs-tracker.sh

4.2 配置 Git Pre-Push 钩子

实现每次 git push 前自动扫描 LFS 规则,全程无感自动化:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/bin/bash
set -e

echo "=================================================="
echo " 🔒 安全加固版 Git LFS 预检查(仅扫描,零风险)"
echo " 作用:自动检查新文件类型 → 自动维护 LFS 规则"
echo " 承诺:不提交、不推送、不修改、不冲突、不中断"
echo "=================================================="

./auto-lfs-tracker.sh --scan-only

echo -e "\n✅ LFS 预检查完成,继续正常推送..."
exit 0
1
chmod +x .git/hooks/pre-push

4.3 存量历史文件迁移(仅执行一次)

针对已有大量资源的旧仓库,批量将历史大文件迁移至 LFS,实现仓库瘦身:

  1. 迁移历史资源文件
1
git lfs migrate import --everything --include="**/resources/**/*"
  1. 清理 Git 历史垃圾对象
1
2
git reflog expire --all --expire=now
git gc --prune=now --aggressive
  1. 强制推送重写历史(单人私有仓库安全)
1
git push origin main --force

4.4 上传 LFS 实体文件(解决 404 报错)

migrate 仅修改提交记录,需手动推送实体文件至远程,避免 CI/克隆资源404:

1
2
git lfs push origin main --all
git push origin main --force

五、日常使用流程

5.1 常规写作与提交

完全保留原生 Git 操作习惯,无需额外干预:

1
2
3
git add .
git commit -m "更新博文:xxx"
git push

推送时钩子自动触发 LFS 扫描,补全缺失规则,全程自动化。

5.2 手动执行扫描脚本

灵活手动维护 LFS 规则:

1
2
3
4
5
6
7
8
# 完整扫描并提交新规则
./auto-lfs-tracker.sh

# 仅扫描不提交(钩子同款模式)
./auto-lfs-tracker.sh --scan-only

# 查看帮助与全套排错命令
./auto-lfs-tracker.sh -h

六、常见问题排错

6.1 克隆/CI 报错:Object does not exist on the server (404)

原因:本地 LFS 实体文件未推送至远程服务器 解决:

1
2
git lfs push origin main --all
git push origin main --force

6.2 Shell 脚本被纳入 LFS,克隆失败

原因:.sh 脚本被误追踪,文本文件无需 LFS 托管 解决:

1
2
3
4
5
git lfs untrack "*.sh"
git add --renormalize auto-lfs-tracker.sh
git add .gitattributes
git commit -m "fix: 移除 .sh 文件从 LFS 托管"
git push origin main --force

6.3 git push 被钩子中断、出现提交冲突

原因:钩子模式下脚本执行了 git commit,打乱推送流程 解决方案:使用 --scan-only 仅扫描不提交(本文钩子已默认配置)

6.4 .git 目录体积偏大

原因:migrate 操作后 Git 保留了旧历史对象,需手动垃圾回收 解决方案:

1
2
git reflog expire --all --expire=now
git gc --prune=now --aggressive

6.5 Actions 克隆私有仓库权限不足

原因:Token 权限缺失、Secrets 名称错误、未勾选对应仓库 排查要点:

  1. 核对细粒度 Token 已勾选 blogs 和 ynhugo.github.io 两个仓库
  2. 确认 Token 拥有 Metadata(Read)、Code(Read and Write) 权限
  3. 检查 Actions 配置中密钥名称为 PRIVATE_BLOG_REPO_TOKEN (区分大小写)
  4. 重新粘贴 Token 至仓库 Secrets,确保无多余空格

6.6 GitHub Actions 部署总耗时 4~5 分钟,速度偏慢

原因:GitHub Actions 无法缓存 Git LFS 大文件,每次构建都会完整拉取所有 LFS 资源,仓库资源体量越大,耗时越长。

解决方案:

  1. 现有架构无需处理:该速度为 Git LFS 方案下的正常极限速度,不影响功能使用,可直接忽略;
  2. 极致提速方案:将图片、压缩包、PDF 等大资源迁出 Git 仓库,托管至外部图床/对象存储,博文使用在线链接引用,彻底规避 LFS 重复下载问题。

七、方案总结

  1. 目录结构兼容传统写作习惯,博文与资源同目录,无需修改文件路径;
  2. Git LFS 专门托管大体积资源,从根源解决 Git 仓库无限膨胀问题;
  3. 脚本 + Git Hook 实现全自动化,新增文件自动识别并纳入 LFS;
  4. 细粒度 Token + 同名 Secrets 实现双仓库权限互通,安全且易维护;
  5. 脚本内置全套排错命令,换设备、重装环境均可快速复原;
  6. 基于 Hugo + Stack 主题模板搭建静态博客,采用 TOML 格式配置,完美适配 GitHub Actions 自动部署,私有内容隔离,公开站点正常访问;
  7. Giscus 评论配置独立存放在私有仓库,通过 Actions 动态合并,兼顾功能与数据安全;
  8. 钩子时序逻辑安全可控,配置文件更新无冲突、无推送风险,长期稳定运行。

注: 本方案基于 Git LFS 实现大文件托管,受 GitHub 机制限制,CI 部署会存在固定资源拉取耗时,属于正常现象。

本方案适合大容量个人博客、技术文档库,一次完整配置后,可实现长期零维护稳定运行。