Skip to content
Open
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
21 changes: 7 additions & 14 deletions .github/workflows/base-std-docs-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1076,7 +1076,6 @@ jobs:
# deriving it avoids another hardcoded main/master mismatch.
DOCS_BASE_BRANCH: ${{ github.event.repository.default_branch }}
BRANCH: ${{ steps.sync.outputs.branch }}
TOUCHED_PATHS: ${{ steps.sync.outputs.touched_paths }}
REJECTED_PAGES: ${{ steps.sync.outputs.rejected_pages }}
REJECTED_COUNT: ${{ steps.sync.outputs.rejected_count }}
PROVENANCE_MD_PATH: ${{ steps.sync.outputs.provenance_md_path }}
Expand Down Expand Up @@ -1164,19 +1163,20 @@ jobs:
fi

# Reviewer checklist + newly-introduced external URLs. Placed
# ahead of the file-touched / provenance sections so the
# ahead of the combined touched-files / provenance table so the
# action-required items are the first thing a reviewer reads
# after the source-PR link. Sync script writes the file at
# $REVIEW_MD_PATH; we splice it in verbatim.
if [[ -n "${REVIEW_MD_PATH:-}" ]] && [[ -f "${REVIEW_MD_PATH}" ]]; then
cat "${REVIEW_MD_PATH}"
fi

echo
echo "## Files touched"
for p in ${TOUCHED_PATHS:-}; do
echo "- \`${p}\`"
done
# The sync script writes one table containing both the docs files
# changed and their upstream provenance. It is the authoritative
# touched-file list, avoiding a redundant standalone list.
if [[ -n "${PROVENANCE_MD_PATH:-}" ]] && [[ -f "${PROVENANCE_MD_PATH}" ]]; then
cat "${PROVENANCE_MD_PATH}"
fi

# Surface pages that Claude tried to write but the validator
# rejected. Reviewer should expect those pages to be missing from
Expand All @@ -1195,13 +1195,6 @@ jobs:
done
fi

# Splice in the source-provenance markdown table the script wrote.
# Lets a reviewer click straight from each doc page to the source
# file(s) in base that drove its edit.
if [[ -n "${PROVENANCE_MD_PATH:-}" ]] && [[ -f "${PROVENANCE_MD_PATH}" ]]; then
cat "${PROVENANCE_MD_PATH}"
fi

echo
echo "_Opened by \`Apply Base Std Update\` workflow._"
} > "$body_file"
Expand Down
94 changes: 94 additions & 0 deletions scripts/sync-from-base-std/__tests__/base-std-routing.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ import path from "node:path";
import { fileURLToPath } from "node:url";
import {
buildProvenanceComment,
loadKnownRoutes,
loadStyleGuide,
renderProvenanceSection,
routeCodeChange,
validateMdx,
} from "../index.mjs";

const REPO_ROOT = path.resolve(
Expand Down Expand Up @@ -120,6 +123,97 @@ test("loadStyleGuide reads the root content instructions", async () => {
assert.equal(await loadStyleGuide({ repoRoot: REPO_ROOT }), expected);
});

test("internal-link validation accepts index aliases and configured redirects", async () => {
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), "base-std-routes-"));
const docsRoot = path.join(repoRoot, "docs");
const b20Root = path.join(docsRoot, "base-chain", "specs", "reference", "b20");
await fs.mkdir(b20Root, { recursive: true });
await fs.writeFile(path.join(b20Root, "index.mdx"), "---\ntitle: B20\n---\n");
await fs.writeFile(
path.join(docsRoot, "docs.json"),
JSON.stringify({
redirects: [
{ source: "/legacy-b20" },
{ source: "/legacy-reference/:slug*" },
],
}),
);

try {
const routes = await loadKnownRoutes({ repoRoot });
assert.ok(routes.exact.has("/base-chain/specs/reference/b20"));
assert.ok(routes.exact.has("/base-chain/specs/reference/b20/index"));

const current = "---\ntitle: Example\n---\n\nExisting copy.\n";
const withValidLinks = `${current}\n[B20](/base-chain/specs/reference/b20#factory)\n[literal](/legacy-b20)\n[old](/legacy-reference/interfaces/IB20)\n`;
assert.equal(
validateMdx(withValidLinks, "docs/example.mdx", routes, current),
null,
);
} finally {
await fs.rm(repoRoot, { recursive: true, force: true });
}
});

test("internal-link validation ignores retained legacy links but rejects new broken links", async () => {
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), "base-std-link-delta-"));
const docsRoot = path.join(repoRoot, "docs");
await fs.mkdir(docsRoot, { recursive: true });
await fs.writeFile(path.join(docsRoot, "index.mdx"), "---\ntitle: Home\n---\n");

try {
const routes = await loadKnownRoutes({ repoRoot });
const current = "---\ntitle: Example\n---\n\n[Legacy](/retired-page)\n";
const retainedLegacyLink = `${current}\nUpdated copy.\n`;
assert.equal(
validateMdx(retainedLegacyLink, "docs/example.mdx", routes, current),
null,
);

const newBrokenLink = `${retainedLegacyLink}\n[Broken](/missing-page)\n`;
assert.match(
validateMdx(newBrokenLink, "docs/example.mdx", routes, current),
/broken new internal link\(s\): `\/missing-page`/,
);
} finally {
await fs.rm(repoRoot, { recursive: true, force: true });
}
});

test("renderProvenanceSection combines touched docs pages and source files", () => {
const section = renderProvenanceSection(
"code-change",
{ source_repo: "base/base-std", sha: "abcdef0123456789" },
[
{
page: "docs/base-chain/specs/reference/b20/index.mdx",
sourceFiles: ["changelog/02_policy.md"],
},
],
);

assert.match(section, /^\n## Files touched & source provenance\n/m);
assert.match(section, /\| Docs page \| Source file\(s\) in base \|/);
assert.match(section, /`docs\/base-chain\/specs\/reference\/b20\/index\.mdx`/);
assert.match(section, /https:\/\/github\.com\/base\/base-std\/blob\/abcdef0123456789\/changelog\/02_policy\.md/);
assert.doesNotMatch(section, /^## Files touched$/m);

const releaseSection = renderProvenanceSection(
"release",
{ source_repo: "base/base-std", tag: "v1.2.3" },
[{ page: "docs/base-chain/specs/reference/b20/index.mdx" }],
);
assert.match(releaseSection, /\| Docs page \| Source provenance \|/);
assert.match(releaseSection, /releases\/tag\/v1\.2\.3/);

const manualSection = renderProvenanceSection(
"manual-update",
{ source_refs: ["https://example.test/source"] },
[{ page: "docs/base-chain/specs/reference/b20/index.mdx" }],
);
assert.match(manualSection, /https:\/\/example\.test\/source/);
});

test("buildProvenanceComment cannot inject a second HTML comment boundary", () => {
const comment = buildProvenanceComment("manual-update", {
intent: "Update docs --> <script>alert(1)</script> --!>",
Expand Down
Loading
Loading