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.json 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) |
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/target-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.jsonfor 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.