aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.agents/skills/herdr-pre-release-audit/references/pre-release-audit.md2
-rw-r--r--.github/workflows/website.yml2
-rw-r--r--AGENTS.md4
-rw-r--r--CONTRIBUTING.md4
-rw-r--r--docs/next/website/src/content/docs/configuration.mdx2
-rw-r--r--docs/next/website/src/content/docs/ja/configuration.mdx2
-rw-r--r--docs/next/website/src/content/docs/zh-cn/configuration.mdx2
-rw-r--r--docs/versions/0.8.0/website/src/content/docs/configuration.mdx2
-rw-r--r--docs/versions/0.8.0/website/src/content/docs/ja/configuration.mdx2
-rw-r--r--docs/versions/0.8.0/website/src/content/docs/zh-cn/configuration.mdx2
-rw-r--r--docs/versions/README.md11
-rw-r--r--scripts/test_preview.py2
-rw-r--r--website/README.md6
-rw-r--r--website/scripts/check-built-docs.mjs4
-rw-r--r--website/scripts/docs-snapshot.mjs4
-rw-r--r--website/scripts/docs-versions.integration.test.ts75
-rw-r--r--website/scripts/docs-versions.mjs51
-rw-r--r--website/scripts/prepare-docs.mjs22
18 files changed, 130 insertions, 69 deletions
diff --git a/.agents/skills/herdr-pre-release-audit/references/pre-release-audit.md b/.agents/skills/herdr-pre-release-audit/references/pre-release-audit.md
index 68f7de19..8666f644 100644
--- a/.agents/skills/herdr-pre-release-audit/references/pre-release-audit.md
+++ b/.agents/skills/herdr-pre-release-audit/references/pre-release-audit.md
@@ -61,7 +61,7 @@ Process:
- Preserve the existing changelog style and sections: `Added`, `Changed`, `Fixed`, `Removed`, and `Breaking Changes` when applicable.
7. Audit next-release public docs.
- - Treat root `README.md` and the current stable docs source as the latest released public docs. While `docs/versions/manifest.json` has `stable_source: legacy`, that source is `website/src/content/docs/`; afterward it is the current immutable snapshot under `docs/versions/<current>/website/src/content/docs/`.
+ - Treat root `README.md` and the version selected by `docs/versions/manifest.json` under `docs/versions/<current>/website/src/content/docs/` as the latest released public docs. Published version docs may contain factual corrections made after the release tag.
- Treat `docs/next/README.md` as the next-release root README and `docs/next/website/src/content/docs/` as the complete unpublished website-doc draft.
- Treat `docs/preview/website/` as bot-owned output for the active preview release. Never edit it during release review and never use it as the stable release source.
- Compare meaningful user-facing changes in the range against next-release docs first.
diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml
index 4c1be29c..b437c61f 100644
--- a/.github/workflows/website.yml
+++ b/.github/workflows/website.yml
@@ -42,7 +42,7 @@ jobs:
with:
bun-version: 1.3.14
- - name: Validate published snapshots
+ - name: Validate published documentation
run: |
node website/scripts/docs-versions.mjs check
node website/scripts/docs-preview.mjs check
diff --git a/AGENTS.md b/AGENTS.md
index 82f89868..edd38bcb 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -163,9 +163,9 @@ Unreleased docs live in `docs/next/website/src/content/docs/`. Update those when
The active preview release docs live in `docs/preview/website/`. Preview CI owns this mutable snapshot and commits it atomically with `website/preview.json`; never edit it manually. Validate it with `node website/scripts/docs-preview.mjs check`.
-Immutable stable release snapshots live in `docs/versions/`. The website build generates `/docs/preview/` from the active preview snapshot and `/docs/<version>/` from stable snapshots. The current stable `/docs/` temporarily uses the tracked legacy `website/src/content/docs/` tree; the next stable release switches it to the current immutable snapshot and removes the legacy copy. Do not edit generated preview, version, or snapshot-backed stable files under `website/src/content/docs/`.
+Published stable-release documentation lives in `docs/versions/`. Release CI seeds each version from the tagged `docs/next` tree, and maintainers may correct factual documentation errors in a published version afterward. Apply a correction separately to `docs/next` when it also applies to future releases; never replace a published tree with the current draft. The website build generates `/docs/preview/` from the active preview snapshot, `/docs/<version>/` from the maintained version directories, and `/docs/` from the version selected by `docs/versions/manifest.json`. Do not edit generated files under `website/src/content/docs/`.
-During release review, finalize `docs/next` and run `just release-docs-check`. Do not copy draft docs into preview or stable manually. Preview CI snapshots the selected commit. After a stable GitHub Release succeeds, release CI snapshots the exact tag, updates `latest.json`, and deploys them together. Normal feature/fix work should not edit root `README.md`, root `CHANGELOG.md`, legacy stable website docs, or `website/latest.json` unless explicitly requested.
+During release review, finalize `docs/next` and run `just release-docs-check`. Do not copy draft docs into preview or published versions manually. Preview CI snapshots the selected commit. After a stable GitHub Release succeeds, release CI seeds a new version from the exact tag, updates `latest.json`, and deploys them together. Normal feature/fix work should not edit root `README.md`, root `CHANGELOG.md`, published version docs, or `website/latest.json` unless it is a focused correction to already-published documentation or explicitly requested.
Put local PRDs, planning notes, and exploratory specs under `.local/prd/`; `.local/` is ignored and locally controlled.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2553c811..90fdb0a5 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -65,9 +65,9 @@ If your proposal changes the visual language, interaction model, workflow, persi
## Documentation for unreleased changes
-The root `README.md`, root `CHANGELOG.md`, and public website docs describe released Herdr builds. Do not update root `README.md`, root `CHANGELOG.md`, `docs/preview/`, `docs/versions/`, or `website/src/content/docs/` for normal PRs.
+The root `README.md`, root `CHANGELOG.md`, and public website docs describe released Herdr builds. Do not update root `README.md`, root `CHANGELOG.md`, `docs/preview/`, `docs/versions/`, or `website/src/content/docs/` for normal code PRs. A focused correction to already-published documentation may update the affected `docs/versions/<version>/` files and should make the same correction under `docs/next` when it remains relevant to future releases.
-If your PR changes user-facing behavior, mention the needed public-doc update in the PR. Update `docs/next/README.md` only when the root README needs to change for the next stable release. Update the draft under `docs/next/website/src/content/docs/` when website docs need to change. Draft changes stay unpublished until preview CI snapshots a selected commit or stable release CI snapshots a tag; contributors and maintainers do not copy them into public docs manually.
+If your PR changes user-facing behavior, mention the needed public-doc update in the PR. Update `docs/next/README.md` only when the root README needs to change for the next stable release. Update the draft under `docs/next/website/src/content/docs/` when website docs need to change. Draft changes stay unpublished until preview CI snapshots a selected commit or stable release CI seeds a new version from a tag; contributors and maintainers do not copy an entire draft tree into published docs manually.
You do not need to edit the changelog for normal PRs. Maintainers prepare `docs/next/CHANGELOG.md` during release review.
diff --git a/docs/next/website/src/content/docs/configuration.mdx b/docs/next/website/src/content/docs/configuration.mdx
index 92f5efa0..f1831d95 100644
--- a/docs/next/website/src/content/docs/configuration.mdx
+++ b/docs/next/website/src/content/docs/configuration.mdx
@@ -373,6 +373,8 @@ position = "bottom-right"
Choose `herdr` for an in-app toast, `terminal` for an outer-terminal notification that works well over SSH, `system` for the local OS notification service, or `off` to disable popups. Herdr suppresses popups for the active tab. Search `ui.toast` in the [Config reference](/docs/config-reference/) for positions, delay behavior, and clipboard feedback settings.
+On macOS, `system` tries `terminal-notifier` first and falls back to `/usr/bin/osascript` when it is unavailable or fails. The fallback appears as Script Editor in Notification Center and cannot activate the hosting terminal. Install `terminal-notifier` with `brew install terminal-notifier`. For a supported, detected terminal, it can activate the terminal app when you click the notification. Alternatively, choose `terminal` to let a supported outer terminal own the notification.
+
## Sound
Sound notifications play through the local Herdr client. Custom sounds must be mp3 files; relative paths are resolved from the config file's directory.
diff --git a/docs/next/website/src/content/docs/ja/configuration.mdx b/docs/next/website/src/content/docs/ja/configuration.mdx
index e2b5ea5e..3069ebef 100644
--- a/docs/next/website/src/content/docs/ja/configuration.mdx
+++ b/docs/next/website/src/content/docs/ja/configuration.mdx
@@ -367,6 +367,8 @@ position = "bottom-right"
アプリ内トーストには `herdr`、SSH 越しでも使いやすい外側のターミナル通知には `terminal`、ローカル OS の通知サービスには `system`、ポップアップを無効にするには `off` を選びます。Herdr はアクティブなタブのポップアップを抑制します。位置、遅延の挙動、クリップボードのフィードバック設定については、[設定リファレンス](/docs/config-reference/)で `ui.toast` を検索してください。
+macOS では、`system` は最初に `terminal-notifier` を試し、利用できない場合や失敗した場合は `/usr/bin/osascript` にフォールバックします。このフォールバックは通知センターで Script Editor として表示され、ホストターミナルをアクティブにできません。`brew install terminal-notifier` で `terminal-notifier` をインストールできます。対応するターミナルを Herdr が検出できた場合、通知のクリック時にそのターミナルアプリをアクティブにできます。または、`terminal` を選んで対応する外側のターミナルに通知を送信させてください。
+
## サウンド
サウンド通知はローカルの Herdr クライアントで再生されます。カスタムサウンドは mp3 ファイルでなければなりません。相対パスは設定ファイルのディレクトリから解決されます。
diff --git a/docs/next/website/src/content/docs/zh-cn/configuration.mdx b/docs/next/website/src/content/docs/zh-cn/configuration.mdx
index 8b8f6c4d..257cb127 100644
--- a/docs/next/website/src/content/docs/zh-cn/configuration.mdx
+++ b/docs/next/website/src/content/docs/zh-cn/configuration.mdx
@@ -367,6 +367,8 @@ position = "bottom-right"
选择 `herdr` 可使用应用内 toast,选择 `terminal` 可使用适合 SSH 场景的外层终端通知,选择 `system` 可使用本地操作系统通知服务,选择 `off` 可禁用弹出通知。Herdr 会抑制活动标签页的弹出通知。在[配置参考](/docs/config-reference/)中搜索 `ui.toast`,可查看位置、延迟行为和剪贴板反馈设置。
+在 macOS 上,`system` 会先尝试 `terminal-notifier`;如果它不可用或执行失败,则回退到 `/usr/bin/osascript`。该回退方式会在通知中心显示为 Script Editor,并且无法激活承载 Herdr 的终端。可运行 `brew install terminal-notifier` 安装 `terminal-notifier`。如果 Herdr 检测到受支持的终端,点击通知时它可以激活该终端应用。也可以选择 `terminal`,让受支持的外层终端负责发送通知。
+
## 声音
声音通知通过本地 Herdr 客户端播放。自定义声音必须是 mp3 文件;相对路径从配置文件所在目录解析。
diff --git a/docs/versions/0.8.0/website/src/content/docs/configuration.mdx b/docs/versions/0.8.0/website/src/content/docs/configuration.mdx
index 92f5efa0..f1831d95 100644
--- a/docs/versions/0.8.0/website/src/content/docs/configuration.mdx
+++ b/docs/versions/0.8.0/website/src/content/docs/configuration.mdx
@@ -373,6 +373,8 @@ position = "bottom-right"
Choose `herdr` for an in-app toast, `terminal` for an outer-terminal notification that works well over SSH, `system` for the local OS notification service, or `off` to disable popups. Herdr suppresses popups for the active tab. Search `ui.toast` in the [Config reference](/docs/config-reference/) for positions, delay behavior, and clipboard feedback settings.
+On macOS, `system` tries `terminal-notifier` first and falls back to `/usr/bin/osascript` when it is unavailable or fails. The fallback appears as Script Editor in Notification Center and cannot activate the hosting terminal. Install `terminal-notifier` with `brew install terminal-notifier`. For a supported, detected terminal, it can activate the terminal app when you click the notification. Alternatively, choose `terminal` to let a supported outer terminal own the notification.
+
## Sound
Sound notifications play through the local Herdr client. Custom sounds must be mp3 files; relative paths are resolved from the config file's directory.
diff --git a/docs/versions/0.8.0/website/src/content/docs/ja/configuration.mdx b/docs/versions/0.8.0/website/src/content/docs/ja/configuration.mdx
index e2b5ea5e..3069ebef 100644
--- a/docs/versions/0.8.0/website/src/content/docs/ja/configuration.mdx
+++ b/docs/versions/0.8.0/website/src/content/docs/ja/configuration.mdx
@@ -367,6 +367,8 @@ position = "bottom-right"
アプリ内トーストには `herdr`、SSH 越しでも使いやすい外側のターミナル通知には `terminal`、ローカル OS の通知サービスには `system`、ポップアップを無効にするには `off` を選びます。Herdr はアクティブなタブのポップアップを抑制します。位置、遅延の挙動、クリップボードのフィードバック設定については、[設定リファレンス](/docs/config-reference/)で `ui.toast` を検索してください。
+macOS では、`system` は最初に `terminal-notifier` を試し、利用できない場合や失敗した場合は `/usr/bin/osascript` にフォールバックします。このフォールバックは通知センターで Script Editor として表示され、ホストターミナルをアクティブにできません。`brew install terminal-notifier` で `terminal-notifier` をインストールできます。対応するターミナルを Herdr が検出できた場合、通知のクリック時にそのターミナルアプリをアクティブにできます。または、`terminal` を選んで対応する外側のターミナルに通知を送信させてください。
+
## サウンド
サウンド通知はローカルの Herdr クライアントで再生されます。カスタムサウンドは mp3 ファイルでなければなりません。相対パスは設定ファイルのディレクトリから解決されます。
diff --git a/docs/versions/0.8.0/website/src/content/docs/zh-cn/configuration.mdx b/docs/versions/0.8.0/website/src/content/docs/zh-cn/configuration.mdx
index 8b8f6c4d..257cb127 100644
--- a/docs/versions/0.8.0/website/src/content/docs/zh-cn/configuration.mdx
+++ b/docs/versions/0.8.0/website/src/content/docs/zh-cn/configuration.mdx
@@ -367,6 +367,8 @@ position = "bottom-right"
选择 `herdr` 可使用应用内 toast,选择 `terminal` 可使用适合 SSH 场景的外层终端通知,选择 `system` 可使用本地操作系统通知服务,选择 `off` 可禁用弹出通知。Herdr 会抑制活动标签页的弹出通知。在[配置参考](/docs/config-reference/)中搜索 `ui.toast`,可查看位置、延迟行为和剪贴板反馈设置。
+在 macOS 上,`system` 会先尝试 `terminal-notifier`;如果它不可用或执行失败,则回退到 `/usr/bin/osascript`。该回退方式会在通知中心显示为 Script Editor,并且无法激活承载 Herdr 的终端。可运行 `brew install terminal-notifier` 安装 `terminal-notifier`。如果 Herdr 检测到受支持的终端,点击通知时它可以激活该终端应用。也可以选择 `terminal`,让受支持的外层终端负责发送通知。
+
## 声音
声音通知通过本地 Herdr 客户端播放。自定义声音必须是 mp3 文件;相对路径从配置文件所在目录解析。
diff --git a/docs/versions/README.md b/docs/versions/README.md
index dbfd01f6..42ca1c69 100644
--- a/docs/versions/README.md
+++ b/docs/versions/README.md
@@ -1,13 +1,18 @@
# Versioned documentation
-This directory contains immutable documentation snapshots for stable Herdr releases.
+This directory contains the published documentation for stable Herdr releases.
-Do not edit snapshot files manually. They must match the release tag recorded in `manifest.json`. Validate them with:
+Release CI creates each version from the tagged `docs/next` tree after the GitHub Release succeeds. Maintainers can correct published documentation in its version directory afterward. When a correction also applies to future releases, make the same focused change under `docs/next`; do not replace a published tree with the current draft.
+
+Validate the manifest and build every published version with:
```bash
node website/scripts/docs-versions.mjs check
+cd website && bun run build
```
-Release CI creates a new snapshot from the tagged `docs/next` tree after the GitHub Release succeeds. `website/scripts/prepare-docs.mjs` renders these snapshots at `/docs/<version>/` and uses the current snapshot for `/docs/` after the legacy stable-doc migration completes. `/docs/preview/` comes only from the active preview release snapshot in `docs/preview/`, never directly from `docs/next/`.
+`website/scripts/prepare-docs.mjs` renders each maintained version at `/docs/<version>/` and uses the version selected by `manifest.json` for `/docs/`. Generated files under `website/src/content/docs/` are not editable sources. `/docs/preview/` comes only from the active preview release snapshot in `docs/preview/`, never directly from `docs/next/`.
+
+The `tag`, `commit`, and `source` fields in `manifest.json` record where release CI initially published a version. Git history records later documentation corrections.
The historical backfill starts at v0.5.11, the first release that included the Astro/Starlight documentation site.
diff --git a/scripts/test_preview.py b/scripts/test_preview.py
index c280f572..137a13f0 100644
--- a/scripts/test_preview.py
+++ b/scripts/test_preview.py
@@ -211,7 +211,7 @@ file: ../../../public/assets/logo.svg
self.assertIn("[Install](/docs/0.7.4/install/)", output)
self.assertIn("file: ../../../../../public/assets/logo.svg", output)
self.assertIn("from '../../../../components/ConfigReference.astro'", output)
- self.assertIn("blob/v0.7.4/docs/next/website/src/content/docs/index.mdx", output)
+ self.assertIn("blob/master/docs/versions/0.7.4/website/src/content/docs/index.mdx", output)
self.assertIn("blob/v0.7.4/SKILL.md", output)
diff --git a/website/README.md b/website/README.md
index a511a861..ceb872af 100644
--- a/website/README.md
+++ b/website/README.md
@@ -15,9 +15,9 @@ Documentation has three lifecycle states:
- `../docs/next/website/` is the committed, author-edited draft. Production builds never read it.
- `../docs/preview/website/` is the latest preview release snapshot, rendered at `/docs/preview/`.
-- `../docs/versions/<version>/website/` contains immutable stable release snapshots, rendered at `/docs/<version>/`.
+- `../docs/versions/<version>/website/` contains maintained stable-release documentation, rendered at `/docs/<version>/`. Release CI seeds a new version from its tag; later factual corrections are made directly in that version directory and mirrored to `docs/next` when they also apply to future releases.
-The current stable site temporarily remains tracked under `src/content/docs/` because it contains post-v0.7.5 documentation corrections. The next stable release switches `docs/versions/manifest.json` to snapshot-backed stable docs and removes that legacy copy. From then on, `src/content/docs/` is entirely generated and ignored.
+The version selected by `docs/versions/manifest.json` is also rendered at `/docs/`. `src/content/docs/` is entirely generated and ignored.
Preview CI snapshots the selected commit and updates `preview.json` in one commit:
@@ -26,7 +26,7 @@ node website/scripts/docs-preview.mjs snapshot <commit>
node website/scripts/docs-preview.mjs check
```
-Stable release CI snapshots the exact tag after the GitHub Release succeeds:
+Stable release CI seeds a new maintained version from the exact tag after the GitHub Release succeeds:
```bash
node website/scripts/docs-versions.mjs publish <tag>
diff --git a/website/scripts/check-built-docs.mjs b/website/scripts/check-built-docs.mjs
index 8841bf74..66e904e4 100644
--- a/website/scripts/check-built-docs.mjs
+++ b/website/scripts/check-built-docs.mjs
@@ -48,7 +48,7 @@ assertIncludes(preview, 'name="robots" content="noindex, nofollow"');
assertIncludes(archived, `data-pagefind-filter="version[content]" content="${versions.current}"`);
assertIncludes(archived, 'name="robots" content="noindex, nofollow"');
if (archived.includes(`This page documents Herdr ${versions.current}`)) {
- throw new Error('the current immutable snapshot must not be labeled as outdated');
+ throw new Error('the current documentation version must not be labeled as outdated');
}
const versionSelect = stable.match(/<select[^>]*aria-label="Documentation version"[^>]*>([\s\S]*?)<\/select>/)?.[1];
if (!versionSelect) throw new Error('stable docs are missing the version selector');
@@ -68,7 +68,7 @@ if (previous) {
const sitemap = await readFile(resolve(distDir, 'sitemap-0.xml'), 'utf8');
assertIncludes(sitemap, 'https://herdr.dev/docs/');
if (nonCanonicalDocsUrl.test(sitemap)) {
- throw new Error('preview or immutable documentation URLs must not appear in the sitemap');
+ throw new Error('preview or versioned documentation URLs must not appear in the sitemap');
}
const build = await inspectFiles(distDir);
diff --git a/website/scripts/docs-snapshot.mjs b/website/scripts/docs-snapshot.mjs
index b85557a1..e9eaf92c 100644
--- a/website/scripts/docs-snapshot.mjs
+++ b/website/scripts/docs-snapshot.mjs
@@ -73,7 +73,7 @@ export async function compareGitTree(git, ref, sourceRoot, snapshotRoot) {
assertRelativePath(relativePath, entry.path);
expected.set(relativePath, entry);
}
- const actualPaths = await listSnapshotPaths(snapshotRoot);
+ const actualPaths = await listDocumentationPaths(snapshotRoot);
const expectedPaths = [...expected.keys()].sort();
if (JSON.stringify(actualPaths) !== JSON.stringify(expectedPaths)) {
throw new Error(`${snapshotRoot} file list differs from ${ref}:${sourceRoot}`);
@@ -89,7 +89,7 @@ export async function compareGitTree(git, ref, sourceRoot, snapshotRoot) {
}
}
-async function listSnapshotPaths(root) {
+export async function listDocumentationPaths(root) {
const paths = [];
async function walk(directory) {
for (const entry of await readdir(directory, { withFileTypes: true })) {
diff --git a/website/scripts/docs-versions.integration.test.ts b/website/scripts/docs-versions.integration.test.ts
index 5e2d36a5..052d9ca2 100644
--- a/website/scripts/docs-versions.integration.test.ts
+++ b/website/scripts/docs-versions.integration.test.ts
@@ -1,6 +1,6 @@
import { afterEach, describe, expect, test } from 'bun:test';
import { execFileSync } from 'node:child_process';
-import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
+import { mkdtemp, mkdir, readFile, rm, symlink, writeFile } from 'node:fs/promises';
import { join, resolve } from 'node:path';
import { tmpdir } from 'node:os';
@@ -21,7 +21,8 @@ describe('documentation release publishing', () => {
await write(root, 'website/latest.json', '{"version":"0.9.0"}\n');
await write(root, 'README.md', 'stable readme\n');
await write(root, 'README.zh-CN.md', 'stable readme zh-cn\n');
- await write(root, 'docs/next/website/src/content/docs/index.mdx', 'next docs\n');
+ const nextDocs = '---\ntitle: Documentation\n---\n\nnext docs\n';
+ await write(root, 'docs/next/website/src/content/docs/index.mdx', nextDocs);
await write(root, 'docs/next/website/src/data/config-reference.json', '{"next":true}\n');
await write(root, 'docs/next/README.md', 'next readme\n');
await write(root, 'docs/next/README.zh-CN.md', 'next readme zh-cn\n');
@@ -40,7 +41,7 @@ describe('documentation release publishing', () => {
await expect(read(root, 'website/src/data/config-reference.json')).rejects.toThrow();
expect(await read(root, 'README.md')).toBe('next readme\n');
expect(await read(root, 'README.zh-CN.md')).toBe('next readme zh-cn\n');
- expect(await read(root, 'docs/versions/1.0.0/website/src/content/docs/index.mdx')).toBe('next docs\n');
+ expect(await read(root, 'docs/versions/1.0.0/website/src/content/docs/index.mdx')).toBe(nextDocs);
const manifest = JSON.parse(await read(root, 'docs/versions/manifest.json'));
expect(manifest.current).toBe('1.0.0');
@@ -65,7 +66,19 @@ describe('documentation release publishing', () => {
`${JSON.stringify({ build_id: 'preview-test', commit: previewCommit })}\n`,
);
runPrepare(root);
- expect(await read(root, 'website/src/content/docs/index.mdx')).toBe('next docs\n');
+ const stableDocs = await read(root, 'website/src/content/docs/index.mdx');
+ expect(stableDocs).toContain('next docs');
+ expect(stableDocs).toContain(
+ 'editUrl: https://github.com/herdrdev/herdr/blob/master/docs/versions/1.0.0/website/src/content/docs/index.mdx',
+ );
+ const versionedDocs = await read(
+ root,
+ 'website/src/content/docs/_versions/1.0.0/index.mdx',
+ );
+ expect(versionedDocs).toContain('next docs');
+ expect(versionedDocs).toContain(
+ 'editUrl: https://github.com/herdrdev/herdr/blob/master/docs/versions/1.0.0/website/src/content/docs/index.mdx',
+ );
expect(await read(root, 'website/src/content/docs/preview/index.mdx')).toContain(
'Preview build `preview-test`',
);
@@ -74,10 +87,36 @@ describe('documentation release publishing', () => {
await write(root, 'website/latest.json', '{"version":"1.0.0"}\n');
runScript(root, ['check']);
+ const correctedDocs = '---\ntitle: Documentation\n---\n\ncorrected docs\n';
+ await write(
+ root,
+ 'docs/versions/1.0.0/website/src/content/docs/index.mdx',
+ correctedDocs,
+ );
+ await write(
+ root,
+ 'docs/versions/1.0.0/website/src/data/config-reference.json',
+ '{"corrected":true}\n',
+ );
+ runScript(root, ['check']);
+
await write(root, 'README.md', 'post-release correction\n');
runScript(root, ['publish', 'v1.0.0']);
expect(await read(root, 'README.md')).toBe('post-release correction\n');
+ runPrepare(root);
+ expect(await read(root, 'website/src/content/docs/index.mdx')).toContain('corrected docs');
+ expect(await read(root, 'website/src/content/docs/_versions/1.0.0/index.mdx')).toContain(
+ 'corrected docs',
+ );
+ expect(await read(root, 'website/src/data/config-reference.json')).toBe(
+ '{"corrected":true}\n',
+ );
+ const versionReferences = JSON.parse(
+ await read(root, 'website/src/data/config-reference-versions.json'),
+ );
+ expect(versionReferences['1.0.0']).toEqual({ corrected: true });
+
runScript(root, ['publish', 'v0.9.0']);
const archivedManifest = JSON.parse(await read(root, 'docs/versions/manifest.json'));
expect(archivedManifest.current).toBe('1.0.0');
@@ -85,6 +124,34 @@ describe('documentation release publishing', () => {
expect(await read(root, 'README.md')).toBe('post-release correction\n');
runScript(root, ['check']);
+ const correctedArchivedDocs = '---\ntitle: Documentation\n---\n\ncorrected archived docs\n';
+ await write(
+ root,
+ 'docs/versions/0.9.0/website/src/content/docs/index.mdx',
+ correctedArchivedDocs,
+ );
+ runScript(root, ['publish', 'v0.9.0']);
+ expect(await read(root, 'docs/versions/0.9.0/website/src/content/docs/index.mdx')).toBe(
+ correctedArchivedDocs,
+ );
+
+ const archivedDocsRoot = resolve(
+ root,
+ 'docs/versions/0.9.0/website/src/content/docs',
+ );
+ await rm(archivedDocsRoot, { recursive: true, force: true });
+ expect(() => runScript(root, ['check'])).toThrow();
+ await write(
+ root,
+ 'docs/versions/0.9.0/website/src/content/docs/index.mdx',
+ correctedArchivedDocs,
+ );
+ const linkedPage = resolve(archivedDocsRoot, 'linked.mdx');
+ await symlink('index.mdx', linkedPage);
+ expect(() => runScript(root, ['check'])).toThrow();
+ await rm(linkedPage);
+ runScript(root, ['check']);
+
delete archivedManifest.versions[0].commit;
await write(root, 'docs/versions/manifest.json', `${JSON.stringify(archivedManifest)}\n`);
expect(() => runScript(root, ['check'])).toThrow();
diff --git a/website/scripts/docs-versions.mjs b/website/scripts/docs-versions.mjs
index 30cb099d..0923d683 100644
--- a/website/scripts/docs-versions.mjs
+++ b/website/scripts/docs-versions.mjs
@@ -2,11 +2,11 @@ import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
import { dirname, resolve } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import {
- compareGitTree,
createGit,
extractGitTree,
gitPathExists,
gitTreesEqual,
+ listDocumentationPaths,
resolveCommit,
} from './docs-snapshot.mjs';
@@ -167,38 +167,26 @@ export async function checkVersions() {
if (!['website/src/content/docs', 'docs/next/website/src/content/docs'].includes(entry.source)) {
throw new Error(`docs version ${entry.version} has unsupported source ${entry.source}`);
}
- const sourceRoot = entry.source;
- const snapshotRoot = resolve(versionsDir, entry.version, 'website/src/content/docs');
- await compareGitTree(git, entry.tag, sourceRoot, snapshotRoot);
- const referenceSource = sourceRoot.includes('docs/next/')
- ? 'docs/next/website/src/data/config-reference.json'
- : 'website/src/data/config-reference.json';
- const referenceSnapshot = resolve(
- versionsDir,
- entry.version,
- 'website/src/data/config-reference.json',
+ const versionRoot = resolve(versionsDir, entry.version, 'website');
+ const documentationPaths = await listDocumentationPaths(
+ resolve(versionRoot, 'src/content/docs'),
);
- if (gitPathExists(git, entry.tag, referenceSource)) {
- const actualReference = await readFile(referenceSnapshot);
- const taggedReference = git(['show', `${entry.tag}:${referenceSource}`], { binary: true });
- if (!actualReference.equals(taggedReference)) {
- throw new Error(`${entry.version} config reference differs from ${entry.tag}:${referenceSource}`);
- }
- } else {
- try {
- await readFile(referenceSnapshot);
- throw new Error(`${entry.version} has an unexpected config reference snapshot`);
- } catch (error) {
- if (error.code !== 'ENOENT') throw error;
- }
+ if (!documentationPaths.includes('index.md') && !documentationPaths.includes('index.mdx')) {
+ throw new Error(`docs version ${entry.version} has no root index`);
+ }
+
+ try {
+ JSON.parse(await readFile(resolve(versionRoot, 'src/data/config-reference.json'), 'utf8'));
+ } catch (error) {
+ if (error.code !== 'ENOENT') throw error;
}
}
if (!seen.has(manifest.current)) {
throw new Error(`current docs version ${manifest.current} has no snapshot`);
}
- process.stdout.write(`validated ${manifest.versions.length} documentation snapshots\n`);
+ process.stdout.write(`validated ${manifest.versions.length} documentation versions\n`);
}
export async function publishVersion(tag) {
@@ -217,8 +205,7 @@ export async function publishVersion(tag) {
if (!existing?.commit || existing.commit !== release.commit) {
throw new Error(`published documentation ${release.version} has mismatched commit provenance`);
}
- await compareGitTree(git, release.commit, existing.source, resolve(versionsDir, release.version, 'website/src/content/docs'));
- process.stdout.write(`documentation snapshot ${release.tag} is already published\n`);
+ process.stdout.write(`documentation ${release.tag} is already published\n`);
return;
}
if (manifest.current && compareVersions(release.version, manifest.current) < 0) {
@@ -229,13 +216,7 @@ export async function publishVersion(tag) {
if (existing.commit && existing.commit !== release.commit) {
throw new Error(`version ${release.version} has mismatched commit provenance`);
}
- await compareGitTree(
- git,
- release.commit,
- existing.source,
- resolve(versionsDir, release.version, 'website/src/content/docs'),
- );
- process.stdout.write(`documentation snapshot ${release.tag} is already archived\n`);
+ process.stdout.write(`documentation ${release.tag} is already archived\n`);
return;
}
const archived = await snapshotTag(tag, 'docs/next/website/src/content/docs');
@@ -251,7 +232,7 @@ export async function publishVersion(tag) {
return;
}
if (existing) {
- throw new Error(`version ${release.version} already has a documentation snapshot`);
+ throw new Error(`version ${release.version} already has published documentation`);
}
const metadata = await snapshotTag(tag, 'docs/next/website/src/content/docs');
diff --git a/website/scripts/prepare-docs.mjs b/website/scripts/prepare-docs.mjs
index a96e2c41..074ef36d 100644
--- a/website/scripts/prepare-docs.mjs
+++ b/website/scripts/prepare-docs.mjs
@@ -44,7 +44,6 @@ if (process.argv[2] === '--rewrite-preview-doc-fixture') {
rewriteVersionDocContent(Buffer.concat(chunks).toString('utf8'), {
version: process.argv[3] ?? '0.7.5',
tag: `v${process.argv[3] ?? '0.7.5'}`,
- sourceRoot: 'docs/next/website/src/content/docs',
relativePath: 'index.mdx',
}),
);
@@ -118,8 +117,8 @@ async function prepareDocs({ draft }) {
stableDocsDir,
(content, relativePath) =>
rewriteStableDocContent(content, {
+ version: currentEntry.version,
tag: currentEntry.tag,
- sourceRoot: currentEntry.source,
relativePath,
}),
);
@@ -162,7 +161,6 @@ async function prepareDocs({ draft }) {
rewriteVersionDocContent(content, {
version,
tag: entry.tag,
- sourceRoot: entry.source,
relativePath,
}),
);
@@ -269,24 +267,22 @@ export function rewritePreviewDocContent(
return insertPreviewNotice(withSourceLink, relativePath, { buildId, commit });
}
-export function rewriteStableDocContent(content, { tag, sourceRoot, relativePath }) {
+export function rewriteStableDocContent(content, { version, tag, relativePath }) {
const taggedContent = rewriteRepositoryLinks(content, tag);
- return setGeneratedEditUrl(
- taggedContent,
- `https://github.com/herdrdev/herdr/blob/${tag}/${sourceRoot}/${relativePath}`,
- );
+ return setGeneratedEditUrl(taggedContent, versionedDocSourceUrl(version, relativePath));
}
-export function rewriteVersionDocContent(content, { version, tag, sourceRoot, relativePath }) {
+export function rewriteVersionDocContent(content, { version, tag, relativePath }) {
const taggedContent = rewriteRepositoryLinks(
content.replaceAll('/docs/', `/docs/${version}/`),
tag,
);
const rewritten = rewriteRelativeDocPaths(taggedContent, 2);
- return setGeneratedEditUrl(
- rewritten,
- `https://github.com/herdrdev/herdr/blob/${tag}/${sourceRoot}/${relativePath}`,
- );
+ return setGeneratedEditUrl(rewritten, versionedDocSourceUrl(version, relativePath));
+}
+
+function versionedDocSourceUrl(version, relativePath) {
+ return `https://github.com/herdrdev/herdr/blob/master/docs/versions/${version}/website/src/content/docs/${relativePath}`;
}
function rewriteRepositoryLinks(content, ref) {