Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,56 @@ jobs:
- name: Build docs
uses: ./.github/actions/docs-build

release-line-docs:
name: Release-line Docs Snapshot
needs: changes
if: github.event_name == 'pull_request' && startsWith(github.base_ref, 'release-') && needs.changes.outputs.docs == 'true'
runs-on: *runner
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0

- name: Verify release-line docs snapshot changed
env:
BASE_REF: ${{ github.base_ref }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
set -euo pipefail

if [[ ! "${BASE_REF}" =~ ^release-([0-9]+)\.([0-9]+)$ ]]; then
echo "Release-line docs check only supports release-X.Y branches; got ${BASE_REF}" >&2
exit 1
fi

docs_version="${BASH_REMATCH[1]}.${BASH_REMATCH[2]}.0"
git fetch --no-tags --prune --depth=1 origin "${BASE_SHA}" || true

changed="$(git diff --name-only "${BASE_SHA}" "${HEAD_SHA}")"
docs_changed="$(grep -E '^(docs/|website/sidebars\.ts$)' <<< "${changed}" || true)"
if [[ -z "${docs_changed}" ]]; then
echo "No source docs changed."
exit 0
fi

snapshot_changed="$(
grep -E "^website/versioned_docs/version-${docs_version//./\\.}/|^website/versioned_sidebars/version-${docs_version//./\\.}-sidebars\\.json$|^website/versions\\.json$" <<< "${changed}" || true
)"
if [[ -n "${snapshot_changed}" ]]; then
echo "Release-line docs snapshot changed for ${docs_version}."
exit 0
fi

{
echo "PRs against ${BASE_REF} that change source docs must refresh the ${docs_version} release-line docs snapshot."
echo
echo "Run from the release branch and commit the generated files:"
echo " make docs-refresh-version DOCS_VERSION=${docs_version}"
} >&2
exit 1

helm:
name: Helm Chart
needs: changes
Expand Down Expand Up @@ -1004,6 +1054,7 @@ jobs:
- fuzz
- openbao-config-compat
- docs
- release-line-docs
- helm
- helm-e2e-smoke
- build-artifacts
Expand All @@ -1028,6 +1079,7 @@ jobs:
FUZZ: ${{ needs.fuzz.result }}
OPENBAO_CONFIG_COMPAT: ${{ needs.openbao-config-compat.result }}
DOCS: ${{ needs.docs.result }}
RELEASE_LINE_DOCS: ${{ needs.release-line-docs.result }}
HELM: ${{ needs.helm.result }}
HELM_E2E_SMOKE: ${{ needs.helm-e2e-smoke.result }}
BUILD_ARTIFACTS: ${{ needs.build-artifacts.result }}
Expand Down Expand Up @@ -1062,6 +1114,7 @@ jobs:
fuzz=${FUZZ}
openbao-config-compat=${OPENBAO_CONFIG_COMPAT}
docs=${DOCS}
release-line-docs=${RELEASE_LINE_DOCS}
helm=${HELM}
helm-e2e-smoke=${HELM_E2E_SMOKE}
build-artifacts=${BUILD_ARTIFACTS}
Expand Down
12 changes: 11 additions & 1 deletion docs/contribute/release-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ journey: contribute

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

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`.
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`.

</Callout>

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

