426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
193 lines
6.8 KiB
JavaScript
193 lines
6.8 KiB
JavaScript
#!/usr/bin/env node
|
|
/** Verifies exact changed-path attribution and fail-closed missing-source handling. */
|
|
import assert from "node:assert/strict";
|
|
import { spawnSync } from "node:child_process";
|
|
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
|
|
const root = new URL("../..", import.meta.url).pathname;
|
|
const awkScript = join(root, "scripts/security/coverage-gate.awk");
|
|
|
|
function writeLcov(dir, sourcePath, found = 2, hit = 2) {
|
|
const file = join(dir, "lcov.info");
|
|
writeFileSync(
|
|
file,
|
|
[`SF:${sourcePath}`, `LF:${found}`, `LH:${hit}`, "end_of_record", ""].join(
|
|
"\n",
|
|
),
|
|
);
|
|
return file;
|
|
}
|
|
|
|
function writeLcovRecords(dir, sourcePaths) {
|
|
const file = join(dir, "lcov.info");
|
|
writeFileSync(
|
|
file,
|
|
sourcePaths
|
|
.flatMap((sourcePath) => [
|
|
`SF:${sourcePath}`,
|
|
"LF:2",
|
|
"LH:2",
|
|
"end_of_record",
|
|
])
|
|
.concat("")
|
|
.join("\n"),
|
|
);
|
|
return file;
|
|
}
|
|
|
|
// Write a single-record lcov under an explicit filename so a test can hand the
|
|
// awk MULTIPLE lcov inputs (one per lane), exactly as the CI does with the
|
|
// per-nearest-config lcov.info files.
|
|
function writeLcovAs(dir, name, sourcePath, found, hit) {
|
|
const file = join(dir, name);
|
|
writeFileSync(
|
|
file,
|
|
[`SF:${sourcePath}`, `LF:${found}`, `LH:${hit}`, "end_of_record", ""].join(
|
|
"\n",
|
|
),
|
|
);
|
|
return file;
|
|
}
|
|
|
|
function runGate({ changed, lcov, enforce = true, threshold = 50 }) {
|
|
const changedArgument = changed
|
|
.replaceAll("\\", "\\\\")
|
|
.replaceAll("\n", "\\n");
|
|
const lcovArgs = Array.isArray(lcov) ? lcov : [lcov];
|
|
return spawnSync(
|
|
"awk",
|
|
[
|
|
"-v",
|
|
`changed=${changedArgument}`,
|
|
"-v",
|
|
`threshold=${threshold}`,
|
|
"-f",
|
|
awkScript,
|
|
...lcovArgs,
|
|
],
|
|
{
|
|
cwd: root,
|
|
env: { ...process.env, COVERAGE_GATE_ENFORCE: enforce ? "1" : "" },
|
|
encoding: "utf8",
|
|
},
|
|
);
|
|
}
|
|
|
|
function assertGate(name, fn) {
|
|
try {
|
|
fn();
|
|
console.log(`ok - ${name}`);
|
|
} catch (error) {
|
|
console.error(`not ok - ${name}`);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
const dir = mkdtempSync(join(tmpdir(), "coverage-gate-"));
|
|
try {
|
|
assertGate("matches identical repo-relative path", () => {
|
|
const lcov = writeLcov(dir, "packages/demo/src/foo.ts");
|
|
const result = runGate({ changed: "packages/demo/src/foo.ts", lcov });
|
|
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
assert.match(result.stdout, /coverage gate OK/);
|
|
});
|
|
|
|
assertGate("matches absolute LCOV path at path boundary", () => {
|
|
const lcov = writeLcov(dir, "/workspace/eliza/packages/demo/src/foo.ts");
|
|
const result = runGate({ changed: "packages/demo/src/foo.ts", lcov });
|
|
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
assert.match(result.stdout, /coverage gate OK/);
|
|
});
|
|
|
|
assertGate("does not match similar filename substring", () => {
|
|
const lcov = writeLcov(dir, "/workspace/eliza/packages/demo/src/foo.tsx");
|
|
const result = runGate({ changed: "packages/demo/src/foo.ts", lcov });
|
|
assert.equal(result.status, 1, result.stdout);
|
|
assert.match(result.stdout, /changed source missing from LCOV/);
|
|
});
|
|
|
|
assertGate("does not match longer path segment prefix", () => {
|
|
const lcov = writeLcov(dir, "/workspace/eliza/packages/demo/src/notfoo.ts");
|
|
const result = runGate({ changed: "packages/demo/src/foo.ts", lcov });
|
|
assert.equal(result.status, 1, result.stdout);
|
|
assert.match(result.stdout, /changed source missing from LCOV/);
|
|
});
|
|
|
|
assertGate("fails when any changed source is absent from LCOV", () => {
|
|
const covered = "packages/demo/src/covered.ts";
|
|
const missing = "packages/demo/src/missing.ts";
|
|
const lcov = writeLcov(dir, covered);
|
|
const result = runGate({ changed: `${covered}\n${missing}`, lcov });
|
|
|
|
assert.equal(result.status, 1, result.stdout);
|
|
assert.match(result.stdout, /100\.00% packages\/demo\/src\/covered\.ts/);
|
|
assert.match(result.stdout, /MISSING: packages\/demo\/src\/missing\.ts/);
|
|
assert.match(result.stdout, /changed source missing from LCOV/);
|
|
});
|
|
|
|
assertGate("prefers the longest matching changed path", () => {
|
|
const rootPath = "src/foo.ts";
|
|
const nestedPath = "packages/demo/src/foo.ts";
|
|
const lcov = writeLcovRecords(dir, [
|
|
`/workspace/eliza/${rootPath}`,
|
|
`/workspace/eliza/${nestedPath}`,
|
|
]);
|
|
const result = runGate({ changed: `${rootPath}\n${nestedPath}`, lcov });
|
|
|
|
assert.equal(result.status, 0, result.stdout);
|
|
assert.match(result.stdout, /100\.00% src\/foo\.ts/);
|
|
assert.match(result.stdout, /100\.00% packages\/demo\/src\/foo\.ts/);
|
|
assert.doesNotMatch(result.stdout, /MISSING:/);
|
|
});
|
|
|
|
assertGate("rejects an executable source reported with LF zero", () => {
|
|
const source = "packages/demo/src/runtime.ts";
|
|
const lcov = writeLcov(dir, source, 0, 0);
|
|
const result = runGate({ changed: source, lcov });
|
|
|
|
assert.equal(result.status, 1, result.stdout);
|
|
assert.match(result.stdout, /MISSING: packages\/demo\/src\/runtime[.]ts/);
|
|
assert.match(result.stdout, /changed source missing from LCOV/);
|
|
});
|
|
assertGate(
|
|
"aggregates a file across lanes: an incidental low record does not fail a file whose real lane clears the floor (#16043)",
|
|
() => {
|
|
const src = "packages/core/src/features/documents/service.ts";
|
|
// Its own focused lane covers it well (8/10 = 80%); another package's lane
|
|
// merely imports it (1/50 = 2%). Pre-fix, the 2% record alone failed the gate.
|
|
const ownLane = writeLcovAs(dir, "core.lcov", src, 10, 8);
|
|
const importLane = writeLcovAs(dir, "meetings.lcov", src, 50, 1);
|
|
const result = runGate({ changed: src, lcov: [ownLane, importLane] });
|
|
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
assert.match(result.stdout, /coverage gate OK/);
|
|
// Counted once, at the best lane's percentage — not the 2% record.
|
|
assert.match(result.stdout, /changed files: 1,/);
|
|
assert.match(
|
|
result.stdout,
|
|
/80\.00% packages\/core\/src\/features\/documents\/service\.ts/,
|
|
);
|
|
},
|
|
);
|
|
|
|
assertGate(
|
|
"still fails a file that is genuinely below the floor in every lane (#16043)",
|
|
() => {
|
|
const src = "packages/core/src/features/documents/service.ts";
|
|
// Both lanes under 50% (19.90% and 2%). The aggregated best is still below,
|
|
// so the gate must fail — aggregation must not blanket-pass shared files.
|
|
const laneA = writeLcovAs(dir, "a.lcov", src, 1000, 199);
|
|
const laneB = writeLcovAs(dir, "b.lcov", src, 50, 1);
|
|
const result = runGate({ changed: src, lcov: [laneA, laneB] });
|
|
assert.equal(result.status, 1, result.stdout);
|
|
assert.match(
|
|
result.stdout,
|
|
/BELOW: packages\/core\/src\/features\/documents\/service\.ts/,
|
|
);
|
|
},
|
|
);
|
|
} finally {
|
|
rmSync(dir, { recursive: true, force: true });
|
|
}
|