跳转到主要内容

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

返回本页常规视图.

十分钟上手

克隆 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.idOINK 的统计 ID,删掉;需要统计时换成你自己的
    params.commentsgiscus 指向 pgsty/oink.pgsty.com 的讨论区,整段删掉或换成你的仓库
    params.tdVersion params.version params.version_menu params.versionsOINK 的版本菜单,删掉
    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 git@github.com: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.mdlayout: 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 .npmrcNode 版本与 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/oink@v0.6.0

两条命令都会改写 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/oink@v0.6.0

    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/oink@v0.6.0
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 Modulego.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/