Documentation and the sites
Documentation and the sites
Three sites, three audiences, three publishing rules:
| Site | Built from | Published when |
|---|---|---|
| docs.schellingboard.org | docs/public/, at each tag |
a release is tagged |
| developers.schellingboard.org | docs/dev/, on main |
docs/dev/ changes on main |
| schellingboard.org | www/ + docs/screenshots/ |
www/ or the screenshots change |
The split of audiences is the point: attendees and organizers read the first, contributors the second, and nobody should land on the wrong one. Keeping the developer docs unversioned and the user docs versioned follows from the same split — user docs must describe the release someone is running, while developer docs must describe the branch they are working on.
The public docs site
Built with docmd from docs/public/, which holds one copy of the documentation
and no snapshots. Published versions are reconstructed from git: scripts/build-docs.sh walks the
release tags, checks each one out into a temporary worktree, and builds it as a version. The newest
is served at the site root, older ones under /<major.minor>/, all listed in the version dropdown.
So docs/public/ is the next release’s documentation. Edit it in the same commit as the change it
describes; it is published when that release is tagged.
| Command | What it does |
|---|---|
make docs |
Live preview of docs/public |
make docs-build |
Build the published site into site/ |
make docs-validate |
Check for broken internal links (runs in CI) |
.github/workflows/docs.yml deploys on tag pushes and on pushes to release/* branches. Pushing to
main deliberately publishes nothing — until a release is tagged, make docs is the only place the
new documentation exists.
Two consequences worth knowing:
- Releasing documentation is tagging the repository. There is no snapshot step, so nothing can drift out of sync with the release it documents.
- A release tag that predates the docs site publishes nothing. The build skips any tag without
docs/public/index.md, which is whyv3.0.0andv3.1.0never appear on the site.
robots.txt, sitemap.xml and 404.html are generated by docmd from the config’s url; don’t
hand-write them.
Dating each page
Every page ends with “Last updated”, and the commits that touched it in a tooltip. docmd’s own git
plugin cannot produce that here: it delegates to its build engine, which runs git log in the
process working directory, so the worktrees the versions are built from are all “outside repository”
— the error is swallowed and the site publishes undated. scripts/docmd-git-history.js (loaded from
docmd.config.json as a local plugin) resolves the repository per page instead, which also means
each version is dated from its own ref rather than from main. Contributor avatars are deliberately
left off, so no reader’s browser is sent to Gravatar. build-docs.sh fails the build if any page
comes out undated.
Hosting setup for the docs site
Two things live outside the repository, so a fresh fork or a moved domain needs them set up again before the first tag push can publish anything:
- GitHub Pages: repository Settings → Pages → Source must be GitHub Actions.
actions/deploy-pagesfails without it. - DNS: a
CNAMErecord fordocs.schellingboard.orgpointing atschellingboard.github.io. The domain itself comes fromurlindocmd.config.json— the build writes it tosite/CNAMEfrom there.
Until both are in place and v3.2.0 is tagged, the
docs.schellingboard.org links in
README.md are dead; the
same pages are readable in docs/public/.
Correcting published documentation
To fix published docs without cutting a release, commit to the release branch of that version —
release/3.2 for the 3.2 docs. The build prefers that branch over the tag, so pushing it
republishes that version, and the deploy log names the ref each version came from
(Version 3.2 ← release/3.2).
Release branches don’t exist until something needs one. Create it from the release tag the first
time that minor release has to be corrected — a release that never needs a fix never gets a branch —
and merge it back into main so the fix reaches the next release too. The same branch carries the
commits of a patch release, so this is where a fix to an older version lives in general, not only a
documentation one; see Releasing a new version.
jj new v3.2.0
jj bookmark create release/3.2 # git: git switch -c release/3.2 v3.2.0
# fix, commit, push — the docs site redeploys
Because the branch tip wins over the tag, anything committed to it is published as that version’s documentation the moment it is pushed, released or not. Land work that isn’t ready to be read on a separate branch.
The developer docs site
The site you are reading. docmd.dev.config.mjs builds docs/dev/ into dev-site/ with its own
title, navigation and search index — a second docmd project in the same repository, sharing nothing
with the user site but the theme and the logo. ADR 0008 has
the reasoning, including why it isn’t a path under docs.schellingboard.org.
| Command | What it does |
|---|---|
make docs-dev |
Live preview of docs/dev (no diagrams — see below) |
make docs-dev-build |
Build the published site into dev-site/ |
make docs-dev-validate |
Check for broken internal links (runs in CI) |
The config’s top level is hand-written, but the children under Decision records, Architecture and
Attendance model are read off disk at build time: a new ADR gets its sidebar entry, breadcrumbs and
prev/next links from its own # heading, with no config edit. Only a page listed in navigation
has a place in the structure, so a directory added next to those needs a pagesIn call of its own.
It is not versioned. .github/workflows/developers.yml publishes on pushes to main that touch
docs/dev/, so the site always describes the default branch; a contributor on a release branch
reads that branch’s docs/dev/ in the repository instead.
Links out of docs/dev/ must be absolute GitHub URLs. The site’s root is docs/dev/, so a
relative ../../CONTRIBUTING.md resolves to nothing once published. make docs-dev-validate
catches those, and is the reason to run it after moving a file. Links to user documentation go to
docs.schellingboard.org rather than to docs/public/ in the repository.
docs/dev/README.md is both the site’s home page and the folder’s index on GitHub, which is what
keeps the repository copy navigable.
Why it deploys to a second repository
The same reason as the landing page below: GitHub Pages serves one custom domain per repository, and this one already serves docs.schellingboard.org. The build is pushed to schellingboard/developers.schellingboard.org, which holds nothing else, using the same organization-owned deploy app the landing page uses — see Hosting setup for the pushed sites.
The architecture diagrams
The C4 model in docs/dev/architecture/diagrams/ reaches the site twice:
/diagrams/— the full LikeC4 explorer, every view with drill-down between levels, built bylikec4 build. It uses hash history (/diagrams/#/…) because GitHub Pages can’t route deep links into a single-page app.<likec4-view view-id="…">— chapters embed a single view inline, rendered by the web componentlikec4 gen webcomponentproduces.
There are no committed PNG exports, so a diagram cannot go stale: the site renders from the .c4
sources on every build. The cost is that GitHub shows no diagram at all when reading the chapters in
the repository, and that make docs-dev — plain docmd dev, with none of the LikeC4 steps —
doesn’t either. Use make arch-diagrams for the explorer while editing the model, and
make docs-dev-build to see an embedded view rendered.
The web component bundle is ~2.5 MB, which is why scripts/likec4-embed.js, not the bundle, is the
site-wide customJs: it loads the bundle only on pages that actually contain a <likec4-view>, and
mirrors docmd’s theme onto each element’s color-scheme.
build-dev-docs.sh fails if a chapter embeds a view-id that views.c4 doesn’t define. The
element renders an empty box otherwise, which is the same silent staleness the PNGs had.
The landing page
schellingboard.org is hand-written HTML in www/ — a landing page and
a screenshot gallery. make www copies it, plus docs/screenshots/, into www-site/; open
www-site/index.html to preview. There is no generator, on purpose: the pages are two bespoke
layouts, and rendering them through docmd would mean custom templates to make a docs tool stop
looking like one.
.github/workflows/www.yml publishes on pushes to main that touch www/, docs/screenshots/ or
the build script. It does not follow the documentation site’s release-tag rule, because the page
describes the project rather than a version — with the consequence that its screenshots can show
interface changes that are merged but not yet released.
Why it deploys to another repository
GitHub Pages serves one custom domain per repository and this one already serves
docs.schellingboard.org, so the built site is pushed to
schellingboard/schellingboard.org, which
holds nothing but that output and is never edited directly. Its Pages source is the default branch,
so the site keeps working even if the workflow is disabled.
Screenshots
docs/screenshots/ is the only copy of the screenshots. The landing page uses them directly and the
documentation site can use them too — see
docs/screenshots/README.md
for the capture checklist and how to reference them from markdown.
They are not under docs/public/, and cannot usefully be: docmd discovers markdown and nothing
else, so a PNG placed there is not copied to the output. Both build scripts do the copying
themselves. One consequence: images appear in make docs-build output but not in the make docs
live preview, which serves what docmd produced.
Hosting setup for the pushed sites
Both the landing page and the developer docs are pushed into a repository of their own, and both use the same setup.
GitHub App: these workflows write to a repository that is not their own, so they mint a token from an organization-owned app rather than carrying a standing credential. The token expires within the hour, the app belongs to the organization instead of a person, and it is installed on the site repositories alone.
- Under the organization’s Settings → Developer settings → GitHub Apps, create an app with the repository permission Contents: Read and write and nothing else. It needs no webhook and no account permissions.
- Install it on
schellingboard/schellingboard.organdschellingboard/developers.schellingboard.orgonly. Each workflow still narrows its own token to the one repository it publishes, so installing the app on both does not let either job touch the other’s site. - In this repository, add the app’s Client ID as the
PAGES_DEPLOY_APP_CLIENT_IDvariable and a generated private key as thePAGES_DEPLOY_APP_PRIVATE_KEYsecret.
To rotate, generate a new private key on the app and replace the secret; the client ID does not change.
DNS:
schellingboard.organddevelopers.schellingboard.orgeach pointing atschellingboard.github.io, and each site repository’s Pages source set to its default branch — not GitHub Actions, which would ignore the pushed files. The domain comes fromwww/CNAMEfor the landing page, which the build copies verbatim, and fromurlindocmd.dev.config.mjsfor the developer site, whichbuild-dev-docs.shwrites todev-site/CNAME.A first commit: the workflow clones the site repository before writing to it, so an entirely empty one fails. Any initial commit does.