跳转到主要内容

这是本节的多页打印视图。 .

返回本页常规视图.

十分钟上手

克隆 OINK 文档站,本地预览,替换站点信息,部署到 GitHub Pages。

这条路径不从空目录开始,而是克隆你正在读的这个站点,删掉不需要的部分,再替换成你自己的信息。本站是 OINK 的回归站,包含每个组件与每种页面类型,并与主题保持同版本;从它开始删减,比从空目录逐项补配置与示例少写很多。

前提:一台能安装 Hugo Extended 与 Go 的机器、一个 GitHub 账号、十分钟。不需要 Node.js,也不需要其它前端工具链。

结果

完成后得到一个双语文档站:左侧栏是你的目录树,右侧是本页目录,顶栏有全文搜索与命令面板,深浅色跟随系统;一份 Markdown 同时产出网页、打印页、纯 Markdown 与 RSS;托管在 GitHub Pages 上。

内容、配置与主题在构建期汇成一个静态站点的示意图
一份内容,四种输出:HTML、打印、Markdown、RSS

步骤

  1. 安装 Hugo Extended 与 Go

    除 Git 之外需要两样。Hugo Extended 必须是 0.160.1 或更高版本:标准版 Hugo 没有内置 Sass 编译器,编译不了主题样式,构建失败。Go 用于解析模块:OINK 以 Hugo Module 发布,Hugo 通过 Go 的模块机制下载并校验 github.com/pgsty/oink

    macOS
    brew install hugo go git
    Linux
    # 发行版仓库里的 Hugo 往往过旧,改用官方 deb 包(本站 CI 也是如此)
    curl -LO https://github.com/gohugoio/hugo/releases/download/v0.164.0/hugo_extended_0.164.0_linux-amd64.deb
    sudo dpkg -i hugo_extended_0.164.0_linux-amd64.deb
    sudo apt install -y golang-go git
    Windows
    winget install Hugo.Hugo.Extended
    winget install GoLang.Go
    winget install Git.Git

    安装完成后核对一次,输出里必须出现 extended

    $ hugo version
    hugo v0.164.0+extended+withdeploy darwin/arm64 BuildDate=2026-07-06T16:39:30Z
    $ go version
    go version go1.26.6 darwin/arm64
    

    其它平台按 Hugo 安装指南go.dev/dl 安装,注意选 extended 版本。

  2. 克隆文档站并预览

    git clone https://github.com/pgsty/oink.pgsty.com my-docs
    cd my-docs
    hugo server

    打开 http://localhost:1313/,中文站在 http://localhost:1313/zh/。第一次启动会下载主题模块(几秒到一分钟,取决于网络),之后修改文件是毫秒级热重载。

    已提交的 go.mod 固定了主题版本,克隆之后即可构建,不需要额外的安装脚本。

    说明

    仓库里的 Makefile 只是几条命令的别名。make devmake check 通过 HUGO_MODULE_REPLACEMENTS 使用同级的 ../oink 主题 checkout;make buildmake serve 始终使用 go.mod 固定的公开版本。新站点用 hugo server 即可。

  3. 替换站点信息

    站点身份:全部在 hugo.yml 里。baseURL 用的是 YAML 锚点,实际地址写在 params.productionURL 上,只改这一处:

    hugo.yml
    title: Product Docs # 顶栏站名与 <title>
    
    params:
      productionURL: &productionURL https://docs.example.com/
      github_repo: https://github.com/example/product-docs # 「编辑当前页面」指向哪
      copyright:
        authors: '[Example Inc.](https://example.com/)'
        from_year: 2026
      footer_center_info: ''
    
    baseURL: *productionURL

    languages.en.titlelanguages.zh.title 会覆盖顶层 title,两处一起改。参数逐项的含义与默认值见配置总览

    删掉本站专用的配置:保留它们会让你的站点指向 OINK 的仓库与账号。

    hugo.yml 里的键 怎么处理
    services.googleAnalytics.id OINK 的统计 ID,删掉;需要统计时换成你自己的
    params.comments giscus 指向 pgsty/oink.pgsty.com 的讨论区,整段删掉或换成你的仓库
    params.tdVersion params.version params.version_menu params.versions OINK 的版本菜单,删掉
    params.github_project_repo 主题仓库链接,删掉
    languages.<lang>.menus.main 顶栏菜单指向 /docs/tutorial 这类本站栏目,按你的目录重写

    换 Logo 与图标:替换这三个文件,文件名保持不变,主题按文件名挂载:

    static/
    static/favicon.svg           # 浏览器标签页图标
    static/favicon.ico
    static/apple-touch-icon.png  # iOS 添加到主屏

    static/logo.svg 是本站自己的品牌组合标,没有参数指向它:删掉,或者换成你的横向字标再设 params.wordmark

    替换内容content/docs/ 是 OINK 自己的主题文档,整棵删除,写你自己的第一页:

    rm -rf content/docs && mkdir -p content/docs
    content/docs/_index.md
    ---
    title: Docs
    linkTitle: Docs
    description: Product documentation.
    weight: 20
    ---
    
    Everything about running Product in production.

    content/blog/ 可以留一篇当模板,也可以整个目录删除(删除后把 menus.main 里的 blog 项一并删掉)。哪些目录必须保留、哪些是文档站自用,见仓库导览

    只做英文站:删除 languages.zh 整段与所有 .zh.md 文件,languages 缩成一段:

    hugo.yml
    defaultContentLanguage: en
    languages:
      en:
        label: English
        locale: en-US
        weight: 1
        title: Product Docs
        menus:
          main:
            - { name: Docs, pageRef: /docs, weight: 20 }
    find content -name '*.zh.md' -delete

    保留双语或换成其它语言对,见多语言

  4. 部署

    在 GitHub 上新建一个空仓库,把本地历史换成你自己的:

    rm -rf .git && git init -b main
    git add . && git commit -m "Initial documentation site"
    git remote add origin [email protected]:example/product-docs.git
    git push -u origin main

    仓库自带 .github/workflows/pages.yml:推到 main 分支即构建并发布,也可以在 Actions 页面手动触发(workflow_dispatch)。它固定 Hugo Extended 与 Go 的版本,用 --printPathWarnings --panicOnWarning 构建,baseURL 由 GitHub Pages 提供,因此发布到 example.github.io/product-docs/ 这类子路径也不必改配置。

    在仓库的 Settings → Pages → Build and deployment → Source 选 GitHub Actions。默认值是 Deploy from a branch,不改这一项 workflow 会在部署步骤失败。

    删除 scripts/ 后要改 workflow

    pages.yml 中的 Verify advertised and pinned release match 一步运行 node scripts/check-release-pin.mjs,校验站点公告的版本与 go.mod 固定的版本一致。删掉 scripts/ 之后,把这一步与 Set up Node.js 一并从 pages.yml 移除。

    Cloudflare Pages、Netlify、Nginx 与离线打包见发布上线:构建命令都是 hugo --gc --minify,区别只在 baseURL 与环境变量。