<CommandBlock
language="bash"
label="configure"
title="Refresh docs for a patch release line"
code={`git switch release-X.Y
make docs-refresh-version DOCS_VERSION=X.Y.0`}
>
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`.
</CommandBlock>

<CommandBlock
language="bash"
label="configure"
Expand Down
5 changes: 5 additions & 0 deletions mk/development.mk
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,11 @@ docs-version: docs-deps ## Snapshot the current docs into a versioned Docusaurus
@test -n "$(DOCS_VERSION)" || { echo "DOCS_VERSION is required, for example: make docs-version DOCS_VERSION=1.2.3"; exit 1; }
@$(DOCS_NPM) --prefix "$(DOCS_DIR)" run version:docs -- "$(DOCS_VERSION)"

.PHONY: docs-refresh-version
docs-refresh-version: docs-deps ## Refresh an existing release-line docs snapshot from the checked-out docs. Set DOCS_VERSION=<X.Y.0>.
@test -n "$(DOCS_VERSION)" || { echo "DOCS_VERSION is required, for example: make docs-refresh-version DOCS_VERSION=1.2.0"; exit 1; }
@$(DOCS_NPM) --prefix "$(DOCS_DIR)" run refresh:docs-version -- "$(DOCS_VERSION)"

# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'.
# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally.
# CertManager is installed by default; skip with:
Expand Down
22 changes: 22 additions & 0 deletions website/docusaurus.config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import fs from 'node:fs';
import {themes as prismThemes} from 'prism-react-renderer';
import type {Config} from '@docusaurus/types';
import type * as Preset from '@docusaurus/preset-classic';
Expand All @@ -11,6 +12,26 @@ const docsPluginDefaultExclude = [
'**/*.test.{js,jsx,ts,tsx}',
'**/__tests__/**',
];
const releaseLineVersionLabels = {
'0.2.0': '0.2.x',
'0.1.0': '0.1.x',
} as const;

function readDocsVersions(): string[] {
try {
const versions = JSON.parse(fs.readFileSync(new URL('./versions.json', import.meta.url), 'utf8'));
return Array.isArray(versions) ? versions : [];
} catch {
return [];
}
}

const docsVersions = new Set(readDocsVersions());
const releaseLineVersions = Object.fromEntries(
Object.entries(releaseLineVersionLabels)
.filter(([version]) => docsVersions.has(version))
.map(([version, label]) => [version, {label}]),
);

const config: Config = {
title: 'OpenBao Operator',
Expand Down Expand Up @@ -65,6 +86,7 @@ const config: Config = {
label: 'next',
path: 'next',
},
...releaseLineVersions,
},
},
blog: false,
Expand Down
1 change: 1 addition & 0 deletions website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
"version:docs": "node ./scripts/snapshot-version.mjs",
"refresh:docs-version": "node ./scripts/refresh-version.mjs",
"verify:docs-version": "node ./scripts/verify-docs-version.mjs",
"typecheck": "tsc --noEmit",
"test:e2e": "playwright test"
Expand Down
121 changes: 121 additions & 0 deletions website/scripts/refresh-version.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import {spawnSync} from 'node:child_process';
import fs from 'node:fs/promises';
import path from 'node:path';

const version = process.argv[2];
const websiteRoot = process.cwd();
const versionsPath = path.join(websiteRoot, 'versions.json');

if (!version) {
console.error('Usage: npm run refresh:docs-version -- <version>');
process.exit(1);
}

function isStableLineVersion(candidate) {
return /^\d+\.\d+\.0$/.test(candidate);
}

function run(command, args) {
const result = spawnSync(command, args, {
cwd: websiteRoot,
stdio: 'inherit',
});

if (result.status !== 0) {
throw new Error(`${[command, ...args].join(' ')} failed with status ${result.status ?? 1}`);
}
}

async function pathExists(target) {
try {
await fs.access(target);
return true;
} catch (error) {
if (error?.code === 'ENOENT') {
return false;
}
throw error;
}
}

async function moveIfExists(from, to) {
if (await pathExists(from)) {
await fs.mkdir(path.dirname(to), {recursive: true});
await fs.rename(from, to);
return true;
}
return false;
}

async function restoreIfMoved(from, to, moved) {
if (!moved) {
return;
}
await fs.rm(to, {recursive: true, force: true});
await fs.mkdir(path.dirname(to), {recursive: true});
await fs.rename(from, to);
}

if (!isStableLineVersion(version)) {
console.error(
`Release-line docs refresh only supports stable release-line versions (X.Y.0). Patch releases update the existing release-line snapshot: ${version}`,
);
process.exit(1);
}

const raw = await fs.readFile(versionsPath, 'utf8');
const originalVersions = JSON.parse(raw);

if (!Array.isArray(originalVersions)) {
throw new Error('versions.json is not an array');
}

const dedupedOriginalVersions = [...new Set(originalVersions)];
if (!dedupedOriginalVersions.includes(version)) {
console.error(
`Docs version ${version} is not present in versions.json. Create the release-line snapshot first with: make docs-version DOCS_VERSION=${version}`,
);
process.exit(1);
}

const tempRoot = path.join(websiteRoot, `.tmp-refresh-version-${process.pid}`);
const versionedDocsDir = path.join(websiteRoot, 'versioned_docs', `version-${version}`);
const versionedSidebarPath = path.join(
websiteRoot,
'versioned_sidebars',
`version-${version}-sidebars.json`,
);
const backupDocsDir = path.join(tempRoot, 'versioned_docs', `version-${version}`);
const backupSidebarPath = path.join(
tempRoot,
'versioned_sidebars',
`version-${version}-sidebars.json`,
);

let movedDocs = false;
let movedSidebar = false;

try {
await fs.rm(tempRoot, {recursive: true, force: true});
movedDocs = await moveIfExists(versionedDocsDir, backupDocsDir);
movedSidebar = await moveIfExists(versionedSidebarPath, backupSidebarPath);
await fs.writeFile(
versionsPath,
`${JSON.stringify(dedupedOriginalVersions.filter((candidate) => candidate !== version), null, 2)}\n`,
);

run(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['run', 'prepare:contribute']);
run(process.platform === 'win32' ? 'npx.cmd' : 'npx', ['docusaurus', 'docs:version', version]);

await fs.writeFile(versionsPath, `${JSON.stringify(dedupedOriginalVersions, null, 2)}\n`);
await fs.rm(tempRoot, {recursive: true, force: true});
} catch (error) {
await fs.rm(versionedDocsDir, {recursive: true, force: true});
await fs.rm(versionedSidebarPath, {force: true});
await restoreIfMoved(backupDocsDir, versionedDocsDir, movedDocs);
await restoreIfMoved(backupSidebarPath, versionedSidebarPath, movedSidebar);
await fs.writeFile(versionsPath, `${JSON.stringify(dedupedOriginalVersions, null, 2)}\n`);
await fs.rm(tempRoot, {recursive: true, force: true});
console.error(error.message);
process.exit(1);
}
12 changes: 12 additions & 0 deletions website/scripts/snapshot-version.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ async function dedupeVersions() {
await fs.writeFile(versionsPath, `${JSON.stringify(deduped, null, 2)}\n`);
}

const prepareResult = spawnSync(
process.platform === 'win32' ? 'npm.cmd' : 'npm',
['run', 'prepare:contribute'],
{
stdio: 'inherit',
},
);

if (prepareResult.status !== 0) {
process.exit(prepareResult.status ?? 1);
}

const result = spawnSync(
process.platform === 'win32' ? 'npx.cmd' : 'npx',
['docusaurus', 'docs:version', version],
Expand Down
4 changes: 2 additions & 2 deletions website/tests/behavior.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ test('version dropdown switches from next docs to the stable release line', asyn
await versionDropdown.hover();

const archivedRelease = page.locator('.dropdown__menu').getByRole('link', {
name: '0.2.0',
name: '0.2.x',
exact: true,
});
await expect(archivedRelease).toBeVisible();
await archivedRelease.click();

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

test.describe('curated legacy redirects stay alive', () => {
Expand Down
2 changes: 1 addition & 1 deletion website/tests/smoke.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ test('stable docs expose the current release banner', async ({page}) => {

await expect(page.getByRole('heading', {name: 'OpenBao Operator documentation'})).toBeVisible();
await expect(page.getByText('Published release documentation')).toBeVisible();
await expect(page.getByText('Version: 0.2.0')).toBeVisible();
await expect(page.getByText('Version: 0.2.x')).toBeVisible();
});

test('architecture section exposes grouped local navigation', async ({page}) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ journey: contribute

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

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`.
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`.

</Callout>

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

<CommandBlock
language="bash"
label="configure"
title="Refresh docs for a patch release line"
code={`git switch release-X.Y
make docs-refresh-version DOCS_VERSION=X.Y.0`}
>
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`.
</CommandBlock>

<CommandBlock
language="bash"
label="configure"
Expand Down
2 changes: 1 addition & 1 deletion website/versioned_docs/version-0.2.0/reference/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1096,7 +1096,7 @@ _Appears in:_
| `filePermissions` _string_ | FilePermissions are the file permissions for plugin files (e.g., "0755"). | | Optional: \{\} <br /> |
| `autoDownload` _boolean_ | AutoDownload controls automatic plugin downloads from OCI registries. | | Optional: \{\} <br /> |
| `autoRegister` _boolean_ | AutoRegister controls automatic plugin registration. | | Optional: \{\} <br /> |
| `downloadBehavior` _string_ | DownloadBehavior specifies how plugins are downloaded. | | Enum: [standard direct] <br />Optional: \{\} <br /> |
| `downloadBehavior` _string_ | DownloadBehavior controls whether OpenBao startup fails or continues when<br />declarative OCI plugin downloads fail. Valid values are "fail" and<br />"continue"; OpenBao defaults to "fail" when unset. | | Enum: [fail continue] <br />Optional: \{\} <br /> |


#### PodMetadataConfig
Expand Down
Loading
Loading