mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 08:20:43 +00:00
test: narrow live Docker package script changes
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { appendFileSync } from "node:fs";
|
||||
import { appendFileSync, existsSync, readFileSync } from "node:fs";
|
||||
import { booleanFlag, parseFlagArgs, stringFlag } from "./lib/arg-utils.mjs";
|
||||
|
||||
const DOCS_PATH_RE = /^(?:docs\/|README\.md$|AGENTS\.md$|.*\.mdx?$)/u;
|
||||
@@ -12,6 +12,7 @@ const ROOT_GLOBAL_PATH_RE =
|
||||
/^(?:package\.json$|pnpm-lock\.yaml$|pnpm-workspace\.yaml$|tsdown\.config\.ts$|vitest\.config\.ts$)/u;
|
||||
const LIVE_DOCKER_TOOLING_PATH_RE =
|
||||
/^(?:scripts\/test-docker-all\.mjs|scripts\/test-docker-all\.sh|scripts\/lib\/live-docker-auth\.sh|scripts\/test-live-(?:acp-bind|cli-backend|codex-harness|gateway-models|models)-docker\.sh|src\/gateway\/gateway-acp-bind\.live\.test\.ts|src\/gateway\/live-agent-probes\.test\.ts)$/u;
|
||||
const LIVE_DOCKER_PACKAGE_SCRIPT_RE = /^test:docker:live-[\w:-]+$/u;
|
||||
const TEST_PATH_RE =
|
||||
/(?:^|\/)(?:test|__tests__)\/|(?:\.|\/)(?:test|spec|e2e|browser\.test)\.[cm]?[jt]sx?$/u;
|
||||
const PUBLIC_EXTENSION_CONTRACT_RE =
|
||||
@@ -66,9 +67,10 @@ export function createEmptyChangedLanes() {
|
||||
|
||||
/**
|
||||
* @param {string[]} changedPaths
|
||||
* @param {{ packageJsonChangeKind?: "liveDockerTooling" | null }} [options]
|
||||
* @returns {ChangedLaneResult}
|
||||
*/
|
||||
export function detectChangedLanes(changedPaths) {
|
||||
export function detectChangedLanes(changedPaths, options = {}) {
|
||||
const paths = [...new Set(changedPaths.map(normalizeChangedPath).filter(Boolean))]
|
||||
.toSorted((left, right) => left.localeCompare(right))
|
||||
.filter((changedPath) => changedPath !== "--");
|
||||
@@ -76,6 +78,8 @@ export function detectChangedLanes(changedPaths) {
|
||||
const reasons = [];
|
||||
let extensionImpactFromCore = false;
|
||||
let hasNonDocs = false;
|
||||
const packageJsonIsLiveDockerTooling =
|
||||
paths.includes("package.json") && options.packageJsonChangeKind === "liveDockerTooling";
|
||||
|
||||
if (paths.length === 0) {
|
||||
reasons.push("no changed paths");
|
||||
@@ -83,6 +87,7 @@ export function detectChangedLanes(changedPaths) {
|
||||
}
|
||||
|
||||
if (
|
||||
!packageJsonIsLiveDockerTooling &&
|
||||
paths.some((changedPath) => RELEASE_METADATA_PATHS.has(changedPath)) &&
|
||||
paths.every(
|
||||
(changedPath) => RELEASE_METADATA_PATHS.has(changedPath) || DOCS_PATH_RE.test(changedPath),
|
||||
@@ -104,6 +109,12 @@ export function detectChangedLanes(changedPaths) {
|
||||
|
||||
hasNonDocs = true;
|
||||
|
||||
if (changedPath === "package.json" && packageJsonIsLiveDockerTooling) {
|
||||
lanes.liveDockerTooling = true;
|
||||
reasons.push(`${changedPath}: live Docker package scripts`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (LIVE_DOCKER_TOOLING_PATH_RE.test(changedPath)) {
|
||||
lanes.liveDockerTooling = true;
|
||||
reasons.push(`${changedPath}: live Docker tooling surface`);
|
||||
@@ -231,6 +242,94 @@ export function listStagedChangedPaths() {
|
||||
return output.split("\n").map(normalizeChangedPath).filter(Boolean);
|
||||
}
|
||||
|
||||
export function classifyPackageJsonChangeFromGit(params) {
|
||||
try {
|
||||
const { before, after } = readPackageJsonBeforeAfter(params);
|
||||
return isLiveDockerPackageScriptOnlyChange(before, after) ? "liveDockerTooling" : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function isLiveDockerPackageScriptOnlyChange(before, after) {
|
||||
const beforePackage = JSON.parse(before);
|
||||
const afterPackage = JSON.parse(after);
|
||||
const beforeAllowed = extractLiveDockerPackageScripts(beforePackage);
|
||||
const afterAllowed = extractLiveDockerPackageScripts(afterPackage);
|
||||
const beforeStripped = stripLiveDockerPackageScripts(beforePackage);
|
||||
const afterStripped = stripLiveDockerPackageScripts(afterPackage);
|
||||
|
||||
return (
|
||||
stableJson(beforeStripped) === stableJson(afterStripped) &&
|
||||
stableJson(beforeAllowed) !== stableJson(afterAllowed)
|
||||
);
|
||||
}
|
||||
|
||||
function readPackageJsonBeforeAfter(params) {
|
||||
const before = readGitText(params.staged ? "HEAD" : params.base, "package.json");
|
||||
if (params.staged) {
|
||||
return { before, after: readGitText("INDEX", "package.json") };
|
||||
}
|
||||
|
||||
let after = readGitText(params.head ?? "HEAD", "package.json");
|
||||
if (params.includeWorktree !== false && existsSync("package.json")) {
|
||||
const worktree = readGitText("WORKTREE", "package.json");
|
||||
if (worktree !== after) {
|
||||
after = worktree;
|
||||
}
|
||||
}
|
||||
return { before, after };
|
||||
}
|
||||
|
||||
function readGitText(ref, filePath) {
|
||||
if (ref === "WORKTREE") {
|
||||
return readFileSync(filePath, "utf8");
|
||||
}
|
||||
const spec = ref === "INDEX" ? `:${filePath}` : `${ref}:${filePath}`;
|
||||
return execFileSync("git", ["show", spec], {
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
encoding: "utf8",
|
||||
maxBuffer: 16 * 1024 * 1024,
|
||||
});
|
||||
}
|
||||
|
||||
function extractLiveDockerPackageScripts(packageJson) {
|
||||
const scripts = packageJson?.scripts;
|
||||
if (!scripts || typeof scripts !== "object" || Array.isArray(scripts)) {
|
||||
return {};
|
||||
}
|
||||
return Object.fromEntries(
|
||||
Object.entries(scripts).filter(([name]) => LIVE_DOCKER_PACKAGE_SCRIPT_RE.test(name)),
|
||||
);
|
||||
}
|
||||
|
||||
function stripLiveDockerPackageScripts(packageJson) {
|
||||
const clone = JSON.parse(JSON.stringify(packageJson));
|
||||
const scripts = clone.scripts;
|
||||
if (!scripts || typeof scripts !== "object" || Array.isArray(scripts)) {
|
||||
return clone;
|
||||
}
|
||||
for (const name of Object.keys(scripts)) {
|
||||
if (LIVE_DOCKER_PACKAGE_SCRIPT_RE.test(name)) {
|
||||
delete scripts[name];
|
||||
}
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
function stableJson(value) {
|
||||
if (Array.isArray(value)) {
|
||||
return `[${value.map(stableJson).join(",")}]`;
|
||||
}
|
||||
if (value && typeof value === "object") {
|
||||
return `{${Object.keys(value)
|
||||
.toSorted((left, right) => left.localeCompare(right))
|
||||
.map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`)
|
||||
.join(",")}}`;
|
||||
}
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
|
||||
export function writeChangedLaneGitHubOutput(result, outputPath = process.env.GITHUB_OUTPUT) {
|
||||
if (!outputPath) {
|
||||
throw new Error("GITHUB_OUTPUT is required");
|
||||
@@ -319,7 +418,14 @@ if (isDirectRun()) {
|
||||
: args.staged
|
||||
? listStagedChangedPaths()
|
||||
: listChangedPathsFromGit({ base: args.base, head: args.head });
|
||||
const result = detectChangedLanes(paths);
|
||||
const packageJsonChangeKind = paths.includes("package.json")
|
||||
? classifyPackageJsonChangeFromGit({
|
||||
base: args.base,
|
||||
head: args.head,
|
||||
staged: args.staged,
|
||||
})
|
||||
: null;
|
||||
const result = detectChangedLanes(paths, { packageJsonChangeKind });
|
||||
if (args.githubOutput) {
|
||||
writeChangedLaneGitHubOutput(result);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { performance } from "node:perf_hooks";
|
||||
import {
|
||||
classifyPackageJsonChangeFromGit,
|
||||
detectChangedLanes,
|
||||
listChangedPathsFromGit,
|
||||
listStagedChangedPaths,
|
||||
@@ -407,7 +408,14 @@ if (isDirectRun()) {
|
||||
: args.staged
|
||||
? listStagedChangedPaths()
|
||||
: listChangedPathsFromGit({ base: args.base, head: args.head });
|
||||
const result = detectChangedLanes(paths);
|
||||
const packageJsonChangeKind = paths.includes("package.json")
|
||||
? classifyPackageJsonChangeFromGit({
|
||||
base: args.base,
|
||||
head: args.head,
|
||||
staged: args.staged,
|
||||
})
|
||||
: null;
|
||||
const result = detectChangedLanes(paths, { packageJsonChangeKind });
|
||||
process.exitCode = await runChangedCheck(result, {
|
||||
...args,
|
||||
explicitPaths: args.paths.length > 0,
|
||||
|
||||
Reference in New Issue
Block a user