验证

本地运行一次生产构建。它比开发服务器严格,路径告警会让构建失败:

hugo --gc --minify --printPathWarnings --panicOnWarning

输出 Total in … 且没有 WARN / ERROR 即通过。再对照预览核对:

  • 顶栏是你的站名与 Logo,浏览器标签页是你的 favicon
  • 侧栏是你自己的目录树,每页都能打开
  • CtrlK(macOS 上是 K)打开命令面板,能搜到刚写的页面
  • 页面标题右侧的菜单里,「编辑当前页面」指向你自己的仓库,不是 pgsty/oink.pgsty.com
  • 部署后 GitHub 仓库 Actions 页面里的 Deploy Oink site to GitHub Pages 是绿的

构建报错见排错与检查

下一步

  • 仓库导览 — 克隆下来的每个目录是什么,哪些可以删。
  • 编写页面 — 一页文档的组成:front matter、标题锚点、链接与图片。
  • 组件总览 — 提示块、标签页、参数表、文件树等,每个组件一页。
  • 品牌外观 — 主色、字体预设、页宽与自定义样式。
  • 发布上线 — GitHub Pages 之外的托管方式与验收清单。

给编码助手的指令

上面四步可以交给编码助手(Claude Code、Codex 等)执行。复制下面这段指令,把方括号里的三处替换为你自己的信息:

