docouture new --flow offers two shapes — decided at scaffold time, switchable later
with docouture branch-model.
Trunk-based — the default
One long-lived branch (main by default) plays both roles: it’s the branch Antora
tracks live as the prerelease version, and it’s the branch docouture-release.yml
requires, checks out, and cuts tags from. There is no second code path anywhere for
this — it’s the degenerate case where the two roles below happen to be the same
branch.
Git-flow
Two independently-named branches, e.g. develop and main:
-
prerelease branch (
develop) — whatantora-playbook.yml’s `content.sources[].branchesaggregates as the live prerelease version, and whatdocouture-publish-prerelease.ymlwatches (pushes to it republish automatically). -
release branch (
main) — whatdocouture-release.ymlrequires/checks out/pushes bump commits to, and whatdocouture-release-preview.ymlwatches (pull requests targeting it get a release preview comment).
Adopting git-flow maps directly onto the existing "merge a docs/release-labelled PR"
release trigger — no event change, only the branch name it’s filtered against.
Hotfix/release/* short-lived branches get no dedicated handling: they’re ordinary
feature-branch-shaped PRs into develop/main from docouture’s perspective, already
covered by the two roles above.
Which workflow watches which branch
| Workflow | Branch role it watches |
|---|---|
|
Neither — branch-agnostic, builds every PR’s own HEAD via
|
|
prerelease branch (push trigger) |
|
both, unconditionally (push trigger — see below) |
|
release branch (pull_request trigger) |
|
release branch (pull_request:closed + workflow_dispatch) |
|
Neither — never push-triggered, only |
docouture-kroki-cache-warm.yml triggers on both branches unconditionally, not just
one: GitHub’s cache-access rule only ever writes a trusted, cross-run cache entry from
a push to the repository’s actual configured default branch — and there’s no
reliable way to know at scaffold time which of the two a git-flow repo has set as
that. Warming both means whichever one really is the default gets its cache entry
written either way; the other branch’s push still produces a normal
base-branch-scoped cache, so nothing is wasted. (On a trunk-based site this
collapses to a single entry — both roles are the same branch.)
A release under git-flow, end to end
-
Feature branch →
develop: onlydocouture-pr-verify.ymlfires on the PR (it’s branch-agnostic). On merge,docouture-publish-prerelease.ymlrepublishes the prerelease docs fromdevelop. -
develop→main, labelleddocs/release:docouture-pr-verify.ymlfires (always) anddocouture-release-preview.ymlnow fires too (targetsmain), posting a preview comment. On merge,docouture-release.ymlcuts the release (checks outmain, tags it, pushes the tag) and callsdocouture-publish.ymlto build and publish. -
The merge to
mainis also an ordinary push — butdocouture-publish-prerelease.ymldoes not fire a second time: its trigger isdevelop-only, so there’s no overlap with `docouture-release.yml’s own publish. (Under trunk-based, where both roles are the same branch, this same-push double-trigger risk is real, and `docouture-release.yml’s own header comment documents the mitigation.)
antora-playbook.yml’s `content.sources[] never lists the release branch
Only the prerelease branch appears under branches: — the release branch is never a
live, content-tracked branch, only the checkout target docouture-release.yml cuts
tags from. A tag is just a pointer to a commit; Antora’s tags: matcher doesn’t care
which branch produced it. So docs/stable/docs/vX.Y.Z genuinely reflect whatever was
on the release branch each time a release was cut, even though the release branch
itself is never named in the playbook. See Versioning modes
for what tags: is set to in each versioning mode.
Switching later: docouture branch-model
$ docouture branch-model git-flow --integration-branch develop
$ docouture branch-model trunk-based --branch main
Trunk-based → git-flow: the current single branch becomes the release branch by
default (least disruption to anything already tagged off it) — --integration-branch
(the new prerelease branch) is required, since there’s nothing on disk to infer that
name from.
Git-flow → trunk-based: lossy — --branch is required and must match one of the two
current branches exactly; a third, invented name is refused.
Either direction re-renders .github/workflows/ (the same machinery docouture
upgrade uses) and patches antora-playbook.yml’s `content.sources[0].branches and
docs/package.json’s `docouture.branching field. It does not rename actual git
branches, touch branch-protection/ruleset rules, or change GitHub’s configured default
branch — those stay manual steps, printed as a reminder after every real run.
docouture.branching is a declared signal, not the source of truth
docs/package.json’s `"docouture": {"branching": "trunk-based"} (or "git-flow") is
cheap and redundant by design — the actual branch names are never persisted anywhere.
docouture branch-model/docouture doctor always re-derive them live, from
antora-playbook.yml’s `content.sources[0].branches and docouture-release.yml’s
checkout `ref:, the same way standalone-vs-versioned mode is already derived live
(docouture-release.yml’s own "Detect mode" step) rather than stored anywhere. This
field only exists so `docouture doctor has something to compare that derivation
against — a mismatch between the two is reported as an advisory warning, not a hard
failure, since it can only ever be stale, never a build-breaking config error the
way the four names in Modifying the navigation are.
Which one to pick
Trunk-based if one branch is genuinely enough — most sites. Git-flow if the
repository this docs site lives alongside already reserves main for
release/hotfix merges and uses develop as its integration branch — matching the
docs site’s branching to the code’s avoids a confusing mismatch between the two.