fix(ci): remove cross-os TypeScript loader bootstrap (#103195)

This commit is contained in:
Vincent Koc
2026-07-09 18:15:12 -07:00
committed by GitHub
parent 874e4a2d40
commit 968b89defe
7 changed files with 82 additions and 82 deletions
@@ -194,7 +194,6 @@ env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
NODE_VERSION: "24.15.0"
OPENCLAW_REPOSITORY: openclaw/openclaw
TSX_VERSION: "4.21.0"
OPENCLAW_CROSS_OS_OPENAI_MODEL: ${{ inputs.openai_model || vars.OPENCLAW_CROSS_OS_OPENAI_MODEL || 'openai/gpt-5.5' }}
jobs:
+1 -1
View File
@@ -1942,7 +1942,7 @@
"test:unit:fast:audit": "node scripts/test-unit-fast-audit.mjs",
"test:voicecall:closedloop": "node scripts/test-voicecall-closedloop.mjs",
"test:watch": "node scripts/test-projects.mjs --watch",
"test:windows:ci": "node scripts/test-projects.mjs src/shared/runtime-import.test.ts src/process/exec.windows.test.ts src/process/windows-command.test.ts src/infra/backup-create.windows.test.ts src/infra/windows-install-roots.test.ts src/node-host/invoke-system-run-allowlist.test.ts src/daemon/schtasks.startup-fallback.test.ts extensions/lobster/src/lobster-runner.test.ts test/scripts/format-generated-module.test.ts test/scripts/npm-runner.test.ts test/scripts/pnpm-runner.test.ts test/scripts/run-with-env.test.ts test/scripts/ui.test.ts test/scripts/vitest-process-group.test.ts",
"test:windows:ci": "node scripts/test-projects.mjs src/shared/runtime-import.test.ts src/process/exec.windows.test.ts src/process/windows-command.test.ts src/infra/backup-create.windows.test.ts src/infra/windows-install-roots.test.ts src/node-host/invoke-system-run-allowlist.test.ts src/daemon/schtasks.startup-fallback.test.ts extensions/lobster/src/lobster-runner.test.ts test/scripts/format-generated-module.test.ts test/scripts/npm-runner.test.ts test/scripts/openclaw-cross-os-release-workflow.test.ts test/scripts/pnpm-runner.test.ts test/scripts/run-with-env.test.ts test/scripts/ui.test.ts test/scripts/vitest-process-group.test.ts",
"tool-display:check": "node --import tsx scripts/tool-display.ts --check",
"tool-display:write": "node --import tsx scripts/tool-display.ts --write",
"ts-topology": "node --import tsx scripts/ts-topology.ts",
+2 -2
View File
@@ -45,9 +45,9 @@ const ANDROID_NATIVE_RE = /^(apps\/android\/|apps\/shared\/)/;
const NODE_SCOPE_RE =
/^(src\/|test\/|extensions\/|packages\/|scripts\/|ui\/|\.github\/|openclaw\.mjs$|package\.json$|pnpm-lock\.yaml$|pnpm-workspace\.yaml$|tsconfig.*\.json$|vitest.*\.ts$|tsdown\.config\.ts$|\.oxlintrc\.json$|\.oxfmtrc\.jsonc$)/;
const WINDOWS_SCOPE_RE =
/^(src\/process\/|src\/infra\/windows-install-roots\.ts$|src\/shared\/(?:import-specifier|runtime-import)(?:\.test)?\.ts$|scripts\/(?:install\.ps1|(?:npm-runner|pnpm-runner|ui|vitest-process-group)\.(?:mjs|js)|lib\/format-generated-module\.mjs)$|test\/scripts\/(?:format-generated-module|install-ps1|npm-runner|pnpm-runner|ui|vitest-process-group)\.test\.ts$|package\.json$|pnpm-lock\.yaml$|pnpm-workspace\.yaml$|\.github\/workflows\/ci\.yml$|\.github\/actions\/setup-node-env\/action\.yml$|\.github\/actions\/setup-pnpm-store-cache\/action\.yml$)/;
/^(src\/process\/|src\/infra\/windows-install-roots\.ts$|src\/shared\/(?:import-specifier|runtime-import)(?:\.test)?\.ts$|scripts\/(?:install\.ps1|openclaw-cross-os-release-checks\.ts|github\/run-openclaw-cross-os-release-checks\.sh|(?:npm-runner|pnpm-runner|ui|vitest-process-group)\.(?:mjs|js)|lib\/format-generated-module\.mjs)$|test\/scripts\/(?:format-generated-module|install-ps1|npm-runner|openclaw-cross-os-release-workflow|pnpm-runner|ui|vitest-process-group)\.test\.ts$|package\.json$|pnpm-lock\.yaml$|pnpm-workspace\.yaml$|\.github\/workflows\/(?:ci|openclaw-cross-os-release-checks-reusable)\.yml$|\.github\/actions\/setup-node-env\/action\.yml$|\.github\/actions\/setup-pnpm-store-cache\/action\.yml$)/;
const WINDOWS_TEST_SCOPE_RE =
/^(src\/process\/(?:exec\.windows|windows-command)\.test\.ts$|src\/infra\/windows-install-roots\.test\.ts$|src\/shared\/runtime-import\.test\.ts$|test\/scripts\/(?:format-generated-module|npm-runner|pnpm-runner|ui|vitest-process-group)\.test\.ts$)/;
/^(src\/process\/(?:exec\.windows|windows-command)\.test\.ts$|src\/infra\/windows-install-roots\.test\.ts$|src\/shared\/runtime-import\.test\.ts$|test\/scripts\/(?:format-generated-module|npm-runner|openclaw-cross-os-release-workflow|pnpm-runner|ui|vitest-process-group)\.test\.ts$)/;
const WINDOWS_DAEMON_SCOPE_RE =
/^src\/daemon\/(?:schtasks(?:[-.][^/]+)?|runtime-hints\.windows-paths(?:\.test)?|test-helpers\/schtasks-(?:base-mocks|fixtures))\.ts$/;
const TEST_ONLY_PATH_RE =
@@ -1,10 +1,9 @@
#!/usr/bin/env bash
set -euo pipefail
tsx_version="${OPENCLAW_RELEASE_TSX_VERSION:-${TSX_VERSION:-4.21.0}}"
script_path="${OPENCLAW_RELEASE_CHECKS_SCRIPT:-workflow/scripts/openclaw-cross-os-release-checks.ts}"
if ! command -v node >/dev/null 2>&1 || ! command -v npm >/dev/null 2>&1; then
if ! command -v node >/dev/null 2>&1; then
if command -v cygpath >/dev/null 2>&1; then
for node_dir in /c/hostedtoolcache/windows/node/*/x64 /c/actions-runner/_work/_tool/node/*/x64; do
if [[ -x "${node_dir}/node.exe" ]]; then
@@ -16,78 +15,15 @@ if ! command -v node >/dev/null 2>&1 || ! command -v npm >/dev/null 2>&1; then
fi
node_cmd="node"
npm_cmd="npm"
npm_cli_js=""
if command -v cygpath >/dev/null 2>&1; then
if command -v node.exe >/dev/null 2>&1; then
node_cmd="node.exe"
node_path="$(command -v node.exe)"
node_dir="$(dirname "$(cygpath -u "${node_path}")")"
if [[ -f "${node_dir}/node_modules/npm/bin/npm-cli.js" ]]; then
npm_cli_js="${node_dir}/node_modules/npm/bin/npm-cli.js"
fi
fi
if command -v npm.cmd >/dev/null 2>&1; then
npm_cmd="npm.cmd"
elif command -v npm.exe >/dev/null 2>&1; then
npm_cmd="npm.exe"
fi
fi
temp_root="${OPENCLAW_RELEASE_TSX_TOOL_ROOT:-${RUNNER_TEMP:-${TMPDIR:-/tmp}}}"
if command -v cygpath >/dev/null 2>&1; then
temp_root="$(cygpath -u "${temp_root}")"
fi
tool_dir="${OPENCLAW_RELEASE_TSX_TOOL_DIR:-${temp_root}/openclaw-release-tsx-${tsx_version}}"
loader_path="${tool_dir}/node_modules/tsx/dist/loader.mjs"
npm_tool_dir="${tool_dir}"
npm_cli_arg="${npm_cli_js}"
loader_arg="${loader_path}"
if command -v cygpath >/dev/null 2>&1; then
npm_tool_dir="$(cygpath -w "${tool_dir}")"
if [[ -n "${npm_cli_js}" ]]; then
npm_cli_arg="$(cygpath -w "${npm_cli_js}")"
fi
loader_arg="$(cygpath -w "${loader_path}")"
fi
command -v "${node_cmd}" >/dev/null 2>&1 || {
echo "node is required to run cross-OS release checks." >&2
exit 127
}
command -v "${npm_cmd}" >/dev/null 2>&1 || {
echo "npm is required to install the cross-OS release-check loader." >&2
exit 127
}
if [[ ! -f "${loader_path}" ]]; then
mkdir -p "${tool_dir}"
# The loader only needs tsx's packaged JS and esbuild platform package.
# Skipping lifecycle scripts avoids npm child-shell drift on Windows runners.
if [[ -n "${npm_cli_js}" ]]; then
if ! "${node_cmd}" "${npm_cli_arg}" install --prefix "${npm_tool_dir}" --no-save --no-package-lock --ignore-scripts "tsx@${tsx_version}" >/dev/null; then
echo "failed to install cross-OS release-check loader with ${node_cmd} ${npm_cli_arg}." >&2
exit 127
fi
elif ! "${npm_cmd}" install --prefix "${npm_tool_dir}" --no-save --no-package-lock --ignore-scripts "tsx@${tsx_version}" >/dev/null; then
echo "failed to install cross-OS release-check loader with ${npm_cmd}." >&2
exit 127
fi
fi
if [[ ! -f "${loader_path}" ]]; then
echo "tsx loader missing after install: ${loader_path}" >&2
find "${tool_dir}" -maxdepth 5 -type f 2>/dev/null | sort | sed 's/^/ /' >&2 || true
exit 127
fi
loader_url="$(
"${node_cmd}" -e '
const { resolve } = require("node:path");
const { pathToFileURL } = require("node:url");
process.stdout.write(pathToFileURL(resolve(process.argv[1])).href);
' "${loader_arg}"
)"
exec "${node_cmd}" --import "${loader_url}" "${script_path}" "$@"
exec "${node_cmd}" "${script_path}" "$@"
+2 -2
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env -S node --import tsx
#!/usr/bin/env node
// Executed directly via Node.js + tsx in the release workflow.
// Executed directly via Node.js native type stripping in the release workflow.
import { spawn } from "node:child_process";
import { randomUUID } from "node:crypto";
+17
View File
@@ -503,6 +503,23 @@ describe("detectChangedScope", () => {
runChangedSmoke: false,
runControlUiI18n: false,
});
for (const releaseCheckPath of [
".github/workflows/openclaw-cross-os-release-checks-reusable.yml",
"scripts/github/run-openclaw-cross-os-release-checks.sh",
"scripts/openclaw-cross-os-release-checks.ts",
"test/scripts/openclaw-cross-os-release-workflow.test.ts",
]) {
expect(detectChangedScope([releaseCheckPath]), releaseCheckPath).toEqual({
runNode: true,
runMacos: false,
runIosBuild: false,
runAndroid: false,
runWindows: true,
runSkillsPython: false,
runChangedSmoke: false,
runControlUiI18n: false,
});
}
expect(detectChangedScope(["scripts/install.ps1"])).toEqual({
runNode: true,
runMacos: false,
@@ -1,10 +1,13 @@
// Openclaw Cross Os Release Workflow tests cover openclaw cross os release workflow script behavior.
import { spawnSync } from "node:child_process";
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
const WORKFLOW_PATH = ".github/workflows/openclaw-cross-os-release-checks-reusable.yml";
const WRAPPER_PATH = "scripts/github/run-openclaw-cross-os-release-checks.sh";
const SCRIPT_PATH = "scripts/openclaw-cross-os-release-checks.ts";
const HARNESS = "bash workflow/scripts/github/run-openclaw-cross-os-release-checks.sh";
const BASH_BIN = process.platform === "win32" ? "bash" : "/bin/bash";
describe("cross-OS release checks workflow", () => {
it("runs the TypeScript release harness through the Windows-safe wrapper", () => {
@@ -13,7 +16,7 @@ describe("cross-OS release checks workflow", () => {
expect(workflow).toContain(HARNESS);
expect(workflow).toContain("suite_filter:");
expect(workflow).toContain('--suite-filter "${INPUT_SUITE_FILTER}"');
expect(workflow).not.toContain('pnpm dlx "tsx@${TSX_VERSION}"');
expect(workflow).not.toContain("TSX_VERSION");
});
it("bounds npm baseline packing during prepare", () => {
@@ -34,16 +37,61 @@ describe("cross-OS release checks workflow", () => {
expect(workflow).toContain("process.stdout.write(`file_name=${fileName}\\n`);");
});
it("uses Windows-safe npm resolution for the TypeScript loader bootstrap", () => {
it("executes the release harness directly with Node", () => {
const wrapper = readFileSync(WRAPPER_PATH, "utf8");
const script = readFileSync(SCRIPT_PATH, "utf8");
const packageJson = JSON.parse(readFileSync("package.json", "utf8")) as {
scripts: Record<string, string>;
};
expect(wrapper).toContain("command -v npm.cmd");
expect(wrapper).toContain('npm_tool_dir="$(cygpath -w "${tool_dir}")"');
expect(wrapper).toContain('npm_cli_arg="$(cygpath -w "${npm_cli_js}")"');
expect(wrapper).toContain('loader_arg="$(cygpath -w "${loader_path}")"');
expect(wrapper).toContain('"${node_cmd}" "${npm_cli_arg}" install --prefix "${npm_tool_dir}"');
expect(wrapper).toContain('"${npm_cmd}" install --prefix "${npm_tool_dir}"');
expect(wrapper.match(/--ignore-scripts/g)).toHaveLength(2);
expect(wrapper).toContain('exec "${node_cmd}" --import "${loader_url}"');
expect(wrapper).toContain('exec "${node_cmd}" "${script_path}" "$@"');
expect(wrapper).not.toContain("npm");
expect(wrapper).not.toContain("tsx");
expect(wrapper).not.toContain("--import");
expect(script).toMatch(/^#!\/usr\/bin\/env node$/mu);
expect(script).not.toContain("--import tsx");
expect(packageJson.scripts["test:windows:ci"]).toContain(
"test/scripts/openclaw-cross-os-release-workflow.test.ts",
);
const result = spawnSync(
BASH_BIN,
[
WRAPPER_PATH,
"--resolve-matrix",
"--ref",
"test/native-node",
"--mode",
"fresh",
"--suite-filter",
"windows/packaged-fresh",
"--windows-runner",
"windows-2025",
],
{
cwd: process.cwd(),
encoding: "utf8",
env: {
...process.env,
OPENCLAW_RELEASE_CHECKS_SCRIPT: SCRIPT_PATH,
},
},
);
expect(result.stderr).toBe("");
expect(result.status).toBe(0);
expect(JSON.parse(result.stdout)).toEqual({
include: [
{
os_id: "windows",
display_name: "Windows",
runner: "windows-2025",
artifact_name: "windows",
suite: "packaged-fresh",
suite_label: "packaged fresh",
lane: "fresh",
},
],
});
});
});