From 691a0e1153506ab60abefacb750d67660eeb6972 Mon Sep 17 00:00:00 2001 From: UnicornChance Date: Mon, 10 Aug 2026 09:02:37 -0700 Subject: [PATCH 01/12] fix(docs): separate main docs from versioned releases --- astro.config.mjs | 10 +++---- src/build/versions.spec.ts | 46 ++++++++++++++++++++++++++++++ src/build/versions.ts | 29 ++++++++++++++----- src/components/VersionPicker.astro | 13 +++++---- src/productUtils.ts | 8 +++--- 5 files changed, 85 insertions(+), 21 deletions(-) diff --git a/astro.config.mjs b/astro.config.mjs index cde93b58..442b2122 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -33,10 +33,10 @@ const productVersions = Object.fromEntries( return [p.id, available]; }) ); -const productLatestTags = Object.fromEntries( +const productLatestSources = Object.fromEntries( PRODUCTS.flatMap(p => { - const tag = versionsByRepo[p.repo]?.latestTag; - return tag ? [[p.id, tag]] : []; + const source = versionsByRepo[p.repo]?.branch; + return source ? [[p.id, source]] : []; }) ); @@ -251,8 +251,8 @@ export default defineConfig({ ]) ) ), - // Per-product latest release tags for VersionPicker label - __PRODUCT_LATEST_TAGS__: JSON.stringify(productLatestTags), + // Per-product latest docs sources for the VersionPicker label + __PRODUCT_LATEST_SOURCES__: JSON.stringify(productLatestSources), // Product registry for client-side components (VersionPicker, Search) __PRODUCTS__: JSON.stringify(PRODUCTS.map(({ id, label, link, repo }) => ({ id, label, link, githubRepo: repo ?? null }))), }, diff --git a/src/build/versions.spec.ts b/src/build/versions.spec.ts index 14118be4..79c4b2dd 100644 --- a/src/build/versions.spec.ts +++ b/src/build/versions.spec.ts @@ -332,6 +332,52 @@ describe('discoverAllVersions with versionSource', () => { expect(calls.some(c => c.includes('matching-refs'))).toBe(true); }); + it('includes the current release when latest docs use an explicit branch', async () => { + vi.mocked(fetch).mockImplementation(async (url: string | URL | Request) => { + const urlStr = url.toString(); + if (new URL(urlStr).hostname === 'raw.githubusercontent.com') { + return { + ok: true, + json: () => Promise.resolve({ + id: 'core', label: 'Core', contentDir: 'core', + archiveCount: 2, versionSource: 'branch', sidebarOrder: [], + }), + } as Response; + } + if (urlStr.includes('matching-refs')) { + return { + ok: true, + json: () => Promise.resolve([ + { ref: 'refs/heads/release/1.0' }, + { ref: 'refs/heads/release/0.63' }, + { ref: 'refs/heads/release/0.62' }, + ]), + } as Response; + } + if (urlStr.includes('/releases')) { + return { + ok: true, + json: () => Promise.resolve([ + { tag_name: 'v1.0.0', prerelease: false, draft: false }, + ]), + } as Response; + } + return { ok: false, status: 404 } as Response; + }); + + const result = await discoverAllVersions( + [{ repo: 'defenseunicorns/uds-core', branch: 'main' }], + {}, + ); + const entry = result['defenseunicorns/uds-core']; + expect(entry.branch).toBe('main'); + expect(entry.latestTag).toBe('v1.0.0'); + expect(entry.versions).toEqual([ + { ref: 'release/1.0', display: 'v1.0', slug: 'v1-0' }, + { ref: 'release/0.63', display: 'v0.63', slug: 'v0-63' }, + ]); + }); + it('defaults to tag discovery when versionSource is not set', async () => { const calls: string[] = []; vi.mocked(fetch).mockImplementation(async (url: string | URL | Request) => { diff --git a/src/build/versions.ts b/src/build/versions.ts index 14c6e473..e8a6fab1 100644 --- a/src/build/versions.ts +++ b/src/build/versions.ts @@ -277,6 +277,7 @@ export async function discoverAllVersions( const docsConfig = await fetchDocsConfig(repo, configBranch, localOverridePath); const archiveCount = docsConfig?.archiveCount ?? 0; const versionSource = docsConfig?.versionSource ?? 'tag'; + const hasExplicitLatestSource = product.branch !== undefined; console.log(`${repo}: discovering versions (source: ${versionSource})...`); @@ -285,23 +286,37 @@ export async function discoverAllVersions( if (versionSource === 'branch') { // Branch-based: archived versions come from release/* branches, - // but latestTag still comes from the releases API. - // Request one extra in case we need to filter out the current release's branch. + // but latestTag still comes from the releases API. When the latest + // docs source is explicit, the current release is also versioned. const [candidates, releaseResult] = await Promise.all([ - discoverBranchVersions(repo, archiveCount + 1), + discoverBranchVersions(repo, archiveCount + (hasExplicitLatestSource ? 0 : 1)), discoverVersions(repo, 0), ]); latestTag = releaseResult.latestTag; - // Exclude the branch matching the current release (e.g. release/1.0 when latestTag is v1.0.x) + // Exclude the branch matching the current release when the latest + // docs source comes from the latest release. let latestDisplay: string | null = null; if (latestTag) { try { latestDisplay = toArchivedVersion(latestTag).display; } catch { /* unparseable tag */ } } - versions = candidates.filter(v => v.display !== latestDisplay).slice(0, archiveCount); + versions = candidates + .filter(v => hasExplicitLatestSource || v.display !== latestDisplay) + .slice(0, archiveCount); } else { - const result = await discoverVersions(repo, archiveCount); + const result = await discoverVersions( + repo, + archiveCount + (hasExplicitLatestSource ? 1 : 0), + ); latestTag = result.latestTag; - versions = result.archived; + if (hasExplicitLatestSource && latestTag && archiveCount > 0) { + try { + versions = [toArchivedVersion(latestTag), ...result.archived].slice(0, archiveCount); + } catch { + versions = result.archived.slice(0, archiveCount); + } + } else { + versions = result.archived; + } } console.log( diff --git a/src/components/VersionPicker.astro b/src/components/VersionPicker.astro index c0773b90..4775e8c7 100644 --- a/src/components/VersionPicker.astro +++ b/src/components/VersionPicker.astro @@ -39,7 +39,7 @@