发布上线
OINK 站点的产物是一个纯静态目录,任何能托管静态文件的地方都能部署,不需要 Node 运行时、服务端渲染或构建插件。托管商一侧只有三件事:用正确的 Hugo 版本执行一条命令、发布 public/、让 baseURL 与最终访问地址一致。
前提是本机已经能完成零告警的生产构建。
确定 baseURL
baseURL 是最常见的故障源,失败方式也隐蔽:页面能打开,但搜索索引 404、页面操作链接指向错误位置、部分资源加载失败。
部署到域名根目录:
部署到子路径(https://example.com/docs/)时,路径必须写进 baseURL:
也可以在构建时覆盖,让同一份源码部署到不同位置:
canonifyURLs 修子路径Hugo 的 canonifyURLs 默认 false,保持这个默认值。OINK 的模板与内容链接都基于 baseURL 解析:路径不对是 baseURL 不对,打开 canonifyURLs 会把本来正确的相对链接一起改写,让问题更难定位。
判断是否配对,看构建后搜索索引的请求路径:浏览器应当去 <baseURL>/offline-search-index.zh.json 取索引,取到别处就是 baseURL 不对。
选一个托管商
源码托管在 GitHub 时,一份 Actions 工作流就够:构建在 Actions 里执行,产物通过 Pages 部署 API 发布,不需要维护 gh-pages 分支。
把下面的文件提交到仓库:
这是本站正在使用的工作流。几处不能删:
fetch-depth: 0— 站点开了enableGitInfo时,「最后修改时间」和贡献者信息要读完整 Git 历史,浅克隆会让它们为空。setup-go+go mod download— Hugo Module 方式引入主题时,Hugo 需要 Go 才能解析模块。用 submodule 安装主题的站点改成submodules: recursive,用离线归档的站点把themes/oink/提交进仓库,这两步都可以去掉。GOWORK: off与HUGO_MODULE_WORKSPACE: off— 防止本地开发用的go.work意外参与 CI 构建,保证 CI 验证的是go.mod里固定的那个公开标签。--baseURL "${{ steps.pages.outputs.base_url }}/"— 项目站点的 URL 形如https://<OWNER>.github.io/<REPO>/,configure-pages会把它算出来,不用手写。--panicOnWarning— 有告警不发布。
在仓库 Settings → Pages → Build and deployment 里把 Source 设为 GitHub Actions,推一次 main,在 Actions 标签页查看第一次运行。
自定义域名在同一设置页的 Custom domain 里填写,并按提示配置 DNS,随后把 hugo.yml 里的 baseURL 换成这个域名。发布流程需要产物里带 CNAME 文件时,把它放进 static/CNAME,Hugo 会原样复制到 public/。
Cloudflare Pages 从关联的 GitHub / GitLab 仓库构建,并为每个评审分支创建预览部署。构建在平台侧完成,仓库里不用放工作流。
在 Workers & Pages 里导入仓库,选定生产分支:
四点说明:
HUGO_VERSION必须显式设置,Production 与 Preview 两个环境都要设。Cloudflare v3 构建镜像的默认 Hugo 版本低于 OINK 要求的0.160.1,不固定版本会在构建镜像更新时静默改变工具链。SKIP_DEPENDENCY_INSTALL=1关掉通用依赖安装步骤。OINK 消费端不需要 Node.js,仓库里只给维护工具用的package.json不应由平台安装。- Hugo 站点不在仓库根目录时,把 Root directory 设成站点目录,输出目录相对它解析。
- 预览部署不要当成生产发布。预览需要用自动生成的 Pages URL 作 base URL 时,构建命令改成
hugo --gc --minify --baseURL "$CF_PAGES_URL",生产发布用规范域名重新构建一次。
检查第一次构建日志:正常的 OINK 消费端构建只有一条 Hugo 命令,不会执行 npm、PostCSS、Autoprefixer,也不会下载主题自有的浏览器资源。
Netlify — 构建命令 hugo --gc --minify,发布目录 public,环境变量 HUGO_VERSION。同样的设置可以写进仓库:
用 submodule 安装主题就打开递归 submodule 检出;用 Hugo Module 就要求构建环境有 Git 和 Go。生产与预览应使用同一个 Hugo 版本,除非预览环境本来就是用来测升级的。
Vercel — 同样的三件事:构建命令 hugo --gc --minify、输出目录 public、环境变量 HUGO_VERSION。它同样不需要安装 npm 依赖。
任何静态服务器(Nginx / Caddy) — 把 public/ 的内容整个铺上去:
站点是纯静态的,没有需要转发给应用服务器的路径。
对象存储 — Hugo 自带 deploy 命令,把目标写进配置即可:
构建之后执行 hugo deploy:它比对远端与 public/ 的差异,只上传变化的文件,并在给了 cloudFrontDistributionID 时使 CDN 缓存失效。不带 --target 时用第一个目标,--dryRun 先看要改什么。两个前提:Hugo 二进制带 withdeploy(hugo version 的输出里能看到),云厂商凭据由标准环境变量或配置文件提供(AWS 上先用 aws s3 ls 确认)。
离线打包 — 网络隔离环境里,在能联网的机器上构建,把产物打成一个包带过去:
构建时就要用目标环境的 baseURL,产物里的绝对链接不能在解包之后再改。
托管商没有 Go — 用 Hugo Module 引入主题需要构建环境有 Go。平台不提供时,改用 Git submodule(构建前执行 git submodule update --init)或离线归档(把 themes/oink/ 提交进仓库),见从零建站与其它安装方式。
预览部署不要被收录
Hugo 的 -e / --environment 只选择构建期行为,不改变站点内容,但 OINK 有三处会跟着它变:production 环境才输出 <meta name="robots" content="index, follow">、才让 robots.txt 变成 Allow: /、才渲染 Google Analytics 模板。PR preview、staging 这类构建不要用 --environment production:
出来的产物自带 noindex, nofollow 与 Disallow: /,也不会向分析服务上报数据。
内容安全策略
主题自带的运行时、字体与图标都是同源资源,严格的内容安全策略(CSP)因此可行。主题不提供一份通用策略:需要哪些指令由站点启用了什么决定。
改变所需指令的地方有五处:
- 作者写的行内 HTML 与行内脚本,
renderer.unsafe: true之下由作者负责。 - ECharts 的
$fn:回调:回调函数由站点注册到window.OinkEchartsFunctions,注册脚本的来源要进script-src。 - 分析脚本:站点自己插入的那段脚本与它上报的目标。
- 远程 API 规范与自建图表服务:落在
connect-src与img-src。 - giscus:
script-src与frame-src要一起放行。
从只覆盖已审查功能的最小策略起步,逐项放行:不需要回调时让 ECharts 选项保持纯数据,审查作者写的行内脚本,只为站点主动启用的集成添加远程来源。产物里的子资源来源可以先用断网构建验证里的脚本扫一遍。
验收清单
部署完成后按这张表走一遍。前四项是构建期的,后面几项要在真实 URL 上查。
sitemap.xml、robots.txt、.md 与 llms.txt 这几项的开关在配置总览,Agent 输出的细节见 Agent 支持。
回滚
静态站点的回滚就是重新发布上一个已知可用的 commit,不要在生产上手工改文件。
- GitHub Pages:在 Actions 里找到上一次成功的
Deploy Oink site to GitHub Pages运行,点 Re-run all jobs;或者git revert出问题的提交再推一次。 - Cloudflare Pages / Netlify / Vercel:在部署列表里选上一个成功的部署,用平台的 Rollback / Publish deploy 把它重新设为生产版本。
- 自建静态服务器:保留上一份
tar.gz,解压覆盖。离线打包里给产物加日期后缀就是为了这一步。
问题出在主题升级而不是内容时,回滚的是 go.mod 里固定的版本,见版本升级。
相关
- 本地预览 — 生产构建的完整命令、清缓存与断网验证
- 排错与检查 — 404、搜索无结果、平台相关故障
- 分析与 SEO — 上线之后让搜索引擎正确收录
- 版本升级 — 升级主题版本与回滚
- 配置总览 —
baseURL、outputs与其它站点键