mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
fix(docs): make Mintlify anchor audit reliable (#111265)
This commit is contained in:
+33
-19
@@ -7,12 +7,21 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { resolveClawHubRepoPath, syncClawHubDocsTree } from "./docs-sync-publish.mjs";
|
||||
import { createPnpmRunnerSpawnSpec } from "./pnpm-runner.mjs";
|
||||
import { resolveNpmRunner } from "./npm-runner.mjs";
|
||||
|
||||
const ROOT = process.cwd();
|
||||
const DOCS_DIR = path.join(ROOT, "docs");
|
||||
const DOCS_JSON_PATH = path.join(DOCS_DIR, "docs.json");
|
||||
const MINTLIFY_BROKEN_LINKS_ARGS = ["dlx", "mint", "broken-links", "--check-anchors"];
|
||||
const MINTLIFY_CLI_VERSION = "4.2.715";
|
||||
const MINTLIFY_BROKEN_LINKS_ARGS = [
|
||||
"exec",
|
||||
"--yes",
|
||||
`--package=mint@${MINTLIFY_CLI_VERSION}`,
|
||||
"--",
|
||||
"mint",
|
||||
"broken-links",
|
||||
"--check-anchors",
|
||||
];
|
||||
const NODE_25_UNSUPPORTED_BY_MINTLIFY = 25;
|
||||
|
||||
if (!fs.existsSync(DOCS_DIR) || !fs.statSync(DOCS_DIR).isDirectory()) {
|
||||
@@ -353,17 +362,26 @@ function parseNodeMajor(version) {
|
||||
return Number.isFinite(major) ? major : 0;
|
||||
}
|
||||
|
||||
function createMintlifyPnpmRunnerSpawnSpec(params, options = {}) {
|
||||
return createPnpmRunnerSpawnSpec({
|
||||
function createMintlifyNpmRunnerSpawnSpec(params, options = {}) {
|
||||
const env = params.env ?? process.env;
|
||||
const runner = resolveNpmRunner({
|
||||
comSpec: params.comSpec,
|
||||
cwd: params.cwd,
|
||||
env: params.env ?? process.env,
|
||||
nodeExecPath: options.nodeExecPath ?? params.nodeExecPath,
|
||||
npmExecPath: params.npmExecPath,
|
||||
env,
|
||||
execPath: options.nodeExecPath ?? params.nodeExecPath,
|
||||
npmArgs: MINTLIFY_BROKEN_LINKS_ARGS,
|
||||
platform: params.platform,
|
||||
pnpmArgs: MINTLIFY_BROKEN_LINKS_ARGS,
|
||||
stdio: "inherit",
|
||||
});
|
||||
return {
|
||||
command: runner.command,
|
||||
args: runner.args,
|
||||
options: {
|
||||
cwd: params.cwd,
|
||||
env: runner.env ?? env,
|
||||
shell: runner.shell,
|
||||
stdio: "inherit",
|
||||
windowsVerbatimArguments: runner.windowsVerbatimArguments,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -377,7 +395,6 @@ function createMintlifyPnpmRunnerSpawnSpec(params, options = {}) {
|
||||
* spawnSyncImpl: typeof spawnSync;
|
||||
* env?: NodeJS.ProcessEnv;
|
||||
* nodeExecPath?: string;
|
||||
* npmExecPath?: string;
|
||||
* platform?: NodeJS.Platform;
|
||||
* comSpec?: string;
|
||||
* }} params
|
||||
@@ -385,11 +402,11 @@ function createMintlifyPnpmRunnerSpawnSpec(params, options = {}) {
|
||||
export function resolveMintlifyAnchorAuditInvocation(params) {
|
||||
const nodeVersion = params.nodeVersion ?? process.versions.node;
|
||||
if (parseNodeMajor(nodeVersion) < NODE_25_UNSUPPORTED_BY_MINTLIFY) {
|
||||
return createMintlifyPnpmRunnerSpawnSpec(params);
|
||||
return createMintlifyNpmRunnerSpawnSpec(params);
|
||||
}
|
||||
|
||||
const node22Probe = "process.exit(Number(process.versions.node.split('.')[0]) === 22 ? 0 : 1)";
|
||||
const node22Runner = createMintlifyPnpmRunnerSpawnSpec(params, { nodeExecPath: "node" });
|
||||
const node22Runner = createMintlifyNpmRunnerSpawnSpec(params, { nodeExecPath: "node" });
|
||||
const candidates = [
|
||||
{
|
||||
command: "fnm",
|
||||
@@ -413,7 +430,7 @@ export function resolveMintlifyAnchorAuditInvocation(params) {
|
||||
}
|
||||
}
|
||||
|
||||
return createMintlifyPnpmRunnerSpawnSpec(params);
|
||||
return createMintlifyNpmRunnerSpawnSpec(params);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -558,7 +575,6 @@ export function auditDocsLinks(options = {}) {
|
||||
* env?: NodeJS.ProcessEnv;
|
||||
* nodeExecPath?: string;
|
||||
* nodeVersion?: string;
|
||||
* npmExecPath?: string;
|
||||
* platform?: NodeJS.Platform;
|
||||
* spawnSyncImpl?: typeof spawnSync;
|
||||
* prepareAnchorAuditDocsDirImpl?: (sourceDir?: string) => string;
|
||||
@@ -585,16 +601,14 @@ export function runDocsLinkAuditCli(options = {}) {
|
||||
|
||||
try {
|
||||
anchorDocsDir = prepareAnchorAuditDocsDirImpl(mirroredDocsDir.dir);
|
||||
// Use the npm Mintlify package explicitly. Some developer machines also
|
||||
// have the Swift Package Manager tool named `mint` on PATH, and that
|
||||
// binary exits with "command 'broken-links' not found".
|
||||
// Mintlify's package graph expects npm's hoisted layout; pnpm dlx can fail
|
||||
// to resolve declared transitive packages from its strict store layout.
|
||||
const invocation = resolveMintlifyAnchorAuditInvocation({
|
||||
comSpec: options.comSpec,
|
||||
cwd: anchorDocsDir,
|
||||
env: options.env,
|
||||
nodeExecPath: options.nodeExecPath,
|
||||
nodeVersion: options.nodeVersion,
|
||||
npmExecPath: options.npmExecPath,
|
||||
platform: options.platform,
|
||||
spawnSyncImpl,
|
||||
});
|
||||
|
||||
@@ -215,7 +215,7 @@ describe("docs-link-audit", () => {
|
||||
expect(mirroredCleaned).toBe(true);
|
||||
});
|
||||
|
||||
it("uses Mintlify through pnpm dlx for anchor validation", () => {
|
||||
it("uses a pinned Mintlify package through npm for anchor validation", () => {
|
||||
let invocation:
|
||||
| {
|
||||
command: string;
|
||||
@@ -225,16 +225,13 @@ describe("docs-link-audit", () => {
|
||||
| undefined;
|
||||
let cleanedDir: string | undefined;
|
||||
const anchorDocsDir = path.join(os.tmpdir(), "docs-link-audit-anchor");
|
||||
const fakePnpm = path.join(anchorDocsDir, "pnpm.cjs");
|
||||
fs.mkdirSync(anchorDocsDir, { recursive: true });
|
||||
fs.writeFileSync(fakePnpm, "#!/usr/bin/env node\n", { mode: 0o755 });
|
||||
|
||||
const exitCode = runDocsLinkAuditCli({
|
||||
args: ["--anchors"],
|
||||
env: { ...process.env, OPENCLAW_DOCS_LINK_SENTINEL: "1" },
|
||||
nodeExecPath: "/opt/node/bin/node",
|
||||
nodeVersion: "22.21.1",
|
||||
npmExecPath: fakePnpm,
|
||||
prepareAnchorAuditDocsDirImpl() {
|
||||
return anchorDocsDir;
|
||||
},
|
||||
@@ -249,8 +246,16 @@ describe("docs-link-audit", () => {
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
expect(invocation).toEqual({
|
||||
command: "/opt/node/bin/node",
|
||||
args: [fakePnpm, "dlx", "mint", "broken-links", "--check-anchors"],
|
||||
command: "npm",
|
||||
args: [
|
||||
"exec",
|
||||
"--yes",
|
||||
"--package=mint@4.2.715",
|
||||
"--",
|
||||
"mint",
|
||||
"broken-links",
|
||||
"--check-anchors",
|
||||
],
|
||||
options: expect.objectContaining({
|
||||
cwd: anchorDocsDir,
|
||||
env: expect.objectContaining({ OPENCLAW_DOCS_LINK_SENTINEL: "1" }),
|
||||
@@ -269,15 +274,12 @@ describe("docs-link-audit", () => {
|
||||
}> = [];
|
||||
let cleanedDir: string | undefined;
|
||||
const anchorDocsDir = path.join(os.tmpdir(), "docs-link-audit-anchor");
|
||||
const fakePnpm = path.join(anchorDocsDir, "pnpm.cjs");
|
||||
fs.mkdirSync(anchorDocsDir, { recursive: true });
|
||||
fs.writeFileSync(fakePnpm, "#!/usr/bin/env node\n", { mode: 0o755 });
|
||||
|
||||
const exitCode = runDocsLinkAuditCli({
|
||||
args: ["--anchors"],
|
||||
nodeExecPath: "/opt/node/bin/node",
|
||||
nodeVersion: "25.3.0",
|
||||
npmExecPath: fakePnpm,
|
||||
prepareAnchorAuditDocsDirImpl() {
|
||||
return anchorDocsDir;
|
||||
},
|
||||
@@ -312,9 +314,11 @@ describe("docs-link-audit", () => {
|
||||
args: [
|
||||
"exec",
|
||||
"--using=22",
|
||||
"node",
|
||||
fakePnpm,
|
||||
"dlx",
|
||||
"npm",
|
||||
"exec",
|
||||
"--yes",
|
||||
"--package=mint@4.2.715",
|
||||
"--",
|
||||
"mint",
|
||||
"broken-links",
|
||||
"--check-anchors",
|
||||
|
||||
Reference in New Issue
Block a user