Skip to content

Commit aeef678

Browse files
authored
docs(release): backport release-line docs refresh (#460)
Signed-off-by: Roel de Cort <roel.decort@adfinis.com>
1 parent b9d4819 commit aeef678

12 files changed

Lines changed: 303 additions & 8 deletions

File tree

.github/workflows/ci.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,56 @@ jobs:
522522
- name: Build docs
523523
uses: ./.github/actions/docs-build
524524

525+
release-line-docs:
526+
name: Release-line Docs Snapshot
527+
needs: changes
528+
if: github.event_name == 'pull_request' && startsWith(github.base_ref, 'release-') && needs.changes.outputs.docs == 'true'
529+
runs-on: *runner
530+
steps:
531+
- name: Checkout
532+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
533+
with:
534+
fetch-depth: 0
535+
536+
- name: Verify release-line docs snapshot changed
537+
env:
538+
BASE_REF: ${{ github.base_ref }}
539+
BASE_SHA: ${{ github.event.pull_request.base.sha }}
540+
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
541+
run: |
542+
set -euo pipefail
543+
544+
if [[ ! "${BASE_REF}" =~ ^release-([0-9]+)\.([0-9]+)$ ]]; then
545+
echo "Release-line docs check only supports release-X.Y branches; got ${BASE_REF}" >&2
546+
exit 1
547+
fi
548+
549+
docs_version="${BASH_REMATCH[1]}.${BASH_REMATCH[2]}.0"
550+
git fetch --no-tags --prune --depth=1 origin "${BASE_SHA}" || true
551+
552+
changed="$(git diff --name-only "${BASE_SHA}" "${HEAD_SHA}")"
553+
docs_changed="$(grep -E '^(docs/|website/sidebars\.ts$)' <<< "${changed}" || true)"
554+
if [[ -z "${docs_changed}" ]]; then
555+
echo "No source docs changed."
556+
exit 0
557+
fi
558+
559+
snapshot_changed="$(
560+
grep -E "^website/versioned_docs/version-${docs_version//./\\.}/|^website/versioned_sidebars/version-${docs_version//./\\.}-sidebars\\.json$|^website/versions\\.json$" <<< "${changed}" || true
561+
)"
562+
if [[ -n "${snapshot_changed}" ]]; then
563+
echo "Release-line docs snapshot changed for ${docs_version}."
564+
exit 0
565+
fi
566+
567+
{
568+
echo "PRs against ${BASE_REF} that change source docs must refresh the ${docs_version} release-line docs snapshot."
569+
echo
570+
echo "Run from the release branch and commit the generated files:"
571+
echo " make docs-refresh-version DOCS_VERSION=${docs_version}"
572+
} >&2
573+
exit 1
574+
525575
helm:
526576
name: Helm Chart
527577
needs: changes
@@ -1004,6 +1054,7 @@ jobs:
10041054
- fuzz
10051055
- openbao-config-compat
10061056
- docs
1057+
- release-line-docs
10071058
- helm
10081059
- helm-e2e-smoke
10091060
- build-artifacts
@@ -1028,6 +1079,7 @@ jobs:
10281079
FUZZ: ${{ needs.fuzz.result }}
10291080
OPENBAO_CONFIG_COMPAT: ${{ needs.openbao-config-compat.result }}
10301081
DOCS: ${{ needs.docs.result }}
1082+
RELEASE_LINE_DOCS: ${{ needs.release-line-docs.result }}
10311083
HELM: ${{ needs.helm.result }}
10321084
HELM_E2E_SMOKE: ${{ needs.helm-e2e-smoke.result }}
10331085
BUILD_ARTIFACTS: ${{ needs.build-artifacts.result }}
@@ -1062,6 +1114,7 @@ jobs:
10621114
fuzz=${FUZZ}
10631115
openbao-config-compat=${OPENBAO_CONFIG_COMPAT}
10641116
docs=${DOCS}
1117+
release-line-docs=${RELEASE_LINE_DOCS}
10651118
helm=${HELM}
10661119
helm-e2e-smoke=${HELM_E2E_SMOKE}
10671120
build-artifacts=${BUILD_ARTIFACTS}

docs/contribute/release-management.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ journey: contribute
7979

8080
<Callout type="important" title="Stable release-line docs snapshots">
8181

