This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Oink project and website documentation

How the Oink theme and website are built, maintained, and deployed.
Section under construction.

Content (planned)

Planned content organization (tentative):

  • About: High-level information about the project, including its purpose, ownership, and overall status.
  • Design: Architectural design, Information Architecture (IA), layout, UX choices, theme related decisions, and other design-level artifacts.
  • Implementation: Code-level structure and conventions, Hugo/Docsy templates, SCSS/JS customizations, patches, and internal shims.
  • Build: Tooling, local development setup, CI/CD workflows, deployment environments, and automation details.
  • Quality: Link checking, accessibility standards, tests, review practices, and other quality-related processes.
  • Roadmap: Milestones, backlog, priorities, technical debt, and design/implementation decisions.

Site build information

Oink version: v0.16.1-dev+003-over-main-be0a08fe

1 - About the project

High-level information about the project, including its purpose, ownership, and overall status.

This section is under development.

1.1 - ReadMe

Oink theme README

Oink is a local-first Hugo theme for engineering documentation. It ships its styles, fonts, search, diagrams, API documentation runtimes, and content components with the theme, so consuming sites need only Hugo Extended, Go, and Git.

Read the current theme README for installation instructions, requirements, and attribution.

1.2 - Changelog

Docsy repository changelog

We document breaking changes and release highlights in this page, with maintainer-facing changes summarized at the end of each release section. For the full list of changes of any particular release, see the release notes.

Useful links: Releases & tags, jump to the latest release, and view the milestones.

Style guide

  • Use past tense when when describing releases.
  • Generally, start each change entry with a verb (in the past tense). For example: Added, Changed, Deprecated, Fixed.
  • It’s ok to follow that with “you can now…”. For example:

    Feature abc: you can now…".

  • For additional guidance, see Keep a Changelog1.

Definitions

Definitions...

Public customization surface

As a Hugo theme, Docsy exposes various features that client projects may rely on, such as:

  • Layouts
  • Styles
  • Configuration options
  • Runtime behavior

Aspects of these features are part of Docsy’s public contract, which we also refer to as the public customization surface. We refer to such feature aspects as public for short.

Because Docsy follows semantic versioning, we will not introduce breaking changes to the public customization surface outside of major version releases.2

Private/internal features

Aspects outside the public customization surface are considered private and internal features and implementation details.

Experimental features

Experimental features are not part of the public customization surface and may change or be removed in future releases.

We release experimental features so that projects can try them out and share feedback.

Breaking change

A breaking change is a backward-incompatible change to Docsy’s public contract that requires client projects to update their configuration, content, or customizations in order to:

  • Build successfully (without errors), or
  • Preserve existing, significant site functionality or user experience, including visual design

See semver.

Official support

Docsy is maintained with very limited resources and only supports the latest releases of Docsy, its dependencies & tools, and operating systems.

Specifically, the Docsy team officially supports the following:

  • Production use: the latest official release of Docsy — a stable semver version from the following sources:

    npm installs of Docsy from GitHub (google/docsy) are for development and testing only, not production use.

  • Issue reports: over the latest official release, a current pre-release, or the main branch.

  • The tool versions as specified for the Docsy release you are using:

  • Operating systems: macOS (latest minor release) and Linux.

Everything else — including Windows — is supported on a best-effort basis.

Bug fixes

We define a bug as undesirable behavior documented through an issue. Classify bug-fix commits or pull requests (PRs) under Fixed or Other changes, unless they extend beyond a fix and affect user-facing functionality. In that case, classify them as a Breaking change or New functionality, depending on scope. Prefer narrow, focused PRs where possible.

v0.16.1 or v0.17.0 - UNRELEASED

UNRELEASED: this planned version is still under development

For the full list of changes, see the 0.16.1 or 0.17.0 release page.

Breaking changes:

New:

Other changes:

Experimental:

