Skip to content

Commit 4ae0a5d

Browse files
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 <[email protected]>
1 parent c5f10b5 commit 4ae0a5d

3 files changed

Lines changed: 73 additions & 2 deletions

File tree

.github/workflows/ci.yml

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,6 @@ jobs:
251251
],
252252
};
253253
});
254-
255254
const createMatrix = (include) => ({ include });
256255
const outputPath = process.env.GITHUB_OUTPUT;
257256
const isCanonicalRepository = process.env.OPENCLAW_CI_REPOSITORY === "openclaw/openclaw";
@@ -285,6 +284,7 @@ jobs:
285284
if (runNodeFull) {
286285
checksFastCoreTasks.push(
287286
{ check_name: "checks-fast-bundled-protocol", runtime: "node", task: "bundled-protocol" },
287+
{ check_name: "QA Smoke CI", runtime: "node", task: "qa-smoke-ci" },
288288
{ check_name: "checks-fast-bun-launcher", runtime: "bun", task: "bun-launcher" },
289289
);
290290
} else {
@@ -922,6 +922,26 @@ jobs:
922922
pnpm test:bundled
923923
pnpm protocol:check
924924
;;
925+
qa-smoke-ci)
926+
output_dir=".artifacts/qa-e2e/smoke-ci-profile"
927+
export OPENCLAW_BUILD_PRIVATE_QA=1
928+
export OPENCLAW_ENABLE_PRIVATE_QA_CLI=1
929+
export OPENCLAW_DISABLE_BUNDLED_PLUGINS=0
930+
export OPENCLAW_QA_REDACT_PUBLIC_METADATA=1
931+
export OPENCLAW_QA_TRANSPORT_READY_TIMEOUT_MS=180000
932+
NODE_OPTIONS=--max-old-space-size=8192 node scripts/build-all.mjs qaRuntime
933+
qa_exit_code=0
934+
pnpm openclaw qa run \
935+
--repo-root . \
936+
--qa-profile smoke-ci \
937+
--concurrency 8 \
938+
--output-dir "$output_dir" || qa_exit_code=$?
939+
echo "QA smoke profile evidence: \`${output_dir}\`" >> "$GITHUB_STEP_SUMMARY"
940+
if [ "$qa_exit_code" -ne 0 ]; then
941+
echo "::error title=QA smoke profile failed::smoke-ci exited ${qa_exit_code}; evidence upload will still run"
942+
exit "$qa_exit_code"
943+
fi
944+
;;
925945
contracts-plugins-ci-routing)
926946
pnpm test:contracts:plugins
927947
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:
938958
;;
939959
esac
940960
961+
- name: Upload QA smoke profile evidence
962+
if: always() && matrix.task == 'qa-smoke-ci'
963+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
964+
with:
965+
name: qa-smoke-profile-${{ github.run_id }}-${{ github.run_attempt }}
966+
path: .artifacts/qa-e2e/smoke-ci-profile/
967+
if-no-files-found: warn
968+
retention-days: 7
969+
941970
checks-fast-plugin-contracts-shard:
942971
permissions:
943972
contents: read

docs/ci.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ or an explicit manual dispatch.
3030
| `security-fast` | Private key detection, changed-workflow audit via `zizmor`, and production lockfile audit | Always on non-draft pushes and PRs |
3131
| `check-dependencies` | Production Knip dependency-only pass plus the unused-file allowlist guard | Node-relevant changes |
3232
| `build-artifacts` | Build `dist/`, Control UI, built-CLI smoke checks, embedded built-artifact checks, and reusable artifacts | Node-relevant changes |
33-
| `checks-fast-core` | Fast Linux correctness lanes such as bundled, protocol, and CI-routing checks | Node-relevant changes |
33+
| `checks-fast-core` | Fast Linux correctness lanes such as bundled, protocol, QA Smoke CI, and CI-routing checks | Node-relevant changes |
3434
| `checks-fast-contracts-plugins-*` | Two sharded plugin contract checks | Node-relevant changes |
3535
| `checks-fast-contracts-channels-*` | Two sharded channel contract checks | Node-relevant changes |
3636
| `checks-node-core-*` | Core Node test shards, excluding channel, bundled, contract, and extension lanes | Node-relevant changes |

test/scripts/ci-workflow-guards.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,13 +795,55 @@ describe("ci workflow guards", () => {
795795

796796
it("keeps workflow guards in fast CI-routing checks", () => {
797797
const workflow = readCiWorkflow();
798+
const preflightStep = workflow.jobs.preflight.steps.find(
799+
(step) => step.name === "Build CI manifest",
800+
);
801+
const taxonomy = parse(readFileSync("taxonomy.yaml", "utf8")) as {
802+
profiles: Array<{ id: string; categoryIds: string[] }>;
803+
};
804+
const smokeProfile = taxonomy.profiles.find((profile) => profile.id === "smoke-ci");
805+
if (!smokeProfile) {
806+
throw new Error("taxonomy.yaml is missing the smoke-ci profile");
807+
}
798808
const fastCoreJob = workflow.jobs["checks-fast-core"];
799809
const runStep = fastCoreJob.steps.find(
800810
(step) => step.name === "Run ${{ matrix.task }} (${{ matrix.runtime }})",
801811
);
812+
const uploadStep = fastCoreJob.steps.find(
813+
(step) => step.name === "Upload QA smoke profile evidence",
814+
);
815+
816+
const ciWorkflowText = readFileSync(".github/workflows/ci.yml", "utf8");
802817

818+
expect(preflightStep.run).not.toContain("qa-smoke-profile");
819+
expect(preflightStep.run).not.toContain("qa_category");
820+
expect(smokeProfile.categoryIds).toHaveLength(30);
821+
for (const categoryId of smokeProfile.categoryIds) {
822+
expect(ciWorkflowText).not.toContain(`"${categoryId}"`);
823+
}
824+
expect(runStep.run).toContain("bundled-protocol)");
825+
expect(runStep.run).toContain("qa-smoke-ci)");
803826
expect(runStep.run).toContain("contracts-plugins-ci-routing)");
804827
expect(runStep.run).toContain("ci-routing)");
828+
expect(ciWorkflowText).toContain(
829+
'{ check_name: "QA Smoke CI", runtime: "node", task: "qa-smoke-ci" }',
830+
);
831+
expect(runStep.run).toContain("--qa-profile smoke-ci");
832+
expect(runStep.run).toContain("--concurrency 8");
833+
expect(runStep.run).not.toContain("--category");
834+
expect(runStep.run).not.toContain("--allow-failures");
835+
expect(runStep.run).toContain("qa_exit_code=0");
836+
expect(runStep.run).toContain('exit "$qa_exit_code"');
837+
expect(runStep.run).toContain("scripts/build-all.mjs qaRuntime");
838+
expect(runStep.run).not.toContain("OPENAI_API_KEY");
839+
expect(runStep.run).toMatch(
840+
/bundled-protocol\)\s+pnpm test:bundled\s+pnpm protocol:check\s+;;\s+qa-smoke-ci\)/,
841+
);
842+
expect(uploadStep.if).toBe("always() && matrix.task == 'qa-smoke-ci'");
843+
expect(uploadStep.with).toMatchObject({
844+
path: ".artifacts/qa-e2e/smoke-ci-profile/",
845+
"if-no-files-found": "warn",
846+
});
805847
expect(runStep.run.match(/test\/scripts\/ci-workflow-guards\.test\.ts/g)?.length).toBe(2);
806848
});
807849

0 commit comments

Comments
 (0)