82-
Before merging the first stable `X.Y.0` release PR for a release line, snapshot the docs for that release line and commit the generated artifacts. Patch releases in the same line publish release notes and reuse the `X.Y.0` docs snapshot. Prereleases continue to use `/docs/next` and release notes only; do not add patch, `-alpha`, `-beta`, or `-rc` entries to `website/versions.json`.
82+
Before merging the first stable `X.Y.0` release PR for a release line, snapshot the docs for that release line and commit the generated artifacts. Patch releases in the same line reuse the `X.Y.0` docs version, but user-facing docs fixes for that patch must refresh the existing `X.Y.0` snapshot from the release branch. Prereleases continue to use `/docs/next` and release notes only; do not add patch, `-alpha`, `-beta`, or `-rc` entries to `website/versions.json`.
8383

8484
</Callout>
8585

@@ -98,6 +98,16 @@ GitHub Actions runs workflow definitions from the branch that receives the push.
9898
This updates `website/versioned_docs/`, `website/versioned_sidebars/`, and `website/versions.json`.
9999
</CommandBlock>
100100

101+
<CommandBlock
102+
language="bash"
103+
label="configure"
104+
title="Refresh docs for a patch release line"
105+
code={`git switch release-X.Y
106+
make docs-refresh-version DOCS_VERSION=X.Y.0`}
107+
>
108+
Run this from the release branch after backporting docs that apply to the patch release. This updates the existing release-line docs snapshot without adding a patch version to `website/versions.json`.
109+
</CommandBlock>
110+
101111
<CommandBlock
102112
language="bash"
103113
label="configure"

mk/development.mk

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,11 @@ docs-version: docs-deps ## Snapshot the current docs into a versioned Docusaurus
389389
@test -n "$(DOCS_VERSION)" || { echo "DOCS_VERSION is required, for example: make docs-version DOCS_VERSION=1.2.3"; exit 1; }
390390
@$(DOCS_NPM) --prefix "$(DOCS_DIR)" run version:docs -- "$(DOCS_VERSION)"
391391

392+
.PHONY: docs-refresh-version
393+
docs-refresh-version: docs-deps ## Refresh an existing release-line docs snapshot from the checked-out docs. Set DOCS_VERSION=<X.Y.0>.
394+
@test -n "$(DOCS_VERSION)" || { echo "DOCS_VERSION is required, for example: make docs-refresh-version DOCS_VERSION=1.2.0"; exit 1; }
395+
@$(DOCS_NPM) --prefix "$(DOCS_DIR)" run refresh:docs-version -- "$(DOCS_VERSION)"
396+
392397
# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'.
393398
# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally.
394399
# CertManager is installed by default; skip with:

website/docusaurus.config.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import fs from 'node:fs';
12
import {themes as prismThemes} from 'prism-react-renderer';
23
import type {Config} from '@docusaurus/types';
34
import type * as Preset from '@docusaurus/preset-classic';
@@ -11,6 +12,26 @@ const docsPluginDefaultExclude = [
1112
'**/*.test.{js,jsx,ts,tsx}',
1213
'**/__tests__/**',
1314
];
15+
const releaseLineVersionLabels = {
16+
'0.2.0': '0.2.x',
17+
'0.1.0': '0.1.x',
18+
} as const;
19+
20+
function readDocsVersions(): string[] {
21+
try {
22+
const versions = JSON.parse(fs.readFileSync(new URL('./versions.json', import.meta.url), 'utf8'));
23+
return Array.isArray(versions) ? versions : [];
24+
} catch {
25+
return [];
26+
}
27+
}
28+
29+
const docsVersions = new Set(readDocsVersions());
30+
const releaseLineVersions = Object.fromEntries(
31+
Object.entries(releaseLineVersionLabels)
32+
.filter(([version]) => docsVersions.has(version))
33+
.map(([version, label]) => [version, {label}]),
34+
);
1435

1536
const config: Config = {
1637
title: 'OpenBao Operator',
@@ -65,6 +86,7 @@ const config: Config = {
6586
label: 'next',
6687
path: 'next',
6788
},
89+
...releaseLineVersions,
6890
},
6991
},
7092
blog: false,

