sneakoscope 4.0.13 → 4.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -2
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/bin/sks.js +1 -1
- package/dist/cli/global-mode-router.js +2 -1
- package/dist/core/commands/mad-sks-command.js +3 -0
- package/dist/core/fsx.js +1 -1
- package/dist/core/providers/glm/bench/glm-bench-model-lock-proof.js +32 -3
- package/dist/core/providers/glm/bench/glm-benchmark-runner.js +29 -5
- package/dist/core/providers/glm/bench/glm-benchmark-types.js +1 -1
- package/dist/core/providers/glm/naruto/glm-naruto-critical-path.js +51 -0
- package/dist/core/providers/glm/naruto/glm-naruto-final-seal.js +9 -2
- package/dist/core/providers/glm/naruto/glm-naruto-orchestrator.js +101 -15
- package/dist/core/providers/glm/naruto/glm-naruto-parallelism-summary.js +55 -0
- package/dist/core/providers/glm/naruto/glm-naruto-requirement-coverage.js +92 -0
- package/dist/core/providers/glm/naruto/glm-naruto-requirement-ledger.js +42 -0
- package/dist/core/providers/glm/naruto/glm-naruto-stage-scheduler.js +85 -0
- package/dist/core/providers/glm/naruto/glm-naruto-task-size-classifier.js +12 -0
- package/dist/core/providers/glm/naruto/glm-naruto-trace.js +4 -0
- package/dist/core/providers/glm/naruto/glm-naruto-verifier-output.js +5 -0
- package/dist/core/providers/glm/naruto/glm-naruto-worker-pool.js +130 -44
- package/dist/core/providers/glm/naruto/glm-naruto-worker-runtime.js +6 -2
- package/dist/core/routes/model-mode-router.js +44 -0
- package/dist/core/version.js +1 -1
- package/package.json +24 -1
- package/dist/scripts/agent-dynamic-pool-fixture.js +0 -80
- package/dist/scripts/agent-native-release-gate.js +0 -274
- package/dist/scripts/agent-patch-swarm-gate-lib.js +0 -113
- package/dist/scripts/agent-real-codex-patch-envelope-smoke.js +0 -126
- package/dist/scripts/agent-route-blackbox-lib.js +0 -132
- package/dist/scripts/blackbox-command-import-smoke.js +0 -143
- package/dist/scripts/blackbox-global-shim.js +0 -77
- package/dist/scripts/blackbox-matrix.js +0 -70
- package/dist/scripts/blackbox-npx-one-shot.js +0 -69
- package/dist/scripts/blackbox-pack-install.js +0 -174
- package/dist/scripts/build-dist.js +0 -64
- package/dist/scripts/check-architecture.js +0 -135
- package/dist/scripts/check-cli-entrypoint.js +0 -43
- package/dist/scripts/check-command-module-budget.js +0 -25
- package/dist/scripts/check-dist-runtime.js +0 -100
- package/dist/scripts/check-feature-quality.js +0 -53
- package/dist/scripts/check-legacy-free.js +0 -66
- package/dist/scripts/check-package-boundary.js +0 -108
- package/dist/scripts/check-pipeline-budget.js +0 -69
- package/dist/scripts/check-pipeline-runtime.js +0 -25
- package/dist/scripts/check-publish-tag.js +0 -30
- package/dist/scripts/check-route-modularity.js +0 -82
- package/dist/scripts/check-runtime-schemas.js +0 -87
- package/dist/scripts/check-source-runtime.js +0 -4
- package/dist/scripts/check-ts-contracts.js +0 -69
- package/dist/scripts/check-ts-suppressions.js +0 -58
- package/dist/scripts/clean-dist.js +0 -8
- package/dist/scripts/codex-0140-feature-gate-lib.js +0 -14
- package/dist/scripts/codex-config-eperm-fixture.js +0 -32
- package/dist/scripts/codex-lb-missing-env-regression.js +0 -40
- package/dist/scripts/codex-native-runtime-e2e-fixture.js +0 -75
- package/dist/scripts/codex-project-config-policy-merge-regression.js +0 -92
- package/dist/scripts/core-skill-legacy-promotion-api-audit.js +0 -54
- package/dist/scripts/ensure-bin-executable.js +0 -10
- package/dist/scripts/fixtures/fake-codex-config-loader.js +0 -51
- package/dist/scripts/github-release-body-helper.js +0 -65
- package/dist/scripts/gpt-image-2-real-file-smoke.js +0 -448
- package/dist/scripts/hooks-no-unsupported-handlers.js +0 -15
- package/dist/scripts/hooks-runtime-replay-warning-zero-v2.js +0 -26
- package/dist/scripts/hooks-runtime-replay-warning-zero.js +0 -10
- package/dist/scripts/hooks-trust-warning-zero.js +0 -14
- package/dist/scripts/lib/codex-sdk-gate-lib.js +0 -92
- package/dist/scripts/lib/ensure-dist-fresh.js +0 -142
- package/dist/scripts/lib/git-worktree-fixture.js +0 -33
- package/dist/scripts/lib/mad-sks-actual-executor-check-lib.js +0 -255
- package/dist/scripts/lib/native-cli-session-swarm-check-lib.js +0 -79
- package/dist/scripts/lib/real-codex-parallel-gate.js +0 -94
- package/dist/scripts/lib/real-codex-parallel-proof-fixture.js +0 -55
- package/dist/scripts/lib/valid-png-fixture.js +0 -25
- package/dist/scripts/mad-sks-live-protected-core-smoke.js +0 -5
- package/dist/scripts/naruto-real-local-gpt-final-smoke.js +0 -25
- package/dist/scripts/perf-gate.js +0 -39
- package/dist/scripts/prepublish-release-check-or-fast.js +0 -121
- package/dist/scripts/release-3112-required-gates.js +0 -30
- package/dist/scripts/release-3113-required-gates.js +0 -25
- package/dist/scripts/release-4000-required-gates.js +0 -36
- package/dist/scripts/release-4001-required-gates.js +0 -13
- package/dist/scripts/release-4002-required-gates.js +0 -14
- package/dist/scripts/release-check-dynamic-execute.js +0 -259
- package/dist/scripts/release-check-dynamic.js +0 -107
- package/dist/scripts/release-check-stamp.js +0 -261
- package/dist/scripts/release-gate-dag-runner.js +0 -56
- package/dist/scripts/release-gate-existence-audit.js +0 -111
- package/dist/scripts/release-gate-planner.js +0 -34
- package/dist/scripts/release-gate-worker.js +0 -10
- package/dist/scripts/release-speed-summary.js +0 -67
- package/dist/scripts/repo-audit.js +0 -83
- package/dist/scripts/rust-smoke.js +0 -5
- package/dist/scripts/sizecheck.js +0 -146
- package/dist/scripts/sks-1-11-gate-lib.js +0 -78
- package/dist/scripts/sks-1-18-gate-lib.js +0 -55
- package/dist/scripts/tmux-removal-inventory.js +0 -36
- package/dist/scripts/write-build-manifest.js +0 -71
- package/dist/scripts/zellij-dashboard-watch.js +0 -41
- package/dist/scripts/zellij-right-column-geometry-proof.js +0 -162
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// @ts-nocheck
|
|
3
|
-
// release:check:dynamic:execute (1.20.2 Area 2).
|
|
4
|
-
//
|
|
5
|
-
// Change-aware gate RUNNER (the plan-only release-check-dynamic.mjs stays as-is).
|
|
6
|
-
// Default = EXECUTE: selects gates whose affected_by globs match changed files
|
|
7
|
-
// (plus always-on), runs each hermetic one via `npm run <id>`, caches successful
|
|
8
|
-
// results, and serves cache hits to skip re-runs on an unchanged tree. Real/heavy
|
|
9
|
-
// gates are deferred to release:real-check. --plan-only prints the plan without
|
|
10
|
-
// running. --publish selects every required_for_publish gate.
|
|
11
|
-
//
|
|
12
|
-
// This script is STANDALONE — it must never be added to the release:check chain,
|
|
13
|
-
// the DAG, or the gate manifest (it would recursively invoke the gate set).
|
|
14
|
-
import fs from 'node:fs';
|
|
15
|
-
import path from 'node:path';
|
|
16
|
-
import crypto from 'node:crypto';
|
|
17
|
-
import { spawnSync } from 'node:child_process';
|
|
18
|
-
import { emitGate, importDist, root } from './sks-1-18-gate-lib.js';
|
|
19
|
-
const { buildGateManifest, selectGates, FORBIDDEN_RECURSIVE_GATES } = await importDist('core/release/gate-manifest.js');
|
|
20
|
-
const { gateCacheKey, readGateCache, writeGateCache, recordGateResult, lookupGateResult } = await importDist('core/release/gate-cache.js');
|
|
21
|
-
let TRACKED = null;
|
|
22
|
-
const args = process.argv.slice(2);
|
|
23
|
-
const planOnly = args.includes('--plan-only');
|
|
24
|
-
const publish = args.includes('--publish');
|
|
25
|
-
const baseArg = readArg(args, '--base');
|
|
26
|
-
const envMode = process.env.SKS_ENV_MODE || (publish ? 'publish' : 'incremental');
|
|
27
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
|
|
28
|
-
const manifest = loadManifest();
|
|
29
|
-
const changedFiles = detectChangedFiles(baseArg);
|
|
30
|
-
const plan = selectGates(manifest.gates, changedFiles, { publish });
|
|
31
|
-
// Hermetic invariant proofs preserved from the planner (self-prove narrowing).
|
|
32
|
-
const docsOnly = selectGates(manifest.gates, ['docs/zellij-ui-design.md'], { publish: false });
|
|
33
|
-
const realOnDocs = docsOnly.selected.filter((g) => g.cost === 'real' || g.cost === 'heavy');
|
|
34
|
-
const publishPlan = selectGates(manifest.gates, [], { publish: true });
|
|
35
|
-
const requiredIds = manifest.gates.filter((g) => g.required_for_publish).map((g) => g.id);
|
|
36
|
-
const selectedPublish = new Set(publishPlan.selected.map((g) => g.id));
|
|
37
|
-
const invariants = {
|
|
38
|
-
docs_only_skips_heavy: realOnDocs.length === 0,
|
|
39
|
-
publish_keeps_required: requiredIds.every((id) => selectedPublish.has(id))
|
|
40
|
-
};
|
|
41
|
-
const distHash = distHashValue();
|
|
42
|
-
const gitCommit = gitHead();
|
|
43
|
-
const manifestHash = fileHash(fs.existsSync(path.join(root, 'release-gates.v2.json')) ? 'release-gates.v2.json' : 'release-gates.json');
|
|
44
|
-
const packageScriptsHash = sha256(JSON.stringify(pkg.scripts || {}));
|
|
45
|
-
const nodeVersion = process.version;
|
|
46
|
-
const npmVersion = npmVersionValue();
|
|
47
|
-
const cache = await readGateCache(root);
|
|
48
|
-
const executed = [];
|
|
49
|
-
const cacheHits = [];
|
|
50
|
-
const skipped = [...plan.skipped];
|
|
51
|
-
const failures = [];
|
|
52
|
-
for (const gate of plan.selected) {
|
|
53
|
-
if (FORBIDDEN_RECURSIVE_GATES.has(gate.id)) {
|
|
54
|
-
skipped.push({ id: gate.id, reason: 'forbidden_recursive_gate' });
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
// Real/heavy gates are never run incrementally or cached — defer to release:real-check.
|
|
58
|
-
if (gate.cost === 'real' || gate.cost === 'heavy') {
|
|
59
|
-
skipped.push({ id: gate.id, reason: 'deferred_to_real_check' });
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
const command = `npm run ${gate.id}`;
|
|
63
|
-
if (FORBIDDEN_RECURSIVE_GATES.has(gate.id) || /npm\s+run\s+(release:check|release:real-check|release:publish|publish:npm|publish:dry|prepublishOnly)\b/.test(command)) {
|
|
64
|
-
failures.push({ id: gate.id, exit_code: null, stdout_tail: '', stderr_tail: 'forbidden_recursive_gate_spawn' });
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
const key = gateCacheKey({
|
|
68
|
-
gateId: gate.id,
|
|
69
|
-
command,
|
|
70
|
-
packageVersion: pkg.version,
|
|
71
|
-
gitCommit,
|
|
72
|
-
inputHashes: hashAffectedFiles(gate.affected_by),
|
|
73
|
-
envMode,
|
|
74
|
-
distHash,
|
|
75
|
-
manifestHash,
|
|
76
|
-
packageScriptsHash,
|
|
77
|
-
gateImplementationHash: gateImplementationHash(gate.id),
|
|
78
|
-
nodeVersion,
|
|
79
|
-
npmVersion
|
|
80
|
-
});
|
|
81
|
-
const hit = lookupGateResult(cache, key);
|
|
82
|
-
if (hit && hit.ok) {
|
|
83
|
-
cacheHits.push({ id: gate.id, key, duration_ms: hit.duration_ms, recorded_at: hit.recorded_at });
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
if (planOnly) {
|
|
87
|
-
executed.push({ id: gate.id, planned: true });
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
const started = Date.now();
|
|
91
|
-
const childEnv = { ...process.env, SKS_RELEASE_DYNAMIC: '1', SKS_ENV_MODE: envMode };
|
|
92
|
-
const res = spawnSync('npm', ['run', gate.id, '--silent'], { cwd: root, env: childEnv, encoding: 'utf8', maxBuffer: 50 * 1024 * 1024 });
|
|
93
|
-
const durationMs = Date.now() - started;
|
|
94
|
-
const ok = res.status === 0;
|
|
95
|
-
executed.push({ id: gate.id, ok, exit_code: res.status, duration_ms: durationMs });
|
|
96
|
-
if (ok) {
|
|
97
|
-
// Only successful gates are cached; failures always re-run.
|
|
98
|
-
recordGateResult(cache, key, gate.id, true, durationMs);
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
failures.push({ id: gate.id, exit_code: res.status, stdout_tail: tail(res.stdout), stderr_tail: tail(res.stderr) });
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
if (!planOnly)
|
|
105
|
-
await writeGateCache(root, cache);
|
|
106
|
-
const ok = failures.length === 0 && invariants.docs_only_skips_heavy && invariants.publish_keeps_required;
|
|
107
|
-
const report = {
|
|
108
|
-
schema: 'sks.release-check-dynamic.v2',
|
|
109
|
-
ok,
|
|
110
|
-
mode: planOnly ? 'plan-only' : publish ? 'publish' : 'incremental',
|
|
111
|
-
base: baseArg || null,
|
|
112
|
-
changed_files: changedFiles,
|
|
113
|
-
selected: plan.selected.map((g) => g.id),
|
|
114
|
-
skipped,
|
|
115
|
-
executed,
|
|
116
|
-
cache_hits: cacheHits,
|
|
117
|
-
failures,
|
|
118
|
-
invariants
|
|
119
|
-
};
|
|
120
|
-
const reportDir = path.join(root, '.sneakoscope', 'reports');
|
|
121
|
-
fs.mkdirSync(reportDir, { recursive: true });
|
|
122
|
-
fs.writeFileSync(path.join(reportDir, 'release-check-dynamic-execute.json'), `${JSON.stringify(report, null, 2)}\n`);
|
|
123
|
-
if (!ok) {
|
|
124
|
-
console.log(JSON.stringify({ schema: 'sks.release-check-dynamic.v2', ok: false, mode: report.mode, failures, invariants }, null, 2));
|
|
125
|
-
process.exit(1);
|
|
126
|
-
}
|
|
127
|
-
emitGate('release:check:dynamic:execute', {
|
|
128
|
-
mode: report.mode,
|
|
129
|
-
selected: report.selected.length,
|
|
130
|
-
executed: executed.length,
|
|
131
|
-
cache_hits: cacheHits.length,
|
|
132
|
-
skipped: skipped.length,
|
|
133
|
-
failures: failures.length
|
|
134
|
-
});
|
|
135
|
-
// ---- helpers ----------------------------------------------------------------
|
|
136
|
-
function loadManifest() {
|
|
137
|
-
const v2Path = path.join(root, 'release-gates.v2.json');
|
|
138
|
-
if (fs.existsSync(v2Path)) {
|
|
139
|
-
const parsed = JSON.parse(fs.readFileSync(v2Path, 'utf8'));
|
|
140
|
-
const releaseNodes = (Array.isArray(parsed.gates) ? parsed.gates : []).filter((gate) => Array.isArray(gate.preset) && gate.preset.includes('release'));
|
|
141
|
-
const byId = new Map(releaseNodes.map((gate) => [gate.id, gate]));
|
|
142
|
-
const dynamic = buildGateManifest(releaseNodes.map((gate) => gate.id));
|
|
143
|
-
return {
|
|
144
|
-
schema: 'sks.release-gate-manifest.v1.from-v2',
|
|
145
|
-
gates: dynamic.gates.map((entry) => {
|
|
146
|
-
const node = byId.get(entry.id);
|
|
147
|
-
const resource = Array.isArray(node?.resource) ? node.resource.join(',') : '';
|
|
148
|
-
return {
|
|
149
|
-
...entry,
|
|
150
|
-
affected_by: usefulCacheInputs(node?.cache?.inputs, entry.affected_by),
|
|
151
|
-
cost: node?.side_effect === 'real-env' || resource.includes('real') ? 'real' : entry.cost
|
|
152
|
-
};
|
|
153
|
-
})
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
const p = path.join(root, 'release-gates.json');
|
|
157
|
-
if (fs.existsSync(p))
|
|
158
|
-
return JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
159
|
-
const dagSource = fs.readFileSync(path.join(root, 'src/scripts/release-parallel-check.ts'), 'utf8');
|
|
160
|
-
const dagIds = [...dagSource.matchAll(/task\('([^']+)'/g)].map((m) => m[1]);
|
|
161
|
-
const releaseCheckIds = [...String(pkg.scripts?.['release:check'] || '').matchAll(/npm run ([^\s&]+)/g)].map((m) => m[1]);
|
|
162
|
-
const ids = [...new Set([...dagIds, ...releaseCheckIds])].filter((id) => id && id !== 'build' && id !== 'release:check:parallel');
|
|
163
|
-
return buildGateManifest(ids);
|
|
164
|
-
}
|
|
165
|
-
function usefulCacheInputs(inputs, fallback) {
|
|
166
|
-
if (!Array.isArray(inputs) || !inputs.length)
|
|
167
|
-
return fallback;
|
|
168
|
-
if (inputs.some((input) => ['src/**', 'package.json', 'release-gates.v2.json', 'schemas/**'].includes(input)))
|
|
169
|
-
return fallback;
|
|
170
|
-
return inputs;
|
|
171
|
-
}
|
|
172
|
-
function hashAffectedFiles(globs) {
|
|
173
|
-
const regexes = (globs || []).map(globToRegExp);
|
|
174
|
-
const hashes = [];
|
|
175
|
-
for (const file of listTrackedFiles()) {
|
|
176
|
-
if (!regexes.some((re) => re.test(file)))
|
|
177
|
-
continue;
|
|
178
|
-
try {
|
|
179
|
-
const buf = fs.readFileSync(path.join(root, file));
|
|
180
|
-
hashes.push(`${file}:${crypto.createHash('sha256').update(buf).digest('hex').slice(0, 16)}`);
|
|
181
|
-
}
|
|
182
|
-
catch { }
|
|
183
|
-
}
|
|
184
|
-
return hashes;
|
|
185
|
-
}
|
|
186
|
-
function sha256(value) {
|
|
187
|
-
return crypto.createHash('sha256').update(String(value)).digest('hex');
|
|
188
|
-
}
|
|
189
|
-
function fileHash(rel) {
|
|
190
|
-
try {
|
|
191
|
-
return crypto.createHash('sha256').update(fs.readFileSync(path.join(root, rel))).digest('hex');
|
|
192
|
-
}
|
|
193
|
-
catch {
|
|
194
|
-
return 'missing';
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
function gateImplementationHash(id) {
|
|
198
|
-
const script = String(pkg.scripts?.[id] || '');
|
|
199
|
-
const match = script.match(/node\s+\.\/([^ ]+\.mjs)/);
|
|
200
|
-
if (match)
|
|
201
|
-
return fileHash(match[1]);
|
|
202
|
-
return sha256(script);
|
|
203
|
-
}
|
|
204
|
-
function npmVersionValue() {
|
|
205
|
-
const res = spawnSync('npm', ['--version'], { cwd: root, encoding: 'utf8' });
|
|
206
|
-
return res.status === 0 ? res.stdout.trim() : 'npm-unavailable';
|
|
207
|
-
}
|
|
208
|
-
function listTrackedFiles() {
|
|
209
|
-
if (TRACKED)
|
|
210
|
-
return TRACKED;
|
|
211
|
-
const res = spawnSync('git', ['ls-files'], { cwd: root, encoding: 'utf8' });
|
|
212
|
-
TRACKED = res.status === 0 ? res.stdout.split('\n').map((s) => s.trim()).filter(Boolean) : [];
|
|
213
|
-
return TRACKED;
|
|
214
|
-
}
|
|
215
|
-
function globToRegExp(glob) {
|
|
216
|
-
return new RegExp('^' + glob.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*\*/g, ' ').replace(/\*/g, '[^/]*').replace(/ /g, '.*') + '$');
|
|
217
|
-
}
|
|
218
|
-
function distHashValue() {
|
|
219
|
-
try {
|
|
220
|
-
const m = JSON.parse(fs.readFileSync(path.join(root, 'dist', 'build-manifest.json'), 'utf8'));
|
|
221
|
-
return String(m.source_digest || m.source_files_hash || m.version || 'no-dist');
|
|
222
|
-
}
|
|
223
|
-
catch {
|
|
224
|
-
return 'no-dist';
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
function gitHead() {
|
|
228
|
-
const res = spawnSync('git', ['rev-parse', 'HEAD'], { cwd: root, encoding: 'utf8' });
|
|
229
|
-
return res.status === 0 ? res.stdout.trim() : 'no-git';
|
|
230
|
-
}
|
|
231
|
-
function detectChangedFiles(base) {
|
|
232
|
-
try {
|
|
233
|
-
let ref = base;
|
|
234
|
-
if (!ref) {
|
|
235
|
-
const mb = spawnSync('git', ['merge-base', 'HEAD', 'origin/main'], { cwd: root, encoding: 'utf8' });
|
|
236
|
-
ref = mb.status === 0 ? mb.stdout.trim() : 'HEAD~1';
|
|
237
|
-
}
|
|
238
|
-
const diff = spawnSync('git', ['diff', '--name-only', `${ref}...HEAD`], { cwd: root, encoding: 'utf8' });
|
|
239
|
-
const status = spawnSync('git', ['status', '--porcelain'], { cwd: root, encoding: 'utf8' });
|
|
240
|
-
const files = new Set();
|
|
241
|
-
if (diff.status === 0)
|
|
242
|
-
diff.stdout.split('\n').map((s) => s.trim()).filter(Boolean).forEach((f) => files.add(f));
|
|
243
|
-
if (status.status === 0)
|
|
244
|
-
status.stdout.split('\n').map((s) => s.slice(3).trim()).filter(Boolean).forEach((f) => files.add(f));
|
|
245
|
-
return [...files];
|
|
246
|
-
}
|
|
247
|
-
catch {
|
|
248
|
-
return [];
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
function readArg(list, name) {
|
|
252
|
-
const i = list.indexOf(name);
|
|
253
|
-
return i >= 0 ? list[i + 1] || null : null;
|
|
254
|
-
}
|
|
255
|
-
function tail(value, limit = 4000) {
|
|
256
|
-
const text = String(value || '');
|
|
257
|
-
return text.length <= limit ? text : text.slice(-limit);
|
|
258
|
-
}
|
|
259
|
-
//# sourceMappingURL=release-check-dynamic-execute.js.map
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// @ts-nocheck
|
|
3
|
-
// release:check:dynamic — change-aware gate planner. Runs only P0 always-on gates
|
|
4
|
-
// plus gates whose affected_by globs match the changed files. Real/heavy gates are
|
|
5
|
-
// deferred to release:real-check. Publish mode never skips required_for_publish gates.
|
|
6
|
-
import fs from 'node:fs';
|
|
7
|
-
import path from 'node:path';
|
|
8
|
-
import { spawnSync } from 'node:child_process';
|
|
9
|
-
import { assertGate, emitGate, importDist, root } from './sks-1-18-gate-lib.js';
|
|
10
|
-
const { buildGateManifest, selectGates } = await importDist('core/release/gate-manifest.js');
|
|
11
|
-
const args = process.argv.slice(2);
|
|
12
|
-
const publish = args.includes('--publish');
|
|
13
|
-
const baseArg = readArg(args, '--base');
|
|
14
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
|
|
15
|
-
// Prefer the checked-in v2 DAG manifest and normalize it for the legacy dynamic selector.
|
|
16
|
-
const manifest = loadDynamicManifest();
|
|
17
|
-
const changedFiles = detectChangedFiles(baseArg);
|
|
18
|
-
const plan = selectGates(manifest.gates, changedFiles, { publish });
|
|
19
|
-
// --- Hermetic invariant proofs (independent of the current git state) ---
|
|
20
|
-
// 1) A docs-only change must NOT select any real/heavy gate.
|
|
21
|
-
const docsOnly = selectGates(manifest.gates, ['docs/zellij-ui-design.md'], { publish: false });
|
|
22
|
-
const realSelectedOnDocs = docsOnly.selected.filter((g) => g.cost === 'real' || g.cost === 'heavy');
|
|
23
|
-
assertGate(realSelectedOnDocs.length === 0, 'docs-only change must not select real/heavy gates', { offenders: realSelectedOnDocs.map((g) => g.id) });
|
|
24
|
-
// At least one heavy/real gate must be skipped on a docs-only change (proves narrowing).
|
|
25
|
-
const skippedHeavy = docsOnly.skipped.length;
|
|
26
|
-
assertGate(skippedHeavy > 0, 'docs-only change must skip at least one unrelated gate', { skipped: skippedHeavy });
|
|
27
|
-
// 2) Publish mode must select every required_for_publish gate.
|
|
28
|
-
const publishPlan = selectGates(manifest.gates, [], { publish: true });
|
|
29
|
-
const requiredIds = manifest.gates.filter((g) => g.required_for_publish).map((g) => g.id);
|
|
30
|
-
const selectedPublish = new Set(publishPlan.selected.map((g) => g.id));
|
|
31
|
-
const missingRequired = requiredIds.filter((id) => !selectedPublish.has(id));
|
|
32
|
-
assertGate(missingRequired.length === 0, 'publish mode must never skip required_for_publish gates', { missingRequired });
|
|
33
|
-
const report = {
|
|
34
|
-
schema: 'sks.release-check-dynamic.v1',
|
|
35
|
-
ok: true,
|
|
36
|
-
mode: publish ? 'publish' : 'incremental',
|
|
37
|
-
base: baseArg || null,
|
|
38
|
-
changed_files: changedFiles,
|
|
39
|
-
selected: plan.selected.map((g) => g.id),
|
|
40
|
-
skipped: plan.skipped,
|
|
41
|
-
invariants: { docs_only_skips_heavy: true, publish_keeps_required: true }
|
|
42
|
-
};
|
|
43
|
-
const reportDir = path.join(root, '.sneakoscope', 'reports');
|
|
44
|
-
fs.mkdirSync(reportDir, { recursive: true });
|
|
45
|
-
fs.writeFileSync(path.join(reportDir, 'release-check-dynamic.json'), `${JSON.stringify(report, null, 2)}\n`);
|
|
46
|
-
emitGate('release:check:dynamic', { selected: report.selected.length, skipped: report.skipped.length, changed_files: changedFiles.length, mode: report.mode });
|
|
47
|
-
function readArg(list, name) {
|
|
48
|
-
const i = list.indexOf(name);
|
|
49
|
-
return i >= 0 ? list[i + 1] || null : null;
|
|
50
|
-
}
|
|
51
|
-
function loadDynamicManifest() {
|
|
52
|
-
const v2Path = path.join(root, 'release-gates.v2.json');
|
|
53
|
-
if (fs.existsSync(v2Path)) {
|
|
54
|
-
const parsed = JSON.parse(fs.readFileSync(v2Path, 'utf8'));
|
|
55
|
-
const releaseNodes = (Array.isArray(parsed.gates) ? parsed.gates : []).filter((gate) => Array.isArray(gate.preset) && gate.preset.includes('release'));
|
|
56
|
-
const byId = new Map(releaseNodes.map((gate) => [gate.id, gate]));
|
|
57
|
-
const dynamic = buildGateManifest(releaseNodes.map((gate) => gate.id));
|
|
58
|
-
return {
|
|
59
|
-
schema: 'sks.release-gate-manifest.v1.from-v2',
|
|
60
|
-
gates: dynamic.gates.map((entry) => {
|
|
61
|
-
const node = byId.get(entry.id);
|
|
62
|
-
const resource = Array.isArray(node?.resource) ? node.resource.join(',') : '';
|
|
63
|
-
return {
|
|
64
|
-
...entry,
|
|
65
|
-
affected_by: usefulCacheInputs(node?.cache?.inputs, entry.affected_by),
|
|
66
|
-
cost: node?.side_effect === 'real-env' || resource.includes('real') ? 'real' : entry.cost
|
|
67
|
-
};
|
|
68
|
-
})
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
const manifestPath = path.join(root, 'release-gates.json');
|
|
72
|
-
if (fs.existsSync(manifestPath))
|
|
73
|
-
return JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
74
|
-
const legacySource = fs.readFileSync(path.join(root, 'src/scripts/release-parallel-check.ts'), 'utf8');
|
|
75
|
-
const legacyIds = [...legacySource.matchAll(/task\('([^']+)'/g)].map((m) => m[1]);
|
|
76
|
-
const releaseCheckIds = [...String(pkg.scripts?.['release:check'] || '').matchAll(/npm run ([^\s&]+)/g)].map((m) => m[1]);
|
|
77
|
-
const ids = [...new Set([...legacyIds, ...releaseCheckIds])].filter((id) => id && id !== 'build' && id !== 'release:check:parallel');
|
|
78
|
-
return buildGateManifest(ids);
|
|
79
|
-
}
|
|
80
|
-
function usefulCacheInputs(inputs, fallback) {
|
|
81
|
-
if (!Array.isArray(inputs) || !inputs.length)
|
|
82
|
-
return fallback;
|
|
83
|
-
if (inputs.some((input) => ['src/**', 'package.json', 'release-gates.v2.json', 'schemas/**'].includes(input)))
|
|
84
|
-
return fallback;
|
|
85
|
-
return inputs;
|
|
86
|
-
}
|
|
87
|
-
function detectChangedFiles(base) {
|
|
88
|
-
try {
|
|
89
|
-
let ref = base;
|
|
90
|
-
if (!ref) {
|
|
91
|
-
const mb = spawnSync('git', ['merge-base', 'HEAD', 'origin/main'], { cwd: root, encoding: 'utf8' });
|
|
92
|
-
ref = mb.status === 0 ? mb.stdout.trim() : 'HEAD~1';
|
|
93
|
-
}
|
|
94
|
-
const diff = spawnSync('git', ['diff', '--name-only', `${ref}...HEAD`], { cwd: root, encoding: 'utf8' });
|
|
95
|
-
const status = spawnSync('git', ['status', '--porcelain'], { cwd: root, encoding: 'utf8' });
|
|
96
|
-
const files = new Set();
|
|
97
|
-
if (diff.status === 0)
|
|
98
|
-
diff.stdout.split('\n').map((s) => s.trim()).filter(Boolean).forEach((f) => files.add(f));
|
|
99
|
-
if (status.status === 0)
|
|
100
|
-
status.stdout.split('\n').map((s) => s.slice(3).trim()).filter(Boolean).forEach((f) => files.add(f));
|
|
101
|
-
return [...files];
|
|
102
|
-
}
|
|
103
|
-
catch {
|
|
104
|
-
return [];
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
//# sourceMappingURL=release-check-dynamic.js.map
|
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// @ts-nocheck
|
|
3
|
-
import { spawnSync } from 'node:child_process';
|
|
4
|
-
import crypto from 'node:crypto';
|
|
5
|
-
import fs from 'node:fs';
|
|
6
|
-
import path from 'node:path';
|
|
7
|
-
import { fileURLToPath } from 'node:url';
|
|
8
|
-
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
9
|
-
const stampPath = process.env.SKS_RELEASE_STAMP_PATH || path.join(root, '.sneakoscope', 'reports', 'release-check-stamp.json');
|
|
10
|
-
const command = process.argv[2] || 'verify';
|
|
11
|
-
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
12
|
-
function fail(message, detail = '') {
|
|
13
|
-
console.error(`Release check stamp failed: ${message}`);
|
|
14
|
-
if (detail)
|
|
15
|
-
console.error(detail.trim());
|
|
16
|
-
process.exit(2);
|
|
17
|
-
}
|
|
18
|
-
function readJson(rel) {
|
|
19
|
-
return JSON.parse(fs.readFileSync(path.join(root, rel), 'utf8'));
|
|
20
|
-
}
|
|
21
|
-
function sha256(value) {
|
|
22
|
-
return crypto.createHash('sha256').update(value).digest('hex');
|
|
23
|
-
}
|
|
24
|
-
function treeDigest(dir) {
|
|
25
|
-
if (!fs.existsSync(dir))
|
|
26
|
-
return { digest: null, file_count: 0 };
|
|
27
|
-
const files = [];
|
|
28
|
-
collectFiles(dir, files);
|
|
29
|
-
const hash = crypto.createHash('sha256');
|
|
30
|
-
for (const file of files.sort()) {
|
|
31
|
-
const rel = path.relative(dir, file).split(path.sep).join('/');
|
|
32
|
-
const stat = fs.statSync(file);
|
|
33
|
-
hash.update(rel);
|
|
34
|
-
hash.update('\0');
|
|
35
|
-
hash.update(String(stat.size));
|
|
36
|
-
hash.update('\0');
|
|
37
|
-
hash.update(sha256(fs.readFileSync(file)));
|
|
38
|
-
hash.update('\0');
|
|
39
|
-
}
|
|
40
|
-
return { digest: hash.digest('hex'), file_count: files.length };
|
|
41
|
-
}
|
|
42
|
-
function fileDigestForPackageFiles(pkg) {
|
|
43
|
-
const hash = crypto.createHash('sha256');
|
|
44
|
-
const files = Array.isArray(pkg.files) ? [...pkg.files].sort() : [];
|
|
45
|
-
for (const entry of files) {
|
|
46
|
-
const full = path.join(root, entry);
|
|
47
|
-
hash.update(entry);
|
|
48
|
-
hash.update('\0');
|
|
49
|
-
if (!fs.existsSync(full)) {
|
|
50
|
-
hash.update('missing\0');
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
const stat = fs.statSync(full);
|
|
54
|
-
if (stat.isDirectory()) {
|
|
55
|
-
const digest = treeDigest(full);
|
|
56
|
-
hash.update(`${digest.digest || 'empty'}:${digest.file_count}`);
|
|
57
|
-
}
|
|
58
|
-
else if (stat.isFile()) {
|
|
59
|
-
hash.update(sha256(fs.readFileSync(full)));
|
|
60
|
-
}
|
|
61
|
-
hash.update('\0');
|
|
62
|
-
}
|
|
63
|
-
return sha256(hash.digest('hex'));
|
|
64
|
-
}
|
|
65
|
-
function gitCommit() {
|
|
66
|
-
const result = spawnSync('git', ['rev-parse', 'HEAD'], { cwd: root, encoding: 'utf8' });
|
|
67
|
-
return result.status === 0 ? result.stdout.trim() : null;
|
|
68
|
-
}
|
|
69
|
-
function runRefreshCommand() {
|
|
70
|
-
const override = process.env.SKS_RELEASE_CHECK_REFRESH_COMMAND;
|
|
71
|
-
if (override) {
|
|
72
|
-
return spawnSync(override, {
|
|
73
|
-
cwd: root,
|
|
74
|
-
encoding: 'utf8',
|
|
75
|
-
env: process.env,
|
|
76
|
-
shell: true,
|
|
77
|
-
stdio: 'inherit'
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
return spawnSync(npmCmd, ['run', 'release:check:full'], {
|
|
81
|
-
cwd: root,
|
|
82
|
-
encoding: 'utf8',
|
|
83
|
-
env: process.env,
|
|
84
|
-
stdio: 'inherit'
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
function releaseGateHash(pkg) {
|
|
88
|
-
const manifests = ['release-gates.v2.json', 'release-gates.json'].map((rel) => {
|
|
89
|
-
const file = path.join(root, rel);
|
|
90
|
-
return fs.existsSync(file) ? `${rel}\0${fs.readFileSync(file, 'utf8')}` : `${rel}\0missing`;
|
|
91
|
-
}).join('\0');
|
|
92
|
-
return sha256(`${pkg.scripts?.['release:check'] || ''}\0${pkg.scripts?.['prepublishOnly'] || ''}\0${manifests}`);
|
|
93
|
-
}
|
|
94
|
-
function collectFiles(dir, out) {
|
|
95
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
96
|
-
const file = path.join(dir, entry.name);
|
|
97
|
-
if (entry.isDirectory())
|
|
98
|
-
collectFiles(file, out);
|
|
99
|
-
else if (entry.isFile())
|
|
100
|
-
out.push(file);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
function gitFiles() {
|
|
104
|
-
const result = spawnSync('git', ['ls-files', '-z', '--cached', '--others', '--exclude-standard'], {
|
|
105
|
-
cwd: root,
|
|
106
|
-
encoding: 'utf8'
|
|
107
|
-
});
|
|
108
|
-
if (result.status !== 0)
|
|
109
|
-
fail('unable to list release-relevant files', result.stderr || result.stdout);
|
|
110
|
-
return result.stdout.split('\0').filter(Boolean);
|
|
111
|
-
}
|
|
112
|
-
function releaseRelevant(file) {
|
|
113
|
-
if (!file || file.startsWith('.sneakoscope/') || file.startsWith('.codex/') || file.startsWith('.agents/'))
|
|
114
|
-
return false;
|
|
115
|
-
if (file.startsWith('node_modules/') || file.startsWith('dist/') || file.startsWith('coverage/'))
|
|
116
|
-
return false;
|
|
117
|
-
if (file.startsWith('crates/sks-core/target/'))
|
|
118
|
-
return false;
|
|
119
|
-
if (/\.tgz$|\.log$/i.test(file))
|
|
120
|
-
return false;
|
|
121
|
-
if (/^(package|package-lock)\.json$/.test(file))
|
|
122
|
-
return true;
|
|
123
|
-
if (/^release-gates(?:\.v2)?\.json$/.test(file))
|
|
124
|
-
return true;
|
|
125
|
-
if (/^tsconfig.*\.json$/.test(file))
|
|
126
|
-
return true;
|
|
127
|
-
if (/^(README|CHANGELOG|LICENSE)(\.md)?$/i.test(file))
|
|
128
|
-
return true;
|
|
129
|
-
return [
|
|
130
|
-
'bin/',
|
|
131
|
-
'src/',
|
|
132
|
-
'scripts/',
|
|
133
|
-
'test/',
|
|
134
|
-
'docs/',
|
|
135
|
-
'crates/sks-core/Cargo.',
|
|
136
|
-
'crates/sks-core/src/'
|
|
137
|
-
].some((prefix) => file.startsWith(prefix));
|
|
138
|
-
}
|
|
139
|
-
function releaseSnapshot() {
|
|
140
|
-
const files = gitFiles().filter(releaseRelevant).sort();
|
|
141
|
-
const hash = crypto.createHash('sha256');
|
|
142
|
-
for (const file of files) {
|
|
143
|
-
const full = path.join(root, file);
|
|
144
|
-
let stat;
|
|
145
|
-
try {
|
|
146
|
-
stat = fs.statSync(full);
|
|
147
|
-
}
|
|
148
|
-
catch {
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
if (!stat.isFile())
|
|
152
|
-
continue;
|
|
153
|
-
const contentHash = sha256(fs.readFileSync(full));
|
|
154
|
-
hash.update(file);
|
|
155
|
-
hash.update('\0');
|
|
156
|
-
hash.update(String(stat.size));
|
|
157
|
-
hash.update('\0');
|
|
158
|
-
hash.update(contentHash);
|
|
159
|
-
hash.update('\0');
|
|
160
|
-
}
|
|
161
|
-
return {
|
|
162
|
-
digest: hash.digest('hex'),
|
|
163
|
-
file_count: files.length
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
function currentStampPayload() {
|
|
167
|
-
const pkg = readJson('package.json');
|
|
168
|
-
const snapshot = releaseSnapshot();
|
|
169
|
-
const dist = treeDigest(path.join(root, 'dist'));
|
|
170
|
-
return {
|
|
171
|
-
schema: 'sks.release-check-stamp.v1',
|
|
172
|
-
package_name: pkg.name,
|
|
173
|
-
package_version: pkg.version,
|
|
174
|
-
git_commit: gitCommit(),
|
|
175
|
-
package_json_sha256: sha256(fs.readFileSync(path.join(root, 'package.json'))),
|
|
176
|
-
package_files_sha256: fileDigestForPackageFiles(pkg),
|
|
177
|
-
dist_build_sha256: dist.digest,
|
|
178
|
-
dist_file_count: dist.file_count,
|
|
179
|
-
release_gate_sha256: releaseGateHash(pkg),
|
|
180
|
-
release_check_sha256: sha256(pkg.scripts?.['release:check'] || ''),
|
|
181
|
-
source_digest: snapshot.digest,
|
|
182
|
-
source_file_count: snapshot.file_count
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
function writeStamp() {
|
|
186
|
-
const payload = {
|
|
187
|
-
...currentStampPayload(),
|
|
188
|
-
generated_at: new Date().toISOString()
|
|
189
|
-
};
|
|
190
|
-
fs.mkdirSync(path.dirname(stampPath), { recursive: true });
|
|
191
|
-
fs.writeFileSync(stampPath, `${JSON.stringify(payload, null, 2)}\n`);
|
|
192
|
-
console.log(`Release check stamp written: ${path.relative(root, stampPath)} (${payload.source_file_count} files)`);
|
|
193
|
-
}
|
|
194
|
-
function inspectStamp() {
|
|
195
|
-
if (!fs.existsSync(stampPath)) {
|
|
196
|
-
return {
|
|
197
|
-
ok: false,
|
|
198
|
-
message: 'missing release:check stamp',
|
|
199
|
-
detail: 'Run `npm run release:check:full` once, then rerun the publish command.'
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
let stamp;
|
|
203
|
-
try {
|
|
204
|
-
stamp = JSON.parse(fs.readFileSync(stampPath, 'utf8'));
|
|
205
|
-
}
|
|
206
|
-
catch (err) {
|
|
207
|
-
return {
|
|
208
|
-
ok: false,
|
|
209
|
-
message: 'unable to read release:check stamp',
|
|
210
|
-
detail: err.message
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
const current = currentStampPayload();
|
|
214
|
-
const mismatches = [];
|
|
215
|
-
for (const key of ['schema', 'package_name', 'package_version', 'package_json_sha256', 'package_files_sha256', 'dist_build_sha256', 'dist_file_count', 'release_gate_sha256', 'release_check_sha256', 'source_digest', 'source_file_count']) {
|
|
216
|
-
if (stamp[key] !== current[key])
|
|
217
|
-
mismatches.push(`${key}: stamp=${stamp[key] || 'missing'} current=${current[key] || 'missing'}`);
|
|
218
|
-
}
|
|
219
|
-
if (mismatches.length) {
|
|
220
|
-
return {
|
|
221
|
-
ok: false,
|
|
222
|
-
message: 'release:check stamp is stale',
|
|
223
|
-
detail: `${mismatches.join('\n')}\nRun \`npm run release:check:full\` again before publishing.`,
|
|
224
|
-
current
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
return { ok: true, current };
|
|
228
|
-
}
|
|
229
|
-
function verifyStamp() {
|
|
230
|
-
const result = inspectStamp();
|
|
231
|
-
if (!result.ok)
|
|
232
|
-
fail(result.message, result.detail);
|
|
233
|
-
const current = result.current;
|
|
234
|
-
console.log(`Release check stamp verified: ${path.relative(root, stampPath)} (${current.source_file_count} files)`);
|
|
235
|
-
}
|
|
236
|
-
function ensureStamp() {
|
|
237
|
-
const first = inspectStamp();
|
|
238
|
-
if (first.ok) {
|
|
239
|
-
console.log(`Release check stamp verified: ${path.relative(root, stampPath)} (${first.current.source_file_count} files)`);
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
console.error('Release check stamp is not current; running full `npm run release:check:full` refresh.');
|
|
243
|
-
if (first.detail)
|
|
244
|
-
console.error(first.detail.trim());
|
|
245
|
-
const refresh = runRefreshCommand();
|
|
246
|
-
if (refresh.status !== 0)
|
|
247
|
-
process.exit(refresh.status || 1);
|
|
248
|
-
const second = inspectStamp();
|
|
249
|
-
if (!second.ok)
|
|
250
|
-
fail(second.message, second.detail);
|
|
251
|
-
console.log(`Release check stamp verified: ${path.relative(root, stampPath)} (${second.current.source_file_count} files)`);
|
|
252
|
-
}
|
|
253
|
-
if (command === 'write')
|
|
254
|
-
writeStamp();
|
|
255
|
-
else if (command === 'verify')
|
|
256
|
-
verifyStamp();
|
|
257
|
-
else if (command === 'ensure')
|
|
258
|
-
ensureStamp();
|
|
259
|
-
else
|
|
260
|
-
fail(`unknown command ${command}`, 'Usage: node ./dist/scripts/release-check-stamp.js <write|verify|ensure>');
|
|
261
|
-
//# sourceMappingURL=release-check-stamp.js.map
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import { runReleaseGateDag } from '../core/release/release-gate-dag.js';
|
|
5
|
-
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
6
|
-
const args = process.argv.slice(2);
|
|
7
|
-
const presetIndex = args.indexOf('--preset');
|
|
8
|
-
const preset = presetIndex >= 0 ? args[presetIndex + 1] : 'release';
|
|
9
|
-
const changedSinceIndex = args.indexOf('--changed-since');
|
|
10
|
-
const changedSince = changedSinceIndex >= 0 ? (args[changedSinceIndex + 1] || null) : null;
|
|
11
|
-
const slaIndex = args.indexOf('--sla');
|
|
12
|
-
const slaMs = slaIndex >= 0 ? parseDurationMs(args[slaIndex + 1] || '') : null;
|
|
13
|
-
const result = await runReleaseGateDag({
|
|
14
|
-
root,
|
|
15
|
-
...(preset === undefined ? {} : { preset }),
|
|
16
|
-
changedSince,
|
|
17
|
-
slaMs,
|
|
18
|
-
full: args.includes('--full'),
|
|
19
|
-
explain: args.includes('--explain'),
|
|
20
|
-
noCache: args.includes('--no-cache'),
|
|
21
|
-
failFast: args.includes('--fail-fast'),
|
|
22
|
-
useGatePacks: args.includes('--use-gate-packs')
|
|
23
|
-
});
|
|
24
|
-
console.log(`SKS Release DAG
|
|
25
|
-
gates: ${result.total_gates} total, ${result.selected_gates} selected, ${result.cached} cached
|
|
26
|
-
affected: ${result.affected_selection?.mode || 'full'} selected=${result.selected_gate_ids.length} skipped=${result.skipped_by_affected.length}
|
|
27
|
-
concurrency: ${result.budget_summary}
|
|
28
|
-
peak_running: ${result.peak_running}
|
|
29
|
-
completed: ${result.completed} pass, ${result.failed} fail
|
|
30
|
-
wall: ${(result.wall_ms / 1000).toFixed(1)}s
|
|
31
|
-
parallelism_gain: ${result.parallelism_gain}
|
|
32
|
-
cpu_time_saved: ${(result.cpu_time_saved_ms / 1000).toFixed(1)}s
|
|
33
|
-
critical_path: ${(result.critical_path_ms / 1000).toFixed(1)}s
|
|
34
|
-
certificate: ${result.completion_certificate.confidence} sla=${(result.completion_certificate.sla_ms / 1000).toFixed(0)}s met=${result.completion_certificate.sla_met}
|
|
35
|
-
report: ${result.report_dir}`);
|
|
36
|
-
if (!result.ok) {
|
|
37
|
-
for (const failure of result.failures) {
|
|
38
|
-
console.error(`[fail] ${failure.id} exit=${failure.exit_code}\n${failure.stderr_tail}`);
|
|
39
|
-
}
|
|
40
|
-
process.exit(1);
|
|
41
|
-
}
|
|
42
|
-
function parseDurationMs(value) {
|
|
43
|
-
const match = String(value || '').trim().match(/^(\d+(?:\.\d+)?)(ms|s|m)?$/i);
|
|
44
|
-
if (!match)
|
|
45
|
-
return null;
|
|
46
|
-
const amount = Number(match[1]);
|
|
47
|
-
const unit = (match[2] || 'ms').toLowerCase();
|
|
48
|
-
if (!Number.isFinite(amount) || amount <= 0)
|
|
49
|
-
return null;
|
|
50
|
-
if (unit === 'm')
|
|
51
|
-
return Math.floor(amount * 60_000);
|
|
52
|
-
if (unit === 's')
|
|
53
|
-
return Math.floor(amount * 1000);
|
|
54
|
-
return Math.floor(amount);
|
|
55
|
-
}
|
|
56
|
-
//# sourceMappingURL=release-gate-dag-runner.js.map
|