可整段复制的指令
请帮我用 OINK 主题建一个文档站,按下面的流程做,遇到不确定的地方按「只在缺信息时问人」处理。

1. 检查环境:运行 `hugo version`,要求输出包含 `extended` 且版本 >= 0.160.1;运行 `go version`,
   要求能拿到版本号。任一不满足就先按官方文档安装,macOS 用 `brew install hugo go`,
   Debian/Ubuntu 装 GitHub Releases 上的 hugo_extended deb 包。
2. 克隆站点模板:`git clone https://github.com/pgsty/oink.pgsty.com [目标目录]` 并进入该目录。
3. 改 hugo.yml 三处:顶层 `title` 与 `languages.<lang>.title` 改成 [站点名称];
   `params.productionURL` 改成 [站点域名](baseURL 是指向它的 YAML 锚点,不要单独改 baseURL);
   `params.github_repo` 改成本站将来的仓库地址。
   同时删掉这些本站专用配置:`services.googleAnalytics`、`params.comments`、
   `params.tdVersion`、`params.version`、`params.version_menu`、`params.versions`、
   `params.github_project_repo`,并把 `menus.main` 改成只指向 /docs 与 /blog。
4. 清空示例内容:删除 `content/docs/` 整棵目录,新建 `content/docs/_index.md`
   (front matter 至少有 title / description / weight);`content/blog/` 下只保留一篇文章当模板。
   删除文档站自用的脚手架:`tests/`、`scripts/`、`playwright.config.mjs`、`package.json`、
   `package-lock.json`、`AGENTS.md`、`TRANSLATION.md`、`CONTRIBUTING.md`、`agent-docs.config.yml`,
   并把 `.github/workflows/` 下除 `pages.yml` 外的 workflow 删掉,
   同时删掉 `pages.yml` 里的 `Set up Node.js` 与 `Verify advertised and pinned release match` 两步。
5. 后台启动 `hugo server`,确认 http://localhost:1313/ 返回 200 且页面标题是新站点名。
6. 校验:运行 `hugo --gc --minify --printPathWarnings --panicOnWarning`,
   要求以 `Total in ...` 结束且没有 WARN/ERROR;有报错就修到通过,不要用忽略告警的方式绕过。
7. 只在缺少 [站点名称]、[站点域名]、仓库地址这三项信息时才问我,其余按上面的默认做法执行。

建好的站点便于助手读取:每页都有 .md 纯文本输出,站点根有 llms.txt,页面标题右侧的菜单里有「复制 Markdown 文本」与「在 Claude 中打开」。见 Agent 支持

不从这个仓库起步、要从空目录搭建,见从零建站与其它安装方式

1 - 仓库导览

克隆下来的每个目录是什么:哪些必须保留、哪些替换为你的信息、哪些是文档站自用可以整个删除。

本页逐项说明 pgsty/oink.pgsty.com 克隆下来的每个文件与目录:哪些必须保留、哪些替换为你自己的信息、哪些是文档站自用可以整棵删除,并给出一个安全的删除顺序。

主题代码不在这个仓库里:它是 go.mod 固定的一个 Hugo Module,存放在 Go 的模块缓存中。这个仓库只有内容、配置与站点自己的少量覆盖。

顶层结构

