Files
gbrain/test/privacy-script-wired.test.ts
T
3a2605e9a0 v0.41.6.0 feat(ci): CI test speedup — 23min → ~9min via matrix 4→6 + weight-aware sharding + auto SHA cache + parallel verify (#1444)
* feat(ci): scripts/run-verify-parallel.sh — parallel verify dispatcher

Fans out the 21 pre-test grep guards via & + wait, captures per-check
exit codes in a tempdir, aggregates failures with named check + log
tail to stderr on miss. Wallclock 27s sequential → 13s parallel
locally (2x). Bigger CI win is shard 1 deload (workflow restructure
in a later commit).

Pinned by test/scripts/run-verify-parallel.test.ts (6 cases: CLI
contract + synthetic dispatcher failure-surfacing).

* feat(ci): weight-aware LPT bin-packer + auto SHA cache hash

scripts/sharding.ts (NEW) — pure TypeScript LPT bin-packer. Sort
weights desc, assign each file to the shard with current minimum total.
Worst-case makespan within 4/3 of optimal, O(n log n). Missing weights
fall back to corpus median (not 0). New test file → ships immediately
without regenerating weights. Pinned by test/scripts/sharding.test.ts
(23 cases).

scripts/mine-shard-weights.ts (NEW) — scrapes per-file timing from
gh run view --log via timestamp delta between ##[group]test/foo.test.ts:
headers within a shard. Three input modes: --run <ID>, --from-file
<PATH>, stdin. Stable JSON output (sorted keys). Initial weights mined
from run 26398061007. Pinned by test/scripts/mine-shard-weights.test.ts
(15 cases).

scripts/ci-cache-hash.sh (NEW) — deterministic 16-char sha256 over
git ls-files -s minus deny-list (CHANGELOG/TODOS/README/LICENSE/
docs/**/*.md). CLAUDE.md, AGENTS.md, skills/**/* deliberately
INCLUDED (8+ test files read them; deny-listing would create
false-pass holes). ~40ms on 1891 files. Pinned by
test/scripts/ci-cache-hash.test.ts (24 cases: 8 CRITICAL false-pass
guards + 7 SAFE deny-list invariants + 9 edge cases).

scripts/test-weights.json (NEW) — 712 weights. Total 3306s observed
runtime; median 30ms; max 6 min outlier.

* chore: bump version and changelog (v0.41.6.0)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 13:15:11 -07:00

75 lines
3.2 KiB
TypeScript

/**
* Regression guard: scripts/check-privacy.sh must run in CI's auto-pipeline.
*
* CLAUDE.md bans the private OpenClaw fork name from public artifacts.
* scripts/check-privacy.sh is the enforcement mechanism. If someone
* refactors the script chain and drops the privacy check, this test
* fails loudly.
*
* v0.26.4 split: `bun run test` is now the fast parallel loop and does
* NOT chain pre-checks; the privacy gate moved to `bun run verify`,
* which CI's test.yml runs as its own job before the matrix fans out.
*
* v0.41.4+ wave: `bun run verify` now delegates to
* scripts/run-verify-parallel.sh which fans out all 20 checks in
* parallel via & + wait. The privacy check is one entry in that
* script's CHECKS[] array. Regression guard updated to follow the
* indirection: (1) verify points at the parallel dispatcher,
* (2) the dispatcher's CHECKS array contains check:privacy,
* (3) CI workflow's verify job calls `bun run verify`.
*/
import { describe, it, expect } from 'bun:test';
import { readFileSync, existsSync } from 'fs';
import { resolve } from 'path';
import { spawnSync } from 'child_process';
const REPO_ROOT = resolve(import.meta.dir, '..');
const PACKAGE_JSON = resolve(REPO_ROOT, 'package.json');
const PRIVACY_SCRIPT = resolve(REPO_ROOT, 'scripts/check-privacy.sh');
const VERIFY_DISPATCHER = resolve(REPO_ROOT, 'scripts/run-verify-parallel.sh');
const TEST_WORKFLOW = resolve(REPO_ROOT, '.github/workflows/test.yml');
describe('check-privacy.sh CI wiring', () => {
it('scripts/check-privacy.sh exists and is executable', () => {
expect(existsSync(PRIVACY_SCRIPT)).toBe(true);
const stat = require('fs').statSync(PRIVACY_SCRIPT);
// eslint-disable-next-line no-bitwise
expect((stat.mode & 0o100) !== 0).toBe(true);
});
it('package.json "verify" script delegates to run-verify-parallel.sh', () => {
const pkg = JSON.parse(readFileSync(PACKAGE_JSON, 'utf-8'));
expect(typeof pkg.scripts?.verify).toBe('string');
// verify body is now `bash scripts/run-verify-parallel.sh`. The
// direct check:privacy substring assertion broke when the && chain
// was replaced with the parallel dispatcher. Follow the indirection.
expect(pkg.scripts.verify).toContain('run-verify-parallel.sh');
});
it('run-verify-parallel.sh dispatches check:privacy', () => {
expect(existsSync(VERIFY_DISPATCHER)).toBe(true);
// The dispatcher exposes --dry-list which prints one check name per
// line. Authoritative check than substring-grepping the script body
// (which could pass on a commented-out entry).
const r = spawnSync('bash', [VERIFY_DISPATCHER, '--dry-list'], {
cwd: REPO_ROOT,
encoding: 'utf-8',
});
expect(r.status).toBe(0);
const checks = r.stdout.trim().split('\n');
expect(checks).toContain('check:privacy');
});
it('package.json "check:privacy" alias points at the script', () => {
const pkg = JSON.parse(readFileSync(PACKAGE_JSON, 'utf-8'));
expect(pkg.scripts?.['check:privacy']).toContain('check-privacy.sh');
});
it('CI test.yml runs `bun run verify` so the privacy gate fires', () => {
expect(existsSync(TEST_WORKFLOW)).toBe(true);
const yml = readFileSync(TEST_WORKFLOW, 'utf-8');
expect(yml).toContain('bun run verify');
});
});