mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
docs: document root build check scripts
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
// Builds browser runtime bundles for the diffs viewer assets.
|
||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
@@ -26,6 +27,9 @@ function toPosixPath(value) {
|
|||||||
return String(value ?? "").replaceAll("\\", "/");
|
return String(value ?? "").replaceAll("\\", "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the esbuild plugin that neutralizes Pierre diffs' browser side-effect import.
|
||||||
|
*/
|
||||||
export function createPierreDiffsSideEffectImportPlugin() {
|
export function createPierreDiffsSideEffectImportPlugin() {
|
||||||
return {
|
return {
|
||||||
name: "openclaw-diffs-pierre-side-effect-imports",
|
name: "openclaw-diffs-pierre-side-effect-imports",
|
||||||
@@ -55,6 +59,9 @@ export function createPierreDiffsSideEffectImportPlugin() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds one configured diffs viewer runtime target.
|
||||||
|
*/
|
||||||
export async function buildDiffsViewerRuntime(targetName) {
|
export async function buildDiffsViewerRuntime(targetName) {
|
||||||
const target = targets[targetName];
|
const target = targets[targetName];
|
||||||
if (!target) {
|
if (!target) {
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
// Writes the local build stamp and re-exports build metadata helpers.
|
||||||
import process from "node:process";
|
import process from "node:process";
|
||||||
import { pathToFileURL } from "node:url";
|
import { pathToFileURL } from "node:url";
|
||||||
import { writeBuildStamp } from "./lib/local-build-metadata.mjs";
|
import { writeBuildStamp } from "./lib/local-build-metadata.mjs";
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
// Runs bundled asset build hooks for the Canvas A2UI runtime.
|
||||||
import { pathToFileURL } from "node:url";
|
import { pathToFileURL } from "node:url";
|
||||||
import { runBundledPluginAssetHooks } from "./bundled-plugin-assets.mjs";
|
import { runBundledPluginAssetHooks } from "./bundled-plugin-assets.mjs";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
// Discovers and runs bundled plugin package asset hooks.
|
||||||
import { spawnSync } from "node:child_process";
|
import { spawnSync } from "node:child_process";
|
||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
@@ -57,6 +58,9 @@ function resolveAssetCommand(packageJson, phase) {
|
|||||||
return typeof command === "string" && command.trim() ? command.trim() : null;
|
return typeof command === "string" && command.trim() ? command.trim() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads bundled plugin asset hook commands for a build or copy phase.
|
||||||
|
*/
|
||||||
export async function readBundledPluginAssetHooks(options = {}) {
|
export async function readBundledPluginAssetHooks(options = {}) {
|
||||||
const repoRoot = options.rootDir ?? rootDir;
|
const repoRoot = options.rootDir ?? rootDir;
|
||||||
const phase = options.phase;
|
const phase = options.phase;
|
||||||
@@ -108,6 +112,9 @@ export async function readBundledPluginAssetHooks(options = {}) {
|
|||||||
return hooks.toSorted((left, right) => left.pluginDir.localeCompare(right.pluginDir));
|
return hooks.toSorted((left, right) => left.pluginDir.localeCompare(right.pluginDir));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs bundled plugin asset hook commands for the selected phase/plugins.
|
||||||
|
*/
|
||||||
export async function runBundledPluginAssetHooks(options = {}) {
|
export async function runBundledPluginAssetHooks(options = {}) {
|
||||||
const phase = options.phase;
|
const phase = options.phase;
|
||||||
const hooks = await readBundledPluginAssetHooks(options);
|
const hooks = await readBundledPluginAssetHooks(options);
|
||||||
@@ -131,6 +138,9 @@ export async function runBundledPluginAssetHooks(options = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses `--phase` and repeated `--plugin` flags for asset hook scripts.
|
||||||
|
*/
|
||||||
export function parseBundledPluginAssetArgs(argv) {
|
export function parseBundledPluginAssetArgs(argv) {
|
||||||
const args = [...argv];
|
const args = [...argv];
|
||||||
const plugins = [];
|
const plugins = [];
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// Classifies changed files into CI lanes and release metadata scopes.
|
||||||
import { execFileSync } from "node:child_process";
|
import { execFileSync } from "node:child_process";
|
||||||
import { appendFileSync, existsSync, readFileSync } from "node:fs";
|
import { appendFileSync, existsSync, readFileSync } from "node:fs";
|
||||||
import { booleanFlag, parseFlagArgs, stringFlag } from "./lib/arg-utils.mjs";
|
import { booleanFlag, parseFlagArgs, stringFlag } from "./lib/arg-utils.mjs";
|
||||||
@@ -24,6 +25,9 @@ const TEST_PATH_RE =
|
|||||||
/(?:^|\/)(?:test|__tests__)\/|(?:\.|\/)(?:test|spec|e2e|browser\.test)\.[cm]?[jt]sx?$/u;
|
/(?:^|\/)(?:test|__tests__)\/|(?:\.|\/)(?:test|spec|e2e|browser\.test)\.[cm]?[jt]sx?$/u;
|
||||||
const PUBLIC_EXTENSION_CONTRACT_RE =
|
const PUBLIC_EXTENSION_CONTRACT_RE =
|
||||||
/^(?:src\/plugin-sdk\/|src\/plugins\/contracts\/|src\/channels\/plugins\/|scripts\/lib\/plugin-sdk-entrypoints\.json$|scripts\/sync-plugin-sdk-exports\.mjs$|scripts\/generate-plugin-sdk-api-baseline\.ts$)/u;
|
/^(?:src\/plugin-sdk\/|src\/plugins\/contracts\/|src\/channels\/plugins\/|scripts\/lib\/plugin-sdk-entrypoints\.json$|scripts\/sync-plugin-sdk-exports\.mjs$|scripts\/generate-plugin-sdk-api-baseline\.ts$)/u;
|
||||||
|
/**
|
||||||
|
* Files whose changes are treated as release metadata only.
|
||||||
|
*/
|
||||||
export const RELEASE_METADATA_PATHS = new Set([
|
export const RELEASE_METADATA_PATHS = new Set([
|
||||||
"CHANGELOG.md",
|
"CHANGELOG.md",
|
||||||
"apps/android/app/build.gradle.kts",
|
"apps/android/app/build.gradle.kts",
|
||||||
@@ -49,6 +53,9 @@ export const RELEASE_METADATA_PATHS = new Set([
|
|||||||
* }} ChangedLaneResult
|
* }} ChangedLaneResult
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes a changed file path into repo-relative POSIX form.
|
||||||
|
*/
|
||||||
export function normalizeChangedPath(inputPath) {
|
export function normalizeChangedPath(inputPath) {
|
||||||
return String(inputPath ?? "")
|
return String(inputPath ?? "")
|
||||||
.trim()
|
.trim()
|
||||||
@@ -56,6 +63,9 @@ export function normalizeChangedPath(inputPath) {
|
|||||||
.replace(/^\.\/+/u, "");
|
.replace(/^\.\/+/u, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the default changed-lanes result object.
|
||||||
|
*/
|
||||||
export function createEmptyChangedLanes() {
|
export function createEmptyChangedLanes() {
|
||||||
return {
|
return {
|
||||||
core: false,
|
core: false,
|
||||||
@@ -76,6 +86,9 @@ export function createEmptyChangedLanes() {
|
|||||||
* @param {{ packageJsonChangeKind?: "liveDockerTooling" | "tooling" | null }} [options]
|
* @param {{ packageJsonChangeKind?: "liveDockerTooling" | "tooling" | null }} [options]
|
||||||
* @returns {ChangedLaneResult}
|
* @returns {ChangedLaneResult}
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* Classifies a list of changed paths into docs, app, extension, core, and tooling lanes.
|
||||||
|
*/
|
||||||
export function detectChangedLanes(changedPaths, options = {}) {
|
export function detectChangedLanes(changedPaths, options = {}) {
|
||||||
const paths = [...new Set(changedPaths.map(normalizeChangedPath).filter(Boolean))]
|
const paths = [...new Set(changedPaths.map(normalizeChangedPath).filter(Boolean))]
|
||||||
.toSorted((left, right) => left.localeCompare(right))
|
.toSorted((left, right) => left.localeCompare(right))
|
||||||
@@ -217,6 +230,9 @@ export function detectChangedLanes(changedPaths, options = {}) {
|
|||||||
* @param {{ paths: string[]; base: string; head?: string; staged?: boolean; mergeHeadFirstParent?: boolean }} params
|
* @param {{ paths: string[]; base: string; head?: string; staged?: boolean; mergeHeadFirstParent?: boolean }} params
|
||||||
* @returns {ChangedLaneResult}
|
* @returns {ChangedLaneResult}
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* Classifies changed paths with optional package.json before/after contents.
|
||||||
|
*/
|
||||||
export function detectChangedLanesForPaths(params) {
|
export function detectChangedLanesForPaths(params) {
|
||||||
const base = params.staged
|
const base = params.staged
|
||||||
? params.base
|
? params.base
|
||||||
@@ -240,6 +256,9 @@ export function detectChangedLanesForPaths(params) {
|
|||||||
* @param {{ base: string; head?: string; includeWorktree?: boolean; cwd?: string; mergeHeadFirstParent?: boolean }} params
|
* @param {{ base: string; head?: string; includeWorktree?: boolean; cwd?: string; mergeHeadFirstParent?: boolean }} params
|
||||||
* @returns {string[]}
|
* @returns {string[]}
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* Lists changed paths from git for a base/head comparison.
|
||||||
|
*/
|
||||||
export function listChangedPathsFromGit(params) {
|
export function listChangedPathsFromGit(params) {
|
||||||
const head = params.head ?? "HEAD";
|
const head = params.head ?? "HEAD";
|
||||||
const cwd = params.cwd ?? process.cwd();
|
const cwd = params.cwd ?? process.cwd();
|
||||||
@@ -318,6 +337,9 @@ function runGitLsFiles(extraArgs, cwd = process.cwd()) {
|
|||||||
return output.split("\n").map(normalizeChangedPath).filter(Boolean);
|
return output.split("\n").map(normalizeChangedPath).filter(Boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists staged changed paths for pre-commit checks.
|
||||||
|
*/
|
||||||
export function listStagedChangedPaths(cwd = process.cwd()) {
|
export function listStagedChangedPaths(cwd = process.cwd()) {
|
||||||
const output = execFileSync("git", ["diff", "--cached", "--name-only", "--diff-filter=ACMRD"], {
|
const output = execFileSync("git", ["diff", "--cached", "--name-only", "--diff-filter=ACMRD"], {
|
||||||
cwd,
|
cwd,
|
||||||
@@ -328,6 +350,9 @@ export function listStagedChangedPaths(cwd = process.cwd()) {
|
|||||||
return output.split("\n").map(normalizeChangedPath).filter(Boolean);
|
return output.split("\n").map(normalizeChangedPath).filter(Boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Classifies package.json script-only changes from git content.
|
||||||
|
*/
|
||||||
export function classifyPackageJsonChangeFromGit(params) {
|
export function classifyPackageJsonChangeFromGit(params) {
|
||||||
try {
|
try {
|
||||||
const { before, after } = readPackageJsonBeforeAfter(params);
|
const { before, after } = readPackageJsonBeforeAfter(params);
|
||||||
@@ -340,6 +365,9 @@ export function classifyPackageJsonChangeFromGit(params) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether package scripts changed only live Docker script entries.
|
||||||
|
*/
|
||||||
export function isLiveDockerPackageScriptOnlyChange(before, after) {
|
export function isLiveDockerPackageScriptOnlyChange(before, after) {
|
||||||
const beforePackage = JSON.parse(before);
|
const beforePackage = JSON.parse(before);
|
||||||
const afterPackage = JSON.parse(after);
|
const afterPackage = JSON.parse(after);
|
||||||
@@ -354,6 +382,9 @@ export function isLiveDockerPackageScriptOnlyChange(before, after) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether package.json changes are limited to scripts.
|
||||||
|
*/
|
||||||
export function isPackageScriptOnlyChange(before, after) {
|
export function isPackageScriptOnlyChange(before, after) {
|
||||||
const beforePackage = JSON.parse(before);
|
const beforePackage = JSON.parse(before);
|
||||||
const afterPackage = JSON.parse(after);
|
const afterPackage = JSON.parse(after);
|
||||||
@@ -444,6 +475,9 @@ function stableJson(value) {
|
|||||||
return JSON.stringify(value);
|
return JSON.stringify(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes changed-lane booleans to the GitHub Actions output file.
|
||||||
|
*/
|
||||||
export function writeChangedLaneGitHubOutput(result, outputPath = process.env.GITHUB_OUTPUT) {
|
export function writeChangedLaneGitHubOutput(result, outputPath = process.env.GITHUB_OUTPUT) {
|
||||||
if (!outputPath) {
|
if (!outputPath) {
|
||||||
throw new Error("GITHUB_OUTPUT is required");
|
throw new Error("GITHUB_OUTPUT is required");
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
// Finds core/plugin architecture boundary smells in TypeScript sources.
|
||||||
import { promises as fs } from "node:fs";
|
import { promises as fs } from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
@@ -10,12 +11,12 @@ import {
|
|||||||
resolveRepoSpecifier,
|
resolveRepoSpecifier,
|
||||||
writeLine,
|
writeLine,
|
||||||
} from "./lib/guard-inventory-utils.mjs";
|
} from "./lib/guard-inventory-utils.mjs";
|
||||||
|
import { mapWithConcurrency } from "./lib/source-file-scan-cache.mjs";
|
||||||
import {
|
import {
|
||||||
collectTypeScriptFilesFromRoots,
|
collectTypeScriptFilesFromRoots,
|
||||||
resolveSourceRoots,
|
resolveSourceRoots,
|
||||||
runAsScript,
|
runAsScript,
|
||||||
} from "./lib/ts-guard-utils.mjs";
|
} from "./lib/ts-guard-utils.mjs";
|
||||||
import { mapWithConcurrency } from "./lib/source-file-scan-cache.mjs";
|
|
||||||
|
|
||||||
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||||
const scanRoots = resolveSourceRoots(repoRoot, ["src/plugin-sdk", "src/plugins/runtime"]);
|
const scanRoots = resolveSourceRoots(repoRoot, ["src/plugin-sdk", "src/plugins/runtime"]);
|
||||||
@@ -165,23 +166,22 @@ function scanRuntimeServiceLocatorSmells(source, filePath) {
|
|||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects architecture smell findings from the configured source roots.
|
||||||
|
*/
|
||||||
export async function collectArchitectureSmells() {
|
export async function collectArchitectureSmells() {
|
||||||
if (!architectureSmellsPromise) {
|
if (!architectureSmellsPromise) {
|
||||||
architectureSmellsPromise = (async () => {
|
architectureSmellsPromise = (async () => {
|
||||||
const files = (await collectTypeScriptFilesFromRoots(scanRoots)).toSorted((left, right) =>
|
const files = (await collectTypeScriptFilesFromRoots(scanRoots)).toSorted((left, right) =>
|
||||||
normalizeRepoPath(repoRoot, left).localeCompare(normalizeRepoPath(repoRoot, right)),
|
normalizeRepoPath(repoRoot, left).localeCompare(normalizeRepoPath(repoRoot, right)),
|
||||||
);
|
);
|
||||||
const entriesByFile = await mapWithConcurrency(
|
const entriesByFile = await mapWithConcurrency(files, undefined, async (filePath) => {
|
||||||
files,
|
const source = await fs.readFile(filePath, "utf8");
|
||||||
undefined,
|
const entries = scanPluginSdkExtensionFacadeSmells(source, filePath);
|
||||||
async (filePath) => {
|
entries.push(...scanRuntimeTypeImplementationSmells(source, filePath));
|
||||||
const source = await fs.readFile(filePath, "utf8");
|
entries.push(...scanRuntimeServiceLocatorSmells(source, filePath));
|
||||||
const entries = scanPluginSdkExtensionFacadeSmells(source, filePath);
|
return entries;
|
||||||
entries.push(...scanRuntimeTypeImplementationSmells(source, filePath));
|
});
|
||||||
entries.push(...scanRuntimeServiceLocatorSmells(source, filePath));
|
|
||||||
return entries;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return entriesByFile.flat().toSorted(compareEntries);
|
return entriesByFile.flat().toSorted(compareEntries);
|
||||||
})();
|
})();
|
||||||
try {
|
try {
|
||||||
@@ -235,6 +235,9 @@ async function runArchitectureSmellsCheck(argv, io) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the architecture smell check and writes human/JSON output.
|
||||||
|
*/
|
||||||
export async function main(argv, io) {
|
export async function main(argv, io) {
|
||||||
return await runArchitectureSmellsCheck(argv, io);
|
return await runArchitectureSmellsCheck(argv, io);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
// Rejects changelog thanks entries that credit bots or internal handles.
|
||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exact handles that changelog thanks entries must not credit.
|
||||||
|
*/
|
||||||
export const FORBIDDEN_CHANGELOG_THANKS_HANDLES = [
|
export const FORBIDDEN_CHANGELOG_THANKS_HANDLES = [
|
||||||
"codex",
|
"codex",
|
||||||
"openclaw",
|
"openclaw",
|
||||||
@@ -13,20 +17,38 @@ export const FORBIDDEN_CHANGELOG_THANKS_HANDLES = [
|
|||||||
"clawsweeper[bot]",
|
"clawsweeper[bot]",
|
||||||
"openclaw-clawsweeper[bot]",
|
"openclaw-clawsweeper[bot]",
|
||||||
];
|
];
|
||||||
|
/**
|
||||||
|
* Handle prefixes that identify forbidden changelog thanks credits.
|
||||||
|
*/
|
||||||
export const FORBIDDEN_CHANGELOG_THANKS_HANDLE_PREFIXES = ["app/"];
|
export const FORBIDDEN_CHANGELOG_THANKS_HANDLE_PREFIXES = ["app/"];
|
||||||
|
/**
|
||||||
|
* Handle suffixes that identify forbidden changelog thanks credits.
|
||||||
|
*/
|
||||||
export const FORBIDDEN_CHANGELOG_THANKS_HANDLE_SUFFIXES = ["[bot]"];
|
export const FORBIDDEN_CHANGELOG_THANKS_HANDLE_SUFFIXES = ["[bot]"];
|
||||||
|
/**
|
||||||
|
* Handles that require an explicit human credit instead.
|
||||||
|
*/
|
||||||
export const CHANGELOG_THANKS_REQUIRE_HUMAN_CREDIT_HANDLES = [
|
export const CHANGELOG_THANKS_REQUIRE_HUMAN_CREDIT_HANDLES = [
|
||||||
"clawsweeper",
|
"clawsweeper",
|
||||||
"openclaw-clawsweeper",
|
"openclaw-clawsweeper",
|
||||||
"clawsweeper[bot]",
|
"clawsweeper[bot]",
|
||||||
"openclaw-clawsweeper[bot]",
|
"openclaw-clawsweeper[bot]",
|
||||||
];
|
];
|
||||||
|
/**
|
||||||
|
* Handle prefixes that require explicit human credit instead.
|
||||||
|
*/
|
||||||
export const CHANGELOG_THANKS_REQUIRE_HUMAN_CREDIT_HANDLE_PREFIXES = ["app/"];
|
export const CHANGELOG_THANKS_REQUIRE_HUMAN_CREDIT_HANDLE_PREFIXES = ["app/"];
|
||||||
|
/**
|
||||||
|
* Handle suffixes that require explicit human credit instead.
|
||||||
|
*/
|
||||||
export const CHANGELOG_THANKS_REQUIRE_HUMAN_CREDIT_HANDLE_SUFFIXES = ["[bot]"];
|
export const CHANGELOG_THANKS_REQUIRE_HUMAN_CREDIT_HANDLE_SUFFIXES = ["[bot]"];
|
||||||
|
|
||||||
const THANKS_PATTERN = /\bThanks\b/iu;
|
const THANKS_PATTERN = /\bThanks\b/iu;
|
||||||
const THANKED_HANDLE_PATTERN = /@([-_/A-Za-z0-9]+(?:\[bot\])?)/giu;
|
const THANKED_HANDLE_PATTERN = /@([-_/A-Za-z0-9]+(?:\[bot\])?)/giu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reports whether a handle is forbidden in changelog thanks text.
|
||||||
|
*/
|
||||||
export function isForbiddenChangelogThanksHandle(handle, options = {}) {
|
export function isForbiddenChangelogThanksHandle(handle, options = {}) {
|
||||||
const { strictBotHandle = false } = options;
|
const { strictBotHandle = false } = options;
|
||||||
const normalized = handle.toLowerCase();
|
const normalized = handle.toLowerCase();
|
||||||
@@ -48,6 +70,9 @@ export function isForbiddenChangelogThanksHandle(handle, options = {}) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reports whether a handle needs a separate human credit.
|
||||||
|
*/
|
||||||
export function requiresExplicitHumanChangelogThanks(handle) {
|
export function requiresExplicitHumanChangelogThanks(handle) {
|
||||||
const normalized = handle.toLowerCase();
|
const normalized = handle.toLowerCase();
|
||||||
if (normalized === "" || normalized === "null") {
|
if (normalized === "" || normalized === "null") {
|
||||||
@@ -64,6 +89,9 @@ export function requiresExplicitHumanChangelogThanks(handle) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds changelog lines that thank forbidden handles.
|
||||||
|
*/
|
||||||
export function findForbiddenChangelogThanks(content) {
|
export function findForbiddenChangelogThanks(content) {
|
||||||
return content
|
return content
|
||||||
.split(/\r?\n/u)
|
.split(/\r?\n/u)
|
||||||
@@ -82,6 +110,9 @@ export function findForbiddenChangelogThanks(content) {
|
|||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the changelog attribution check.
|
||||||
|
*/
|
||||||
export async function main(argv = process.argv.slice(2)) {
|
export async function main(argv = process.argv.slice(2)) {
|
||||||
if (argv[0] === "--is-forbidden-handle") {
|
if (argv[0] === "--is-forbidden-handle") {
|
||||||
process.exitCode = isForbiddenChangelogThanksHandle(argv[1] ?? "", {
|
process.exitCode = isForbiddenChangelogThanksHandle(argv[1] ?? "", {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
// Checks channel-agnostic core surfaces for channel-specific coupling.
|
||||||
import { promises as fs } from "node:fs";
|
import { promises as fs } from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import ts from "typescript";
|
import ts from "typescript";
|
||||||
@@ -117,6 +118,9 @@ function isModuleSpecifierStringNode(node) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds channel-specific references inside channel-agnostic protected sources.
|
||||||
|
*/
|
||||||
export function findChannelAgnosticBoundaryViolations(
|
export function findChannelAgnosticBoundaryViolations(
|
||||||
content,
|
content,
|
||||||
fileName = "source.ts",
|
fileName = "source.ts",
|
||||||
@@ -236,6 +240,9 @@ export function findChannelAgnosticBoundaryViolations(
|
|||||||
return violations;
|
return violations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds reverse dependencies from channel core into plugin/runtime surfaces.
|
||||||
|
*/
|
||||||
export function findChannelCoreReverseDependencyViolations(content, fileName = "source.ts") {
|
export function findChannelCoreReverseDependencyViolations(content, fileName = "source.ts") {
|
||||||
return findChannelAgnosticBoundaryViolations(content, fileName, {
|
return findChannelAgnosticBoundaryViolations(content, fileName, {
|
||||||
checkModuleSpecifiers: true,
|
checkModuleSpecifiers: true,
|
||||||
@@ -246,6 +253,9 @@ export function findChannelCoreReverseDependencyViolations(content, fileName = "
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds user-facing channel names in ACP-owned text sources.
|
||||||
|
*/
|
||||||
export function findAcpUserFacingChannelNameViolations(content, fileName = "source.ts") {
|
export function findAcpUserFacingChannelNameViolations(content, fileName = "source.ts") {
|
||||||
const sourceFile = ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true);
|
const sourceFile = ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true);
|
||||||
const violations = [];
|
const violations = [];
|
||||||
@@ -265,6 +275,9 @@ export function findAcpUserFacingChannelNameViolations(content, fileName = "sour
|
|||||||
return violations;
|
return violations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds raw system mark literals where shared constants should be used.
|
||||||
|
*/
|
||||||
export function findSystemMarkLiteralViolations(content, fileName = "source.ts") {
|
export function findSystemMarkLiteralViolations(content, fileName = "source.ts") {
|
||||||
const sourceFile = ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true);
|
const sourceFile = ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true);
|
||||||
const violations = [];
|
const violations = [];
|
||||||
@@ -307,6 +320,9 @@ const boundaryRuleSets = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs all channel-agnostic boundary checks.
|
||||||
|
*/
|
||||||
export async function main() {
|
export async function main() {
|
||||||
const violations = [];
|
const violations = [];
|
||||||
for (const ruleSet of boundaryRuleSets) {
|
for (const ruleSet of boundaryRuleSets) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
// Checks CLI bootstrap chunks for forbidden eager imports and size regressions.
|
||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import module from "node:module";
|
import module from "node:module";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
@@ -57,6 +58,9 @@ function resolveRelativeImport(importer, specifier, fsImpl = fs) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists static import/export specifiers from a JavaScript source string.
|
||||||
|
*/
|
||||||
export function listStaticImportSpecifiers(source) {
|
export function listStaticImportSpecifiers(source) {
|
||||||
return [...source.matchAll(STATIC_IMPORT_RE)].map((match) => match.groups?.specifier ?? "");
|
return [...source.matchAll(STATIC_IMPORT_RE)].map((match) => match.groups?.specifier ?? "");
|
||||||
}
|
}
|
||||||
@@ -110,6 +114,9 @@ function walkStaticImportGraph(params) {
|
|||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects forbidden external import errors for CLI bootstrap entrypoints.
|
||||||
|
*/
|
||||||
export function collectCliBootstrapExternalImportErrors(params = {}) {
|
export function collectCliBootstrapExternalImportErrors(params = {}) {
|
||||||
const rootDir = params.rootDir ?? process.cwd();
|
const rootDir = params.rootDir ?? process.cwd();
|
||||||
const entrypoints = params.entrypoints ?? DEFAULT_ENTRYPOINTS;
|
const entrypoints = params.entrypoints ?? DEFAULT_ENTRYPOINTS;
|
||||||
@@ -152,6 +159,9 @@ function listJsFiles(dirPath, fsImpl = fs) {
|
|||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects gateway-run chunk budget errors from built CLI output.
|
||||||
|
*/
|
||||||
export function collectGatewayRunChunkBudgetErrors(params = {}) {
|
export function collectGatewayRunChunkBudgetErrors(params = {}) {
|
||||||
const rootDir = params.rootDir ?? process.cwd();
|
const rootDir = params.rootDir ?? process.cwd();
|
||||||
const fsImpl = params.fs ?? fs;
|
const fsImpl = params.fs ?? fs;
|
||||||
@@ -227,6 +237,9 @@ export function collectGatewayRunChunkBudgetErrors(params = {}) {
|
|||||||
return errors.toSorted((left, right) => left.localeCompare(right));
|
return errors.toSorted((left, right) => left.localeCompare(right));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the CLI bootstrap import and chunk-budget checks.
|
||||||
|
*/
|
||||||
export function checkCliBootstrapExternalImports(params = {}) {
|
export function checkCliBootstrapExternalImports(params = {}) {
|
||||||
const errors = [
|
const errors = [
|
||||||
...collectCliBootstrapExternalImportErrors(params),
|
...collectCliBootstrapExternalImportErrors(params),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
// Measures CLI startup memory with an isolated home and RSS hook.
|
||||||
import { spawnSync as defaultSpawnSync } from "node:child_process";
|
import { spawnSync as defaultSpawnSync } from "node:child_process";
|
||||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||||
import os from "node:os";
|
import os from "node:os";
|
||||||
@@ -358,6 +359,9 @@ function runStartupMemoryCheck(argv = process.argv.slice(2), params = {}) {
|
|||||||
return { skipped: false, results };
|
return { skipped: false, results };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test-only access to pure startup memory helper functions.
|
||||||
|
*/
|
||||||
export const testing = {
|
export const testing = {
|
||||||
cases,
|
cases,
|
||||||
parseArgs,
|
parseArgs,
|
||||||
|
|||||||
Reference in New Issue
Block a user