diff --git a/scripts/ci-hydrate-testbox-env.sh b/scripts/ci-hydrate-testbox-env.sh index 481f24e6c88..fc546b39020 100755 --- a/scripts/ci-hydrate-testbox-env.sh +++ b/scripts/ci-hydrate-testbox-env.sh @@ -3,30 +3,32 @@ set -euo pipefail profile_path="${1:-$HOME/.openclaw-testbox-live.profile}" helper_path="${2:-$HOME/.local/bin/openclaw-testbox-env}" +quoted_profile_path="$(printf "%q" "$profile_path")" mkdir -p "$(dirname "$helper_path")" bash scripts/ci-hydrate-live-auth.sh "$profile_path" -cat >"$helper_path" <<'SH' +cat >"$helper_path" <&2 +default_profile_path=$quoted_profile_path +profile_path="\${OPENCLAW_TESTBOX_PROFILE_FILE:-\$default_profile_path}" +if [[ ! -f "\$profile_path" ]]; then + echo "Missing Testbox provider env profile: \$profile_path" >&2 exit 1 fi set -a # shellcheck disable=SC1090 -source "$profile_path" +source "\$profile_path" set +a -if [[ "$#" -eq 0 ]]; then - exec "${SHELL:-/bin/bash}" +if [[ "\$#" -eq 0 ]]; then + exec "\${SHELL:-/bin/bash}" fi -exec "$@" +exec "\$@" SH chmod 700 "$helper_path" diff --git a/scripts/test-projects.test-support.mjs b/scripts/test-projects.test-support.mjs index bf3ebc1a99f..381f2395beb 100644 --- a/scripts/test-projects.test-support.mjs +++ b/scripts/test-projects.test-support.mjs @@ -956,6 +956,7 @@ const TOOLING_SOURCE_TEST_TARGETS = new Map([ "scripts/github/resolve-openclaw-ref.sh", ["test/scripts/resolve-openclaw-ref.test.ts"], ], + ["scripts/ci-hydrate-testbox-env.sh", ["test/scripts/ci-hydrate-testbox-env.test.ts"]], [ "scripts/github/run-openclaw-cross-os-release-checks.sh", ["test/scripts/openclaw-cross-os-release-workflow.test.ts"], diff --git a/test/scripts/ci-hydrate-testbox-env.test.ts b/test/scripts/ci-hydrate-testbox-env.test.ts new file mode 100644 index 00000000000..7dc8aa90649 --- /dev/null +++ b/test/scripts/ci-hydrate-testbox-env.test.ts @@ -0,0 +1,56 @@ +// Testbox env hydration tests keep custom helper profiles self-contained. +import { execFileSync } from "node:child_process"; +import { existsSync, readFileSync, statSync } from "node:fs"; +import { join } from "node:path"; +import { afterEach, describe, expect, it } from "vitest"; +import { cleanupTempDirs, makeTempDir } from "../helpers/temp-dir.js"; + +const tempDirs = new Set(); +const SCRIPT = "scripts/ci-hydrate-testbox-env.sh"; + +afterEach(() => { + cleanupTempDirs(tempDirs); +}); + +function runBash(args: string[], env: NodeJS.ProcessEnv = {}): string { + return execFileSync("/bin/bash", ["--noprofile", "--norc", ...args], { + cwd: process.cwd(), + encoding: "utf8", + env: { + ...process.env, + ...env, + }, + stdio: ["ignore", "pipe", "pipe"], + }); +} + +describe("scripts/ci-hydrate-testbox-env.sh", () => { + it("bakes custom profile paths into the generated helper default", () => { + const root = makeTempDir(tempDirs, "openclaw-testbox-env-"); + const home = join(root, "home"); + const profilePath = join(root, "custom profile.env"); + const helperPath = join(root, "bin", "openclaw-testbox-env"); + + runBash([SCRIPT, profilePath, helperPath], { + HOME: home, + OPENAI_API_KEY: "testbox-sentinel-key", + }); + + expect(existsSync(profilePath)).toBe(true); + expect(readFileSync(profilePath, "utf8")).toContain( + "export OPENAI_API_KEY=testbox-sentinel-key", + ); + expect(statSync(helperPath).mode & 0o777).toBe(0o700); + + const helper = readFileSync(helperPath, "utf8"); + expect(helper).toContain("default_profile_path="); + expect(helper).toContain("custom\\ profile.env"); + expect(helper).not.toContain(".openclaw-testbox-live.profile"); + + const output = runBash([helperPath, "env"], { + HOME: home, + OPENCLAW_TESTBOX_PROFILE_FILE: "", + }); + expect(output).toContain("OPENAI_API_KEY=testbox-sentinel-key\n"); + }); +});