mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-31 12:58:35 +00:00
fix(scripts): run Windows check commands through shims
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { appendFileSync, existsSync, readFileSync } from "node:fs";
|
||||
import { booleanFlag, parseFlagArgs, stringFlag } from "./lib/arg-utils.mjs";
|
||||
import { isDirectRunUrl } from "./lib/direct-run.mjs";
|
||||
|
||||
const GIT_OUTPUT_MAX_BUFFER = 64 * 1024 * 1024;
|
||||
|
||||
@@ -445,8 +446,7 @@ function parseArgs(argv) {
|
||||
}
|
||||
|
||||
function isDirectRun() {
|
||||
const direct = process.argv[1];
|
||||
return Boolean(direct && import.meta.url.endsWith(direct));
|
||||
return isDirectRunUrl(process.argv[1], import.meta.url);
|
||||
}
|
||||
|
||||
function printHuman(result) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
import { shrinkwrapPackageDirsForChangedPaths } from "./generate-npm-shrinkwrap.mjs";
|
||||
import { booleanFlag, parseFlagArgs, stringFlag } from "./lib/arg-utils.mjs";
|
||||
import { printTimingSummary } from "./lib/check-timing-summary.mjs";
|
||||
import { isDirectRunUrl } from "./lib/direct-run.mjs";
|
||||
import {
|
||||
acquireLocalHeavyCheckLockSync,
|
||||
resolveLocalHeavyCheckEnv,
|
||||
@@ -472,8 +473,7 @@ function parseArgs(argv) {
|
||||
}
|
||||
|
||||
function isDirectRun() {
|
||||
const direct = process.argv[1];
|
||||
return Boolean(direct && import.meta.url.endsWith(direct));
|
||||
return isDirectRunUrl(process.argv[1], import.meta.url);
|
||||
}
|
||||
|
||||
if (isDirectRun()) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { appendFileSync } from "node:fs";
|
||||
import { isDirectRunUrl } from "./lib/direct-run.mjs";
|
||||
|
||||
/** @typedef {{ runNode: boolean; runMacos: boolean; runAndroid: boolean; runWindows: boolean; runSkillsPython: boolean; runChangedSmoke: boolean; runControlUiI18n: boolean }} ChangedScope */
|
||||
/** @typedef {{ runFastOnly: boolean; runPluginContracts: boolean; runCiRouting: boolean }} NodeFastScope */
|
||||
@@ -286,8 +287,7 @@ export function writeGitHubOutput(
|
||||
}
|
||||
|
||||
function isDirectRun() {
|
||||
const direct = process.argv[1];
|
||||
return Boolean(direct && import.meta.url.endsWith(direct));
|
||||
return isDirectRunUrl(process.argv[1], import.meta.url);
|
||||
}
|
||||
|
||||
/** @param {string[]} argv */
|
||||
|
||||
@@ -2,10 +2,38 @@ import { spawn, spawnSync, type SpawnOptions } from "node:child_process";
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { resolveNpmRunner } from "../../npm-runner.mjs";
|
||||
import { resolvePnpmRunner } from "../../pnpm-runner.mjs";
|
||||
import { buildCmdExeCommandLine } from "../../windows-cmd-helpers.mjs";
|
||||
import type { CommandResult, RunOptions } from "./types.ts";
|
||||
|
||||
export const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");
|
||||
|
||||
type HostCommandInvocation = {
|
||||
args: string[];
|
||||
command: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
shell?: boolean;
|
||||
windowsVerbatimArguments?: boolean;
|
||||
};
|
||||
|
||||
type ResolveHostCommandOptions = {
|
||||
comSpec?: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
execPath?: string;
|
||||
existsSync?: (path: string) => boolean;
|
||||
platform?: NodeJS.Platform;
|
||||
};
|
||||
|
||||
function hostInvocationFromRunner(runner: HostCommandInvocation): HostCommandInvocation {
|
||||
if (runner.env === undefined) {
|
||||
const invocation = { ...runner };
|
||||
delete invocation.env;
|
||||
return invocation;
|
||||
}
|
||||
return runner;
|
||||
}
|
||||
|
||||
export function say(message: string): void {
|
||||
process.stdout.write(`==> ${message}\n`);
|
||||
}
|
||||
@@ -23,15 +51,81 @@ export function shellQuote(value: string): string {
|
||||
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
||||
}
|
||||
|
||||
function portableBasename(value: string): string {
|
||||
return value.split(/[/\\]/u).at(-1) ?? value;
|
||||
}
|
||||
|
||||
function portableExtension(value: string): string {
|
||||
return path.posix.extname(portableBasename(value)).toLowerCase();
|
||||
}
|
||||
|
||||
function isBareCommand(command: string, name: "npm" | "pnpm"): boolean {
|
||||
return portableBasename(command) === command && command.toLowerCase() === name;
|
||||
}
|
||||
|
||||
function resolveEnvValue(env: NodeJS.ProcessEnv, name: string): string | undefined {
|
||||
const key = Object.keys(env).find((candidate) => candidate.toLowerCase() === name.toLowerCase());
|
||||
return key === undefined ? undefined : env[key];
|
||||
}
|
||||
|
||||
export function resolveHostCommandInvocation(
|
||||
command: string,
|
||||
args: string[],
|
||||
options: ResolveHostCommandOptions = {},
|
||||
): HostCommandInvocation {
|
||||
const env = options.env ?? process.env;
|
||||
const platform = options.platform ?? process.platform;
|
||||
const comSpec = options.comSpec ?? resolveEnvValue(env, "ComSpec") ?? "cmd.exe";
|
||||
|
||||
if (isBareCommand(command, "pnpm")) {
|
||||
const runner = resolvePnpmRunner({
|
||||
comSpec,
|
||||
npmExecPath: env.npm_execpath,
|
||||
nodeExecPath: options.execPath ?? process.execPath,
|
||||
platform,
|
||||
pnpmArgs: args,
|
||||
});
|
||||
return hostInvocationFromRunner(runner);
|
||||
}
|
||||
|
||||
if (isBareCommand(command, "npm")) {
|
||||
const runner = resolveNpmRunner({
|
||||
comSpec,
|
||||
env,
|
||||
execPath: options.execPath ?? process.execPath,
|
||||
existsSync: options.existsSync,
|
||||
npmArgs: args,
|
||||
platform,
|
||||
});
|
||||
return hostInvocationFromRunner(runner);
|
||||
}
|
||||
|
||||
const extension = portableExtension(command);
|
||||
if (platform === "win32" && (extension === ".cmd" || extension === ".bat")) {
|
||||
return {
|
||||
args: ["/d", "/s", "/c", buildCmdExeCommandLine(command, args)],
|
||||
command: comSpec,
|
||||
shell: false,
|
||||
windowsVerbatimArguments: true,
|
||||
};
|
||||
}
|
||||
|
||||
return { args, command, shell: false };
|
||||
}
|
||||
|
||||
export function run(command: string, args: string[], options: RunOptions = {}): CommandResult {
|
||||
const result = spawnSync(command, args, {
|
||||
const env = { ...process.env, ...options.env };
|
||||
const invocation = resolveHostCommandInvocation(command, args, { env });
|
||||
const result = spawnSync(invocation.command, invocation.args, {
|
||||
cwd: options.cwd ?? repoRoot,
|
||||
encoding: "utf8",
|
||||
env: { ...process.env, ...options.env },
|
||||
env: invocation.env ?? env,
|
||||
input: options.input,
|
||||
maxBuffer: 50 * 1024 * 1024,
|
||||
stdio: options.quiet ? ["pipe", "pipe", "pipe"] : ["pipe", "pipe", "pipe"],
|
||||
shell: invocation.shell,
|
||||
timeout: options.timeoutMs,
|
||||
windowsVerbatimArguments: invocation.windowsVerbatimArguments,
|
||||
});
|
||||
|
||||
const timedOut = (result.error as NodeJS.ErrnoException | undefined)?.code === "ETIMEDOUT";
|
||||
@@ -67,10 +161,14 @@ export async function runStreaming(
|
||||
options: RunOptions & { logPath?: string } = {},
|
||||
): Promise<number> {
|
||||
return await new Promise((resolve, reject) => {
|
||||
const child = spawn(command, args, {
|
||||
const env = { ...process.env, ...options.env };
|
||||
const invocation = resolveHostCommandInvocation(command, args, { env });
|
||||
const child = spawn(invocation.command, invocation.args, {
|
||||
cwd: options.cwd ?? repoRoot,
|
||||
env: { ...process.env, ...options.env },
|
||||
env: invocation.env ?? env,
|
||||
shell: invocation.shell,
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
windowsVerbatimArguments: invocation.windowsVerbatimArguments,
|
||||
} satisfies SpawnOptions);
|
||||
|
||||
let log = "";
|
||||
|
||||
@@ -6,6 +6,7 @@ import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { parse as parseYaml } from "yaml";
|
||||
import { listChangedPathsFromGit, listStagedChangedPaths } from "./changed-lanes.mjs";
|
||||
import { resolveNpmRunner } from "./npm-runner.mjs";
|
||||
|
||||
const ROOT_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const EXACT_VERSION_PATTERN = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/u;
|
||||
@@ -17,10 +18,6 @@ function usage() {
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function npmCommand() {
|
||||
return process.platform === "win32" ? "npm.cmd" : "npm";
|
||||
}
|
||||
|
||||
function normalizeOverrideValue(value) {
|
||||
if (value === null || value === undefined) {
|
||||
return value;
|
||||
@@ -144,10 +141,25 @@ function packageJsonForShrinkwrap(packageJson, shrinkwrapOverrides) {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
export function createNpmShrinkwrapCommand(args, options = {}) {
|
||||
return resolveNpmRunner({
|
||||
comSpec: options.comSpec,
|
||||
env: options.env,
|
||||
execPath: options.execPath,
|
||||
existsSync: options.existsSync,
|
||||
npmArgs: args,
|
||||
platform: options.platform,
|
||||
});
|
||||
}
|
||||
|
||||
function runNpm(args, cwd) {
|
||||
execFileSync(npmCommand(), args, {
|
||||
const npm = createNpmShrinkwrapCommand(args);
|
||||
execFileSync(npm.command, npm.args, {
|
||||
cwd,
|
||||
env: npm.env ?? process.env,
|
||||
shell: npm.shell,
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
windowsVerbatimArguments: npm.windowsVerbatimArguments,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -393,7 +405,7 @@ function listPublishablePluginPackageDirs() {
|
||||
const extensionsDir = path.join(ROOT_DIR, "extensions");
|
||||
return readdirSync(extensionsDir, { withFileTypes: true })
|
||||
.filter((entry) => entry.isDirectory())
|
||||
.map((entry) => path.join("extensions", entry.name))
|
||||
.map((entry) => path.posix.join("extensions", entry.name))
|
||||
.filter((packageDir) => {
|
||||
const packageJsonPath = path.join(ROOT_DIR, packageDir, "package.json");
|
||||
if (!existsSync(packageJsonPath)) {
|
||||
|
||||
18
scripts/lib/direct-run.mjs
Normal file
18
scripts/lib/direct-run.mjs
Normal file
@@ -0,0 +1,18 @@
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
export function isDirectRunPath(directPath, modulePath, platform = process.platform) {
|
||||
if (!directPath || !modulePath) {
|
||||
return false;
|
||||
}
|
||||
const pathImpl = platform === "win32" ? path.win32 : path;
|
||||
const normalize =
|
||||
platform === "win32"
|
||||
? (value) => pathImpl.resolve(value).toLowerCase()
|
||||
: (value) => pathImpl.resolve(value);
|
||||
return normalize(directPath) === normalize(modulePath);
|
||||
}
|
||||
|
||||
export function isDirectRunUrl(directPath, moduleUrl, platform = process.platform) {
|
||||
return isDirectRunPath(directPath, fileURLToPath(moduleUrl), platform);
|
||||
}
|
||||
Reference in New Issue
Block a user