了解构建与运行时边界。
OINK
Oink 是一款从 Docsy 演化而来的独立、本地优先 Hugo 文档主题。它保留 Docsy 成熟的内容模型,同时把一套实现确立为标准产品:文档外壳、仅依赖 Hugo 的消费端构建、本地浏览器运行时、多语言基础设施,以及可复用的内容组件。
公开 Hugo Module 是 github.com/pgsty/oink;文档与回归内容独立存放在
github.com/pgsty/oink.pgsty.com。
产品契约
唯一标准主题
Oink 不是叠加在另一套 Docsy 安装之上的皮肤。项目不存在 oink.enabled
开关、不建立 params.oink.*
命名空间,也没有需要同步维护的第二套视觉实现。主题仓库根目录中的布局与资源就是产品本身。
语言、模块、菜单、输出与标记设置使用 Hugo 原生配置;语义仍然适用时沿用 Docsy 现有参数;只有当主题确实需要用户做出选择时,才增加职责单一的参数。
消费端仅依赖 Hugo 构建
站点导入模块后,生产构建命令只有:
hugo --gc --minify
消费站点无需安装 Node.js、npm、PostCSS、Autoprefixer 或浏览器端软件包。项目站点仓库中的维护工具不属于消费端构建契约。
默认本地优先
Bootstrap、Font Awesome、Web 字体、本地搜索、图表与 API 文档运行时,以及 Oink 内容组件都随主题提供。资源从生成后的站点提供;在可行的情况下,只有实际使用相应能力的页面才会加载它们。
作者仍可链接互联网、嵌入远程媒体、启用托管服务,或配置 PlantUML 与 Diagrams.net 端点。但这些网络边界必须显式声明;对于主题自带能力,Oink 不会暗中选择公共端点。
把多语言作为基础设施
语言行为完全根据 Hugo 已配置的语言和页面译文推导。Oink 会输出语言、书写方向、canonical、hreflang
和 Open Graph locale 元数据,并支持 .md 与 .zh.md 这样的并置译文。
交付内容
- 响应式文档与博客外壳、导航、搜索、打印输出、深色模式和移动端行为;
- 本地 Mermaid、KaTeX、Markmap、Swagger UI、Redoc、Asciinema、ECharts 与 Infographic 运行时;
- 折叠块、标签页、卡片、导航卡片、文档卡片和轮播;
- 翻译资源,以及记录来源、许可证和校验值的版本化
VENDOR.json; - Hugo 模块声明、Apache-2.0 许可证与必需归属信息。
不在交付范围内的内容
主题仓库不包含项目网站、生成的 public/ 输出、npm
workspace、产品专用控件或部署配置。这些职责留在消费站点或独立的项目站点仓库中。
生产站点应固定发布标签或不可变 commit,而不是跟随 main。
代码仓库
| 仓库 | 用途 |
|---|---|
pgsty/oink |
发布主题与 Hugo Module |
pgsty/oink.pgsty.com |
文档、示例、测试与部署 |
本地开发主题时,把两个仓库克隆为同级目录,并用被忽略的 Go workspace 连接。
项目状态
当前验证基线为 Hugo Extended 0.164.0,主题声明的最低版本为
0.160.1。本地构建成功,本身并不能证明公开标签、托管站点或下游部署已经存在。
项目保留 Docsy 的 Apache-2.0 历史与归属信息。源码与离线发行包必须保留
LICENSE、NOTICE 以及适用的第三方声明。
后续步骤
1 - 快速开始
Oink 以 Hugo Module github.com/pgsty/oink 发布。消费站点只需 Hugo
Extended 即可构建;Node.js、npm、PostCSS 和 CDN 托管的浏览器软件包不属于构建契约。
前置条件
安装 Git、Go 与 Hugo Extended 0.160.1 或更高版本。项目站点目前使用 0.164.0
验证:
git --version
go version
hugo version
Hugo 版本输出必须包含 extended。
添加模块
在 Hugo 站点根目录中,如果站点还没有模块,先初始化模块,再固定一个 Oink 版本:
hugo mod init github.com/example/product-docs
hugo mod get github.com/pgsty/oink@THEME_REF
请把 THEME_REF 替换为 v0.16.0 之类的已发布标签,或不可变的 commit。然后在
hugo.yaml 中添加导入:
module:
imports:
- path: github.com/pgsty/oink
提交生成的 go.mod 与 go.sum。生产构建不要跟随未固定版本的分支。
预览站点
启动编辑服务器:
hugo server --disableFastRender
生成生产构建产物:
hugo --gc --minify
Oink 已随主题提供 Bootstrap、Font
Awesome、字体、搜索、图表、API 文档运行时和内容组件。消费站点不需要
node_modules 目录。
使用本地 checkout 开发
把主题与站点克隆为同级目录,再使用本地 Go workspace:
~/pgsty/
├── oink/
└── product-docs/
cd ~/pgsty/product-docs
go work init .
go work edit -replace=github.com/pgsty/oink=../oink
export HUGO_MODULE_WORKSPACE=go.work
hugo server
不要把 go.work 提交到版本库。已提交的 go.mod
仍固定公开模块;workspace 只在本机把它替换为同级 checkout。
添加双语内容
先创建英文页面:
content/docs/operations.md
然后在旁边添加译文:
content/docs/operations.zh.md
front matter 标识符、代码、命令、参数名与链接目标应保持语义一致;面向读者的正文则需要翻译。为了让不同语言下的深层链接保持稳定,请在中文标题中显式保留英文标题 ID:
## 故障恢复 {#failure-recovery}
配置最小站点
最基本的配置很精简:
title: Product Docs
baseURL: https://docs.example.com/
defaultContentLanguage: en
languages:
en:
label: English
locale: en-US
weight: 1
zh:
label: 简体中文
locale: zh-CN
weight: 2
params:
logo: icons/logo.svg
offlineSearch: true
module:
imports:
- path: github.com/pgsty/oink
hugoVersion:
extended: true
min: 0.160.1
站点扩展后,再逐步加入菜单、输出格式、Markdown 扩展、仓库链接和可选功能。支持的配置模型详见配置。
发布前验证
至少完成以下检查:
- 使用已经提交模块文件的全新 checkout 构建;
- 使用固定版本的 Hugo Extended 运行
hugo --gc --minify; - 浏览具有代表性的英文与中文页面;
- 验证语言切换、搜索、移动导航、深色模式与打印输出;
- 如果站点承诺离线运行,检查浏览器网络请求。
这些检查只能证明构建产物成立。发布该产物并验证托管地址,是两个独立的部署步骤。
2 - 架构
Oink 是一款直接运行于 Hugo 的主题,而不是应用服务器,也不是套在 Docsy 外面的运行时包装层。Hugo 在构建阶段解析内容、配置、布局与资源,再生成可由普通文件托管服务发布的静态站点。
系统边界
flowchart LR C[站点内容] --> H[Hugo Extended] G[Hugo 配置] --> H T[Oink Hugo Module] --> H V[已提交的第三方资源] --> T H --> P[静态 public 目录] P --> B[浏览器]
消费端边界始于站点与已经解析的主题模块,止于 Hugo 生成的静态文件。在这条路径中,不需要 JavaScript 包管理器、CSS 后处理器可执行文件或远程资源下载。
交互功能仍会在浏览器中运行 JavaScript。“仅依赖 Hugo”描述的是构建依赖,并不意味着用户界面完全没有 JavaScript。
仓库边界
主题仓库
github.com/pgsty/oink 是公开 Hugo
Module。仓库根目录包含标准布局、partial、短代码、SCSS、JavaScript、字体、图标、浏览器运行时、翻译资源、go.mod
与 hugo.yaml。VENDOR.json 记录随附的第三方资源。
该仓库不包含项目网站或 npm
workspace。README.md、LICENSE、NOTICE、theme.toml
与 vendor 清单等根元数据,是发布和标注主题来源所必需的内容。
项目站点仓库
github.com/pgsty/oink.pgsty.com
包含文档、双语示例、回归页面、站点专用布局与资源、基于 npm 的站点测试,以及部署配置。它在
hugo.yaml 中导入公开主题模块,并在 go.mod 中固定版本。
跨仓库本地开发时,被忽略的 go.work
会替换为同级主题 checkout;站点模块不会提交相对文件系统 replacement。
构建流水线
Hugo 会合并四类输入:
- 消费站点的页面 bundle 与 Markdown 内容;
- Hugo 原生配置和受支持的主题参数;
- 主题模板、翻译、SCSS 与 JavaScript;
- 已提交的 static 或 Hugo Asset 资源。
Hugo 使用内置流水线编译 SCSS、打包页面 JavaScript、压缩生产资源、为适用产物生成指纹,并按照配置的
baseURL 重写相对 URL。Oink 不调用 Hugo 的 postCSS pipe。
最终 public/
目录包含 HTML、CSS、JavaScript、字体、搜索索引、feed、sitemap 与复制的静态文件;部署时不需要源码树。
页面外壳
标准页面外壳由小型 partial 组装:
- 全局 navbar 与响应式次级导航;
- 语言和颜色模式控件;
- 可调整宽度、可折叠的文档侧栏;
- 面包屑、目录、阅读元数据、反馈与仓库链接;
- 公共页脚与打印布局。
Hugo 的正常模板查找机制仍可用于站点扩展。应覆盖最小范围的 partial,而不是复制
baseof.html 或整个外壳。
条件运行时加载
内容短代码会在 page store 中记录功能使用情况。资源 partial 检查这些标记,并且最多加入一次对应本地运行时:
flowchart TD
S[短代码渲染] --> M[设置页面功能标记]
M --> A[组装资源]
A --> Q{是否使用功能?}
Q -- 是 --> L[加入一次本地运行时]
Q -- 否 --> O[省略运行时]
因此普通文章不会加载 ECharts、Asciinema 或 Infographic,同时功能页仍可包含多个组件实例。
多语言路由
Oink 把语言身份交给 Hugo 管理。选择器使用每页的 .Translations
与按权重排序的站点语言。缺少译文时回退到目标语言首页;同一组数据也用于 canonical 与 alternate 元数据。
安全边界
Oink 区分作者数据与作者提供的可执行代码:
- 结构化 ECharts 选项按 JSON 或 YAML 解析并安全序列化;
- ECharts 中的 JavaScript 默认拒绝,除非显式启用 unsafe 迁移开关;
- 组件 ID 与配置由模板生成,不通过未转义 HTML 字符串拼装;
- 托管搜索、分析、评论、远程媒体与服务端点始终由站点显式决定。
Goldmark 的 unsafe
设置允许受信任的项目作者使用行内 HTML;它不是针对不受信任输入的净化器。
上游维护
Oink 保留 Docsy 的源码历史与 Apache-2.0 义务。上游变更会被分类为适用、已被 Oink 有意差异取代,或无关。适用变更会移植到标准实现中,而不会重新制造上游与品牌两套运行模式。
扩展边界
一项实现如果广泛可复用、具有稳定内容 API,并能自行管理资源与无障碍行为,就应放入主题;如果它嵌入产品数据、价格、目录假设或一次性落地页结构,则应保留在站点中。
3 - 本地优先运行
OINK 的本地优先原则很简单:由主题提供的功能,不得暗中依赖公共 CDN、构建期下载或未经配置的公共服务。完整发行包能够在网络隔离环境中构建,其核心页面也能在该环境中浏览。
本地优先覆盖的范围
主题会从生成后的站点提供以下依赖:
| 能力 | 本地交付方式 |
|---|---|
| 页面外壳与响应式界面 | Bootstrap 与 OINK CSS/JavaScript |
| 图标与字体 | Font Awesome、Open Sans、Chakra Petch、IBM Plex Mono |
| 搜索 | Lunr、CJK 子串回退与按语言生成的索引 |
| 图表与公式 | Mermaid、KaTeX 与 Markmap |
| API 文档 | Swagger UI 与 Redoc |
| 富内容 | Asciinema、ECharts、Infographic 与轮播运行时 |
这些资源提交在 assets/ 或 static/ 中。Hugo 会按照站点 baseURL
发布它们,部署在子路径时也不例外。
本地优先不覆盖的范围
OINK 无法让作者任意添加的内容自动离线。以下项目仍然是显式网络选择:
- 外部链接、远程图片、视频、iframe 与 API 规范;
- Algolia、Google CSE 等托管搜索;
- 分析、评论、身份提供商与其他 SaaS 集成;
- 作者主动选择远程渲染器时的 PlantUML 或 Diagrams.net。
使用这些功能的页面仍然可以是有效页面,但站点不应再宣称该页面能够完全离线使用。
依赖服务的图表
PlantUML 与 Diagrams.net 不同于纯浏览器库:其常规工作流依赖渲染或编辑服务。因此 OINK 不提供隐含的公共端点。
启用 PlantUML 却未设置
params.plantuml.svg_image_url,或启用 Diagrams.net 却未设置
params.drawio.drawio_server
时,构建会失败并给出可操作的错误信息。你可以配置受控的本地端点、发布预渲染图片,或者明确选择远程服务:
params:
plantuml:
enable: true
svg: true
svg_image_url: https://diagrams.internal.example/plantuml/svg/
drawio:
enable: true
drawio_server: https://diagrams.internal.example/
为了继续渲染继承而来的图表示例,OINK 文档回归站显式配置了公共演示服务。这是样例站自己的选择,不是主题默认值,也不应复制到网络隔离站点。
本地搜索
设置:
params:
offlineSearch: true
Hugo 会为每种语言生成搜索索引。浏览器对拉丁文字查询使用本地 Lunr 搜索,对 CJK 文本使用本地子串回退。查询内容不会离开站点。
为了提高搜索质量,请编写清晰的标题与摘要、设置正确的页面语言,并排除不应进入公开客户端索引的生成页面或敏感页面。本地索引可被每位访客下载,不能充当访问控制手段。
页面级资源加载
OINK 不会给每个页面都加载所有运行时。Mermaid、KaTeX、Markmap、Swagger UI、Redoc、Asciinema、ECharts、Infographic 与轮播,会根据页面功能标记按需选取。不使用某个组件的页面不会收到对应运行时。
同一页包含多个相同组件实例时,运行时仍只会加入一次。在 Hugo 流水线允许的情况下,生产资源会生成指纹,便于提供完整性元数据并使用长期缓存。
第三方来源追踪
VENDOR.json 是随附依赖的机器可读清单。每项依赖都会记录:
- 名称与固定版本;
- 原始来源;
- 适用的许可证文件;
- 已选取产物的路径与 SHA-256 值;
- 维护者更新流程。
主题会在 vendor 资源旁保留相应许可证。更新运行时意味着同时更新产物、许可证与 NOTICE 材料、校验值和测试,使整个变更可以一次性审查。
获取离线归档
使用 Oink release 附带的版本化主题归档与 checksum。把两个文件传入隔离环境后运行:
shasum -a 256 -c oink-vX.Y.Z.tar.gz.sha256
tar -xzf oink-vX.Y.Z.tar.gz
mkdir -p product-docs/themes
mv oink-vX.Y.Z product-docs/themes/oink
让隔离站点使用解压后的传统主题:
theme: oink
归档必须包含
go.mod、hugo.yaml、布局、资源、静态文件、翻译、LICENSE、NOTICE 与
VENDOR.json。在断网构建中使用前,应先检查归档内容。
验证隔离站点
有意义的网络隔离验收必须同时覆盖构建阶段与浏览器阶段:
- 从已经验证的主题归档和空 Hugo 缓存开始;
- 阻断出站 HTTP、HTTPS 与 Go Module 代理;
- 运行 Hugo 生产构建命令;
- 浏览生成结果中的英文与中文页面;
- 操作搜索、深色模式、图表、API 文档与内容组件;
- 检查全部 HTML 和 CSS 子资源 URL,确认没有意外远程来源。
项目站点回归套件会针对本地主题候选版本执行这些检查。一次成功只能证明被测提交与环境;每个候选版本以及每次随附依赖更新后都应重新验证。
内容安全策略
本地资源让严格的内容安全策略(CSP)更容易实现,但 OINK 不会为所有站点虚构一份万能策略。作者行内 HTML、ECharts unsafe 模式、分析服务、远程规范与自定义集成都可能改变所需指令。
请从能够支持已审查功能的最小策略开始。让 ECharts 保持结构化数据模式,避免任意行内脚本,只为站点主动启用的集成增加远程来源。
4 - 内容组件
OINK 把已经在多个 PGSTY 站点证明具有复用价值的内容组件纳入主题。每个组件都有稳定的作者接口、唯一实例 ID、本地资源和明确的安全边界。站点专用的数据控件仍然留在主题之外。
加载模型
交互式短代码会标记页面实际使用的功能。OINK 随后为每个所需样式或运行时只加入一次,即使页面中存在多个组件实例也不例外。普通页面不会下载从未使用的组件代码。
相对资源与链接参数会经过 Hugo URL 处理,因此部署到 baseURL
子路径时仍然正确。在适用情况下,组件标记还覆盖打印、深色模式、移动端、键盘操作与减少动态效果偏好。
Asciinema
使用 asciinema 播放保存在本地的 .cast 终端录像:
{{< asciinema
file="oink/demo.cast"
speed="1.5"
markers="0:开始,1:完成"
>}}
file 是必填参数,也可以作为第一个位置参数传入。支持的选项包括
theme、fit(width、height、both 或 none)、autoplay、loop、
preload、speed、startAt、poster、cols、rows、idleTimeLimit、
pauseOnMarkers,以及逗号分隔的 markers。
为了离线使用,请把 cast 文件保存在本地。只有作者显式提供远程 URL 时,组件才会访问远端。
ECharts
默认安全模式接受 JSON 或 YAML,并把解析后的值序列化到 application/json
元素中:
{{< echarts height="280px" >}}
xAxis: { type: category, data: [源码, 构建, 发布] }
yAxis: { type: value }
series: [{ type: bar, data: [1, 2, 3] }]
{{< /echarts >}}
height 默认为 400px,并且必须使用安全的 CSS 长度单位。theme
用来选择 ECharts 主题,full=true 则移除通常的正文宽度限制。
旧页面可能包含 JavaScript 围栏代码块与 $fn:name 引用。除非短代码设置
unsafe=true,或站点临时启用以下开关,否则 OINK 会拒绝这种可执行形式:
params:
content:
echarts_unsafe: true
该开关只能用于经过审查的迁移过程。新图表应始终采用结构化 JSON/YAML 模式。
Infographic
infographic 使用本地运行时渲染 AntV Infographic DSL:
{{< infographic >}}
infographic list-row-simple-horizontal-arrow
data
items
- label 源码
desc Markdown 与配置
- label 构建
desc Hugo Extended
- label 发布
desc 静态文件
{{< /infographic >}}
height 接受 auto 或安全 CSS 长度;full=true
会移除通常的正文宽度限制。DSL 会作为数据序列化,而不是作为可执行脚本插入页面。
卡片与轮播
doc-card 与 nav-card 共用同一套卡片实现;doc-cards 与 nav-cards
可以创建一至四列的响应式卡片组。这些别名让现有站点内容继续使用语义最贴切的名称,同时避免复制标记与样式。
{{< nav-cards cols="3" >}}
{{< nav-card
title="架构"
link="/zh/docs/oink/architecture/"
icon="fa-solid fa-diagram-project"
desc="了解构建与运行时边界。"
>}}
{{< nav-card
title="部署"
link="/zh/docs/oink/deployment/"
badge="仅依赖 Hugo"
>}}发布静态输出。{{< /nav-card >}}
{{< /nav-cards >}}
卡片接受 title、link、image、alt、icon、desc、accent 与
badge。卡片正文可以包含 Markdown 链接。desc 中的 {version}
之类 token,在站点参数存在同名值时会被替换。
把文档卡片放进 doc-carousel,即可生成无障碍横向轮播:
{{< doc-carousel label="OINK 工作流" >}}
{{< doc-card title="编写" >}}创建成对内容。{{< /doc-card >}}
{{< doc-card title="构建" >}}运行 Hugo Extended。{{< /doc-card >}}
{{< doc-card title="验证" >}}检查静态站点。{{< /doc-card >}}
{{< /doc-carousel >}}
label
提供轮播的无障碍名称。方向键与可见的上一个/下一个按钮都能移动轨道;启用减少动态效果偏好时,不必要的动画会被禁用。
折叠块
details 输出原生 details 与 summary 元素:
{{% details title="为什么只依赖 Hugo?" closed="false" %}}
已经提交的浏览器资源让消费端构建保持可复现。
{{% /details %}}
为什么只依赖 Hugo?
title 设置摘要。折叠块默认关闭;设置 closed=false 可让它初始展开。
标签页
OINK 沿用 Docsy 的 tabpane 与 tab 创作模型,同时保留导入站点依赖的
selected=true 与空白处理行为:
{{< tabpane text=true >}}
{{< tab header="本地" selected=true >}}
使用完整本地主题构建。
{{< /tab >}}
{{< tab header="Cloudflare" >}}
从源分支运行同一条 Hugo 命令。
{{< /tab >}}
{{< /tabpane >}}
Markdown 内容应设置
text=true;否则标签页会按代码进行语法高亮。标签页还支持按语言保存选择、禁用标签,以及右对齐条目。生成的标签与面板 ID 会形成正确的 ARIA 对应关系。
参数
param 输出页面参数;页面没有该参数时,会回退到同名站点参数:
当前版本:{{< param version >}}
当前版本:v0.16.0
指定参数不存在时,短代码会让构建失败。这是有意设计:缺少发布版本或仓库信息时,不应悄悄生成误导性文档。
现有富内容能力
OINK 也为继承而来的内容功能提供本地运行时:
mermaid、math与markmap围栏代码块;swaggerui与redocAPI 文档短代码;- Docsy blocks、alert、image、include、readfile、cards 等既有短代码。
创作规则
- 优先使用结构化数据,而不是可执行内容。
- 为图片编写有意义的
alt文本,并为轮播设置清晰的label。 - 除非内容确实需要,否则不要启用自动播放。
- 创建新包装组件时,要在同一页测试多个完全相同的实例。
- 检查键盘导航、焦点可见性、深浅色主题、移动布局、打印输出与减少动态效果行为。
- 把带有业务语义的数据组件留在消费站点。
5 - 配置
OINK 遵循“原生优先”的配置模型。站点身份、语言、菜单、输出、taxonomy、标记与模块继续放在 Hugo 规定的位置;语义仍然适用的 Docsy 参数也保持原位。只有无法可靠推导的行为选择,OINK 才会增加职责明确的配置。
配置原则
- 优先使用 Hugo 配置,不创建主题专用的重复项。
- 优先使用成熟的 Docsy 参数,不另造 OINK 同义词。
- 品牌、内容、仓库与 UI 选项应放在各自语义位置。
- 内部 vendor 路径与模板组装方式不属于公开 API。
- 遇到非法值或缺少必需端点时,应尽早失败。
OINK 不提供 oink.enabled 开关,也不建立 params.oink.*
配置树。增加这些配置会制造第二套主题模式,让每项修复、测试和文档都产生歧义。
完整基线配置
以下示例把英文设为首要语言、简体中文设为第二语言:
title: Product Documentation
baseURL: https://docs.example.com/
defaultContentLanguage: en
enableRobotsTXT: true
languages:
en:
label: English
locale: en-US
weight: 1
title: Product Documentation
menus:
main:
- { name: Docs, pageRef: /docs, weight: 10 }
- { name: Blog, pageRef: /blog, weight: 20 }
zh:
label: 简体中文
locale: zh-CN
weight: 2
title: 产品文档
menus:
main:
- { name: 文档, pageRef: /docs, weight: 10 }
- { name: 博客, pageRef: /blog, weight: 20 }
outputs:
home: [HTML]
section: [HTML, RSS, print]
markup:
goldmark:
renderer:
unsafe: true
extensions:
passthrough:
enable: true
delimiters:
block: [['\[', '\]'], ['$$', '$$']]
inline: [['\(', '\)']]
highlight:
noClasses: false
params:
logo: icons/logo.svg
offlineSearch: true
offlineSearchIndex: summary
offlineSearchMaxResults: 10
github_repo: https://github.com/example/product-docs
github_branch: main
footer_icp: ''
footer_icp_url: https://beian.miit.gov.cn/
copyright:
authors: Example Authors
from_year: 2026
ui:
showLightDarkModeMenu: true
quick_links: [docs, blog]
sidebar_menu_foldable: true
sidebar_item_overflow: wrap
breadcrumb_disable: false
module:
imports:
- path: github.com/pgsty/oink
hugoVersion:
extended: true
min: 0.160.1
模块版本固定在站点的 go.mod 中。使用传统主题 checkout 时,可以把仓库放在
themes/oink/,并改用 theme: oink。
语言
defaultContentLanguage 决定不带路径前缀的首要站点;语言 weight
控制显示顺序;label 是该语言的自称;locale 提供完整的 HTML 与 SEO
locale。对于 RTL 语言,还应设置 languageDirection: rtl。
文件命名
本站使用并置模型:
content/docs/guide.md
content/docs/guide.zh.md
基本名称相同的文件互为译文,其逻辑页面身份应保持一致。OINK 读取 Hugo 建立的翻译关系,不会根据任意 URL 模式猜测。
选择器状态
语言选择器不需要模式参数。只配置一种语言时隐藏;配置两种或更多语言时,点击语言图标会按
weight 顺序切换到下一种语言,悬停半秒或聚焦图标则打开完整菜单。
当前页面缺少目标译文时,会进入目标语言首页。不要为了让选择器停留在同一路径而生成貌似存在、实际失效的页面 URL。
品牌与代码仓库
请设置站点与各语言的 title 和描述。params.logo 可以指向 Hugo
Asset,也可以指向 static/
下的路径。favicon 与社交分享图应放在文档指定的资源位置。
仓库元数据用于生成“编辑此页”、问题反馈和最后修改记录链接:
params:
github_repo: https://github.com/example/product-docs
github_project_repo: https://github.com/example/product
github_branch: main
github_subdir: site
在支持的位置,github_project_repo 默认回退到 github_repo。github_subdir
是内容站在 monorepo 中的路径。github_branch
必须能够解析;用于展示的版本号不一定是 Git ref。
导航与布局
OINK 沿用 Docsy 菜单与 UI 参数,并增加职责明确的外壳控制项:
params:
page_width: normal
ui:
quick_links: [docs, blog]
sidebar_width_min: 220
sidebar_width_max: 480
sidebar_item_overflow: wrap
sidebar_menu_compact: true
sidebar_menu_foldable: true
sidebar_root_enabled: true
sidebar_root_menu: true
sidebar_search_disable: false
breadcrumb_disable: false
showLightDarkModeMenu: true
page_context_menu:
enable: true
links: []
readingtime:
enable: true
page_width 接受 normal、wide 或 full,也可以在页面 front
matter 中覆盖。侧栏最小与最大值以像素为单位,用来限制桌面端拖动调整的范围。sidebar_item_overflow: wrap
会让长标签换行;其他值保持紧凑的省略号行为。
quick_links 指定外壳中显示的顶层 page
reference。请在各语言主菜单中定义相应的本地化名称。
页面上下文菜单在所有视口宽度下都把“复制 Markdown”“查看 Markdown”、编辑、反馈与打印入口放在页面标题旁。links
默认为空,因此站点未主动启用时,不会向外部 AI 服务发送页面信息。自定义链接可使用经过 URL 编码的
{url}、{title} 与 {markdown_url} 占位符:
params:
ui:
page_context_menu:
enable: true
links: []
# - name: 询问外部助手
# icon: fa-solid fa-wand-magic-sparkles
# url: https://assistant.example/new?source={markdown_url}&title={title}
首页与页脚
首页内容位于
data/home/<language>.yaml;缺少相应语言数据时回退到英文。可配置的顶层区块包括
hero、metrics、capabilities、principles、cta 与
footer。每个区块都可以省略,因此无需复制布局也能得到更精简的首页。例如:
hero:
eyebrow: 本地优先的产品文档
title_lines:
- words:
- { mark: P, text: roduct, color: red }
- { mark: D, text: ocs, color: blue }
lead: 只用 Hugo 构建和交付的技术文档。
actions:
- { label: 阅读文档, url: docs/, icon: fa-solid fa-book, style: primary }
footer:
brand:
name: Product Docs
tagline: 支持 **Markdown** 的简短介绍。
slogan: 让答案离产品更近。
columns:
- title: 产品
links:
- { label: 概览, url: docs/ }
首页会在通用小页脚上方渲染品牌与导航组成的大页脚。小页脚左侧来自
params.copyright,中间使用可选的 params.footer_icp 与
params.footer_icp_url,右侧列出所有已配置语言。版权作者与大页脚品牌文字中的 Markdown 会渲染为真实链接与行内标记。
搜索
starter 默认使用本地搜索:
params:
offlineSearch: true
offlineSearchIndex: summary
offlineSearchSummaryLength: 70
offlineSearchMaxResults: 10
offlineSearchIndex
控制每种语言索引中可下载的文本范围,四档范围逐级累加:title
索引标题与分类元数据;heading 增加页面标题;summary
增加描述或摘要;content 再加入完整正文。content
是兼容旧行为的默认值,而多数文档站可从体积更小的 summary
开始。offlineSearchMaxResults 同时约束 Lunr 与 CJK 子串兜底结果数。
每种语言都会得到独立索引。通过 Docsy 既有配置仍可使用托管搜索,但启用它们会显式增加外部服务边界。除非已经决定界面应显示哪一种,否则不要同时配置多个相互竞争的搜索提供方。
内容运行时
纯浏览器运行时
Mermaid 与 KaTeX 会根据内容自动检测;Markmap 需要在站点级启用:
params:
markmap:
enable: true
mermaid:
theme: default
Swagger UI、Redoc、Asciinema、ECharts、Infographic 与轮播资源会在相应短代码出现时加载。它们的本地运行时路径属于内部实现,不应配置。
服务端点
PlantUML 与 Diagrams.net 需要显式端点:
params:
plantuml:
enable: true
svg: true
svg_image_url: https://diagrams.internal.example/plantuml/svg/
drawio:
enable: true
drawio_server: https://diagrams.internal.example/
网络隔离站点应保持这些功能关闭,除非上述 URL 可以在隔离网络内部访问。
ECharts 迁移开关
结构化 ECharts 输入默认安全:
params:
content:
echarts_unsafe: false
只有迁移包含 JavaScript 且已经审查的旧页面时,才把它设为
true。更好的做法是在最小范围的短代码实例上设置
unsafe=true,随后重写图表并删除例外。
页面级覆盖
Hugo 的 .Param 查找机制允许在 front matter 中覆盖许多站点参数:
---
title: Wide reference
page_width: wide
hide_feedback: true
hide_readingtime: true
ui:
no_left_sidebar: false
scrollSpy:
disable: false
---
只应为真实的内容差异使用覆盖,不要靠逐页设置重建另一套视觉系统。
避免虚假配置
不要暴露:
- 在“Docsy”与“OINK”外壳之间切换的开关;
- vendor JavaScript、CSS、字体或内部 partial 的路径;
- 品牌命名空间下重复的语言或仓库值;
- 只用于二选一复制实现的开关。
如果站点需要定制产品矩阵或门户,请把该组件留在站点,并使用范围明确的 hook 或短代码。清晰的本地业务功能,优于误导性的全局主题选项。
验证配置变更
修改配置后:
- 分别使用最低支持版本与当前验证版本的 Hugo Extended 构建;
- 测试每种已配置语言,以及至少一个缺少译文的页面;
- 如果同时支持根路径与子路径部署,验证两种
baseURL输出; - 检查本地搜索与可选运行时请求;
- 检查桌面端和移动端外壳、深浅色主题与打印输出。
真正可接受的配置必须能够正确构建并按预期运行,而不只是可以被 YAML 解析。
6 - 部署
OINK 部署包含两个独立阶段:Hugo 先生成完整的 public/
目录,静态托管服务再发布该目录。请把构建验证与线上验证分开,避免把一次成功的本地命令误认为已经完成生产发布。
生产构建
在站点根目录使用固定版本的 Hugo Extended 运行:
hugo --gc --minify --cleanDestinationDir
--gc 会清理不再使用的缓存资源,--minify
生成生产资源,--cleanDestinationDir 删除上次构建残留的文件。如果 publishDir
不是站点专用输出目录,使用最后一个参数前必须先检查命令目标。
构建应顺利完成,并且不能用忽略告警的方式掩盖缺失内容、端点或资源。上传前请先在本地检查
public/。
本地预览
编辑期间运行:
hugo server --disableFastRender
Hugo 开发服务器只能证明源码可以渲染;它不是生产托管服务,实时重载行为也不属于生成后的站点。每次发布前都应执行一次干净的生产构建。
静态托管
任何能够提供目录和文件的主机都可以发布 OINK:
- 对象存储与 CDN;
- GitHub Pages、GitLab Pages 或类似的 Git 驱动静态托管;
- Netlify、Cloudflare Pages 或其他构建并发布的平台;
- Nginx、Caddy、Apache 或内部文件服务器。
请把 baseURL 设置为生产环境 canonical URL。如果站点发布在
https://example.com/manual/
之类的子路径下,应包含该路径并进行测试;OINK 的本地资源与组件 URL 设计为支持子路径部署。
Cloudflare Pages
让 Pages 直接连接源分支。OINK 不需要由 GitHub Actions 预先构建并推送孤立 Pages 分支。
当前 starter 使用以下设置:
| 设置 | 值 |
|---|---|
| 生产分支 | main,或经过审查的源分支 |
| 根目录 | 独立站点目录 |
| 构建命令 | hugo --gc --minify |
| 构建输出目录 | public |
HUGO_VERSION |
0.164.0 |
SKIP_DEPENDENCY_INSTALL |
1 |
截至 2026-08-08,Cloudflare Pages v3 构建镜像文档中的默认 Hugo 版本为
0.147.7,低于 OINK 最低要求
0.160.1。应当在 Production 与 Preview 中都显式设置
HUGO_VERSION,不要依赖持续变化的平台默认值。 SKIP_DEPENDENCY_INSTALL=1
可阻止平台的通用依赖安装器增加站点并不需要的前端安装步骤。
预览环境如需使用 Pages 生成的地址作为构建 canonical URL,可以运行:
hugo --gc --minify --baseURL "$CF_PAGES_URL"
Cloudflare 官方文档把 public 列为 Hugo 标准输出目录,并说明了 HUGO_VERSION
覆盖与 CF_PAGES_URL base
URL 用法。调整构建镜像或固定 Hugo 版本时,请重新核对平台文档。
参阅 Cloudflare Hugo 指南与Cloudflare 构建镜像。
网络隔离部署
在断网环境中,应同时传入站点源码与已经验证的主题归档,而不是依赖首次构建时下载 Hugo Module:
- 验证主题归档附带的 SHA-256 文件;
- 在环境中安装受支持的 Hugo Extended 二进制文件;
- 把主题解压到站点的
themes/oink/目录; - 设置
theme: oink,并在站点中运行hugo --gc --minify; - 把
public/发布到内部静态服务器。
除非已经配置隔离网络内可达的端点,否则请保持 PlantUML 与 Diagrams.net 关闭。外部链接与嵌入内容仍由内容作者负责。
响应头与缓存
带指纹的 CSS 与 JavaScript 可以使用长期 immutable 缓存。HTML、搜索索引、Feed 与站点地图应使用较短缓存或重新验证,以便新部署及时生效。
项目站点提供了适用于部分托管平台的 static/_headers
示例。它只是起点,并非可移植标准。应根据站点实际使用的行内内容与集成审查安全响应头。
预览与生产 URL
canonical、hreflang、Open Graph、Feed 与绝对链接都依赖
baseURL。生产构建应使用生产 URL;如果链接验证或社交元数据需要准确,预览构建可以使用临时地址。
不要把针对预览地址生成的产物直接发布到生产环境;反过来,也不要因为预览中出现有意传入的预览域名就判定失败。
部署验收
每一层都要独立验证:
源码与配置
- 预期提交与固定主题版本确实存在;
baseURL、语言、菜单、仓库元数据与可选端点正确;- 未发布草稿或秘密信息没有进入公开内容树。
构建产物
- 使用固定版本的 Hugo Extended 完成干净生产构建;
- 英文、中文、Feed、站点地图、搜索索引与
404.html均存在; - 本地资源在根路径与配置的子路径下都能解析;
- 产物包含所需许可证与归属说明。
托管站点
- 生产 URL 返回新产物;
- canonical 与备用语言 URL 使用生产域名;
- 导航、搜索、语言切换、深色模式、打印和代表性组件在真实浏览器中工作;
- 重定向、自定义响应头、缓存策略与
404行为符合配置; - “支持网络隔离”的结论有浏览器网络审计作为依据。
绿色构建日志只完成产物阶段;托管检查全部通过后,部署才算完成。
回滚
保留上一份已知可用的静态产物或托管平台部署标识。新版本未通过线上验证时,应先恢复该产物,再诊断源码或平台行为。使用新的、未固定工具链重新构建旧提交,并不等同于恢复原产物。
7 - 迁移现有站点
OINK 旨在替换各站点复制的公共外壳、运行时与短代码,而无需批量重写普通正文。安全迁移应按依赖关系删除覆盖项,把产品专用行为留在站点,并在修改生产环境前先验证临时副本。
迁移原则
- 固定目标实现,不要把生产站点迁移到未固定版本的分支。
- 删除覆盖项之前先完成清点。
- 删除公共主题副本,不删除站点业务逻辑。
- 在 OINK API 兼容的地方,保持内容 URL、front matter 与短代码行为不变。
- unsafe 或在线例外必须显式声明,并且只在过渡期使用。
- 分别测试构建产物、浏览器行为与托管站点行为。
固定目标版本
请在 go.mod 中固定已发布标签,或使用完整的版本化归档。预发布评估期间,Hugo
Module 站点可以使用被忽略的 Go
workspace,在不修改已提交模块版本的情况下解析本地 checkout:
go work init .
go work edit -replace=github.com/pgsty/oink=/absolute/path/to/oink
export HUGO_MODULE_WORKSPACE=go.work
hugo --gc --minify
站点的 hugo.yaml 导入 github.com/pgsty/oink;workspace 只替换本地 checkout。
清点现有覆盖项
把每个站点级文件归入以下四类之一:
| 类别 | 处理方式 |
|---|---|
| 公共外壳的完全或近似副本 | OINK 验证通过后删除 |
| OINK 已提供的可复用组件 | 删除或机械重命名 |
| 范围明确的品牌或产品定制 | 保留,再缩小到最小 hook |
| 业务专用数据或交互 | 留在站点 |
layouts/、assets/、static/、配置与构建工作流必须一起检查。复制的短代码通常还伴随一份 JavaScript
bundle、样式表、vendor 文件和 CI 安装步骤。
迁移配置
搜索与品牌
启用主题提供的本地搜索,并让外壳使用站点自己的 Logo:
params:
logo: img/product.svg
offlineSearch: true
继续在原有语义位置使用
title、languages.*、github_repo、github_project_repo、
github_branch、page_width 与 ui.*,不要把它们迁入 oink.* 命名空间。
ECharts 旧内容
旧 Pigsty 页面可能在 ECharts 块中包含 JavaScript。仅在经过审查的过渡期启用:
params:
content:
echarts_unsafe: true
新建与已经转换的图表都应使用 JSON 或 YAML。迁移完成后删除站点级开关;确实暂时无法转换时,也应把
unsafe=true 限制在具体短代码上。
字体
旧 Sass 开关 $td-enable-google-fonts: true 现在会选择 OINK 随附的本地 Open
Sans,而不会请求 Google Fonts。$td-web-font-path
不再参与当前构建。需要其他字体的站点必须提供获准使用的本地资源及其许可证。
删除公共覆盖项
临时构建证明等价后,可以删除站点中的以下副本:
layouts/baseof.html与公共 docs/blogbaseof*.html;- 公共 navbar、footer、sidebar、目录(TOC)、search、head CSS partial 及相应 hook;
- 旧的公共品牌文档外壳 partial;
asciinema、echarts、infographic、doc-carousel、details、tab/tabpane、card 与param短代码副本;- 只服务于上述已删除实现的 JavaScript、Lunr 副本、轮播代码与 SCSS;
- 不再被任何站点资源需要的消费端 PostCSS 与 Autoprefixer 步骤。
应按引用关系删除,而不是直接清空
layouts/。首页、下载页与门户仍可能调用本地 icon、search dialog、blog
row 或 tag filter 等 partial。
保留站点专用行为
保留语义属于具体产品的内容与代码:
- 产品矩阵与兼容性数据;
- 价格、下载、门户、解决方案与目录页面;
- 站点专用首页结构;
- 自定义重定向、响应头、分析或身份集成;
- 承载业务数据、而非通用呈现逻辑的内容组件。
对于 Pigsty 家族,pgvers、pgext_matrix、pgext_os_matrix、home-docs
以及当前 metric 实现继续留在站点层。
参考站点矩阵
当前迁移计划采用以下边界:
| 站点 | 删除或迁移 | 保留 |
|---|---|---|
| SILO | 公共 docs/blog 外壳、核心短代码与重复运行时;设置 logo: img/silo.svg |
首页、下载页与产品数据 |
| PGSTY | 公共外壳与核心短代码;设置 logo: img/logo/logo.svg |
门户、解决方案与企业页面 |
| SOW | 公共 docs/blog 外壳、核心短代码与重复运行时;设置 logo: img/sow.svg |
首页与仓库专用内容 |
| Pigsty | 公共外壳、核心短代码与重复运行时;设置 logo: icons/logo.svg,并在过渡期审查旧 ECharts |
扩展矩阵、首页与价格页、目录样式 |
该矩阵是清点工作的起点,并不意味着可以删除所有名称相似的文件。必须在目标检出目录中解析实际模板引用。
演练工作流
请在消费站点的临时副本中演练迁移。应用本地 Oink workspace,每次删除一组计划覆盖项,阻断非预期网络与前端工具访问,再执行生产构建:
HUGO_MODULE_WORKSPACE=go.work hugo --gc --minify
演练不得修改源工作区。保留失败副本用于诊断,并记录准确的主题 commit、Hugo 版本、已删除文件与输出数量。
当前证据
最近一次记录的演练发生在 2026-08-08,使用 Hugo Extended 0.164.0:
| 站点 | 演练结果 | HTML 文件数 |
|---|---|---|
| SILO | 删除 20 个公共覆盖项;完整构建中英文内容,OINK 外壳、同源搜索与站点 Logo 生效 | 1,095 |
| PGSTY | 删除 20 个公共覆盖项;构建双语门户,并用临时 docs 页面验证外壳 | 16 |
| SOW | 删除 20 个公共覆盖项;完整构建中英文内容,OINK 外壳、同源搜索与站点 Logo 生效 | 128 |
| Pigsty | 删除 24 个公共覆盖项;保留三个业务矩阵短代码,并启用经过审查的旧 ECharts unsafe 模式 | 2,473 |
这些是临时副本的构建结果,不代表四个生产站点已经完成迁移或部署。
生产迁移流程
对每个站点依次执行:
- 创建专用迁移分支;
- 固定 OINK 候选版本并记录其源码提交;
- 每次只删除一组内聚的覆盖项;
- 执行干净的 Hugo-only 构建与针对性自动化测试;
- 比较具有代表性的首页、文档页、博客页、特殊页面与
404页面; - 检查移动导航、两种颜色模式、语言切换、搜索、打印与站点保留的业务组件;
- 部署预览,并验证真实 URL 与网络请求;
- 评审通过后才合并并部署,随后执行生产冒烟测试。
对于 OINK 有意改变外壳的部分,应记录合理差异,而不是强求像素级相同。
回滚
保留迁移前的主题 pin、站点提交与已知可用部署产物。回滚时三者应一致恢复。针对新主题只重新引入一部分随机复制布局,会形成比任一完整版本都更难诊断的混合状态。
8 - 发布流程
Oink 把实现、验证、公开发布与部署视为不同状态。一次绿色本地构建是有价值的证据,但它不是公开标签、可下载模块,也不是已经部署的文档更新。
发布状态
| 状态 | 必需证据 |
|---|---|
| 源码完成 | 范围、文档、变更日志、归属信息与评审全部完成 |
| 验证完成 | 主题模块与项目站点检查通过 |
| 公开发布 | pgsty/oink 中存在不可变根标签,并可通过 Go 解析 |
| 文档完成 | pgsty/oink.pgsty.com 固定并记录该标签 |
| 部署完成 | 托管文档与目标消费站点通过验证 |
应报告准确状态与证据;不要把本地构建称为发布。
版本管理
主题版本在 github.com/pgsty/oink 使用 vX.Y.Z
这样的根标签。主题现在就是仓库根模块,因此不再使用嵌套的 theme/vX.Y.Z 标签。
项目站点的 version 参数标识发布的站点变体,不会自动成为 Git ref。安装说明与
go.mod 必须使用真实可解析的主题标签。
验证主题仓库
从干净的 pgsty/oink checkout 开始:
- 检查源码 diff 与归属信息变更;
- 验证
VENDOR.json中每个文件与 SHA-256; - 确认仓库没有生成的
public/、资源缓存、node_modules/或内嵌 example site; - 使用最低版本与当前支持版本的 Hugo Extended,通过 Hugo Module 路径构建最小消费站点;
- 检查 module zip,确认布局、资源、翻译、静态文件、许可证与 NOTICE 都存在。
module zip 测试很重要,因为 Go 会从发布模块中排除 vendor
等特殊目录名。Oink 把随附依赖放在
assets/third_party/,确保它们能进入模块发行物。
验证项目站点
把 pgsty/oink 与 pgsty/oink.pgsty.com
克隆为同级目录,再用被忽略的 workspace 连接:
cd oink.pgsty.com
go work init .
go work edit -replace=github.com/pgsty/oink=../oink
export HUGO_MODULE_WORKSPACE=go.work
npm install
npm test
检查具有代表性的中英文页面、移动导航、两种颜色模式、本地搜索、打印输出、图表、API 文档与
404 页面。这只能验证候选版本与站点配合正常,不会发布任何仓库。
标记并发布主题
评审完成后,在主题仓库中创建一个不可变的签名根标签:
git tag -s vX.Y.Z -m "Oink vX.Y.Z"
git push origin main vX.Y.Z
推送以及创建 GitHub release 都需要明确授权。标签公开后,从干净环境验证:
hugo mod get github.com/pgsty/[email protected]
hugo mod graph
如果 release 附带离线归档,请发布并独立验证 SHA-256。归档中必须保留
LICENSE、NOTICE 与 VENDOR.json。
更新项目站点
主题标签能够公开解析后,更新独立站点仓库:
hugo mod get github.com/pgsty/[email protected]
hugo mod tidy
npm test
把
go.mod、go.sum、版本参数、变更日志与升级指南一起提交。先验证部署预览,评审通过后再推进生产发布分支。
发布后验证
公开发布后:
- 从干净 clone 获取标签并检查签名;
- 通过公开 Go proxy 解析模块;
- 使用文档命令构建一个全新最小站点;
- 打开生产文档,验证模块说明、canonical 链接、语言、搜索与资源;
- 验证所有发布归档与 checksum;
- 记录最终标签、模块版本、托管 URL 与产物哈希。
热修复与回滚
热修复范围可以更小,但仍要经过同一证据链。绝不能移动或替换已经发布的标签。站点需要回滚时,应恢复到已知产物;主题需要修复时,则发布新的补丁版本。
完成定义
只有批准的标签已经存在、公开模块能够解析、必需检查通过、项目站点固定该标签,并且托管冒烟测试成功,版本才算发布完成。任何未完成项都应按实际状态报告。