chore: scope SQLite session lifecycle CI (#121917)

* oc-358: scope SQLite lifecycle CI

* chore(ci): cover shared session lifecycle owner
This commit is contained in:
Josh Lehman
2026-08-11 15:06:46 -07:00
committed by GitHub
parent 4ccbdf58bf
commit d035dba6c7
7 changed files with 230 additions and 33 deletions
+50 -13
View File
@@ -93,6 +93,7 @@ jobs:
frozen_target: ${{ steps.manifest.outputs.frozen_target }}
run_qa_smoke_ci: ${{ steps.manifest.outputs.run_qa_smoke_ci }}
run_prompt_snapshots: ${{ steps.manifest.outputs.run_prompt_snapshots }}
run_sqlite_session_lifecycle: ${{ steps.manifest.outputs.run_sqlite_session_lifecycle }}
checks_fast_core_matrix: ${{ steps.manifest.outputs.checks_fast_core_matrix }}
run_plugin_contracts_shards: ${{ steps.manifest.outputs.run_plugin_contracts_shards }}
plugin_contracts_matrix: ${{ steps.manifest.outputs.plugin_contracts_matrix }}
@@ -681,7 +682,20 @@ jobs:
eventName !== "pull_request" ||
typeof changedNodeTestPlan.hasPromptSnapshotAffectingChange !== "function" ||
changedNodeTestPlan.hasPromptSnapshotAffectingChange(changedPaths);
const runBuildArtifacts = runNodeFull && changedScopeHasBuildImpact;
const supportsSqliteSessionLifecycleProof = existsSync(
"test/scripts/sqlite-sessions-transcripts-flip-proof.built-cli.e2e.test.ts",
);
const changedScopeHasSqliteSessionLifecycleImpact =
changedPaths === null ||
eventName === "workflow_dispatch" ||
typeof changedNodeTestPlan.hasSqliteSessionLifecycleAffectingChange !== "function" ||
changedNodeTestPlan.hasSqliteSessionLifecycleAffectingChange(changedPaths);
const runSqliteSessionLifecycle =
runNodeFull &&
supportsSqliteSessionLifecycleProof &&
changedScopeHasSqliteSessionLifecycleImpact;
const runBuildArtifacts =
runNodeFull && (changedScopeHasBuildImpact || runSqliteSessionLifecycle);
const runQaSmokeCi =
runNodeFull &&
changedScopeHasQaImpact &&
@@ -752,6 +766,7 @@ jobs:
compatibility_target: compatibilityTarget,
run_qa_smoke_ci: runQaSmokeCi,
run_prompt_snapshots: runNodeFull && changedScopeHasPromptSnapshotImpact,
run_sqlite_session_lifecycle: runSqliteSessionLifecycle,
checks_fast_core_matrix: createMatrix(checksFastCoreTasks),
run_plugin_contracts_shards: runPluginContractShards,
plugin_contracts_matrix: createMatrix(
@@ -1437,6 +1452,38 @@ jobs:
path: .local/gateway-watch-regression/
retention-days: 7
sqlite-session-lifecycle:
permissions:
contents: read
name: check-sqlite-session-lifecycle
needs: [preflight, build-artifacts]
if: ${{ !cancelled() && always() && needs.preflight.outputs.run_sqlite_session_lifecycle == 'true' && needs.build-artifacts.result == 'success' }}
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'blacksmith-8vcpu-ubuntu-2404' || 'ubuntu-24.04') }}
timeout-minutes: 20
steps:
- *linux_node_checkout_step
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
sticky-disk: ${{ github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false' }}
use-actions-cache: ${{ github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'false' || 'true' }}
- name: Download exact-run built runtime
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: dist-runtime-build
path: .artifacts/dist-runtime-build
- name: Extract built runtime
run: tar --extract --file .artifacts/dist-runtime-build/dist-runtime-build.tar.zst --use-compress-program unzstd
- name: Verify SQLite session lifecycle
env:
OPENCLAW_E2E_USE_PREBUILT_DIST: "1"
OPENCLAW_VITEST_NO_OUTPUT_TIMEOUT_MS: "660000"
run: node scripts/run-vitest.mjs run --config test/vitest/vitest.e2e.config.ts test/scripts/sqlite-sessions-transcripts-flip-proof.built-cli.e2e.test.ts
native-i18n:
permissions:
contents: read
@@ -2545,9 +2592,6 @@ jobs:
- check_name: check-plugin-sdk-api-baseline
group: plugin-sdk-api-baseline
runner: blacksmith-4vcpu-ubuntu-2404
- check_name: check-sqlite-session-flip-proof
group: sqlite-session-flip-proof
runner: blacksmith-8vcpu-ubuntu-2404
- check_name: check-additional-extension-package-boundary
group: extension-package-boundary
# Light-run critical-path pole: cold runs spend ~100s in nine
@@ -2774,15 +2818,6 @@ jobs:
plugin-sdk-api-baseline)
run_check "plugin-sdk:api:check" pnpm run plugin-sdk:api:check
;;
sqlite-session-flip-proof)
if [ ! -f test/scripts/sqlite-sessions-transcripts-flip-proof.e2e.test.ts ]; then
echo "[skip] SQLite sessions/transcripts flip proof is not present in this checkout"
else
# This lane owns a 420-second E2E case plus cold hosted-runner setup;
# keep the global watchdog strict and scope compatibility headroom here.
run_check "sqlite sessions/transcripts flip proof" env OPENCLAW_VITEST_NO_OUTPUT_TIMEOUT_MS=660000 node scripts/run-vitest.mjs run --config test/vitest/vitest.e2e.config.ts test/scripts/sqlite-sessions-transcripts-flip-proof.e2e.test.ts
fi
;;
extension-package-boundary)
run_check "test:extensions:package-boundary:compile" pnpm run test:extensions:package-boundary:compile
run_check "test:extensions:package-boundary:canary" pnpm run test:extensions:package-boundary:canary
@@ -3751,6 +3786,7 @@ jobs:
- security-fast
- pnpm-store-warmup
- build-artifacts
- sqlite-session-lifecycle
- native-i18n
- checks-ui
- checks-ui-e2e
@@ -3784,6 +3820,7 @@ jobs:
SELECTED_RESULTS: |
pnpm-store-warmup=${{ needs.pnpm-store-warmup.result }}
build-artifacts=${{ needs.build-artifacts.result }}
sqlite-session-lifecycle=${{ needs.sqlite-session-lifecycle.result }}
native-i18n=${{ needs.native-i18n.result }}
checks-ui=${{ needs.checks-ui.result }}
checks-ui-e2e=${{ needs.checks-ui-e2e.result }}
+10 -9
View File
@@ -102,6 +102,7 @@ Separate iOS and macOS Periphery workflows enforce a zero-findings dead-code pol
- **Workflow Sanity** runs `actionlint`, `zizmor` over all workflow YAML files, the composite-action interpolation guard, and the conflict-marker guard. The PR-scoped `security-fast` job also runs `zizmor` over changed workflow files so workflow security findings fail early in the main CI graph.
- **Docs on `main` pushes** are checked by the standalone `Docs` workflow with the same ClawHub docs mirror used by CI, so mixed code+docs pushes do not also queue the CI `check-docs` shard. Pull requests and manual CI still run `check-docs` from CI when docs changed.
- **TUI PTY** splits by proof ownership. The dedicated `core-runtime-tui-pty` Node shard owns the full real-backend suite against the exact-head built CLI. The `build-artifacts` job keeps only a local model roundtrip and a real Gateway connection canary, so every artifact boundary proves the built launcher without duplicating the full serial suite inside the build job.
- **SQLite session lifecycle** runs the built-CLI migration, restart, compaction, cleanup, and session RPC proof only when the diff touches its direct storage/session owners or a reachable session path in the embedded runner. The dedicated `check-sqlite-session-lifecycle` job downloads the exact runtime produced by `build-artifacts`; manual and release dispatches always select it when the target contains the proof.
- **CI routing-only edits, the small set of core-test fixtures the fast task runs directly, and narrow plugin contract helper edits** use a fast Node-only manifest path: `preflight`, `security-fast`, and only the fast lanes the change touches — a single `checks-fast-core` CI-routing task, the two plugin contract shards, or both. That path skips build artifacts, Node 22 compatibility, channel contracts, full core shards, bundled-plugin shards, and additional guard matrices.
- **Windows Node checks** are scoped to Windows-specific process/path wrappers, npm/pnpm/UI runner helpers, package manager config, and the CI workflow surfaces that execute that lane; unrelated source, plugin, install-smoke, and test-only changes stay on the Linux Node lanes.
@@ -182,15 +183,15 @@ for commands and recovery.
## Runners
| Runner | Jobs |
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ubuntu-24.04` | `security-fast`, manual CI dispatch and non-canonical repository fallbacks, pull-request retries of both UI E2E jobs, the QA Smoke aggregate, CodeQL security and quality scans, workflow-sanity, labeler, auto-response, the standalone Docs workflow, and the whole Install Smoke workflow |
| `blacksmith-4vcpu-ubuntu-2404` | `preflight`, `pnpm-store-warmup`, `native-i18n`, `checks-fast-core` except QA Smoke CI, plugin/channel contract shards, most bundled/lower-weight Linux Node shards, `check-*` lanes except `check-lint`, selected `check-additional-*` shards, `check-docs`, and `skills-python` |
| `blacksmith-8vcpu-ubuntu-2404` | Retained heavy Linux Node suites, first-attempt same-repo pull requests and pushes for the serial Chromium/Vite `checks-ui-e2e` lane (three Control UI shards plus one browser extension shard), boundary/extension-heavy `check-additional-*` shards, and `android` |
| `blacksmith-16vcpu-ubuntu-2404` | Automatic QA Smoke CI shards, first-attempt same-repo pull requests and pushes for `checks-ui-e2e-real-gateway`, `build-artifacts` in CI and Testbox, and `check-lint` (CPU-sensitive enough that 8 vCPU cost more than they saved) |
| `blacksmith-8vcpu-windows-2025` | `checks-windows` |
| `blacksmith-6vcpu-macos-15` | `macos-node` on `openclaw/openclaw`; forks fall back to `macos-15` |
| `blacksmith-12vcpu-macos-26` | `macos-swift` and `ios-build` on `openclaw/openclaw`; forks fall back to `macos-26` |
| Runner | Jobs |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `ubuntu-24.04` | `security-fast`, manual CI dispatch and non-canonical repository fallbacks, pull-request retries of both UI E2E jobs, the QA Smoke aggregate, CodeQL security and quality scans, workflow-sanity, labeler, auto-response, the standalone Docs workflow, and the whole Install Smoke workflow |
| `blacksmith-4vcpu-ubuntu-2404` | `preflight`, `pnpm-store-warmup`, `native-i18n`, `checks-fast-core` except QA Smoke CI, plugin/channel contract shards, most bundled/lower-weight Linux Node shards, `check-*` lanes except `check-lint`, selected `check-additional-*` shards, `check-docs`, and `skills-python` |
| `blacksmith-8vcpu-ubuntu-2404` | Retained heavy Linux Node suites, first-attempt same-repo pull requests and pushes for the serial Chromium/Vite `checks-ui-e2e` lane (three Control UI shards plus one browser extension shard), boundary/extension-heavy `check-additional-*` shards, `check-sqlite-session-lifecycle`, and `android` |
| `blacksmith-16vcpu-ubuntu-2404` | Automatic QA Smoke CI shards, first-attempt same-repo pull requests and pushes for `checks-ui-e2e-real-gateway`, `build-artifacts` in CI and Testbox, and `check-lint` (CPU-sensitive enough that 8 vCPU cost more than they saved) |
| `blacksmith-8vcpu-windows-2025` | `checks-windows` |
| `blacksmith-6vcpu-macos-15` | `macos-node` on `openclaw/openclaw`; forks fall back to `macos-15` |
| `blacksmith-12vcpu-macos-26` | `macos-swift` and `ios-build` on `openclaw/openclaw`; forks fall back to `macos-26` |
## Runner registration budget
+46
View File
@@ -148,6 +148,52 @@ export function hasPromptSnapshotAffectingChange(changedPaths: string[], options
return hasImportGraphImpactOnTargets(sourcePaths, [PROMPT_SNAPSHOT_ENTRY], cwd);
}
// The lifecycle proof crosses dynamic Gateway method registration, doctor
// migrations, shared session coordination, the public session SDK, and the
// built CLI. Keep those owners on the direct surface; use the import graph only
// inside the embedded-runner neighborhood, whose session reachability is not
// apparent from filenames.
const SQLITE_SESSION_LIFECYCLE_PREFIX_RE =
/^(?:src\/(?:agents\/(?:sessions\/|[^/]*(?:session|transcript|compaction)[^/]*)|commands\/doctor-session-|config\/sessions\/|gateway\/(?:agent-turn\/agent-session-persist|server-chat\.(?:load-gateway-session-row|persist-session-lifecycle)|server-methods\/sessions|server\.sessions|session-|sessions-)|plugin-sdk\/session-|sessions\/|state\/openclaw-agent-(?:db|schema))|\.github\/actions\/setup-node-env\/)/u;
const SQLITE_SESSION_LIFECYCLE_EXACT_RE =
/^(?:src\/config\/sessions\.ts|test\/helpers\/(?:openclaw-test-instance|sqlite-sessions-transcripts-flip-proof(?:-assertions)?)\.ts|test\/scripts\/(?:sqlite-sessions-transcripts-flip-proof(?:\.built-cli)?\.e2e\.test|vitest-e2e-global-setup\.test)\.ts|test\/vitest\/vitest\.e2e\.(?:config|global-setup)\.ts|scripts\/lib\/ci-changed-node-test-plan\.mts|\.github\/workflows\/ci\.yml|openclaw\.mjs|package\.json|pnpm-lock\.yaml|pnpm-workspace\.yaml)$/u;
const SQLITE_SESSION_LIFECYCLE_ENTRY =
"test/scripts/sqlite-sessions-transcripts-flip-proof.e2e.test.ts";
const SQLITE_SESSION_LIFECYCLE_IMPORT_CANDIDATE_RE = /^src\/agents\/embedded-agent-runner\/run\//u;
/**
* True when a changed path touches a SQLite session lifecycle owner or reaches
* the proof from the embedded-runner neighborhood.
*/
export function hasSqliteSessionLifecycleAffectingChange(
changedPaths: string[],
options: CwdOptions = {},
) {
const cwd = options.cwd ?? process.cwd();
if (
changedPaths.some(
(changedPath) =>
(!isTestFileTarget(changedPath) && SQLITE_SESSION_LIFECYCLE_PREFIX_RE.test(changedPath)) ||
SQLITE_SESSION_LIFECYCLE_EXACT_RE.test(changedPath),
)
) {
return true;
}
const sourcePaths = changedPaths.filter(
(changedPath) =>
SQLITE_SESSION_LIFECYCLE_IMPORT_CANDIDATE_RE.test(changedPath) &&
!isTestFileTarget(changedPath),
);
// Deleted sources cannot be graphed; fail safe to running the lifecycle proof.
if (sourcePaths.some((changedPath) => !existsSync(path.join(cwd, changedPath)))) {
return true;
}
if (sourcePaths.length === 0) {
return false;
}
return hasImportGraphImpactOnTargets(sourcePaths, [SQLITE_SESSION_LIFECYCLE_ENTRY], cwd);
}
function createBoundaryShard() {
// Boundary tests scan the source tree (including test files) and build
// their own fixtures; they do not consume the built dist artifact. When the
@@ -7,6 +7,7 @@ import {
hasBuildArtifactAffectingChange,
hasPromptSnapshotAffectingChange,
hasQaSmokeAffectingChange,
hasSqliteSessionLifecycleAffectingChange,
} from "../../scripts/lib/ci-changed-node-test-plan.mts";
import { hasImportGraphImpactOnTargets } from "../../scripts/test-projects.test-support.mts";
import { listGitTrackedFiles } from "../../src/test-utils/repo-files.js";
@@ -135,6 +136,46 @@ describe("CI changed Node test plan", () => {
expect(hasPromptSnapshotAffectingChange(["src/infra/definitely-deleted-module.ts"])).toBe(true);
});
it("classifies SQLite session lifecycle impact by owner and import graph", () => {
expect(
hasSqliteSessionLifecycleAffectingChange([
"src/agents/embedded-agent-runner/run/attempt-session-runtime-prepare.ts",
]),
).toBe(true);
expect(
hasSqliteSessionLifecycleAffectingChange(["src/gateway/server-methods/sessions.ts"]),
).toBe(true);
expect(
hasSqliteSessionLifecycleAffectingChange(["src/sessions/session-lifecycle-admission.ts"]),
).toBe(true);
expect(hasSqliteSessionLifecycleAffectingChange(["src/config/sessions.ts"])).toBe(true);
expect(
hasSqliteSessionLifecycleAffectingChange([
"test/scripts/sqlite-sessions-transcripts-flip-proof.built-cli.e2e.test.ts",
]),
).toBe(true);
expect(
hasSqliteSessionLifecycleAffectingChange(["src/media-understanding/provider-id.ts"]),
).toBe(false);
expect(hasSqliteSessionLifecycleAffectingChange(["src/agents/model-auth.ts"])).toBe(false);
expect(hasSqliteSessionLifecycleAffectingChange(["extensions/discord/src/index.ts"])).toBe(
false,
);
expect(
hasSqliteSessionLifecycleAffectingChange([
"src/config/sessions/session-registry-maintenance.test.ts",
]),
).toBe(false);
expect(
hasSqliteSessionLifecycleAffectingChange(["src/infra/definitely-deleted-module.ts"]),
).toBe(false);
expect(
hasSqliteSessionLifecycleAffectingChange([
"src/agents/embedded-agent-runner/run/deleted-session-runtime.ts",
]),
).toBe(true);
});
it("fails safe to the full plan for broad changes", () => {
expect(createChangedNodeTestShards(["package.json"])).toBeNull();
});
+67 -9
View File
@@ -248,19 +248,33 @@ function runCiManifestFixture(options: {
? `throw new Error("planner import failure");\n`
: `
export const createChangedNodeTestShards = (changedPaths) =>
changedPaths.includes("src/focused.ts")
changedPaths.includes("src/focused.ts") ||
changedPaths.includes("test/scripts/sqlite-sessions-transcripts-flip-proof.built-cli.e2e.test.ts")
? [{
checkName: "changed-node-plan",
configs: [],
requiresDist: false,
runner: "ubuntu-24.04",
shardName: "changed-node-plan",
targets: ["src/focused.test.ts"],
targets: changedPaths.includes("src/focused.ts")
? ["src/focused.test.ts"]
: ["test/scripts/sqlite-sessions-transcripts-flip-proof.built-cli.e2e.test.ts"],
}]
: null;
export const hasBuildArtifactAffectingChange = (changedPaths) =>
!changedPaths.includes("test/scripts/sqlite-sessions-transcripts-flip-proof.built-cli.e2e.test.ts");
export const hasSqliteSessionLifecycleAffectingChange = (changedPaths) =>
changedPaths.includes("src/sqlite-session-owner.ts") ||
changedPaths.includes("test/scripts/sqlite-sessions-transcripts-flip-proof.built-cli.e2e.test.ts");
`,
"utf8",
);
const sqliteLifecycleProof = path.join(
root,
"test/scripts/sqlite-sessions-transcripts-flip-proof.built-cli.e2e.test.ts",
);
mkdirSync(path.dirname(sqliteLifecycleProof), { recursive: true });
writeFileSync(sqliteLifecycleProof, "export {};\n");
writeFileSync(
path.join(scriptsDir, "channel-contract-test-plan.mts"),
`export const createChannelContractTestShards = () => [{ checkName: "channel-contracts" }];\n`,
@@ -2866,6 +2880,7 @@ NODE
"control-ui-i18n",
"native-i18n",
"qa-smoke-ci-profile",
"sqlite-session-lifecycle",
]);
const hostedRetryJobs = new Set(["checks-ui-e2e", "checks-ui-e2e-real-gateway"]);
for (const { jobName, stepWith } of stickyConsumers) {
@@ -5151,6 +5166,7 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}"
expect(current.outputs.run_native_i18n).toBe("true");
expect(current.outputs.run_openclawkit_tests).toBe("true");
expect(current.outputs.run_qa_smoke_ci).toBe("true");
expect(current.outputs.run_sqlite_session_lifecycle).toBe("true");
expect(current.outputs.run_channel_contracts_shards).toBe("true");
expect(current.outputs.run_protocol_event_coverage).toBe("true");
expect(current.outputs.run_format_check).toBe("true");
@@ -5233,6 +5249,25 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}"
}),
]);
expect(changedPullRequest.outputs.run_checks_node_core_dist).toBe("true");
expect(changedPullRequest.outputs.run_sqlite_session_lifecycle).toBe("false");
const sqliteLifecyclePullRequest = runCiManifestFixture({
bundledPlanner: true,
changedPaths: ["src/sqlite-session-owner.ts"],
eventName: "pull_request",
});
expect(sqliteLifecyclePullRequest.status, sqliteLifecyclePullRequest.output).toBe(0);
expect(sqliteLifecyclePullRequest.outputs.run_sqlite_session_lifecycle).toBe("true");
expect(sqliteLifecyclePullRequest.outputs.run_build_artifacts).toBe("true");
const sqliteLifecycleTestPullRequest = runCiManifestFixture({
bundledPlanner: true,
changedPaths: ["test/scripts/sqlite-sessions-transcripts-flip-proof.built-cli.e2e.test.ts"],
eventName: "pull_request",
});
expect(sqliteLifecycleTestPullRequest.status, sqliteLifecycleTestPullRequest.output).toBe(0);
expect(sqliteLifecycleTestPullRequest.outputs.run_sqlite_session_lifecycle).toBe("true");
expect(sqliteLifecycleTestPullRequest.outputs.run_build_artifacts).toBe("true");
const plannerImportFailure = runCiManifestFixture({
bundledPlanner: true,
@@ -5821,18 +5856,40 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}"
expect(proofStep.run).toContain("Selected target predates");
});
it("scopes cold-runner watchdog headroom to the SQLite flip proof", () => {
it("runs the scoped SQLite lifecycle proof against the exact built artifact", () => {
const workflow = readCiWorkflow();
const additionalJob = workflow.jobs["check-additional-shard"];
const runStep = additionalJob.steps.find(
const additionalRunStep = additionalJob.steps.find(
(step: WorkflowStep) => step.name === "Run additional check shard",
);
expect(runStep.run).toContain("sqlite-session-flip-proof)");
expect(runStep.run).toContain(
'run_check "sqlite sessions/transcripts flip proof" env OPENCLAW_VITEST_NO_OUTPUT_TIMEOUT_MS=660000 node scripts/run-vitest.mjs run',
const lifecycleJob = workflow.jobs["sqlite-session-lifecycle"];
const downloadStep = lifecycleJob.steps.find(
(step: WorkflowStep) => step.name === "Download exact-run built runtime",
);
expect(runStep.env.OPENCLAW_VITEST_NO_OUTPUT_TIMEOUT_MS).toBeUndefined();
const extractStep = lifecycleJob.steps.find(
(step: WorkflowStep) => step.name === "Extract built runtime",
);
const proofStep = lifecycleJob.steps.find(
(step: WorkflowStep) => step.name === "Verify SQLite session lifecycle",
);
expect(additionalJob.strategy.matrix.include).not.toContainEqual(
expect.objectContaining({ group: "sqlite-session-flip-proof" }),
);
expect(additionalRunStep.run).not.toContain("sqlite-session-flip-proof)");
expect(lifecycleJob.needs).toEqual(["preflight", "build-artifacts"]);
expect(lifecycleJob.if).toContain(
"needs.preflight.outputs.run_sqlite_session_lifecycle == 'true'",
);
expect(downloadStep.uses).toBe(DOWNLOAD_ARTIFACT_V8);
expect(downloadStep.with.name).toBe("dist-runtime-build");
expect(extractStep.run).toContain("dist-runtime-build.tar.zst");
expect(proofStep.env.OPENCLAW_E2E_USE_PREBUILT_DIST).toBe("1");
expect(proofStep.env.OPENCLAW_VITEST_NO_OUTPUT_TIMEOUT_MS).toBe("660000");
expect(proofStep.run).toContain(
"test/scripts/sqlite-sessions-transcripts-flip-proof.built-cli.e2e.test.ts",
);
expect(workflow.jobs["ci-gate"].needs).toContain("sqlite-session-lifecycle");
});
it("restores the dist build cache before building and saves only cache misses", () => {
@@ -6058,6 +6115,7 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}"
const selectedJobs = [
"pnpm-store-warmup",
"build-artifacts",
"sqlite-session-lifecycle",
"native-i18n",
"checks-ui",
"checks-ui-e2e",
@@ -61,6 +61,14 @@ describe("vitest E2E global setup", () => {
);
});
it("uses an exact-run prebuilt dist without rebuilding it", async () => {
const runCommand = vi.fn<SetupCommandRunner>();
await runE2eGlobalSetup(runCommand, { OPENCLAW_E2E_USE_PREBUILT_DIST: "1" });
expect(runCommand).not.toHaveBeenCalled();
});
posixIt("forwards output and SIGTERM through the runner process group", async () => {
const fixtureDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-e2e-setup-group-"));
const fixturePath = path.join(fixtureDir, "build-fixture.mjs");
+8 -2
View File
@@ -28,19 +28,25 @@ export function runE2eSetupCommand(args: string[], env: NodeJS.ProcessEnv): Prom
export async function runE2eGlobalSetup(
runCommand: SetupCommandRunner = runE2eSetupCommand,
env: NodeJS.ProcessEnv = process.env,
): Promise<void> {
// Exact-run CI artifact consumers already have the complete built surface.
// Rebuilding here would discard that provenance and duplicate the slow step.
if (env.OPENCLAW_E2E_USE_PREBUILT_DIST === "1") {
return;
}
const commands = [
{
args: ["scripts/run-node.mjs", "--version"],
env: {
...process.env,
...env,
OPENCLAW_BUILD_PRIVATE_QA: "1",
OPENCLAW_RUN_NODE_SKIP_DTS_BUILD: "0",
},
},
{
args: ["--import", "tsx", "scripts/tsdown-build.mts", "--config", "tsdown.ai.config.ts"],
env: process.env,
env,
},
];
for (const { args, env } of commands) {