diff --git a/gatsby/URL_MAPPING_ARCHITECTURE.md b/gatsby/URL_MAPPING_ARCHITECTURE.md index 113fc8a6..421e91f7 100644 --- a/gatsby/URL_MAPPING_ARCHITECTURE.md +++ b/gatsby/URL_MAPPING_ARCHITECTURE.md @@ -391,7 +391,25 @@ Rules are evaluated in order; the first matching rule wins. --- -### Rule 11: TiDB-in-Kubernetes with Branch Alias +### Rule 11: TiDB-in-Kubernetes Release Notes from Main + +**Effect**: Publishes TiDB-in-Kubernetes release notes from `main` at stable URLs so they override the copies from the configured stable release branch. + +**Source Pattern**: `/{lang}/tidb-in-kubernetes/main/releases/{filename}` + +**Target Pattern**: `/{lang}/tidb-in-kubernetes/stable/{filename}` + +**Example**: +- Source: `en/tidb-in-kubernetes/main/releases/release-2.0.0.md` +- Target: `/tidb-in-kubernetes/stable/release-2.0.0` +- Source: `zh/tidb-in-kubernetes/main/releases/release-2.0.0.md` +- Target: `/zh/tidb-in-kubernetes/stable/release-2.0.0` + +**Use Case**: Release notes are maintained on `main`, but their canonical published URLs must resolve under `stable`. The earlier releases-index rule continues to map `_index.md` to `/releases/tidb-operator`. + +--- + +### Rule 12: TiDB-in-Kubernetes with Branch Alias **Effect**: Maps TiDB-in-Kubernetes pages with branch aliasing (main → dev, release-* → v*). @@ -416,7 +434,7 @@ Rules are evaluated in order; the first matching rule wins. --- -### Rule 12: Fallback Rule +### Rule 13: Fallback Rule **Effect**: Generic fallback for any remaining paths. @@ -512,24 +530,43 @@ Rules are evaluated in order; the first matching rule wins. ### Rule 5: Links from TiDB Operator Releases Landing Page (Path-Based, /releases/*) -**Effect**: Resolves `/releases/*` links from the operator releases landing page to TiDB-in-Kubernetes `dev` branch URLs. +**Effect**: Resolves `/releases/*` links from the operator releases landing page to TiDB-in-Kubernetes `stable` URLs. **Path Pattern**: `/{lang}/releases/tidb-operator/{...any}` **Link Pattern**: `/releases/{docname}` -**Target Pattern**: `/{lang}/tidb-in-kubernetes/dev/{docname}` +**Target Pattern**: `/{lang}/tidb-in-kubernetes/stable/{docname}` **Example**: - Current Page: `/releases/tidb-operator` - Link: `/releases/release-2.0.0` -- Result: `/tidb-in-kubernetes/dev/release-2.0.0` (or `/en/tidb-in-kubernetes/dev/release-2.0.0` if default language not omitted) +- Result: `/tidb-in-kubernetes/stable/release-2.0.0` (or `/en/tidb-in-kubernetes/stable/release-2.0.0` if default language not omitted) + +**Use Case**: The operator releases landing page is under `/releases/`, while release notes from `main` are published under `/tidb-in-kubernetes/stable/*` to override the copies from the stable release branch. + +--- + +### Rule 6: TiDB-in-Kubernetes Main TOC Release Links (Path-Based) + +**Effect**: Resolves release-note links from the `main` TiDB-in-Kubernetes TOC to the stable URLs that publish the corresponding `main` release-note files. + +**Path Pattern**: `/{lang}/tidb-in-kubernetes/dev/TOC-tidb-operator-releases` + +**Link Pattern**: `/releases/{docname}` + +**Target Pattern**: `/{lang}/tidb-in-kubernetes/stable/{docname}` + +**Example**: +- Current TOC: `/tidb-in-kubernetes/dev/TOC-tidb-operator-releases` +- Link: `/releases/release-2.0.0` +- Result: `/tidb-in-kubernetes/stable/release-2.0.0` -**Use Case**: The operator releases landing page is under `/releases/`, but the actual release notes pages are published under `/tidb-in-kubernetes/dev/*` (`main` is published as `dev`). +**Use Case**: `TOC-tidb-operator-releases.md` from `main` is resolved under the `dev` branch alias, but its `releases/release-*.md` entries are published under `/stable/*`. Other TOC links continue to resolve under `dev`, and versioned TOCs continue to preserve their version. --- -### Rule 6: Namespace Index Links (Direct Mapping) +### Rule 7: Namespace Index Links (Direct Mapping) **Effect**: Resolves namespace index links (ending with `/_index`) to namespace URLs (published as `/developer`, `/best-practices`, `/api`, `/ai`, `/tidbcloud`, `/tidbcloudlake`). @@ -559,7 +596,7 @@ Rules are evaluated in order; the first matching rule wins. --- -### Rule 7: Namespace Links (Direct Mapping) +### Rule 8: Namespace Links (Direct Mapping) **Effect**: Resolves namespace links (`develop`, `best-practices`, `api`, `ai`, `tidb-cloud`, `tidb-cloud-lake`) to namespace URLs (published as `/developer`, `/best-practices`, `/api`, `/ai`, `/tidbcloud`, `/tidbcloudlake`). @@ -586,7 +623,7 @@ Rules are evaluated in order; the first matching rule wins. --- -### Rule 8: TiDBCloud Page Links (Path-Based) +### Rule 9: TiDBCloud Page Links (Path-Based) **Effect**: Resolves relative links from TiDBCloud pages to TiDBCloud URLs. @@ -608,7 +645,7 @@ Rules are evaluated in order; the first matching rule wins. --- -### Rule 9: TiDB Cloud Lake Page Links (Path-Based) +### Rule 10: TiDB Cloud Lake Page Links (Path-Based) **Effect**: Resolves relative links from TiDB Cloud Lake pages to `/tidbcloudlake/*` URLs. @@ -627,7 +664,7 @@ Rules are evaluated in order; the first matching rule wins. --- -### Rule 10: Developer/Best-Practices/API/AI Namespace Page Links (Path-Based) +### Rule 11: Developer/Best-Practices/API/AI Namespace Page Links (Path-Based) **Effect**: Resolves relative links from namespace pages to TiDB stable branch URLs. @@ -651,7 +688,7 @@ Rules are evaluated in order; the first matching rule wins. --- -### Rule 11: TiDB/TiDB-in-Kubernetes Page Links (Path-Based) +### Rule 12: TiDB/TiDB-in-Kubernetes Page Links (Path-Based) **Effect**: Resolves relative links from TiDB or TiDB-in-Kubernetes pages, preserving branch/version. diff --git a/gatsby/__tests__/get-files-from-tocs.test.ts b/gatsby/__tests__/get-files-from-tocs.test.ts index 3cbb4d2d..01a9d013 100644 --- a/gatsby/__tests__/get-files-from-tocs.test.ts +++ b/gatsby/__tests__/get-files-from-tocs.test.ts @@ -110,6 +110,32 @@ describe("getFilesFromTocs TOC selection rules", () => { ]); }); + it("tidb-in-kubernetes main reads the operator releases TOC", async () => { + const { getFilesFromTocs } = require("../toc-filter"); + + const graphql = jest.fn().mockResolvedValue({ + data: { + allMdx: { + nodes: [ + makeNode( + "en/tidb-in-kubernetes/main/TOC", + "docs/markdown-pages/en/tidb-in-kubernetes/main/TOC.md" + ), + makeNode( + "en/tidb-in-kubernetes/main/TOC-tidb-operator-releases", + "docs/markdown-pages/en/tidb-in-kubernetes/main/TOC-tidb-operator-releases.md" + ), + ], + }, + }, + }); + + const { tocFilesMap } = await getFilesFromTocs(graphql); + expect(new Set(tocFilesMap.get("en/tidb-in-kubernetes/dev")!)).toEqual( + new Set(["toc-only", "operator-releases-only"]) + ); + }); + it("tidbcloud always reads all TOCs under tidbcloud directory", async () => { const { getFilesFromTocs } = require("../toc-filter"); diff --git a/gatsby/__tests__/toc.test.ts b/gatsby/__tests__/toc.test.ts index db3e9544..6adf0bf1 100644 --- a/gatsby/__tests__/toc.test.ts +++ b/gatsby/__tests__/toc.test.ts @@ -1,4 +1,5 @@ import { mdxAstToToc } from "../toc"; +import { extractFilesFromToc } from "../toc-filter"; describe("mdxAstToToc tag query parsing", () => { it("does not emit a bogus ?undefined query when the image URL has no query string", () => { @@ -100,3 +101,52 @@ describe("mdxAstToToc tag query parsing", () => { }); }); }); + +describe("mdxAstToToc TiDB Operator releases navigation", () => { + it("maps main release TOC links to stable URLs and preserves TOC membership", () => { + const toc = mdxAstToToc( + [ + { + type: "list", + children: [ + { + type: "listItem", + children: [ + { + type: "paragraph", + children: [{ type: "text", value: "v2.0" }], + }, + { + type: "list", + children: [ + { + type: "listItem", + children: [ + { + type: "paragraph", + children: [ + { + type: "link", + url: "releases/release-2.0.0.md", + children: [{ type: "text", value: "2.0 GA" }], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ] as any, + "en/tidb-in-kubernetes/main/TOC-tidb-operator-releases" + ); + + expect(toc[0].children?.[0].link).toBe( + "/tidb-in-kubernetes/stable/release-2.0.0" + ); + expect(extractFilesFromToc(toc)).toEqual(["release-2.0.0"]); + }); +}); diff --git a/gatsby/link-resolver/__tests__/link-resolver.test.ts b/gatsby/link-resolver/__tests__/link-resolver.test.ts index 9ed75d9e..62732b2f 100644 --- a/gatsby/link-resolver/__tests__/link-resolver.test.ts +++ b/gatsby/link-resolver/__tests__/link-resolver.test.ts @@ -355,7 +355,7 @@ describe("resolveMarkdownLink", () => { "/releases/release-2.0.0", "/en/releases/tidb-operator" ); - expect(result).toBe("/tidb-in-kubernetes/dev/release-2.0.0"); + expect(result).toBe("/tidb-in-kubernetes/stable/release-2.0.0"); }); it("should resolve /releases/* links from releases/tidb-operator page (en - currentPageUrl without language prefix)", () => { @@ -363,7 +363,7 @@ describe("resolveMarkdownLink", () => { "/releases/release-2.0.0", "/releases/tidb-operator" ); - expect(result).toBe("/tidb-in-kubernetes/dev/release-2.0.0"); + expect(result).toBe("/tidb-in-kubernetes/stable/release-2.0.0"); }); it("should resolve /releases/* links from releases/tidb-operator page (en - default language omitted)", () => { @@ -371,7 +371,7 @@ describe("resolveMarkdownLink", () => { "/release-2.0.0", "/en/releases/tidb-operator" ); - expect(result).toBe("/tidb-in-kubernetes/dev/release-2.0.0"); + expect(result).toBe("/tidb-in-kubernetes/stable/release-2.0.0"); }); it("should resolve /releases/* links from releases/tidb-operator page (en - currentPageUrl without language prefix)", () => { @@ -379,7 +379,7 @@ describe("resolveMarkdownLink", () => { "/release-2.0.0", "/releases/tidb-operator" ); - expect(result).toBe("/tidb-in-kubernetes/dev/release-2.0.0"); + expect(result).toBe("/tidb-in-kubernetes/stable/release-2.0.0"); }); it("should resolve /releases/* links from releases/tidb-operator page (zh - language prefix included)", () => { @@ -387,7 +387,7 @@ describe("resolveMarkdownLink", () => { "/releases/release-2.0.0", "/zh/releases/tidb-operator" ); - expect(result).toBe("/zh/tidb-in-kubernetes/dev/release-2.0.0"); + expect(result).toBe("/zh/tidb-in-kubernetes/stable/release-2.0.0"); }); it("should resolve /releases/* links from releases/tidb-operator page (zh - language prefix included)", () => { @@ -395,7 +395,56 @@ describe("resolveMarkdownLink", () => { "/release-2.0.0", "/zh/releases/tidb-operator" ); - expect(result).toBe("/zh/tidb-in-kubernetes/dev/release-2.0.0"); + expect(result).toBe("/zh/tidb-in-kubernetes/stable/release-2.0.0"); + }); + + it("should resolve operator release links without a leading slash and preserve the hash", () => { + const result = resolveMarkdownLink( + "releases/release-2.0.0#upgrade", + "/ja/releases/tidb-operator" + ); + expect(result).toBe( + "/ja/tidb-in-kubernetes/stable/release-2.0.0#upgrade" + ); + }); + + it.each([ + ["en", "/tidb-in-kubernetes/stable/release-2.0.0"], + ["zh", "/zh/tidb-in-kubernetes/stable/release-2.0.0"], + ["ja", "/ja/tidb-in-kubernetes/stable/release-2.0.0"], + ])( + "should resolve %s release links from the main tidb-in-kubernetes TOC to stable", + (lang, expected) => { + const result = resolveMarkdownLink( + "/releases/release-2.0.0", + `/${lang}/tidb-in-kubernetes/dev/TOC-tidb-operator-releases` + ); + expect(result).toBe(expected); + } + ); + + it("should resolve TOC release links without a leading slash and preserve the hash", () => { + const result = resolveMarkdownLink( + "releases/release-2.0.0#upgrade", + "/tidb-in-kubernetes/dev/TOC-tidb-operator-releases" + ); + expect(result).toBe("/tidb-in-kubernetes/stable/release-2.0.0#upgrade"); + }); + + it("should keep non-release links from the main tidb-in-kubernetes TOC on dev", () => { + const result = resolveMarkdownLink( + "/deploy/deploy-tidb-on-kubernetes", + "/en/tidb-in-kubernetes/dev/TOC-tidb-operator-releases" + ); + expect(result).toBe("/tidb-in-kubernetes/dev/deploy-tidb-on-kubernetes"); + }); + + it("should preserve versioned tidb-in-kubernetes TOC release links", () => { + const result = resolveMarkdownLink( + "/releases/release-2.0.0", + "/zh/tidb-in-kubernetes/v2.0/TOC" + ); + expect(result).toBe("/zh/tidb-in-kubernetes/v2.0/release-2.0.0"); }); it("should resolve releases namespace links (en - matches Rule 4, not Rule 1)", () => { diff --git a/gatsby/link-resolver/config.ts b/gatsby/link-resolver/config.ts index aa7b0e38..e89ba417 100644 --- a/gatsby/link-resolver/config.ts +++ b/gatsby/link-resolver/config.ts @@ -33,11 +33,18 @@ export const defaultLinkResolverConfig: LinkResolverConfig = { targetPattern: "/{lang}/tidb/stable/{docname}", }, // Current page: /{lang}/releases/tidb-operator - // Link: /releases/{docname} -> /{lang}/tidb-in-kubernetes/dev/{docname} + // Link: /releases/{docname} -> /{lang}/tidb-in-kubernetes/stable/{docname} { pathPattern: "/{lang}/releases/tidb-operator", linkPattern: "/{...any}/{docname}", - targetPattern: "/{lang}/tidb-in-kubernetes/dev/{docname}", + targetPattern: "/{lang}/tidb-in-kubernetes/stable/{docname}", + }, + // Current TOC: /{lang}/tidb-in-kubernetes/dev/TOC-tidb-operator-releases + // Link: /releases/{docname} -> /{lang}/tidb-in-kubernetes/stable/{docname} + { + pathPattern: "/{lang}/tidb-in-kubernetes/dev/TOC-tidb-operator-releases", + linkPattern: "/releases/{docname}", + targetPattern: "/{lang}/tidb-in-kubernetes/stable/{docname}", }, // Rule 1: Links starting with specific namespaces (direct link mapping) // Special handling for namespace index links: diff --git a/gatsby/url-resolver/__tests__/url-resolver.test.ts b/gatsby/url-resolver/__tests__/url-resolver.test.ts index 7bd3deeb..05c2b1e0 100644 --- a/gatsby/url-resolver/__tests__/url-resolver.test.ts +++ b/gatsby/url-resolver/__tests__/url-resolver.test.ts @@ -351,6 +351,60 @@ describe("calculateFileUrl", () => { expect(url).toBe("/en/tidb/v8.1/releases"); }); + it.each([ + ["en", "/en/tidb-in-kubernetes/stable/release-2.0.0/"], + ["zh", "/zh/tidb-in-kubernetes/stable/release-2.0.0/"], + ["ja", "/ja/tidb-in-kubernetes/stable/release-2.0.0/"], + ])( + "should map %s tidb-in-kubernetes release notes from main to stable", + (lang, expected) => { + const absolutePath = path.join( + sourceBasePath, + `${lang}/tidb-in-kubernetes/main/releases/release-2.0.0.md` + ); + const url = calculateFileUrlWithConfig(absolutePath, testConfig); + expect(url).toBe(expected); + } + ); + + it("should keep the tidb-in-kubernetes releases index mapping", () => { + const absolutePath = path.join( + sourceBasePath, + "en/tidb-in-kubernetes/main/releases/_index.md" + ); + const url = calculateFileUrlWithConfig(absolutePath, testConfig); + expect(url).toBe("/en/releases/tidb-operator/"); + }); + + it("should map main and stable-branch release notes to the same stable URL", () => { + const mainUrl = calculateFileUrlWithConfig( + path.join( + sourceBasePath, + "en/tidb-in-kubernetes/main/releases/release-2.0.0.md" + ), + testConfig + ); + const stableBranchUrl = calculateFileUrlWithConfig( + path.join( + sourceBasePath, + "en/tidb-in-kubernetes/release-1.6/releases/release-2.0.0.md" + ), + testConfig + ); + + expect(mainUrl).toBe("/en/tidb-in-kubernetes/stable/release-2.0.0/"); + expect(stableBranchUrl).toBe(mainUrl); + }); + + it("should continue mapping non-release pages from main to dev", () => { + const absolutePath = path.join( + sourceBasePath, + "en/tidb-in-kubernetes/main/deploy/deploy-tidb-on-kubernetes.md" + ); + const url = calculateFileUrlWithConfig(absolutePath, testConfig); + expect(url).toBe("/en/tidb-in-kubernetes/dev/deploy-tidb-on-kubernetes/"); + }); + it("should resolve releases folder zh", () => { const absolutePath = path.join( sourceBasePath, diff --git a/gatsby/url-resolver/config.ts b/gatsby/url-resolver/config.ts index ab3c4100..2b8e12d7 100644 --- a/gatsby/url-resolver/config.ts +++ b/gatsby/url-resolver/config.ts @@ -107,8 +107,7 @@ export const defaultUrlResolverConfig: UrlResolverConfig = { // When filename = "_index": /en/tidb-cloud-lake/master/{folders}/_index.md -> /en/tidbcloudlake/{folders} // When filename != "_index": /en/tidb-cloud-lake/master/{folders}/{filename}.md -> /en/tidbcloudlake/{filename} { - sourcePattern: - "/{lang}/tidb-cloud-lake/{branch}/{...folders}/{filename}", + sourcePattern: "/{lang}/tidb-cloud-lake/{branch}/{...folders}/{filename}", targetPattern: "/{lang}/tidbcloudlake/{filename}", filenameTransform: { ignoreIf: ["_index"], @@ -140,6 +139,13 @@ export const defaultUrlResolverConfig: UrlResolverConfig = { ignoreIf: ["_index", "_docHome"], }, }, + // tidb-in-kubernetes release notes from main + // Release notes from main override the copies from the stable release branch. + // /en/tidb-in-kubernetes/main/releases/{filename} -> /en/tidb-in-kubernetes/stable/{filename} + { + sourcePattern: "/{lang}/tidb-in-kubernetes/main/releases/{filename}", + targetPattern: "/{lang}/tidb-in-kubernetes/stable/{filename}", + }, // tidb-in-kubernetes with branch and optional folders // /en/tidb-in-kubernetes/main/{...folders}/{filename} -> /en/tidb-in-kubernetes/stable/{filename} // /en/tidb-in-kubernetes/release-1.6/{...folders}/{filename} -> /en/tidb-in-kubernetes/v1.6/{filename}