mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-10-27 23:27:09 +09:00
## Summary - make the MCP page generator render the CLI package version directly in the page title - publish `Webstudio MCP v<release>` from the exact generated page in each successful release - merge each successful release tag into the publication-only `docs` branch - serialize publication and ignore reruns or older release tags already contained in `docs` ## Root cause The first publication design advanced `docs` to the release commit without putting the CLI package version into the generated page. The repository uses `0.0.0-webstudio-version` until release time, so the release workflow now replaces that existing version placeholder with the successful tag version before publishing. ## Todo - [x] Generate the CLI version in the MCP page title. - [x] Publish the generated page from the exact release revision. - [x] Make publication idempotent and prevent version downgrades. - [x] Validate generation, formatting, linting, and release-branch behavior. - [ ] Merge this pull request. - [ ] Publish `0.285.0` as the initial version on the existing `docs` branch. ## Verification - `pnpm docs:mcp:test` - `pnpm docs:mcp:typecheck` - `pnpm check:generated-docs` - `pnpm lint` - `pnpm oxfmt --check` - `git diff --check` - isolated Git simulation for release merge, exact generated-page selection, version replacement, and rerun detection - regression tests demonstrated failing without version rendering and validation, then passing after restoration Ref #5986
71 lines
2.1 KiB
TypeScript
71 lines
2.1 KiB
TypeScript
import { execFileSync } from "node:child_process";
|
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
import { resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const manualTitle = "# Webstudio MCP Manual";
|
|
const manualPlaceholder = "{{manual}}";
|
|
const versionPlaceholder = "{{version}}";
|
|
const repositoryRoot = fileURLToPath(new URL("../..", import.meta.url));
|
|
const outputPath = resolve(repositoryRoot, "docs/university/mcp.md");
|
|
const template = readFileSync(new URL("mcp-page.md", import.meta.url), "utf8");
|
|
const { version: cliVersion } = JSON.parse(
|
|
readFileSync(resolve(repositoryRoot, "packages/cli/package.json"), "utf8")
|
|
) as { version: string };
|
|
|
|
const getMcpManual = () =>
|
|
execFileSync(
|
|
process.execPath,
|
|
[
|
|
resolve(repositoryRoot, "packages/cli/local.js"),
|
|
"man",
|
|
"mcp",
|
|
"--verbose",
|
|
],
|
|
{
|
|
encoding: "utf8",
|
|
cwd: repositoryRoot,
|
|
env: {
|
|
...process.env,
|
|
CI: "1",
|
|
FORCE_COLOR: "0",
|
|
NO_COLOR: "1",
|
|
},
|
|
maxBuffer: 10 * 1024 * 1024,
|
|
}
|
|
).trim();
|
|
|
|
export const renderMcpDocumentation = (manual: string, version: string) => {
|
|
const normalizedManual = manual.replaceAll("\r\n", "\n").trim();
|
|
if (normalizedManual.startsWith(`${manualTitle}\n`) === false) {
|
|
throw new Error(
|
|
`Expected the CLI manual to begin with ${JSON.stringify(manualTitle)}.`
|
|
);
|
|
}
|
|
const manualBody = normalizedManual.slice(manualTitle.length).trimStart();
|
|
const placeholderCount = template.split(manualPlaceholder).length - 1;
|
|
if (placeholderCount !== 1) {
|
|
throw new Error(
|
|
`Expected one ${manualPlaceholder} placeholder, found ${placeholderCount}.`
|
|
);
|
|
}
|
|
if (version.trim() === "") {
|
|
throw new Error("Expected a CLI version.");
|
|
}
|
|
return template
|
|
.replace(versionPlaceholder, version)
|
|
.replace(manualPlaceholder, manualBody);
|
|
};
|
|
|
|
export const generateMcpDocumentation = () => {
|
|
writeFileSync(
|
|
outputPath,
|
|
renderMcpDocumentation(getMcpManual(), cliVersion),
|
|
"utf8"
|
|
);
|
|
};
|
|
|
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
generateMcpDocumentation();
|
|
}
|