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.
  • 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.
  • 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:

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 docsy.dev/package.json.

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

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

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

Test suites

From the repo root:

ScriptRole
test:fixture-siteFast, offline checks over minimal monolingual fixture sites — paths docsy.dev can’t cover
test:hugo-versionsFast, offline checks of the Hugo versions declarations and constraints
test:smokeSlow, network-bound; builds a site from GitHub several ways (NPM, Hugo module, clone, minimum-Hugo)
test:toolingUnit tests for repo scripts
test:websiteFull docsy.dev 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 docsy.dev’s links with Lychee, caching external-link results in the committed docsy.dev/.lycheecache (the “refcache”) so checks stay fast and offline-friendly. Config lives in docsy.dev/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 docsy.dev/.

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 (motivated by the version-menu near-miss during 0.15.0 prep).

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 under tasks/<release>/release-prep/ (see prior releases for examples) so reviewers can sanity-check the classifications. Treat the audit — not PR-level scope hints — as the source of truth for what the changelog and release blog must cover.

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.15.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.15.1-prep
      # Or you have a local create-branch alias:
      gcb release-v0.15.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.15.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.15.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.15.1 using the following from a (bash or zsh) terminal.

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

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

      Docsy is probably already at v0.15.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 docsy.dev/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.15.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 from selected sites, and push any required adjustments.

    • 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
      
    • If the test site uses Docsy as a Git submodule:

      cd themes/docs
      git fetch
      git switch -t REPO/BRANCH-NAME # e.g. chalin/chalin-m24-0.14.0-pre-release
      
  9. Get PR approved and merged.

  10. Pull the PR to get the last changes.

  11. Test Docsy from docsy-example and the docsy-starter. (Consider updating the Docsy version for these examples in the examples page.)

  12. Ensure that you’re:

    • On the target $BASE branch
    • At the commit that you want to tag as v0.15.0
  13. Create the new tag for v0.15.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.15.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.15.0
      * [new tag]         v0.15.0 -> v0.15.0
      + git push upstream v0.15.0
      * [new tag]         v0.15.0 -> v0.15.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. Update the deploy/prod branch from $BASE.

    For stable releases from main, use:

    git checkout deploy/prod
    git merge --ff-only main
    git push-all-remotes deploy/prod
    

    For patch releases from release, selectively merge from release.

    The branch update will trigger a production deploy of the website.

  16. Wait for the production deploy to complete and check that docsy.dev has been updated to the new release.

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

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

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

    • Release title: use the release version.

      v0.15.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.15.0 report and upgrade guide][blog]
      - [Changelog v0.15.0][changelog] entry
      
      
      [blog]: <https://www.docsy.dev/blog/2026/0.15.0/>
      [changelog]: <https://www.docsy.dev/project/about/changelog/#v0.15.0>
      
    • Select Create a discussion for this release.

  18. Publish the release: click Publish release.

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

  20. 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.

  21. 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.

  22. Update the doc-rooted branch from deploy/prod:

    git checkout doc-rooted
    git merge --ff-only deploy/prod
    npm run doc-rooted -- build
    # Optionally take a look at the preview
    npm run doc-rooted -- serve
    curl http://localhost:1313/index.md
    # Push the changes
    git push-all-remotes doc-rooted
    

    If the fast-forward merge fails, stop and reconcile the branch history. Once pushed, wait for the Netlify deploy and check the doc-rooted preview.

  23. 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.15.1-dev:

    VERSION=v0.15.1-dev
    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:

    • Once the deploy/prod branch has been updated, wait for the production deploy to complete and check that example.docsy.dev has been updated to the new release.
    • To create a new release draft, visit Docsy-example release draft.
  3. Update the Examples page Docsy version in the Starter templates table to v0.15.1.

Post Docsy-release followup

Assuming that both the Docsy and Docsy-example releases v0.15.1-dev 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 docsy.dev/config/_default/params.yaml version: 0.14.3 → 0.14.3-dev
    ✓ Updated docsy.dev/config/_default/params.yaml tdBuildId: (none) → 003-over-main-cf4f514b
    ...
    $ npm run -s set:version:example:git-info
    ...
    
  2. 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.

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

    Set version to v0.15.1-dev
    
  4. Get PR approved and merged.

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.