ci: run QA smoke profile in CI (#94291)

* ci: add qa lab smoke profile dispatch

* ci: prove qa lab smoke profile on prs

* ci: preserve manual qa lab profile dispatch

* ci: run qa lab smoke profile on pull requests

* ci: keep QA smoke mock lane bounded

* ci: run QA smoke PR proof through crabline

* ci: keep mock QA timeouts on caller fallbacks

* ci: prebuild QA smoke runtime

* ci: delegate smoke QA evidence workflow

* ci: trust release branch smoke evidence refs

* ci: trim smoke evidence workflow comments

* ci: align smoke evidence wrapper with QA profile contract

* ci: keep smoke profile evidence mock-only

* ci: make smoke profile evidence manual

* ci: shard qa smoke profile in ci

* ci: drop qa-channel-only smoke shard

* ci: derive qa smoke shards from taxonomy

* ci: keep qa smoke planner legacy-safe

* ci: enforce qa smoke shard failures

* ci: run qa smoke in existing fast shard

* ci: opt qa smoke into crabline concurrency

* test(ci): align qa smoke guard with taxonomy cleanup

* ci: split qa smoke into dedicated check

---------

Co-authored-by: Dallin Romney <dallinromney@gmail.com>
This commit is contained in:
Colin Johnson
2026-06-24 12:47:45 -04:00
committed by GitHub
parent c5f10b5f7c
commit 4ae0a5d958
3 changed files with 73 additions and 2 deletions
+30 -1
View File
@@ -251,7 +251,6 @@ jobs:
],
};
});
const createMatrix = (include) => ({ include });
const outputPath = process.env.GITHUB_OUTPUT;
const isCanonicalRepository = process.env.OPENCLAW_CI_REPOSITORY === "openclaw/openclaw";
@@ -285,6 +284,7 @@ jobs:
if (runNodeFull) {
checksFastCoreTasks.push(
{ check_name: "checks-fast-bundled-protocol", runtime: "node", task: "bundled-protocol" },
{ check_name: "QA Smoke CI", runtime: "node", task: "qa-smoke-ci" },
{ check_name: "checks-fast-bun-launcher", runtime: "bun", task: "bun-launcher" },
);
} else {
@@ -922,6 +922,26 @@ jobs:
pnpm test:bundled
pnpm protocol:check
;;
qa-smoke-ci)
output_dir=".artifacts/qa-e2e/smoke-ci-profile"
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
qa_exit_code=0
pnpm openclaw qa run \
--repo-root . \
--qa-profile smoke-ci \
--concurrency 8 \
--output-dir "$output_dir" || qa_exit_code=$?
echo "QA smoke profile evidence: \`${output_dir}\`" >> "$GITHUB_STEP_SUMMARY"
if [ "$qa_exit_code" -ne 0 ]; then
echo "::error title=QA smoke profile failed::smoke-ci exited ${qa_exit_code}; evidence upload will still run"
exit "$qa_exit_code"
fi
;;
contracts-plugins-ci-routing)
pnpm test:contracts:plugins
pnpm test src/commands/status.scan-result.test.ts src/scripts/ci-changed-scope.test.ts test/scripts/changed-lanes.test.ts test/scripts/ci-workflow-guards.test.ts test/scripts/run-vitest.test.ts test/scripts/test-projects.test.ts
@@ -938,6 +958,15 @@ jobs:
;;
esac
- name: Upload QA smoke profile evidence
if: always() && matrix.task == 'qa-smoke-ci'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: qa-smoke-profile-${{ github.run_id }}-${{ github.run_attempt }}
path: .artifacts/qa-e2e/smoke-ci-profile/
if-no-files-found: warn
retention-days: 7
checks-fast-plugin-contracts-shard:
permissions:
contents: read
+1 -1
View File
@@ -30,7 +30,7 @@ or an explicit manual dispatch.
| `security-fast` | Private key detection, changed-workflow audit via `zizmor`, and production lockfile audit | Always on non-draft pushes and PRs |
| `check-dependencies` | Production Knip dependency-only pass plus the unused-file allowlist guard | Node-relevant changes |
| `build-artifacts` | Build `dist/`, Control UI, built-CLI smoke checks, embedded built-artifact checks, and reusable artifacts | Node-relevant changes |
| `checks-fast-core` | Fast Linux correctness lanes such as bundled, protocol, and CI-routing checks | Node-relevant changes |
| `checks-fast-core` | Fast Linux correctness lanes such as bundled, protocol, QA Smoke CI, and CI-routing checks | Node-relevant changes |
| `checks-fast-contracts-plugins-*` | Two sharded plugin contract checks | Node-relevant changes |
| `checks-fast-contracts-channels-*` | Two sharded channel contract checks | Node-relevant changes |
| `checks-node-core-*` | Core Node test shards, excluding channel, bundled, contract, and extension lanes | Node-relevant changes |
+42
View File
@@ -795,13 +795,55 @@ describe("ci workflow guards", () => {
it("keeps workflow guards in fast CI-routing checks", () => {
const workflow = readCiWorkflow();
const preflightStep = workflow.jobs.preflight.steps.find(
(step) => step.name === "Build CI manifest",
);
const taxonomy = parse(readFileSync("taxonomy.yaml", "utf8")) as {
profiles: Array<{ id: string; categoryIds: string[] }>;
};
const smokeProfile = taxonomy.profiles.find((profile) => profile.id === "smoke-ci");
if (!smokeProfile) {
throw new Error("taxonomy.yaml is missing the smoke-ci profile");
}
const fastCoreJob = workflow.jobs["checks-fast-core"];
const runStep = fastCoreJob.steps.find(
(step) => step.name === "Run ${{ matrix.task }} (${{ matrix.runtime }})",
);
const uploadStep = fastCoreJob.steps.find(
(step) => step.name === "Upload QA smoke profile evidence",
);
const ciWorkflowText = readFileSync(".github/workflows/ci.yml", "utf8");
expect(preflightStep.run).not.toContain("qa-smoke-profile");
expect(preflightStep.run).not.toContain("qa_category");
expect(smokeProfile.categoryIds).toHaveLength(30);
for (const categoryId of smokeProfile.categoryIds) {
expect(ciWorkflowText).not.toContain(`"${categoryId}"`);
}
expect(runStep.run).toContain("bundled-protocol)");
expect(runStep.run).toContain("qa-smoke-ci)");
expect(runStep.run).toContain("contracts-plugins-ci-routing)");
expect(runStep.run).toContain("ci-routing)");
expect(ciWorkflowText).toContain(
'{ check_name: "QA Smoke CI", runtime: "node", task: "qa-smoke-ci" }',
);
expect(runStep.run).toContain("--qa-profile smoke-ci");
expect(runStep.run).toContain("--concurrency 8");
expect(runStep.run).not.toContain("--category");
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).not.toContain("OPENAI_API_KEY");
expect(runStep.run).toMatch(
/bundled-protocol\)\s+pnpm test:bundled\s+pnpm protocol:check\s+;;\s+qa-smoke-ci\)/,
);
expect(uploadStep.if).toBe("always() && matrix.task == 'qa-smoke-ci'");
expect(uploadStep.with).toMatchObject({
path: ".artifacts/qa-e2e/smoke-ci-profile/",
"if-no-files-found": "warn",
});
expect(runStep.run.match(/test\/scripts\/ci-workflow-guards\.test\.ts/g)?.length).toBe(2);
});