improve: validate built CLI with Bun 1.4 (#129192)

* ci: validate built CLI with Bun 1.4

Amp-Thread-ID: https://ampcode.com/threads/T-01a037b7-66db-71f0-91e7-1578b383afb2

* test: align Bun Docker image assertion

Amp-Thread-ID: https://ampcode.com/threads/T-01a037b7-66db-71f0-91e7-1578b383afb2

* test: tolerate cold CLI startup in CI

Amp-Thread-ID: https://ampcode.com/threads/T-01a037b7-66db-71f0-91e7-1578b383afb2

---------

Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
Peter Steinberger
2026-08-25 03:44:57 -07:00
committed by GitHub
parent 97cd06b946
commit 5af05ab1a2
14 changed files with 43 additions and 17 deletions
+1 -1
View File
@@ -321,7 +321,7 @@ runs:
shell: bash
run: |
set -euo pipefail
npm install -g bun@1.3.14
npm install -g bun@1.4.0
- name: Runtime versions
shell: bash
+6 -1
View File
@@ -1346,7 +1346,7 @@ jobs:
uses: ./.ci-harness/.github/actions/setup-node-env
with:
cache-mode: ${{ needs.preflight.outputs.cache_mode }}
install-bun: "false"
install-bun: "true"
node-compile-cache: "true"
node-compile-cache-scope: "build"
# Same-repo Blacksmith runs restore the dependency cache published by
@@ -1428,6 +1428,11 @@ jobs:
- name: Smoke test CLI launcher status json
run: node openclaw.mjs status --json --timeout 1
- name: Smoke test built CLI with Bun
run: |
bun openclaw.mjs --help
bun openclaw.mjs status --json --timeout 1
# Doctor index proof, singleton smoke, and startup-memory check are
# independent dist readers; Blacksmith runners have the cores to overlap
# them, while hosted 4-core runners keep the serial order so the RSS
+1 -1
View File
@@ -1449,7 +1449,7 @@ jobs:
--source-archive-sha256 "$SOURCE_ARCHIVE_SHA256"
- name: Install Bun for global smoke
run: npm install -g bun@1.3.14
run: npm install -g bun@1.4.0
- name: Run Bun global install candidate-payload smoke
working-directory: .release-harness
+1 -1
View File
@@ -18,7 +18,7 @@ ARG OPENCLAW_NODE_BOOKWORM_SLIM_IMAGE="docker.io/library/node:24-bookworm-slim@s
ARG OPENCLAW_NODE_BOOKWORM_SLIM_DIGEST="sha256:3638d9a6fe4030bd716be989438248074489337ba3275657f93595428be4fc03"
# Keep in sync with .github/actions/setup-node-env/action.yml bun-version.
# To update: docker buildx imagetools inspect docker.io/oven/bun:<version> and use the manifest-list digest.
ARG OPENCLAW_BUN_IMAGE="docker.io/oven/bun:1.3.14@sha256:e10577f0db68676a7024391c6e5cb4b879ebd17188ab750cf10024a6d700e5c4"
ARG OPENCLAW_BUN_IMAGE="docker.io/oven/bun:1.4.0@sha256:5ff609364c049b54eb0ff560ec96319729a972078ef2c755d758f0c6ef89c2d6"
# Base images are pinned to SHA256 digests for reproducible builds.
# Dependabot refreshes these blessed digests; release builds consume the
+1 -1
View File
@@ -275,7 +275,7 @@ const jsRuntimeEntrypoints = new Set([
]);
const awsMacosCorepackEntrypoints = new Set(["pnpm", "yarn", "corepack"]);
const awsMacosBunEntrypoints = new Set(["bun", "bunx"]);
const awsMacosBunVersion = "1.3.14";
const awsMacosBunVersion = "1.4.0";
const awsMacosSwiftEntrypoints = new Set(["swift", "xcodebuild"]);
const awsMacosSwiftScriptTargets = new Set([
"mac:package",
+2 -2
View File
@@ -147,8 +147,8 @@ main() {
export NO_COLOR=1
mkdir -p "$HOME" "$BUN_INSTALL/bin" "$BUN_INSTALL/install/global" "$XDG_CACHE_HOME"
export PATH="$BUN_INSTALL/bin:$(dirname "$(command -v node)"):$PATH"
# Release publishes @openclaw/ai first. Bun 1.3.14 ignores bundled deps in
# local tarballs, so resolve that one package from the exact candidate bytes.
# Release publishes @openclaw/ai first. Pin the local tarball install to
# exact candidate bytes instead of allowing public-registry resolution.
node --input-type=module - \
"$BUN_INSTALL/install/global/package.json" \
"$AI_PACKAGE_TGZ" <<'NODE'
+1 -1
View File
@@ -86,7 +86,7 @@ DOCKER_COMMAND_TIMEOUT="$DOCKER_RUN_TIMEOUT" docker_e2e_docker_run_cmd run -d \
"$IMAGE_NAME" \
bash -lc '
set -euo pipefail
npm install -g --prefix /tmp/bun-runtime bun@1.3.14 --no-fund --no-audit
npm install -g --prefix /tmp/bun-runtime bun@1.4.0 --no-fund --no-audit
cd /repo
BUN_BIN=/tmp/bun-runtime/bin/bun \
OPENCLAW_BUN_GLOBAL_SMOKE_HOST_BUILD=0 \
+6 -2
View File
@@ -148,6 +148,7 @@ async function runIsolatedGatewayCli(params: {
stateDir: string;
configPath: string;
env?: NodeJS.ProcessEnv;
timeoutMs?: number;
}): Promise<{
code: number | null;
signal: NodeJS.Signals | null;
@@ -185,7 +186,7 @@ async function runIsolatedGatewayCli(params: {
},
encoding: "utf8",
killSignal: "SIGKILL",
timeout: 20_000,
timeout: params.timeoutMs ?? 20_000,
},
);
return { code: 0, signal: null, stdout: result.stdout, stderr: result.stderr };
@@ -235,6 +236,9 @@ describe("gateway-backed CLI process exit", () => {
root,
stateDir,
configPath,
// The first source child cold-loads the CLI graph and can contend with
// neighboring CI shards before it reaches the Gateway turn.
timeoutMs: 45_000,
});
expect(result, result.stderr).toMatchObject({ code: exitCode, signal: null, stderr: "" });
@@ -244,7 +248,7 @@ describe("gateway-backed CLI process exit", () => {
result: { payloads: [{ text }] },
});
},
30_000,
60_000,
);
it.each([
+1 -1
View File
@@ -85,7 +85,7 @@ describe("Dockerfile", () => {
'ARG OPENCLAW_NODE_BOOKWORM_SLIM_IMAGE="docker.io/library/node:24-bookworm-slim@sha256:3638d9a6fe4030bd716be989438248074489337ba3275657f93595428be4fc03"',
);
expect(dockerfile).toContain(
'ARG OPENCLAW_BUN_IMAGE="docker.io/oven/bun:1.3.14@sha256:e10577f0db68676a7024391c6e5cb4b879ebd17188ab750cf10024a6d700e5c4"',
'ARG OPENCLAW_BUN_IMAGE="docker.io/oven/bun:1.4.0@sha256:5ff609364c049b54eb0ff560ec96319729a972078ef2c755d758f0c6ef89c2d6"',
);
expect(dockerfile).toContain("FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS workspace-deps");
expect(dockerfile).toContain("FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS build");
+18 -1
View File
@@ -7405,13 +7405,26 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}"
expect(realGatewayRunContract).not.toContain("--testTimeout");
});
it("does not rebuild Control UI after build:ci-artifacts", () => {
it("builds artifacts once and smoke-tests the built CLI with Node and Bun", () => {
const workflow = readCiWorkflow();
const buildArtifactSteps = workflow.jobs["build-artifacts"].steps;
const setupStep = buildArtifactSteps.find(
(step: WorkflowStep) => step.name === "Setup Node environment",
);
const buildDistStep = buildArtifactSteps.find(
(step: WorkflowStep) => step.name === "Build dist",
);
const nodeHelpSmoke = buildArtifactSteps.find(
(step: WorkflowStep) => step.name === "Smoke test CLI launcher help",
);
const nodeStatusSmoke = buildArtifactSteps.find(
(step: WorkflowStep) => step.name === "Smoke test CLI launcher status json",
);
const bunSmoke = buildArtifactSteps.find(
(step: WorkflowStep) => step.name === "Smoke test built CLI with Bun",
);
expect(setupStep.with["install-bun"]).toBe("true");
expect(buildDistStep.run).toBe("pnpm build:ci-artifacts");
expect(buildArtifactSteps.map((step: WorkflowStep) => step.name)).not.toContain(
"Build Control UI",
@@ -7419,6 +7432,10 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}"
expect(buildArtifactSteps.some((step: WorkflowStep) => step.run === "pnpm ui:build")).toBe(
false,
);
expect(nodeHelpSmoke.run).toBe("node openclaw.mjs --help");
expect(nodeStatusSmoke.run).toBe("node openclaw.mjs status --json --timeout 1");
expect(bunSmoke.run).toContain("bun openclaw.mjs --help");
expect(bunSmoke.run).toContain("bun openclaw.mjs status --json --timeout 1");
});
it("keeps source-only Control UI locale drift advisory", () => {
+2 -2
View File
@@ -2575,7 +2575,7 @@ describe("scripts/crabbox-wrapper", () => {
expect(output.args).toContain("--shell");
expect(result.stderr).toContain("Node/Corepack/pnpm/Bun");
expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js");
expect(remoteCommand).toContain("bun_version=1.3.14");
expect(remoteCommand).toContain("bun_version=1.4.0");
expect(remoteCommand).toContain('bun_root="$tool_root/bun-v${bun_version}"');
expect(remoteCommand).toContain(
'npm install --global --prefix "$bun_root" --fetch-timeout=120000 --fetch-retries=2 --fetch-retry-mintimeout=2000 --fetch-retry-maxtimeout=15000 "bun@${bun_version}"',
@@ -2871,7 +2871,7 @@ describe("scripts/crabbox-wrapper", () => {
it("bootstraps Bun for AWS macOS script-stdin bun shebangs", () => {
const script = ["#!/usr/bin/env bun", "console.log(Bun.version);"].join("\n");
const { output } = runSuccessfulMacosScript(script);
expect(output.scriptContent).toContain("bun_version=1.3.14");
expect(output.scriptContent).toContain("bun_version=1.4.0");
expect(output.scriptContent).toContain(
'npm install --global --prefix "$bun_root" --fetch-timeout=120000 --fetch-retries=2 --fetch-retry-mintimeout=2000 --fetch-retry-maxtimeout=15000 "bun@${bun_version}"',
);
+1 -1
View File
@@ -4520,7 +4520,7 @@ source "$ROOT_DIR/scripts/lib/docker-e2e-logs.sh"
"npm install -g --prefix /tmp/openclaw-proof",
"corepack prepare pnpm@11.22.0 --activate",
"pnpm add --global --allow-build=openclaw",
"bun@1.3.14",
"bun@1.4.0",
'test "$(command -v openclaw)" = "/tmp/openclaw-proof/bin/openclaw"',
'test "$(command -v openclaw)" = "$PNPM_HOME/openclaw"',
"OPENCLAW_BUN_GLOBAL_SMOKE_PROOF_PATH",
@@ -543,7 +543,7 @@ describe("install smoke no-push root image transport", () => {
expect(bunVerify.run).toContain("install-smoke-candidate-payload.mts verify");
expect(bunVerify.run).toContain('--run-id "$PRODUCER_RUN_ID"');
expect(bunVerify.run).toContain('--run-attempt "$PRODUCER_RUN_ATTEMPT"');
expect(step(bunConsumer, "Install Bun for global smoke").run).toBe("npm install -g bun@1.3.14");
expect(step(bunConsumer, "Install Bun for global smoke").run).toBe("npm install -g bun@1.4.0");
expect(step(bunConsumer, "Run Bun global install candidate-payload smoke")).toMatchObject({
"working-directory": ".release-harness",
env: {
+1 -1
View File
@@ -2171,7 +2171,7 @@ chmod +x "$BUN_INSTALL/bin/openclaw"
expect(workflow).toContain("bun_global_install_smoke:");
expect(workflow).toContain("Setup trusted release harness for Bun smoke");
expect(workflow).toContain("uses: ./.release-harness/.github/actions/setup-release-harness");
expect(workflow).toContain("npm install -g bun@1.3.14");
expect(workflow).toContain("npm install -g bun@1.4.0");
expect(workflow).toContain('install-bun: "false"');
expect(workflow).toContain("Run Bun global install candidate-payload smoke");
expect(workflow).toContain("working-directory: .release-harness");