Documentation versioning
How the docs of each product (gitaiflow, astwire, …) are versioned, and how to release a new version. For the version of the docs site itself, see RELEASING.md.
On this page ▾
Audience: anyone who edits a product's docs or cuts a release.
1. The model
One docs tree, always the latest. Older versions are either filtered views of that tree (from each doc's header) or built from a git ref (a tag or a branch). Nothing is copied per version.
| Kind | Built from | Use it when |
|---|---|---|
| Filtered view (default) | The latest docs, narrowed by each doc's since / removed |
A release only adds or removes pages. Same text in every version. |
| Frozen snapshot | A git tag of this repo (<slug>/v<version>) |
An end-of-life version whose text must stay exactly as it was. |
| Maintenance branch | A git branch (e.g. release/1.4) |
A supported older version that still gets doc fixes. |
Start with filtered views. Move a version to a tag or branch only when its wording has to differ from the latest.
2. Declare the versions (projects/<slug>/project.json)
"versions": [
{ "id": "1.1.3", "status": "current" },
{ "id": "1.1.1", "status": "supported" },
{ "id": "1.0.0", "status": "eol", "ref": "gitaiflow/v1.0.0" }
]| Field | Meaning |
|---|---|
id |
A release: an exact version (1.1.3) or a release line (1.1). Use one style per project. Lifecycle fields are compared at the precision of the id: with line ids, since: 1.1.3 counts as part of 1.1. |
status |
current (what the normal URLs show; the first entry if none says so), supported, or eol. |
label |
Optional display name. Default: the id, plus "(latest)" on the current one. |
ref |
Optional git tag or branch to build this version from. Default: the tag <slug>/v<id> if it exists. tag is the older spelling of the same thing. |
List versions newest first. One entry shows the version as a plain label with no dropdown; no versions list means no version UI at all.
3. Mark every doc with the version it appeared in
---
since: 1.1.3 # added in this version (required in a versioned project)
changed: 1.1.4 # last meaningful change (optional)
deprecated: 1.2.0 # optional
removed: 1.3.0 # optional
---- A doc belongs to version
Vwhensince <= VandV < removed. - Choosing an older version in the dropdown hides docs that didn't exist yet (sidebar, search, previous/next). A page outside the chosen version says so.
- "Added in X" is shown only on docs newer than the project's oldest listed version, so it marks what's new rather than every page.
- A doc removed in the current version stays routable (old links keep working) but disappears from navigation, search and the sitemap; it's only visible in older versions.
- Don't delete a doc that a supported version still documents. Set
removed: <version>instead. Deleting it makes it vanish from the older version too. - Release notes (
releases/vX.Y.Z.md) use the version they describe assince.
npm run new:doc fills since with the project's current version. npm run docs:check reports a missing since, or one newer than the current version.
4. Release a new version of a product
Do this before editing docs for the new version, on a clean main:
npm run docs:release -- gitaiflow 1.2.0 --notes # dry run: shows the plan
npm run docs:release -- gitaiflow 1.2.0 --notes --write
git push origin gitaiflow/v1.1.3 # push the tag it madedocs:release does the mechanical part:
- Freezes the version being replaced: tags the docs as they are now (
gitaiflow/v1.1.3). Skipped when that version is built from aref, or with--no-tag. - Updates
project.json:1.2.0becomescurrent,1.1.3becomessupported.--eol 1.1.1marks an older version end-of-life. - With
--notes, writesreleases/v1.2.0.mdfrom the release-notes template, withsince: 1.2.0.
It refuses when projects/<slug> has uncommitted changes (the tag would not match the docs), never moves an existing tag, and rejects a version that isn't newer or uses a different style (1.2 vs 1.2.0).
Then:
- Commit (
docs(gitaiflow): release 1.2.0). - Edit the docs for 1.2.0. New docs:
npm run new:doc(sincedefaults to 1.2.0). Docs that go away:removed: 1.2.0. - Merge to
main. The site rebuilds.
To tag the current version without releasing anything (for example to freeze 1.1.3 today): npm run docs:release -- gitaiflow --freeze --write.
Which ref for which version
The common industry approach (Read the Docs, Antora):
| Version status | Build it from |
|---|---|
current |
main (the normal docs) |
supported |
A filtered view while text is shared; a maintenance branch ("ref": "release/1.1") once its wording diverges and still gets fixes |
eol |
A tag: frozen, never changes |
To start a maintenance branch: git branch release/1.1 gitaiflow/v1.1.3 && git push origin release/1.1, then add "ref": "release/1.1" to that entry.
5. Frozen versions in the build
Each version with a usable ref is exported with git archive and built like normal content under /projects/<slug>/v/<id>/….
- Links between pages of a version stay inside that version.
- Pages of an older version are canonical to the same page in the latest docs, or
noindexwhen there isn't one, and are left out ofsitemap.xml. - A version with no ref is a filtered view, silently. A version whose entry names a
refthat can't be found falls back to a filtered view and warns. - Broken links inside an old ref are warnings, never build failures.
CI requirements
Dates and older versions need git history, tags and branches:
- GitLab: set
GIT_DEPTH: 0(full clone). The build also triesgit fetch --unshallow --tags,git fetch --tagsand, for a branch ref,git fetch origin <branch>on its own. - Skip those automatic fetches with
DOCS_NO_UNSHALLOW=1andDOCS_NO_FETCH_TAGS=1.
Warnings
npm run build and npm run check-content print one grouped list of warnings, including explicit refs that fell back to filtered views. See RELEASING.md section 6.