refactor(tooling): centralize positive env integers (#113277)

This commit is contained in:
Vincent Koc
2026-07-24 16:23:35 +08:00
committed by GitHub
parent 21f0cef5d5
commit a18e8fbe0c
10 changed files with 62 additions and 98 deletions
+2 -16
View File
@@ -5,32 +5,18 @@ import { spawnSync } from "node:child_process";
import { existsSync } from "node:fs";
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { readPositiveEnvInt } from "./lib/numeric-options.mjs";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const entryCandidates = ["dist/entry.js", "dist/entry.mjs"];
const startupMetadataPath = "dist/cli-startup-metadata.json";
const DEFAULT_BUILD_TIMEOUT_MS = 10 * 60 * 1000;
function positiveEnvInt(name, env, fallback) {
const raw = env[name]?.trim();
if (raw === undefined || raw === "") {
return fallback;
}
if (!/^[1-9]\d*$/.test(raw)) {
throw new Error(`invalid ${name}: ${raw}`);
}
const value = Number(raw);
if (!Number.isSafeInteger(value)) {
throw new Error(`invalid ${name}: ${raw}`);
}
return value;
}
/**
* Resolves the CLI startup build timeout from environment.
*/
export function resolveCliStartupBuildTimeoutMs(env = process.env) {
return positiveEnvInt("OPENCLAW_CLI_STARTUP_BUILD_TIMEOUT_MS", env, DEFAULT_BUILD_TIMEOUT_MS);
return readPositiveEnvInt("OPENCLAW_CLI_STARTUP_BUILD_TIMEOUT_MS", env, DEFAULT_BUILD_TIMEOUT_MS);
}
/**
+2 -16
View File
@@ -9,30 +9,16 @@ import {
collectBundledPluginBuildEntries,
NON_PACKAGED_BUNDLED_PLUGIN_DIRS,
} from "./lib/bundled-plugin-build-entries.mjs";
import { readPositiveEnvInt } from "./lib/numeric-options.mjs";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const DEFAULT_BUILD_TIMEOUT_MS = 10 * 60 * 1000;
function positiveEnvInt(name, env, fallback) {
const raw = env[name]?.trim();
if (raw === undefined || raw === "") {
return fallback;
}
if (!/^[1-9]\d*$/.test(raw)) {
throw new Error(`invalid ${name}: ${raw}`);
}
const value = Number(raw);
if (!Number.isSafeInteger(value)) {
throw new Error(`invalid ${name}: ${raw}`);
}
return value;
}
/**
* Resolves the extension memory build timeout from environment.
*/
export function resolveExtensionMemoryBuildTimeoutMs(env = process.env) {
return positiveEnvInt(
return readPositiveEnvInt(
"OPENCLAW_EXTENSION_MEMORY_BUILD_TIMEOUT_MS",
env,
DEFAULT_BUILD_TIMEOUT_MS,
+8 -17
View File
@@ -1,5 +1,6 @@
// Npm Verify Exec script supports OpenClaw repository automation.
import { execFileSync, type ExecFileSyncOptionsWithStringEncoding } from "node:child_process";
import { readPositiveEnvInt } from "./numeric-options.mjs";
export type NpmVerifyCommandInvocation = {
command: string;
@@ -14,21 +15,6 @@ type NpmVerifyExecOptions = ExecFileSyncOptionsWithStringEncoding & {
const DEFAULT_NPM_VERIFY_COMMAND_TIMEOUT_MS = 5 * 60 * 1000;
const DEFAULT_NPM_VERIFY_COMMAND_MAX_BUFFER_BYTES = 16 * 1024 * 1024;
function positiveEnvInt(name: string, fallback: number): number {
const raw = process.env[name]?.trim();
if (raw === undefined || raw === "") {
return fallback;
}
if (!/^[1-9]\d*$/u.test(raw)) {
throw new Error(`invalid ${name}: ${raw}`);
}
const value = Number(raw);
if (!Number.isSafeInteger(value)) {
throw new Error(`invalid ${name}: ${raw}`);
}
return value;
}
export function runNpmVerifyCommand(
invocation: NpmVerifyCommandInvocation,
cwd: string,
@@ -36,11 +22,16 @@ export function runNpmVerifyCommand(
): string {
const timeoutMs =
options.timeoutMs ??
positiveEnvInt("OPENCLAW_NPM_VERIFY_COMMAND_TIMEOUT_MS", DEFAULT_NPM_VERIFY_COMMAND_TIMEOUT_MS);
readPositiveEnvInt(
"OPENCLAW_NPM_VERIFY_COMMAND_TIMEOUT_MS",
process.env,
DEFAULT_NPM_VERIFY_COMMAND_TIMEOUT_MS,
);
const maxBuffer =
options.maxBufferBytes ??
positiveEnvInt(
readPositiveEnvInt(
"OPENCLAW_NPM_VERIFY_COMMAND_MAX_BUFFER_BYTES",
process.env,
DEFAULT_NPM_VERIFY_COMMAND_MAX_BUFFER_BYTES,
);
+1
View File
@@ -1,3 +1,4 @@
export function parsePositiveInt(raw: string, label: string): number;
export function readPositiveEnvInt(name: string, env: NodeJS.ProcessEnv, fallback: number): number;
export function parseNonNegativeInt(raw: string, label: string): number;
export function parsePositiveNumber(raw: string, label: string): number;
+16
View File
@@ -12,6 +12,22 @@ export function parsePositiveInt(raw, label) {
return value;
}
/** Read a safe positive integer from an environment variable. */
export function readPositiveEnvInt(name, env, fallback) {
const raw = env[name]?.trim();
if (raw === undefined || raw === "") {
return fallback;
}
if (!/^[1-9]\d*$/u.test(raw)) {
throw new Error(`invalid ${name}: ${raw}`);
}
const value = Number(raw);
if (!Number.isSafeInteger(value)) {
throw new Error(`invalid ${name}: ${raw}`);
}
return value;
}
/** Parse a safe non-negative integer option. */
export function parseNonNegativeInt(raw, label) {
const text = String(raw).trim();
+2 -16
View File
@@ -6,6 +6,7 @@ import { readFileSync } from "node:fs";
import { join } from "node:path";
import { pathToFileURL } from "node:url";
import { resolveNpmDistTagMirrorAuth as resolveNpmDistTagMirrorAuthBase } from "./lib/npm-publish-plan.mjs";
import { readPositiveEnvInt } from "./lib/numeric-options.mjs";
import {
LOCAL_BUILD_METADATA_DIST_PATHS,
PACKAGE_DIST_INVENTORY_RELATIVE_PATH,
@@ -280,25 +281,10 @@ export function parseReleaseTagVersion(version: string): ParsedReleaseTag | null
return null;
}
function positiveEnvInt(name: string, env: NodeJS.ProcessEnv, fallback: number): number {
const raw = env[name]?.trim();
if (raw === undefined || raw === "") {
return fallback;
}
if (!/^[1-9]\d*$/u.test(raw)) {
throw new Error(`invalid ${name}: ${raw}`);
}
const value = Number(raw);
if (!Number.isSafeInteger(value)) {
throw new Error(`invalid ${name}: ${raw}`);
}
return value;
}
export function resolveNpmReleaseCheckCommandTimeoutMs(
env: NodeJS.ProcessEnv = process.env,
): number {
return positiveEnvInt(
return readPositiveEnvInt(
"OPENCLAW_NPM_RELEASE_CHECK_COMMAND_TIMEOUT_MS",
env,
DEFAULT_RELEASE_CHECK_COMMAND_TIMEOUT_MS,
+2 -16
View File
@@ -6,6 +6,7 @@ import { existsSync, readFileSync, readdirSync } from "node:fs";
import { basename, delimiter, join } from "node:path";
import { pathToFileURL } from "node:url";
import { formatErrorMessage } from "../src/infra/errors.ts";
import { readPositiveEnvInt } from "./lib/numeric-options.mjs";
import { writePackageDistInventoryForPublish } from "./lib/package-dist-inventory.ts";
import { preparePackageChangelog } from "./package-changelog.mjs";
import { createPnpmRunnerSpawnSpec } from "./pnpm-runner.mjs";
@@ -175,23 +176,8 @@ function ensurePreparedArtifacts(): void {
process.exit(1);
}
function positiveEnvInt(name: string, env: NodeJS.ProcessEnv, fallback: number): number {
const raw = env[name]?.trim();
if (raw === undefined || raw === "") {
return fallback;
}
if (!/^[1-9]\d*$/u.test(raw)) {
throw new Error(`invalid ${name}: ${raw}`);
}
const value = Number(raw);
if (!Number.isSafeInteger(value)) {
throw new Error(`invalid ${name}: ${raw}`);
}
return value;
}
export function resolvePrepackCommandTimeoutMs(env: NodeJS.ProcessEnv = process.env): number {
return positiveEnvInt(
return readPositiveEnvInt(
"OPENCLAW_PREPACK_COMMAND_TIMEOUT_MS",
env,
DEFAULT_PREPACK_COMMAND_TIMEOUT_MS,
+5 -17
View File
@@ -32,6 +32,7 @@ import {
listBundledPluginPackArtifacts,
} from "./lib/bundled-plugin-build-entries.mjs";
import { collectPackUnpackedSizeErrors as collectNpmPackUnpackedSizeErrors } from "./lib/npm-pack-budget.mjs";
import { readPositiveEnvInt } from "./lib/numeric-options.mjs";
import {
isLegacyPluginDependencyInstallStagePath,
LOCAL_BUILD_METADATA_DIST_PATHS,
@@ -201,21 +202,6 @@ const PACKED_PLUGIN_SDK_TYPESCRIPT_SMOKE_FIXTURE = resolve(
"scripts/fixtures/packed-plugin-sdk-type-smoke.ts",
);
function positiveEnvInt(name: string, fallback: number): number {
const raw = process.env[name]?.trim();
if (raw === undefined || raw === "") {
return fallback;
}
if (!/^[1-9]\d*$/u.test(raw)) {
throw new Error(`invalid ${name}: ${raw}`);
}
const value = Number(raw);
if (!Number.isSafeInteger(value)) {
throw new Error(`invalid ${name}: ${raw}`);
}
return value;
}
export function runReleaseCheckCommand(
invocation: ReleaseCheckCommandInvocation,
options: {
@@ -235,16 +221,18 @@ export function runReleaseCheckCommand(
killSignal: "SIGKILL",
maxBuffer:
options.maxBuffer ??
positiveEnvInt(
readPositiveEnvInt(
"OPENCLAW_RELEASE_CHECK_COMMAND_MAX_BUFFER_BYTES",
process.env,
DEFAULT_RELEASE_CHECK_COMMAND_MAX_BUFFER_BYTES,
),
shell: invocation.shell ?? options.shell,
stdio: options.stdio,
timeout:
options.timeoutMs ??
positiveEnvInt(
readPositiveEnvInt(
"OPENCLAW_RELEASE_CHECK_COMMAND_TIMEOUT_MS",
process.env,
DEFAULT_RELEASE_CHECK_COMMAND_TIMEOUT_MS,
),
windowsVerbatimArguments: invocation.windowsVerbatimArguments,
+2
View File
@@ -1360,6 +1360,8 @@ const TOOLING_SOURCE_TEST_TARGETS = new Map([
],
],
["scripts/lib/npm-verify-exec.ts", ["test/scripts/npm-verify-exec.test.ts"]],
["scripts/lib/numeric-options.mjs", ["test/scripts/numeric-options.test.ts"]],
["scripts/lib/numeric-options.d.mts", ["test/scripts/numeric-options.test.ts"]],
["scripts/lib/openclaw-test-state.mjs", ["test/scripts/openclaw-test-state.test.ts"]],
[
"scripts/lib/workspace-bootstrap-smoke.mjs",
+22
View File
@@ -0,0 +1,22 @@
import { describe, expect, it } from "vitest";
import { readPositiveEnvInt } from "../../scripts/lib/numeric-options.mjs";
describe("readPositiveEnvInt", () => {
it("uses the fallback for missing or blank values", () => {
expect(readPositiveEnvInt("LIMIT", {}, 42)).toBe(42);
expect(readPositiveEnvInt("LIMIT", { LIMIT: " " }, 42)).toBe(42);
});
it("reads strict positive safe integers", () => {
expect(readPositiveEnvInt("LIMIT", { LIMIT: " 123 " }, 42)).toBe(123);
});
it.each(["0", "-1", "1.5", "1e3", "0x10", "9007199254740992"])(
"rejects invalid value %s",
(raw) => {
expect(() => readPositiveEnvInt("LIMIT", { LIMIT: raw }, 42)).toThrow(
`invalid LIMIT: ${raw}`,
);
},
);
});