diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ae1f6fb798db..9efa38cc0278 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -872,12 +872,17 @@ jobs: ;; qa-smoke-ci) output_dir=".artifacts/qa-e2e/smoke-ci-profile" + package_dir=".artifacts/qa-e2e/smoke-ci-package" export OPENCLAW_BUILD_PRIVATE_QA=1 export OPENCLAW_ENABLE_PRIVATE_QA_CLI=1 export OPENCLAW_DISABLE_BUNDLED_PLUGINS=0 export OPENCLAW_QA_REDACT_PUBLIC_METADATA=1 export OPENCLAW_QA_TRANSPORT_READY_TIMEOUT_MS=180000 - NODE_OPTIONS=--max-old-space-size=8192 node scripts/build-all.mjs qaRuntime + export NODE_OPTIONS=--max-old-space-size=12288 + node scripts/package-openclaw-for-docker.mjs \ + --output-dir "$package_dir" \ + --output-name openclaw-current.tgz + export OPENCLAW_CURRENT_PACKAGE_TGZ="$PWD/$package_dir/openclaw-current.tgz" qa_exit_code=0 pnpm openclaw qa run \ --repo-root . \ diff --git a/test/e2e/qa-lab/runtime/qa-otel-smoke-runtime.ts b/test/e2e/qa-lab/runtime/qa-otel-smoke-runtime.ts index 6e54f03b2282..4a643a7154dc 100644 --- a/test/e2e/qa-lab/runtime/qa-otel-smoke-runtime.ts +++ b/test/e2e/qa-lab/runtime/qa-otel-smoke-runtime.ts @@ -1505,7 +1505,15 @@ function buildQaEnv(port: number): NodeJS.ProcessEnv { return env; } -function buildQaArgs(options: CliOptions): string[] { +function resolveQaOutputDir(outputDir: string, repoRoot = process.cwd()): string { + const relative = path.relative(repoRoot, path.resolve(repoRoot, outputDir)); + if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) { + throw new Error("--output-dir must stay within the repo root"); + } + return relative ? relative.split(path.sep).join("/") : "."; +} + +function buildQaArgs(options: CliOptions, repoRoot = process.cwd()): string[] { const args = [ "qa", "suite", @@ -1516,7 +1524,7 @@ function buildQaArgs(options: CliOptions): string[] { "--concurrency", "1", "--output-dir", - options.outputDir, + resolveQaOutputDir(options.outputDir, repoRoot), "--fast", ]; if (options.primaryModel) { @@ -2018,6 +2026,7 @@ export const testing = { appendGatewayStdoutArtifactLogs, appendCapturedBodyText, assertSmoke, + buildQaArgs, buildQaEnv, createBoundedTextAccumulator, createStdoutDiagnosticLogCapture, diff --git a/test/e2e/qa-lab/runtime/qa-otel-smoke.e2e.test.ts b/test/e2e/qa-lab/runtime/qa-otel-smoke.e2e.test.ts index 69f9d068d821..4d9f54848787 100644 --- a/test/e2e/qa-lab/runtime/qa-otel-smoke.e2e.test.ts +++ b/test/e2e/qa-lab/runtime/qa-otel-smoke.e2e.test.ts @@ -200,6 +200,27 @@ describe("qa-otel-smoke receiver bounds", () => { ); }); + it("passes a repo-relative output dir to the child QA suite", () => { + const repoRoot = path.join(path.sep, "repo"); + const options = testing.parseArgs([ + "--output-dir", + path.join(repoRoot, ".artifacts", "qa-e2e", "otel-smoke"), + ]); + + expect(testing.buildQaArgs(options, repoRoot)).toContain(".artifacts/qa-e2e/otel-smoke"); + const repoRootArgs = testing.buildQaArgs( + testing.parseArgs(["--output-dir", repoRoot]), + repoRoot, + ); + expect(repoRootArgs[repoRootArgs.indexOf("--output-dir") + 1]).toBe("."); + expect(() => + testing.buildQaArgs( + testing.parseArgs(["--output-dir", path.join(path.sep, "outside", "otel-smoke")]), + repoRoot, + ), + ).toThrow("--output-dir must stay within the repo root"); + }); + it("parses body-size limit env values as strict positive integers", () => { expect(testing.readPositiveIntegerEnv("OTEL_TEST_LIMIT", 64, {})).toBe(64); expect( diff --git a/test/scripts/ci-workflow-guards.test.ts b/test/scripts/ci-workflow-guards.test.ts index 84ccd48c76e5..3677186a2b89 100644 --- a/test/scripts/ci-workflow-guards.test.ts +++ b/test/scripts/ci-workflow-guards.test.ts @@ -916,7 +916,10 @@ describe("ci workflow guards", () => { expect(runStep.run).not.toContain("--allow-failures"); expect(runStep.run).toContain("qa_exit_code=0"); expect(runStep.run).toContain('exit "$qa_exit_code"'); - expect(runStep.run).toContain("scripts/build-all.mjs qaRuntime"); + expect(runStep.run).toContain("scripts/package-openclaw-for-docker.mjs"); + expect(runStep.run).toContain("OPENCLAW_CURRENT_PACKAGE_TGZ"); + expect(runStep.run).toContain("--max-old-space-size=12288"); + expect(runStep.run).not.toContain("scripts/build-all.mjs qaRuntime"); expect(runStep.run).not.toContain("OPENAI_API_KEY"); expect(runStep.run).toMatch( /bundled-protocol\)\s+pnpm test:bundled\s+pnpm protocol:check\s+;;\s+qa-smoke-ci\)/,