For maintainers:

  • Committed the npm lockfiles; dependency installs are now lock-exact, and unreviewed dependency scripts are disabled (#2700).

v0.16.0

For an introduction to this release, see the 0.16.0 release report. For Hugo-specific notes, see the Hugo 0.158+ upgrade guide. For the full list of changes, see the 0.16.0 release page or the git history since 0.15.0.

New:

Breaking changes:

Other changes:

Experimental:

  • Added a shared chrome build mode (td.chrome) that renders the repeated chrome (navbar, footer, left-nav) once per locale, for much cheaper link checking of large sites (#2659).

For maintainers:

  • Reorganized the repository package boundary: theme/package.json owns theme runtime dependencies, and the root package orchestrates the oink.pgsty.com and theme workspaces (#2617).
  • Added build and test guards (Hugo deprecation output, fixture-site regressions) and moved link checking from htmltest to Lychee. See 0.16.0 release report.

v0.15.0

For an introduction to this release, see the 0.15.0 release report. For the full list of changes, see the 0.15.0 release page.

New:

Breaking changes:

  • Community and footer link paths changed for multilingual sites; see blog (#2580).
  • Version menu markup and mobile visibility changed for sites using params.versions; see Version menu entries (#2557, #2586).
  • card shortcode rendering changed for Markdown arguments; breakage risk is low. See blog.

Other changes:

v0.14.3

Patch release 0.14.3 applies the layout fix for #2561, which ensures .td-main > .row grows vertically (#2569).

v0.14.2

Key fix for this patch: Apply .td-main flex only when sidebar exists (#2546).

For the full list of changes, see the release report and 0.14.2 release page.

Breaking changes (style-only):

New:

Other changes:

  • Added name attribute to search form field for better semantics and autofill (#2549).
  • Package version build metadata and footer icon tweaks (#2547).

v0.14.1

Patch release 0.14.1: fixed ToC sidebar width in xl viewports (#2538).

v0.14.0

Resources:

Breaking changes:

New:

Other changes:

v0.13.0

Resources:

Breaking changes:

New:

Other changes:

  • Improved accessibility: color contrast and typography (#2285).
  • Dark mode fixes and improvements:
  • Mobile navbar: added scroll indicators for overflow navigation (#2406).
  • Better NPM support: resolved optional and peer dependency issues (#2115). See breaking changes in the blog post.
  • Dependency updates: Bootstrap 5.3.8, Hugo 0.152.2, Node LTS ≥24.
  • Updated translations: added Occitan locale (#2173) and refreshed Simplified Chinese (#2313) and Ukrainian (#2331).
  • TOC visibility control: documented the notoc page parameter (available since 2016) for hiding the table of contents on specific pages (#2405).
  • Build-time rendering of mathematical and chemical formulae: now uses Hugo’s embedded KaTeX engine (#2276, #2394, #2395). For details, see LaTeX support with KaTeX.

Experimental:

v0.12.0

For the full list of changes, see the 0.12.0 release page.

Breaking changes:

  • Renamed the default Docsy heading render hook and heading self-link partials. This is a breaking change only if your project uses this feature. For details, see Heading self links (#2223).
  • Relocated and adapted layouts in response to Hugo’s new template system. For details, see Adapt to new template system in Hugo v0.146.0 #2243.
  • IMPORTANT: if your project overrides any of the layout files mentioned in #2243, then apply the same name changes in your project files. In particular, note that:
    • Taxonomy-related layout files: names have been swapped, and terms.html is now singular (#2257):
      • Renames _default/taxonomy.html to term.html (singular)
      • Renames _default/terms.html to taxonomy.html
    • Renames layouts/**/content.html by adding a _td- filename prefix (#2259).

Potential breaking changes:

  • Removed shortcode figure, hugo’s built-in shortcode figure can/will be used instead.

New:

Other changes:

  • Blog section index page content and title were ignored, they are now displayed (#1787). To recover the old behavior use the following style override: .td-section.td-blog .td-content { display: none; }.
  • Adds a comment shortcode, as a drop-in replacement for the one removed from Hugo’s built-in shortcode.

v0.11.0

For the full list of changes, see the 0.11.0 release page.

New:

v0.10.0

For an introduction to this release, see the 0.10.0 release report. For the full list of changes, see the 0.10.0 release page.

New: color themes and dark-mode support! For details, see Color themes and dark-mode support.

Breaking changes:

  • Removed shortcode card-code that was deprecated in 0.7.0; use shortcode card with named parameter code=true instead.
  • The following SCSS variables are inlined in favor of dark-mode compatible styling: $border-color, $td-sidebar-tree-root-color, $td-sidebar-bg-color, $td-sidebar-border-color (#1952)

Style changes (potentially breaking):

  • Adjusted the style of various shortcodes and elements so that they are compatible with light/dark mode. For details, see Important style changes in Color themes and dark-mode support.

v0.9.1

Patch release. For details, see 0.9.1.

v0.9.0

For an introduction and commentary, see the 0.9.0 release report. For the full list of commits, see the 0.9.0 release page. The most significant changes of this release are listed next.

Breaking changes:

  • Repository Links now work for multi-language sites (#1744).

    For any given page, repository links are now computed from a page’s resolved File path — as resolved through mount points, if any. That is, the path used is the one that refers to the file’s actual location on disk, not its logical path in Hugo’s union file system.

    This is a breaking change for pages of sites that use mounts and path_base_for_github_subdir. Projects will need to adjust the value of path_base_for_github_subdir to be relative to the file’s physical location.

  • Class names to disable repository links were misnamed with a suffix of the form --KIND. The new suffix is __KIND. For details, see Disabling links.

  • Heading self-link support has been reimplemented and projects must now explicitly enable the feature. For details, see Heading self links.

Footer changes: refactoring, for easier customization, and simplification. For details concerning all footer changes, see #1818.

  • Footer layout factored into parts: left, right, and center, with copyright a subpart of center. For details see Footer layout
  • Footer copyright, supports date-range, and site copyright fallback. For details, see Footer copyright.
  • Footer streamlined: the About-page footer link and All-rights-reserved text are now hidden by default. For details, see Footer streamlined.

Other changes:

v0.8.0

For the full list of changes, see the 0.8.0 release page.

Breaking changes:

  • Docsy is packaged as a single Hugo module (#1120). For details, see Use Docsy as a Hugo Module.
  • Important: for non-Hugo-module projects, running npm install in the Docsy theme directory now creates a github.com sibling folder (via Docsy’s postinstall script). For guidance on the Hugo-reported “failed to load modules” error, see #2116.
  • Page feedback, or User feedback:
    • In support of projects configuring analytics outside of Docsy, feedback functionality is enabled regardless of whether site.Config.Services.GoogleAnalytics.ID is set (#1727).
    • Feedback-event attribute changes (#1726):
      • Event name is page_helpfulrather thanclick
      • Event value for “yes” is 100 by default, rather than 1, allowing for more response options in the future. To override the default set params.ui.feedback.max_value.
  • SCSS: @function prepend() and file assets/scss/support/_functions.scss have been dropped. Instead use the more general SASS/SCSS list join() function (#1385).

v0.7.2

For the full list of changes, see the 0.7.2 release page. We mention some noteworthy changes here:

  • Algolia
    • #1651 DocSearch fixed for mobile and for sites with two search boxes (in the top and left navs).
    • #1662 DocSearch is supported by Docsy through site config.
    • For details, see Algolia DocSearch.
  • Tabbed panes:
    • persistLang is deprecated, use persist instead
    • Persistence is enabled by default (independent of the old persistLang parameter value) ; to disable use persist=disabled
    • Various fixes and enhancements, with more to come; for details, see #1641 and Tabbed panes.
  • Left-nav, and right-nav (TOC + page meta): spacing issues have been resolved; for details, see #1661.

v0.7.1

For the full list of changes, see the 0.7.1 release page.

Followup changes to Bootstrap (BS) 5.2 upgrade (#470):

  • td-blog-posts-list__item and td-blog-posts-list__body replace the .media and .media-body classes, dropped by BS 5 #1560.
  • Docsy test for Bootstrap version has been made more robust, and can be disabled. For details, see #1579.

v0.7.0

For the full list of changes, see the 0.7.0 release page.

New:

  • Click to copy button for Chroma-highlighted code blocks: If you already implemented this functionality on your website, you can disable it. For details see Chroma highlighting docs.

Breaking changes:

  • Hugo release 0.110.0 or later is required.
  • Upgraded Bootstrap (#470) to v5.2. For a list of Bootstrap’s breaking changes, see the Bootstrap migration page. Docsy-specific changes:
    • Clean up of unused, or rarely used, variables, functions, and mixins:
      • Dropped $primary-light
      • Dropped color-diff()
      • Dropped bg-gradient-variant() mixin (#1369)
    • Docsy’s RTL support has been removed because it is incompatible with BSv5. For progress on the reintroduction of RTL support, see #1442.
  • Shortcodes:
    • Now using Hugo’s native support for processing HTML & markdown, not file extension testing. (#906)
    • Dropped support for pre-Hugo-0.54.x behavior of shortcodes with markdown, {{%...%}}. (#939)
    • blocks/section: default and accepted values of the type argument have changed! For details, see blocks/section (#1472).
    • Card shortcodes (#1376)]:
      • Renamed CSS class td-card-deck to td-card-group.
      • card, card-code: markup of inner content (HTML/markdown) now depends on the syntax of the calling shortcode, not on extension of page file any more #906.
      • card-code is deprecated; use card with named parameter code=true instead.
  • Detection of draw.io diagrams is now disabled by default #1185

Other changes:

  • $list-inline-padding is increased in support of footer icons (#1523). If this global adjustment is a problem for your project, let us know and we can contextualize the adjustment to the footer.
  • Non-breaking changes that result from the Bootstrap v5 upgrade:
    • Draw.io diagram edit button: replaced custom colors by BS’s outline primary.

v0.6.0

For the full list of changes, see the 0.6.0 release page.

With this release we declare a feature freeze while we migrate to the newest Bootstrap version. See the announcement for more information.

New:

  • Simplified use of mermaid diagrams: when using a mermaid code block on your page, mermaid is now automatically enabled (needs hugo version >= 0.93.0). For existing sites built with hugo 0.93.0+, parameter mermaid.enable can be removed from site config.

  • Add render hook for chem code blocks: add auto-activation of math and chem blocks via KaTeX and mhchem. Support for formula rendering activation on individual pages only. Hugo version >= 0.93.0 required.

v0.5.1

For the full list of changes, see the 0.5.1 release page. BREAKING CHANGES are documented below.

After you update your project’s Docsy:

  • Update your project setup (see 0.4.0) if you haven’t already.
  • Run npm install.

New:

Breaking changes:

  • Tabbed panes, text display. By default, the content of a tab inside a tabbed pane is shown as code. As of version 0.4 of the shortcode, you can add the parameter code=false to your tabpane or tab shortcode in order to render tab content(s) as text (markdown or html). As of version 0.5 the name of this parameter was changed, we now use text=true in order to mark content as text.
  • Display logo by default. Most projects show their logo in the navbar. In support of this majority, Docsy now displays a logo by default. For details on how to hide the logo (or your brand name), see Styling your project logo and name.
  • Upgraded Bootstrap to v4.6.2 from v4.6.1, resulting in some style changes (such as an adjustment in the size of small). For details, see v4.6.2 release page.
  • Upgraded FontAwesome to v6 from v5. While many icons were renamed, the v5 names still work. For details about icon renames and more, see What’s changed in v6.
  • Search-box: the HTML structure and class names have changed, due to the Font Awesome upgrade, for both online and offline search. This may affect your project if you have overridden search styling or scripts.

Other changes:

v0.5.0

Unpublished.

v0.4.0

For the full list of changes, see the 0.4.0 release page. Potential BREAKING CHANGES are documented below.

After you update your project’s Docsy, run npm install.

Update your project setup:

If your project uses Docsy as follows:

Docsy now fetches Bootstrap and FontAwesome as NPM packages rather than git submodules. This has an impact on your project-build setup. To migrate your site, follow these steps (execute commands from your project’s root directory):

  1. Delete obsolete Docsy Git submodules:
    git rm themes/docsy/assets/vendor/Font-Awesome
    git rm themes/docsy/assets/vendor/bootstrap
    
    These commands remove the submodules from Git’s tracking, from the .gitmodules file, and deletes the submodule files under themes/docsy/assets/vendor.
  2. Get Docsy dependencies:
    (cd themes/docsy && npm install)
    
  3. Update your build scripts to fetch Docsy dependencies automatically. For example, if your site build uses NPM scripts, consider getting Docsy dependencies via a prepare script as follows:
    {
      "name": "my-website",
      "scripts": {
        "prepare": "cd themes/docsy && npm install",
        "...": "..."
      },
      "...": "..."
    }
    
  4. (Optional) Build script cleanup. If your project uses Docsy as a git submodule, Docsy updates no longer require the --recursive flag when running git submodule update. Consider dropping the flag if you have no other recursive git submodules.

Proceed as usual to build or serve your site.

v0.3.0

For the full list of changes, see the 0.3.0 release page.

Breaking changes:

v0.2.0

For the full list of changes, see the 0.2.0 release page.

New:


  1. Old entries might not follow this guidance; feel free to update them as needed. ↩︎

  2. Docsy is not yet at version 1.0.0, so we are bound by the pre-v1 semantic versioning rules. We treat minor releases as if they were major releases. ↩︎

1.3 - Maintainer notes

Notes for Docsy maintainers

For our main contributing page covering license agreements, code of conduct and more, see Contributing. This page is for maintainers only.

Content placement

Keep project content DRY by writing each fact in the artifact whose purpose and audience it serves. Each artifact links to the more detailed ones rather than restating them:

  • Changelog: a lean record of what changed, for developers who want a quick overview. No upgrade advice, implementation detail, or background. Entries link to the release report for details and cite a change’s key issues — PRs only when there is no key issue, such as for contributor credit. Maintainer-facing changes get a short For maintainers list at the end of the release section.
  • Release and upgrade blog posts: what’s new, what to watch out for, and actionable upgrade guidance — the historical narrative. Link to the site docs for current behavior and reference detail. Don’t enumerate PRs and issues; link an open tracker only where it adds follow-up context. Upgrades are a chore, so keep posts maximally actionable yet lean: the release summary reads like a selective table of contents — a link per section with a clause of guiding glue — and each fact appears in one section, its home. Maintainer-facing changes are summarized in a For maintainers section at the end of the documented changes.
  • Site docs (docs/): Docsy as it is now. Minimal historical references or links to issues and PRs.
  • Release notes and milestones: the exhaustive record — generated release notes list every PR, PRs link their motivating issues, and the release milestone gathers the issues resolved. Authored artifacts link to these rather than reproducing the enumeration.
  • Test and code comments: implementation rationale and regression background.

PR descriptions

Generally speaking, a PR opening comment should be a Markdown list that explains the “why” behind the changes, and at a very high level what was changed. Start each item with a verb in the present tense, 3rd person singular.

PR authors are encouraged to flag the scope of changes when a PR touches Docsy’s public customization surface – especially for breaking changes – to help reviewers and release-time audits. For example:

- Scope: breaking (removal), user-facing (new)

Suggested scope labels (use one or more):

  • breaking, user-facing, internal-only, docs-only.

Optionally qualify with kinds in parentheses, mapping to release-blog and changelog sections: new, change, fix, removal, deprecation.

The release-time audit (see Release-prep audit) is the source of truth for what gets documented; PR-level scope labels are a hint, not a substitute.

Hugo versions

The repo tracks two distinct Hugo versions, as documented below. Their declarations, synchronization requirements, and relative-version constraints are guarded by test:hugo-versions.

Only current-state pages — docs and the changelog’s official support section — render these versions live, via the hugoMinVersion site param and the hugo-version shortcode. Blog posts are historical snapshots and render versions time-insensitively: a post that renders one of these version params freezes it in its front matter, so updating the post (say, for a patch release) means editing one field. (Version literals in narrative text are already time-insensitive.) Page params take precedence over site params, so the same {{% param hugoMinVersion %}} call is frozen in a post and live in docs. Guarded by test:hugo-versions.

Minimum Hugo version

Docsy declares the minimum Hugo version required to support the features that Docsy provides and to cover important security fixes.

This version is declared in three places that must agree:

theme.toml is Hugo’s legacy theme descriptor: its min_version is read only as a fallback when the module config sets none, and the file’s sole remaining external consumer is the themes showcase, which ingests it from the theme’s git repo. Hence the npm package omits it (theme/package.json files).

Raising the minimum is a breaking change for theme users, only done to support new features or security fixes. To validate that a Docsy site actually builds with Hugo pinned to the declared minimum, run test:smoke.

Officially supported Hugo version

The Hugo version that Docsy officially supports is pinned as the hugo-extended dev dependency in oink.pgsty.com/package.json.

This version is generally kept in sync with the latest Hugo release; to update it, run:

  • npm -C oink.pgsty.com run update:hugo for the latest
  • npm -C oink.pgsty.com install -DE hugo-extended@X.Y.Z for a specific version

Docs render this version live through the hugo-version shortcode (hugo.Version): oink.pgsty.com builds always run the pinned Hugo.

Test suites

From the repo root:

Script Role
test:fixture-site Fast, offline checks over minimal monolingual fixture sites — paths oink.pgsty.com can’t cover
test:hugo-versions Fast, offline checks of the Hugo versions declarations and constraints
test:smoke Slow, network-bound; builds a site from GitHub several ways (NPM, Hugo module, clone, minimum-Hugo)
test:tooling Unit tests for repo scripts
test:website Full oink.pgsty.com checks: format, links, hugo-build, alt-site, md-output, and favicon tests

All but test:smoke run in CI; smoke tests are run manually for PR-branch validation (they auto-target the current branch’s GitHub upstream).

The md-output and favicon tests compare built output against committed golden files. When a golden test reports intended drift, run npm run update:goldens to rebuild the site and refresh both suites’ goldens, then review the diff and commit it.

test:website checks oink.pgsty.com’s links with Lychee, caching external-link results in the committed oink.pgsty.com/.lycheecache (the “refcache”) so checks stay fast and offline-friendly. Config lives in oink.pgsty.com/lychee.toml. CI installs a pinned lychee binary (see .github/workflows/test.yaml); a plain site build doesn’t need it.

  • Refresh after adding or changing external links: npm run fix:refcache re-runs the check, adding any missing entries and renormalizing — then commit the updated .lycheecache.
  • Inspect or prune with npm run refcache (-- -s for a summary, -- -p 10% to drop the oldest tenth).

Both scripts work from the repo root or oink.pgsty.com/.

Release-prep audit

Before drafting the changelog entry and release blog post, run a careful audit of every PR and raw commit in the release range so nothing user-visible slips through.

For each PR/commit in git log v<prev>..main:

  1. Inspect the actual diff (not just the title or PR description). Use gh pr view <num> and git show <sha> as needed.
  2. Classify the change: breaking, user-facing, internal-only, or docs-only (see definitions in Public customization surface and Breaking change).
  3. For every breaking or user-facing item, verify it appears in both the changelog and the release blog post — with cross-links to the relevant user-guide sections where applicable.
  4. Be especially alert to: new/renamed params, partials, shortcodes, layouts, CSS classes, i18n keys, default-behavior shifts, and changes to the version menu, navigation, or other rendered output.

Capture the audit as a working document and summarize its findings (the classifications and where each item is covered) in the release-prep PR description, so reviewers can sanity-check them.

Publishing a release

These notes are WIP for creating a release from a local copy of the repo. These instructions assume the release is:

  • v0.16.1

If not adjust accordingly.

  1. Change directory to your local Docsy repo.

    • Expecting final adjustments as you prepare for the release? Create a branch to work from. For example:

      git checkout -b release-v0.16.1-prep
      # Or you have a local create-branch alias:
      gcb release-v0.16.1-prep
      
    • Serve the site and continue working through these steps from the served version of these notes.

  2. Create or update a changelog entry for v0.16.1.

    • This step is driven by the release-prep audit.
    • The section should provide a brief summary of breaking changes using the section template at the end of the file.
    • Ensure to remove the UNRELEASED note, if still present.
    • You’ll create a new section for the next release in a later step.
  3. Update the release report blog post for v0.16.1, if any.

    • Remove draft status.
    • Set date (or lastmod if already published) to today’s date.
  4. Run npm run fix.

  5. Update Docsy version to v0.16.1 using the following from a (bash or zsh) terminal.

    • First set the VERSION variable; we use it throughout the steps below.

      VERSION=v0.16.1
      
    • Then run the set:version script.

      Docsy is probably already at v0.16.1-dev, so you can run:

      npm run set:version
      

      Otherwise, set the version explicitly:

      npm run set:version -- --version $VERSION
      

      Both forms update the version related fields in package.json and oink.pgsty.com/config files.

  6. Run npm run ci:test, which runs ci:prepare and more to ensure that, e.g., vendor assets and go.mod dependencies are up-to-date, etc.

  7. Submit a PR with your changes.

    • Set the BASE variable to the target branch: main if this is a stable release, and release for patch releases.

      BASE=main # or release for patch releases
      
    • Commit any changes accumulated from the previous steps using this title:

      Release v0.16.0 preparation
      
    • Create a PR (with version-checks disabled) using the following command that will open a PR-creation page in your browser:

      export SKIP_VERSION_CHECK=1
      gh pr create --web --title "Release $VERSION preparation" \
        --base $BASE \
        --body "- Contributes to #<ADD-RELEASE-PREP-ISSUE-HERE>"
      
    • Use the web interface to fill in the PR details.

    • Submit the PR.

  8. Test the PR branch:

    • Run-edit-cycle, after each run sub-step below:

      • Push any adjustments to the PR.
      • Restart this step 8 from the top, if justified.
    • Run the smoke tests, which auto-target the PR branch pushed in the previous step and include a build at the minimum Hugo version:

      npm run test:smoke
      
    • Test consumer sites:

  9. Get PR approved and merged.

  10. Pull the PR to get the last changes.

  11. Post-merge check from consumer sites. In each worktree from step 8, update the site’s Docsy pin from the PR branch tip to merged main, then:

    • Build and confirm zero warnings; re-run the site’s sanity checks.
    • Re-run the full test procedure only if the merge involved a non-trivial conflict or rebase.
  12. Ensure that you’re:

    • On the target $BASE branch
    • At the commit that you want to tag as v0.16.0
  13. Create the new tag for v0.16.0.

    • Set the REL variable to the release version or use the VERSION variable if you set it in the previous step.

      REL=${VERSION:-v0.16.0}
      REL=v${REL#v} # tags are v-prefixed; normalize to exactly one leading v
      echo "REL=$REL"
      
    • Create the new tag.

      git tag $REL
      
    • Also create the nested theme module tag. Since the theme moved under theme/, it is its own Go module (github.com/google/docsy/theme), and Go resolves it via a subdirectory-prefixed tag — this is what consuming sites get when they import …/docsy/theme:

      git tag theme/$REL
      
    • Double check:

      git tag --sort=-creatordate | head -3
      
  14. Push the new tags (the release tag $REL and the theme module tag theme/$REL): either to all remotes at once, or one at a time.

    Push to all remotes
    • List the remotes so you know what you’ll be pushing to:

      git remote
      
    • Check that the push-all-remotes alias is defined, and if not, define it:

      git config --global --list | grep alias.push-all-remotes
      
      Define a push-all-remotes alias

      First check if the push-all-remotes alias is already defined:

      git config --global --list | grep alias.push-all-remotes
      

      If not, define the alias:

      git config --global alias.push-all-remotes \
        '!f() { for r in $(git remote); do (set -x; git push "$r" "$1"); done; }; f'
      
    • If you have git hooks enabled that auto-update the Docsy package version, disable the hook check for now:

      export SKIP_VERSION_CHECK=1
      
    • Push the tags to the remotes (the release tag, then the theme module tag):

      $ git push-all-remotes $REL
      + git push origin v0.16.0
      * [new tag]         v0.16.0 -> v0.16.0
      + git push upstream v0.16.0
      * [new tag]         v0.16.0 -> v0.16.0
      ...
      $ git push-all-remotes theme/$REL
      ...
      
    • Sanity check over upstream for example:

      git ls-remote --tags upstream | grep $REL
      
    • Unset the SKIP_VERSION_CHECK variable when you’re done:

      unset SKIP_VERSION_CHECK
      
    Push to a single remote
    • Push to a single remote at a time, such as upstream:
    git push upstream $REL
    git push upstream theme/$REL
    
    • Sanity check over upstream for example:

      git ls-remote --tags upstream | grep $REL
      
  15. Publish the theme package:

    • Publish to the npm registry from theme/ at the tagged release commit.
    • Verify the published version.
    • Verify that the latest and next dist-tags point at it.
  16. Merge the reviewed site update to main and push it. The site repository uses main as its only long-lived branch, and that push triggers the production deploy.

  17. Wait for the production deploy to complete and check that oink.pgsty.com has been updated to the new release.

  18. Draft a new release using GitHub web; fill in the fields as follows:

    • Visit tags to find the new release tag v0.16.0.

    • Select Create a new release from the v0.16.0 tag dropdown menu

    • Release title: use the release version.

      v0.16.0
      
    • Click Generate release notes to get the release details inserted into the release notes text area.

    • Add the following text atop the generated release notes:

      ## Release summary
      
      - [Release 0.16.0 report and upgrade guide][blog]
      - [Changelog v0.16.0][changelog] entry
      
      
      [blog]: <https://oink.pgsty.com/blog/2026/0.16.0/>
      [changelog]: <https://oink.pgsty.com/project/about/changelog/#v0.16.0>
      
    • Select Create a discussion for this release.

  19. Publish the release: click Publish release.

  20. Test the release with a downstream project and/or the docsy-example site.

  21. If you find issues, determine whether they need to be fixed immediately. If so, get fixes submitted, reviewed and approved. Go back to step 1 to publish a dot release.

  22. Update the release branch once the release is final.

    For a stable release, fast-forward release to the final release commit from main:

    git checkout release
    git merge --ff-only main
    git push-all-remotes release
    

    For patch releases, the release-prep PR should already target release, so there is no separate main to release fast-forward.

  23. Optionally validate the doc-rooted configuration from main:

    npm run doc-rooted -- build
    # Optionally take a look at the preview
    npm run doc-rooted -- serve
    curl http://localhost:1313/index.md
    

    This is a configuration check, not a separate publishing branch.

  24. Update, create, or close GitHub milestones as appropriate.

If all is well, release the Docsy example as detailed next.

Docsy example release

The steps you follow are similar to the ones above for the Docsy release, but with the following modifications:

  1. Update the version of the example to v0.16.0:

    VERSION=v0.16.0
    npm run set:version:example -- --version $VERSION
    
  2. Perform step 6 onwards as above to test, create a PR, create a release and publish it with one difference:

  3. Update the Examples page Docsy version in the Starter templates table to v0.16.1.

Post Docsy-release followup

Assuming that both the Docsy and Docsy-example releases v0.16.0 have been successfully deployed, and that at least one other project has been successfully tested with the new release, then perform the following actions before any further changes are merged into the main branch:

  1. Update the package version to a dev ID for Docsy and Docsy-example:

    $ npm run -s set:version:git-info
    ✓ Updated package.json version: 0.14.3 → 0.14.3-dev+003-over-main-cf4f514b
    ✓ Updated oink.pgsty.com/config/_default/params.yaml version: 0.14.3 → 0.14.3-dev
    ✓ Updated oink.pgsty.com/config/_default/params.yaml tdBuildId: (none) → 003-over-main-cf4f514b
    ...
    $ npm run -s set:version:example:git-info
    ...
    
  2. Retire temporary measures that the shipped release makes obsolete, verifying checks as you go.

    • Remove any temporary ignore rules from oink.pgsty.com/lychee.toml and confirm that the link check passes.

    • Search for other release-scoped markers and act on those now that the release is shipped, for example:

      git grep -En 'Remove after|TODO\(0\.' -- ':(exclude)*public*'
      
    • Leave markers naming a later release in place.

  3. In the Changelog:

    • Create a new entry for the next release by copying the ENTRY TEMPLATE at the end of the file.

    • Fix the new release URL, which ends with latest?FIXME=..., so that it refers to the actual release, now that it exists.

  4. Submit a PR with your changes, using a title like:

    Set version to v0.16.0
    
  5. Get PR approved and merged.

Consumer-site test procedure

To test a Docsy branch or release from a consumer site, for each site:

  1. Create a dedicated worktree + branch off the site’s default branch; keep it for the site’s post-release Docsy-update PR.

  2. Point the site at the target Docsy commit, per install mode:

    • Hugo module: map the theme module to the local checkout – env-only, no repo edits:

      export HUGO_MODULE_REPLACEMENTS="github.com/google/docsy/theme -> DOCSY_CHECKOUT_PATH/theme"
      
    • npm package: npm install -D file:DOCSY_CHECKOUT_PATH for sites that npm install from GitHub (google/docsy); append /theme for sites that use the registry package (@docsy/theme).

    • Git submodule:

      cd themes/docsy
      git fetch FORK BRANCH-NAME
      git checkout FETCH_HEAD
      cd ../.. && git add themes/docsy # stage so prebuild targets this SHA
      
  3. Apply the release post’s upgrade actions – all of them, before the first build: check every applies-if guard against the site, including the companion Hugo guide’s actions when the release raises the Hugo minimum. This doubles as a dry run of the post; report any gap or inaccuracy as feedback on it.

    • For Hugo-module sites, confirm that the replacement is live once the import path targets the theme module:

      hugo mod graph | grep 'github.com/google/docsy/theme'
      
  4. Build: confirm zero errors and warnings.

  5. Run the site’s test suite:

    • Run npm test or the site’s canonical test script.
    • Confirm that all checks pass.
    • Run the release post’s sanity checks.
  6. Spot-check key pages and output files, in the build output or a served preview:

    • Pages – confirm each renders with intact chrome, styles, and favicons:
      • Home page – also confirm that the generator meta element reports the expected Hugo version (Docsy’s version isn’t emitted)
      • Docs landing page, and a random docs page
      • Blog landing page and a random blog post, when the site has a blog
      • Some other random page
      • The 404 page
    • Other output files – confirm each looks sane:
      • The main CSS and JS files
      • When the site enables LLMS support: llms.txt, and the .md output of the pages above
      • _redirects, when present
      • sitemap.xml – note that some sites normalize it after the build
  7. A/B diff the generated site:

    • If the site’s public/ folder is a git repository (a setup worth adopting; see oink.pgsty.com’s make:public npm script), build at the current (pre-update) pin and commit the output as the baseline. git diff then reports the changes directly. Do not remove public/ if it’s a symlink to a different directory.

    • Otherwise, build at the current pin, set public/ aside as a baseline directory, rebuild at the new pin, and diff, for example:

      diff -rq --exclude='*.map' BASELINE_DIR/ public/
      
    • Confirm at least one difference exists.

    • Assess each difference:

      • Map it to an announced change, or flag it as a potential regression.
      • Investigate issues and report their root causes.
    • Report the results.

Release helper scripts

  • NPM scripts: set:version and set:version:*; update:hugo (see Hugo versions)
  • scripts/get-build-id.sh: Builds X.Y.Z-dev+…-over-main-… from the latest semver tag on main, commit offset, and tip SHA; if package.json’s X.Y.Z core is already greater than that git-derived core, keeps the higher core (release prep ahead of tagging).
  • scripts/set-package-version/index.mjs: Low-level version manager. See script help for usage.

2 - Build

Tooling, local development setup, CI/CD workflows, deployment environments, and automation details.

2.1 - CI/CD

Agent-support checks

The site has an AFDocs configuration and npm script to generate a scorecard locally:

To generate a fresh scorecard, run each of these commands in separate terminals:

npm run serve             # From one terminal
npm run check:afdocs:dev  # From another terminal

The latter command saves the generated scorecard to docs/content/agent-support/afdocs-scorecard.txt under content, which will be included in Scorecard examples on the next build.

Note that the scorecard generation is not run as a part of the full CI/CD pipeline. It needs to be run manually.

Read more: AFDocs config file format.

Prettier formatting

We use Prettier to format the project-site files using the following command:

npm run check:format

To fix formatting, run:

npm run fix:format

Workaround for i18n files

The translation files in the i18n directory are formatted using Prettier. But Prettier removes the blank line before the # Feedback section heading. This seems to be a known issue, for example see:

We’ve worked around this bug, and avoided using prettier-ignore directives, by formatting the preceding entry in the YAML file to be a block scalar, like this:

community_guideline: >-
  Contribution Guidelines

This ensures that the blank line is preserved. Hopefully Prettier will be fixed and we’ll be able to remove this hack.

2.2 - Git repository layout and branch model

Repositories

Oink uses two focused repositories:

Repository Responsibility
Oink theme Published Hugo Module, layouts, assets, and i18n
Oink project site Documentation, examples, regression tests, and CI

The theme repository has no embedded example site or npm workspace. Consumer sites import github.com/pgsty/oink; the project site is one such consumer.

For local development, clone both repositories as siblings and connect them with an ignored Go workspace:

~/pgsty/
├── oink/
└── oink.pgsty.com/
cd ~/pgsty/oink.pgsty.com
go work init .
go work edit -replace=github.com/pgsty/oink=../oink
export HUGO_MODULE_WORKSPACE=go.work
npm install
npm run serve

Branch model

The theme repository uses:

  • main for the next theme release;
  • release for the current stable release and maintenance work;
  • vX.Y.Z tags for immutable public releases.

The site repository uses:

  • main as its only long-lived branch for documentation, previews, and production deployment.

Site-only changes do not require a theme release. Theme changes are first validated against a local sibling checkout, released from the theme repository, then pinned in the site’s go.mod.

Published site variants

A variant’s identity comes from its configuration directory under config/:

Site variant Source branch Version params
Production main production/
Next/local preview main _default/
Doc-rooted (experimental) main doc-rooted/

The production workflow builds directly from main, uploads public/ as a GitHub Pages artifact, and deploys it through the Pages API. The repository does not maintain a generated Pages branch.

Pull request deploy previews use the Next configuration.

Release workflow

  1. Develop the theme on main and test it against the sibling site checkout.
  2. Merge the release candidate to release and create the vX.Y.Z tag in the theme repository.
  3. Update the site with hugo mod get github.com/pgsty/oink@vX.Y.Z, run its checks, and merge the resulting go.mod and go.sum changes.
  4. Merge and push the reviewed site update to main; that push triggers the production deployment.

This keeps theme artifacts immutable and lets documentation deploy on its own schedule.

3 - Implementation

Code-level structure and conventions, Hugo/Docsy templates, SCSS/JS customizations, patches, and internal shims.

This section documents code-level implementation details for the Docsy website, including patches, internal shims, and customizations.

Patches and workarounds

3.1 - ScrollSpy patch

Runtime patch for Bootstrap ScrollSpy to handle invalid CSS selector IDs.

As of Docsy 0.13.0

Problem

As of Bootstrap 5.3.8 (the version used by Docsy 0.13.0), ScrollSpy fails if a page contains a heading ID that is not also a valid CSS # selector. This can happen, for example, if a heading ID starts with a digit. For technical details about this bug, see #2329.

Solution

Docsy 0.13.0 implements a runtime patch for ScrollSpy that intercepts ScrollSpy’s initialization to properly handle heading IDs starting with digits or containing other characters that form invalid CSS selectors. This allows active TOC entry tracking to work correctly without altering the original heading IDs, so links to headings continue to work as expected.

The patch is automatically applied when ScrollSpy is enabled (which is the default). For implementation details, see #2382, #2383.

Maintenance

CI/CD automatically keeps the patch up-to-date when Bootstrap is updated. The ci:prepare script extracts the method from Bootstrap, applies the patch, and updates the runtime patch file. If the Bootstrap method code has changed to a degree that the patch no longer works, CI will fail, indicating that the patch file needs manual review and updates.

Until the upstream ScrollSpy fix is released in a future Bootstrap version, this patch ensures that active TOC entry tracking works reliably for all pages.

References

4 - Project repositories

Source repositories for the Oink theme and project site.

5 - Style guide

Writing and formatting conventions for Docsy project documentation.

This project follows Google’s developer documentation style guide, and uses Prettier and Markdownlint to enforce basic formatting rules.

Front matter

  • Do not quote string values unless doing so would otherwise cause ambiguity or unintended type interpretation.
  • Drop linkTitle when it is the same as title.

Content

Alerts

  • Prefer Hugo’s blockquote alert syntax over the legacy alert shortcode for new and edited content. Both render similar output, but the blockquote form is better supported by tools and agents, and naturally renders as is in Markdown format for AI agents.

Lists

  • Use periods when list items are complete sentences (including imperative steps).
  • Omit periods when list items are fragments, labels, or link-only bullets.
  • Keep punctuation consistent within each list. When this isn’t possible, ask the author how they prefer reworking the list item text: e.g., by making all sentences complete.

Verb tense

  • Use present tense for all content, except as noted below.
  • For release and upgrade blog posts:
    • Use present tense when referring to the release itself, for example:

      Docsy 0.14.0 adds …

    • Use past tense only when describing previous releases or pre-release behavior, for example:

      Before Docsy 0.14.0, the navbar was …

  • For the Changelog: use past tense; see its style guide.