website/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"write-translations": "docusaurus write-translations",
1717
"write-heading-ids": "docusaurus write-heading-ids",
1818
"version:docs": "node ./scripts/snapshot-version.mjs",
19+
"refresh:docs-version": "node ./scripts/refresh-version.mjs",
1920
"verify:docs-version": "node ./scripts/verify-docs-version.mjs",
2021
"typecheck": "tsc --noEmit",
2122
"test:e2e": "playwright test"
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import {spawnSync} from 'node:child_process';
2+
import fs from 'node:fs/promises';
3+
import path from 'node:path';
4+
5+
const version = process.argv[2];
6+
const websiteRoot = process.cwd();
7+
const versionsPath = path.join(websiteRoot, 'versions.json');
8+
9+
if (!version) {
10+
console.error('Usage: npm run refresh:docs-version -- <version>');
11+
process.exit(1);
12+
}
13+
14+
function isStableLineVersion(candidate) {
15+
return /^\d+\.\d+\.0$/.test(candidate);
16+
}
17+
18+
function run(command, args) {
19+
const result = spawnSync(command, args, {
20+
cwd: websiteRoot,
21+
stdio: 'inherit',
22+
});
23+
24+
if (result.status !== 0) {
25+
throw new Error(`${[command, ...args].join(' ')} failed with status ${result.status ?? 1}`);
26+
}
27+
}
28+
29+
async function pathExists(target) {
30+
try {
31+
await fs.access(target);
32+
return true;
33+
} catch (error) {
34+
if (error?.code === 'ENOENT') {
35+
return false;
36+
}
37+
throw error;
38+
}
39+
}
40+
41+
async function moveIfExists(from, to) {
42+
if (await pathExists(from)) {
43+
await fs.mkdir(path.dirname(to), {recursive: true});
44+
await fs.rename(from, to);
45+
return true;
46+
}
47+
return false;
48+
}
49+
50+
async function restoreIfMoved(from, to, moved) {
51+
if (!moved) {
52+
return;
53+
}
54+
await fs.rm(to, {recursive: true, force: true});
55+
await fs.mkdir(path.dirname(to), {recursive: true});
56+
await fs.rename(from, to);
57+
}
58+
59+
if (!isStableLineVersion(version)) {
60+
console.error(
61+
`Release-line docs refresh only supports stable release-line versions (X.Y.0). Patch releases update the existing release-line snapshot: ${version}`,
62+
);
63+
process.exit(1);
64+
}
65+
66+
const raw = await fs.readFile(versionsPath, 'utf8');
67+
const originalVersions = JSON.parse(raw);
68+
69+
if (!Array.isArray(originalVersions)) {
70+
throw new Error('versions.json is not an array');
71+
}
72+
73+
const dedupedOriginalVersions = [...new Set(originalVersions)];
74+
if (!dedupedOriginalVersions.includes(version)) {
75+
console.error(
76+
`Docs version ${version} is not present in versions.json. Create the release-line snapshot first with: make docs-version DOCS_VERSION=${version}`,
77+
);
78+
process.exit(1);
79+
}
80+
81+
const tempRoot = path.join(websiteRoot, `.tmp-refresh-version-${process.pid}`);
82+
const versionedDocsDir = path.join(websiteRoot, 'versioned_docs', `version-${version}`);
83+
const versionedSidebarPath = path.join(
84+
websiteRoot,
85+
'versioned_sidebars',
86+
`version-${version}-sidebars.json`,
87+
);
88+
const backupDocsDir = path.join(tempRoot, 'versioned_docs', `version-${version}`);
89+
const backupSidebarPath = path.join(
90+
tempRoot,
91+
'versioned_sidebars',
92+
`version-${version}-sidebars.json`,
93+
);
94+
95+
let movedDocs = false;
96+
let movedSidebar = false;
97+
98+
try {
99+
await fs.rm(tempRoot, {recursive: true, force: true});
100+
movedDocs = await moveIfExists(versionedDocsDir, backupDocsDir);
101+
movedSidebar = await moveIfExists(versionedSidebarPath, backupSidebarPath);
102+
await fs.writeFile(
103+
versionsPath,
104+
`${JSON.stringify(dedupedOriginalVersions.filter((candidate) => candidate !== version), null, 2)}\n`,
105+
);
106+
107+
run(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['run', 'prepare:contribute']);
108+
run(process.platform === 'win32' ? 'npx.cmd' : 'npx', ['docusaurus', 'docs:version', version]);
109+
110+
await fs.writeFile(versionsPath, `${JSON.stringify(dedupedOriginalVersions, null, 2)}\n`);
111+
await fs.rm(tempRoot, {recursive: true, force: true});
112+
} catch (error) {
113+
await fs.rm(versionedDocsDir, {recursive: true, force: true});
114+
await fs.rm(versionedSidebarPath, {force: true});
115+
await restoreIfMoved(backupDocsDir, versionedDocsDir, movedDocs);
116+
await restoreIfMoved(backupSidebarPath, versionedSidebarPath, movedSidebar);
117+
await fs.writeFile(versionsPath, `${JSON.stringify(dedupedOriginalVersions, null, 2)}\n`);
118+
await fs.rm(tempRoot, {recursive: true, force: true});
119+
console.error(error.message);
120+
process.exit(1);
121+
}

