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

Return to the regular view of this page.

Build

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

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
npm run check:afdocs:dev

The latter command saves the generated scorecard to docs/content/agent-support/afdocs-scorecard.txt under docsy.dev/content/en, 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.

Formatting and lint checks

Prettier formats Docsy and the website files, and Markdownlint lints the Markdown. To run the checks, or to apply automatic fixes:

npm run check
npm run fix

CI runs these checks and the test suites.

Hugo shortcodes

Prettier formats a paired shortcode’s body as Markdown. When the body isn’t Markdown, such as a tabpane’s code, wrap the shortcode:

<!-- prettier-ignore-start -->
{{< tabpane >}}
...
{{< /tabpane >}}
<!-- prettier-ignore-end -->

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 - Git repo info and branch model

Monorepo

The main Docsy repository is effectively a monorepo containing the Docsy:

  • Theme in the theme directory
  • Website in the docsy.dev directory. The website uses the Docsy theme, of course, with extra styling.

The main Docsy example site is Goldydocs, located in the Docsy example site repository.

Local build

Site builds resolve the theme through the checkout’s parent directory (--themesDir ../.. with theme: docsy/theme), so the checkout directory must be named docsy. From a checkout named differently (a git worktree, for example), either run npm scripts through the wt helper (npm run wt -- serve), or set HUGO_THEME=DIR_NAME/theme, where DIR_NAME is the checkout’s directory name.

With the prerequisites installed, install and serve the website from the checkout root:

npm run install:safe
npm run serve

Branch model

This repository’s branch model is as follows:

  • main: development branch for the next theme release and next site content.
  • release: release and maintenance branch for the latest theme release.
  • deploy/prod and doc-rooted: publishing branches used by Netlify. These branches determine what is published (see the table below); they are not feature development branches.

The Goldydocs repo has the same model, except for doc-rooted which is not used.

Published sites

Netlify publishes the following site variants. A variant’s version identity (version and related params) comes from its config directory under docsy.dev/config/:

Site variantPublishing branchVersion params
Latest releasedeploy/prodproduction/
Next (dev)main_default/
Doc-rooted (experimental)doc-rooteddoc-rooted/

PR deploy previews build like the Next variant.

Tags

  • Release tags (vX.Y.Z and theme/vX.Y.Z) mark official theme releases.
  • They never move: the release-tag-integrity ruleset blocks their update and deletion, with no bypass.
  • Only the designated releaser creates them, per the release-tags ruleset.

Workflow

Overview

  1. Theme and site work is done on main.

  2. When ready to release:

  3. Publish site updates: fast-forward deploy/prod from release.

  4. Netlify deploys from deploy/prod and doc-rooted.

Release from main

  1. After git fetch upstream, if release has commits that main doesn’t (git log upstream/main..upstream/release lists them, after a patch on release), restore the fast-forward path before the release-preparation PR merges.

  2. Once the release tag, RELEASE_TAG (for example, v0.18.2), is pushed, fast-forward release to it:

    git fetch upstream --tags
    git switch -C release upstream/release
    git merge --ff-only RELEASE_TAG
    git push upstream release
    

Patch on release

Fix on main first whenever the fix applies there; a fix that doesn’t apply there lands only on release. Then:

  1. Open a PR against release with the fix (cherry-picked from main when it landed there) and the release-preparation changes, and merge it.
  2. Port the release-facing site updates (changelog, release blog post, tdVersion.latest) onto a branch off main and merge them by PR. A PR from release itself would carry the patch’s version stamps.

Branch sync and invariants

main: for its rules and the merge gates, see Merge requirements.

release:

  • Follows main: divergence lasts only from a patch on release to the next release from main.
  • Every official release tag is reachable from it.
  • Stays at the latest release between releases: it is the base for patches.
  • Never rewritten: no force pushes, no deletion (enforced by its ruleset).
  • Receives content only through releases from main or patches on release.
  • Checks (including EasyCLA and workflow security analysis) run on PRs into release; acting on them is the merging maintainer’s call. Merge with EasyCLA green, though: a miss on release can’t be undone and blocks the next restore until the author signs.

deploy/prod: follows release as a pointer, never with commits of its own; the published docs change only when release moves.

Why this model?

  • Keeps theme releases predictable while main moves quickly.
  • Keeps release a follower of main, so a patch never opens a second line of development.
  • Protects release against rewriting without gating it: requiring main’s PR-scoped gates there would refuse the fast-forward from main, whose content already passed them.
  • Keeps deploy/prod a pointer, so publishing is a deliberate last step, separate from cutting the release.