克隆下来的 my-docs/

  • my-docs/
    • hugo.yml站点唯一配置:身份、语言、菜单、参数、模块导入
    • go.mod固定主题版本
    • go.sum主题模块的校验和
    • content/全部内容,目录结构就是侧栏结构
      • _index.md首页;_index.zh.md 是它的中文对等页
      • search.mdGoogle 自定义搜索的结果页,用不到可删
      • docs/文档树:OINK 自己的主题文档
      • blog/博客:工程记录与版本发布
    • assets/要经 Hugo 处理的资源
      • scss/站点样式覆盖,三个 partial
      • images/需要缩放裁切的图片
      • parts/include shortcode 引入的 Markdown 与 YAML 片段
    • static/原样复制到站点根,不做处理
      • logo.svg品牌组合标,没有参数指向它
      • favicon.svg浏览器标签页图标
      • favicon.ico
      • apple-touch-icon.pngiOS 添加到主屏
      • images/截图与示意图
    • layouts/站点模板覆盖:只覆盖最窄的那一个
      • _shortcodes/站点自己的 shortcode
    • data/数据驱动的页面
      • home/首页分区:en.yaml / zh.yaml
      • landing/Landing 页数据
      • download/发布与下载页数据
    • .github/
      • workflows/pages.yml 部署;另外两个是本站回归测试
    • tests/文档站自用:Playwright、goldens、构建断言
      • browser/Playwright 规格
      • hugo-build/构建断言
      • md-output/Markdown 输出 goldens
      • alt-site/备用配置构建
      • favicons/
      • release-pin/
      • fixtures/
    • scripts/文档站自用:翻译对等与链接检查
      • check-doc-translations.mjs
      • check-markdown-style.mjs
      • check-rendered-links.mjs
      • check-rendered-markdown.mjs
      • check-release-pin.mjs
    • Makefilebuild / serve 直接调 Hugo;dev / check 指向同级 ../oink
    • package.json测试工具链,站点构建用不到
    • package-lock.json
    • playwright.config.mjs
    • agent-docs.config.ymlAgent 文档评分工具的配置
    • AGENTS.md给编码 Agent 的仓库说明
    • TRANSLATION.md双语翻译流程
    • CONTRIBUTING.md
    • README.md
    • LICENSEApache-2.0,站点代码
    • LICENSE-CC-BY-4.0内容许可
    • NOTICE

上面没有列出的还有 .gitignore.gitattributes.nvmrc.npmrc,以及被 .gitignore 排除的生成物:public/(构建产物)、resources/(Hugo 资源缓存)、node_modules/。后一组不进版本库。

仓库里没有 i18n/:界面文字(「上一页」「本页目录」这类)由主题的 32 份语言文件提供。要改其中某一句,在站点根目录建 i18n/zh.yaml,只写需要覆盖的键。

各项的处理方式