website/scripts/snapshot-version.mjs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,18 @@ async function dedupeVersions() {
3333
await fs.writeFile(versionsPath, `${JSON.stringify(deduped, null, 2)}\n`);
3434
}
3535

36+
const prepareResult = spawnSync(
37+
process.platform === 'win32' ? 'npm.cmd' : 'npm',
38+
['run', 'prepare:contribute'],
39+
{
40+
stdio: 'inherit',
41+
},
42+
);
43+
44+
if (prepareResult.status !== 0) {
45+
process.exit(prepareResult.status ?? 1);
46+
}
47+
3648
const result = spawnSync(
3749
process.platform === 'win32' ? 'npx.cmd' : 'npx',
3850
['docusaurus', 'docs:version', version],

website/tests/behavior.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,15 @@ test('version dropdown switches from next docs to the stable release line', asyn
4242
await versionDropdown.hover();
4343

4444
const archivedRelease = page.locator('.dropdown__menu').getByRole('link', {
45-
name: '0.2.0',
45+
name: '0.2.x',
4646
exact: true,
4747
});
4848
await expect(archivedRelease).toBeVisible();
4949
await archivedRelease.click();
5050

5151
await expect(page).toHaveURL(/\/openbao-operator\/docs\/get-started\/deployment-decision-guide$/);
5252
await expect(page.getByText('Published release documentation')).toBeVisible();
53-
await expect(page.getByText('Version: 0.2.0')).toBeVisible();
53+
await expect(page.getByText('Version: 0.2.x')).toBeVisible();
5454
});
5555

5656
test.describe('curated legacy redirects stay alive', () => {

website/tests/smoke.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ test('stable docs expose the current release banner', async ({page}) => {
4646

4747
await expect(page.getByRole('heading', {name: 'OpenBao Operator documentation'})).toBeVisible();
4848
await expect(page.getByText('Published release documentation')).toBeVisible();
49-
await expect(page.getByText('Version: 0.2.0')).toBeVisible();
49+
await expect(page.getByText('Version: 0.2.x')).toBeVisible();
5050
});
5151

5252
test('architecture section exposes grouped local navigation', async ({page}) => {

website/versioned_docs/version-0.2.0/contribute/release-management.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ journey: contribute
7171

7272
<Callout type="important" title="Stable release-line docs snapshots">
7373

74-
Before merging the first stable `X.Y.0` release PR for a release line, snapshot the docs for that release line and commit the generated artifacts. Patch releases in the same line publish release notes and reuse the `X.Y.0` docs snapshot. Prereleases continue to use `/docs/next` and release notes only; do not add patch, `-alpha`, `-beta`, or `-rc` entries to `website/versions.json`.
74+
Before merging the first stable `X.Y.0` release PR for a release line, snapshot the docs for that release line and commit the generated artifacts. Patch releases in the same line reuse the `X.Y.0` docs version, but user-facing docs fixes for that patch must refresh the existing `X.Y.0` snapshot from the release branch. Prereleases continue to use `/docs/next` and release notes only; do not add patch, `-alpha`, `-beta`, or `-rc` entries to `website/versions.json`.
7575

7676
</Callout>
7777

@@ -84,6 +84,16 @@ Before merging the first stable `X.Y.0` release PR for a release line, snapshot
8484
This updates `website/versioned_docs/`, `website/versioned_sidebars/`, and `website/versions.json`.
8585
</CommandBlock>
8686

87+
<CommandBlock
88+
language="bash"
89+
label="configure"
90+
title="Refresh docs for a patch release line"
91+
code={`git switch release-X.Y
92+
make docs-refresh-version DOCS_VERSION=X.Y.0`}
93+
>
94+
Run this from the release branch after backporting docs that apply to the patch release. This updates the existing release-line docs snapshot without adding a patch version to `website/versions.json`.
95+
</CommandBlock>
96+
8797
<CommandBlock
8898
language="bash"
8999
label="configure"

0 commit comments

Comments
 (0)