路径 是什么 fork 后怎么处理
hugo.yml 站点的唯一配置文件,没有 config/ 目录也没有分环境覆盖 替换为你的信息:身份、语言、菜单、品牌
go.mod go.sum 固定主题版本并记录校验和 必须保留,一起提交
content/ 全部内容;目录结构决定侧栏结构 必须保留;里面的 docs/blog/ 换成你自己的
content/search.md layout: search 的整页搜索结果,只在配了 Google 自定义搜索(params.gcs_engine_id)时才有内容 用主题自带的本地搜索时可以删
assets/scss/ 站点样式覆盖(_variables_project.scss 等) 要改配色字体就保留,不改可以清空
assets/images/ 需要 Hugo 处理(缩放、裁切)的图片 换成你自己的
assets/parts/ include shortcode 引入的片段 随引用它的页面一起替换或删除
static/ 原样复制到站点根 替换为你的:logo、favicon、截图
layouts/_shortcodes/ 本站自己的四个 shortcode,当前内容里已无引用 可删
data/home/ 首页分区数据(Hero、能力面板) 改成你的;删除后首页退回普通页面
data/landing/ data/download/ Landing 页与发布下载页的数据 用不到就删
.github/workflows/pages.yml 推到 main 就构建并发布到 GitHub Pages 保留,按你的仓库改
.github/workflows/site-checks.yml browser-quality.yml 本站的回归测试流水线 文档站自用,可删
tests/ scripts/ playwright.config.mjs package.json package-lock.json 本站的回归测试与检查工具链 文档站自用,可删
Makefile 主题与站点共同开发的快捷方式(要求同级有 ../oink 文档站自用,可删
AGENTS.md TRANSLATION.md CONTRIBUTING.md agent-docs.config.yml 本站的协作约定 换成你自己的,或删
README.md LICENSE LICENSE-CC-BY-4.0 NOTICE 说明与许可 换成你自己的
.nvmrc .npmrc Node 版本与 npm 配置 package.json 一起删
用 OINK 建站不需要 Node.js

这个仓库里的 package.jsontests/scripts/ 用于维护文档站本身。你的站点构建只有一条命令:hugo --gc --minify

删除顺序

顺序是先删外围、再删内容、最后清数据。每删一步构建一次,出问题时能定位到具体步骤。

  1. 删脚手架

    这一批与站点渲染无关,删除后不影响任何页面。

    rm -rf tests scripts node_modules
    rm -f package.json package-lock.json playwright.config.mjs .nvmrc .npmrc
    rm -f AGENTS.md TRANSLATION.md CONTRIBUTING.md agent-docs.config.yml Makefile
    rm -f .github/workflows/site-checks.yml .github/workflows/browser-quality.yml

    删除 scripts/ 之后必须改 .github/workflows/pages.yml:把 Set up Node.jsVerify advertised and pinned release match 两步删掉,否则部署会在该步骤失败。

  2. 删示例内容

    content/docs/ 是 OINK 自己的主题文档,content/blog/ 是它的工程博客,与你的产品无关。

    rm -rf content/docs
    mkdir -p content/docs
    rm -rf content/blog        # 不要博客的话;要的话只留一篇当模板

    同时改 hugo.yml 里每种语言下的 menus.main:那些菜单项指向 /docs/tutorial/blog/release 这些已不存在的路径。content/_index.md 是首页,保留它,把正文换成你的。

  3. 清数据

    data/ 下三组数据分别供首页、Landing 页与发布页使用。首页数据保留后修改,另外两组用不到就删除。

    rm -rf data/landing data/download

    data/home/en.yamldata/home/zh.yaml 决定首页有哪些分区,逐项含义见首页与落地页。删除整个 data/home/ 也能构建,首页退回为普通内容页。

  4. 换身份

    最后把 hugo.yml 里的站名、params.productionURLparams.github_repo 与品牌参数换成你的,替换 static/ 下的 logo 与 favicon,删掉 services.googleAnalyticsparams.commentsparams.version* 这些 OINK 专用配置。逐条清单见十分钟上手第 3 步。

主题的位置

主题以 Hugo Module 的形式引用,两处配置指向它:

hugo.yml
module:
  imports:
    - path: github.com/pgsty/oink
  hugoVersion:
    extended: true
    min: '0.160.1'
go.mod
require github.com/pgsty/oink v0.6.0

hugo.yml 声明使用哪个主题,go.mod 固定用它的哪一版,go.sum 记录该版本的校验和。三个文件都要提交。主题源码不进你的仓库:Hugo 把它下载到 Go 的模块缓存,hugo mod graph 显示实际解析结果。

升级到最新版:

hugo mod get -u github.com/pgsty/oink

固定到某一个版本:

hugo mod get github.com/pgsty/[email protected]

两条命令都会改写 go.modgo.sum。生产站点固定到发布标签,不要跟随 main。升级前后检查什么、如何回滚,见版本升级

站点覆盖

layouts/ 下的文件按 Hugo 的模板查找顺序盖过主题里的同名文件。本站只放了一类:

  • layouts/_shortcodes/*.html:站点自己的 shortcode。产品文档需要带业务语义的 shortcode 时也放这里。

标题自链锚点由主题的 _markup/render-heading.html 提供,站点不需要自己建这个钩子。

要改外壳(侧栏、页脚、页尾)时,覆盖最窄的那个 partial,不要整份复制 baseof.html:复制之后每次主题升级都要手工合并。

验证

每删一步运行一次构建,报错能定位到刚删除的内容:

hugo --gc --minify --printPathWarnings --panicOnWarning

删除完成后,这几条应当成立:

  • 构建以 Total in … 结束,没有 WARN / ERROR
  • 顶栏菜单没有指向已删目录的死链
  • 标题右侧仍然有自链锚点(主题自带的标题渲染钩子,站点不需要覆盖)
  • git status 里没有 public/resources/

2 - 从零建站与其它安装方式

从空目录搭一个最小 OINK 站点,以及 Module / submodule / 离线归档 / 克隆四种安装方式的取舍。

本页从空目录搭建一个最小 OINK 站点:十几行 hugo.yml 加一条 hugo mod get,得到一个可预览的单语站点。代价是首页、示例内容与可参照的组件用法都要自己写。

已有 Hugo 站点时不需要脚手架:装上主题模块,再补三项 goldmark 前置配置(见hugo.yml),正文不用重写。已有 Docsy 站点见版本升级

后半部分是四种安装方式的取舍:Hugo Module、Git submodule、离线归档、固定版本克隆。

从空目录到第一页

  1. 建骨架并获取主题

    hugo new site --format yaml my-docs
    cd my-docs
    hugo mod init github.com/example/my-docs
    hugo mod get github.com/pgsty/[email protected]

    hugo mod init 后面跟的是你自己站点的模块路径,通常就是仓库地址。hugo mod get 会写出 go.modgo.sum,两个都要提交。

    最新版本号在 GitHub Releases;本页出现的 v0.6.0 是本站当前固定的版本。生产站点固定到发布标签,不要跟随 main@latest 是一次性解析动作,不是版本策略。

  2. hugo.yml

    hugo new site 生成的 hugo.yaml 改名为 hugo.yml(两个后缀 Hugo 都接受,本文统一用后者),内容替换为下面这份,可直接构建:

    hugo.yml
    title: Product Docs
    baseURL: https://docs.example.com/
    defaultContentLanguage: en
    # enableGitInfo: true        # 页面「最后修改」时间来自 git,先 git init 再打开
    
    languages:
      en:
        label: English
        locale: en-US
        weight: 1
        title: Product Docs
        params:
          description: Everything about running Product in production
        menus:
          main:
            - { name: Docs, pageRef: /docs, weight: 20 }
            - { name: Blog, pageRef: /blog, weight: 50 }
    
    # 三项 Goldmark 前置:OINK 的原生 Markdown 组件全靠它们
    markup:
      goldmark:
        renderer:
          unsafe: true # 允许内容里的行内 HTML
        parser:
          attribute:
            block: true # {.steps} {.cards} {caption=} 这类属性行
          wrapStandAloneImageWithinParagraph: false # 块级图片才能带属性行
      highlight:
        noClasses: false # 代码配色跟随深浅色模式
    
    params:
      offline_search: true
      github_repo: https://github.com/example/product-docs
      copyright:
        authors: '[Example Inc.](https://example.com/)'
        from_year: 2026
      ui:
        dark_mode: true
        sidebar_menu_foldable: true
        section_index: cards
    
    outputs:
      home: [HTML, markdown, LLMS]
      page: [HTML, markdown]
      section: [HTML, RSS, print, markdown]
    
    module:
      imports:
        - path: github.com/pgsty/oink
      hugoVersion:
        extended: true
        min: '0.160.1'

    五段分别管什么:

    管什么 少了会怎样
    顶层 + languages 站名、域名、语言与顶栏菜单 baseURL 不对,线上所有绝对链接指错
    markup.goldmark 三项组件前置 属性行变成正文里的一行 {.steps}
    params 搜索、仓库链接、外壳开关 交互功能默认关闭,主题不替站点决定
    outputs 每页的 .mdllms.txt、打印页 页面菜单里没有「复制 Markdown」,也没有打印视图
    module 引用主题、声明 Hugo 下限 构建时找不到主题

    写公式还需要 Goldmark 的 passthrough 扩展,见公式。每个键的完整含义与默认值见配置总览

  3. 写第一页

    content/ 下的每个一级目录是一个分区,目录结构就是侧栏结构。文档分区至少要有一个 _index.md

    content/docs/_index.md
    ---
    title: Docs
    linkTitle: Docs
    description: Everything about running Product in production.
    weight: 20
    ---
    
    从[安装](/docs/install/)开始。
    content/docs/install.md
    ---
    title: Install
    description: Install Product on a fresh machine.
    weight: 10
    ---
    
    ## Prerequisites {#prerequisites}
    
    > [!IMPORTANT]
    > Product 需要 PostgreSQL 18 或更高版本。
    
    ## Install {#install}
    
    ```bash
    curl -fsSL https://get.example.com | bash
    ```

    标题写显式 {#id}:后续加译文时两种语言的锚点才能对应。页面写法见编写页面

  4. 预览

    hugo server

    打开 http://localhost:1313/,侧栏里有 Docs → Install。修改文件是毫秒级热重载。

其它安装方式

上面用的是 Hugo Module。另外三种方式面向特定约束:网络隔离、平台要求构建输入包含完整主题树、组织内部需要评审主题副本。除 hugo mod vendor 之外,它们都不建立 Go 模块,站点用 theme: oink 而不是 module.imports 引用主题;共同的代价是版本解析与完整性校验由你自己负责。

Hugo Module(推荐)

hugo mod init github.com/example/product-docs
hugo mod get github.com/pgsty/[email protected]
hugo.yml
module:
  imports:
    - path: github.com/pgsty/oink

唯一能让 Hugo 自己解析版本、校验 checksum、并在 go.sum 里留下审计记录的方式。hugo mod graph 看实际解析结果,hugo mod get -u 升级。需要本机有 Go。

Git submodule

在站点仓库里记录准确的主题 commit:

git submodule add https://github.com/pgsty/oink.git themes/oink
git -C themes/oink fetch --tags
git -C themes/oink checkout v0.6.0
git add .gitmodules themes/oink
hugo.yml
theme: oink

CI 必须在运行 Hugo 之前初始化 submodule,否则 themes/oink 是空目录:

git submodule update --init --recursive

离线归档

网络隔离环境使用。两条路径,都先在联网机器上准备,再整体搬入。

hugo mod vendor:把已解析的主题源码固化进站点目录,之后构建既不联网也不需要 Go。

hugo mod vendor          # 生成 _vendor/,里面是主题的完整源码树
tar czf my-docs.tgz .    # 连 _vendor/ 一起搬进隔离环境

_vendor/ 存在时 Hugo 优先使用它(hugo mod graph 输出 +vendor),hugo.yml 里的 module.imports 保持不变。这一步需要 Go,之后的构建不需要。升级主题要回到联网环境重新执行 hugo mod gethugo mod vendor

_vendor/ 只收主题挂载出来的目录(assets data i18n layouts static)以及 hugo.yamltheme.toml,不含 LICENSENOTICEVENDOR.json。要对外分发这份归档,把这三个文件从主题仓库一并取来。

用 tag 源码归档:不建 Go 模块,直接把某个版本的主题解压到 themes/oink/

curl -L -o oink.tar.gz \
  https://github.com/pgsty/oink/archive/refs/tags/v0.6.0.tar.gz
mkdir -p themes/oink
tar xzf oink.tar.gz -C themes/oink --strip-components=1
hugo.yml
theme: oink

主题仓库的根目录就是模块根目录,解压出来直接是 layouts/assets/i18n/static/ 这一层,不需要再进入下一级。重新分发时必须保留 LICENSENOTICEVENDOR.json。最后一个记录了每个第三方运行时的版本、来源、许可证路径与 SHA-256,是离线审计的依据。

跨机器传输时,在联网侧从不可变标签生成归档与校验值:

git clone --branch v0.6.0 --depth 1 \
  https://github.com/pgsty/oink.git oink
git -C oink archive --format=tar.gz --prefix=oink/ \
  --output=../oink-v0.6.0.tar.gz v0.6.0
shasum -a 256 oink-v0.6.0.tar.gz \
  > oink-v0.6.0.tar.gz.sha256

把归档与 .sha256 一起传入隔离环境,先校验再解压:

shasum -a 256 -c oink-v0.6.0.tar.gz.sha256
mkdir -p themes
tar -xzf oink-v0.6.0.tar.gz -C themes

这样得到的归档是自建产物,不是项目发行物。某个标签的发行页面是否附带归档与校验文件按发布而定,使用公开附件时独立验证其校验值。

断网构建之前确认归档内容完整,这十一项都要在:

themes/oink/

  • oink/
    • go.mod模块路径声明,Hugo Module 方式解析用
    • hugo.yaml主题默认参数与 Hugo 版本下限
    • theme.toml主题元数据,theme: oink 方式需要
    • LICENSEApache-2.0
    • NOTICE上游署名,再分发时必须保留
    • VENDOR.json第三方运行时清单:版本、来源、许可证路径、SHA-256
    • assets/SCSS、JS 与随主题分发的第三方运行时
    • layouts/模板、partial、shortcode、render hook
    • static/字体文件,原样发布
    • i18n/32 份界面语言文件
    • data/页尾出处行用的 SPDX 许可证表

固定版本克隆

托管平台要求构建输入包含完整主题树时用:

git clone https://github.com/pgsty/oink.git themes/oink
git -C themes/oink checkout v0.6.0

与 submodule 的区别是主题文件直接进入你的仓库历史,没有 .gitmodules 这层间接。记录最终解析出的 commit 与恢复流程。

四种方式对比

方式 需要 Go 版本可审计 主题源码进你的仓库 适用
Hugo Module go.sum 自动校验 默认推荐
Git submodule 仓库记录 commit 以引用形式 需要主题源码在库内
离线归档 手工核对 checksum 网络隔离
固定版本克隆 需自行记录 平台要求完整树
消费站点不需要前端工具链

Bootstrap、Font Awesome、字体、搜索与图表运行时全部随主题分发。站点不需要 node_modules、PostCSS、RTLCSS,也不需要 CDN。为 Docsy 站点安装 npm 依赖的教程属于上游 Docsy 的流程,不适用于 OINK。

用本地主题 checkout 开发

同时修改主题与站点时才需要这一节。把两个仓库克隆为同级目录:

同级目录布局
~/pgsty/
├── oink/            # 主题
└── product-docs/    # 你的站点

用环境变量 HUGO_MODULE_REPLACEMENTS 把模块临时替换为本地 checkout,go.mod 不变:

cd ~/pgsty/product-docs
HUGO_MODULE_REPLACEMENTS='github.com/pgsty/oink -> ../oink' hugo server

文档站仓库的 Makefile 就是这几条命令的别名,make devmake check 要求主题 checkout 在同级目录 ../oink

Makefile:文档站里的写法
build:
	hugo --cleanDestinationDir --minify

check:
	HUGO_MODULE_REPLACEMENTS='github.com/pgsty/oink -> $(abspath ../oink)' npm test

dev:
	HUGO_MODULE_REPLACEMENTS='github.com/pgsty/oink -> $(abspath ../oink)' hugo server --renderToMemory

Go workspace(go work init + HUGO_MODULE_WORKSPACE=go.work)是等价的另一种做法。两种做法都只作用于本机:CI 与生产构建用的是 go.mod 里的版本,go.work 不要提交。

验证

hugo mod graph                                       # 主题实际解析到哪一版
hugo --gc --minify --printPathWarnings --panicOnWarning

构建以 Total in … 结束、没有 WARN / ERROR 即通过。再确认:

  • /docs/ 打得开,侧栏里有你写的页面
  • 顶栏有搜索框,搜得到刚写的标题
  • 深浅色切换按钮在,切换后代码块配色跟着变(说明 markup.highlight.noClasses: false 生效)
  • git status 里有 go.modgo.sum,没有 public/resources/