@rightkit/release 0.2.123 → 0.2.124
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/build-release.mjs +2 -4
- package/ownership-evidence.mjs +4 -2
- package/ownership-report.mjs +2 -1
- package/ownership.mjs +26 -7
- package/package.json +1 -1
- package/rightkit-versions.json +10 -3
package/build-release.mjs
CHANGED
|
@@ -241,10 +241,8 @@ try {
|
|
|
241
241
|
}
|
|
242
242
|
const ownershipEvidence = preflight.find((check) => check.name === "ownership")?.evidence;
|
|
243
243
|
if (!ownershipEvidence) fail("ownership evidence was not produced by release preflight");
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
if (ownershipEvidence.source !== "enrollment-pending-advisory" && ownershipEvidence.commit !== commit) {
|
|
247
|
-
fail("ownership scan source differs from release commit");
|
|
244
|
+
if (ownershipEvidence.commit !== commit) {
|
|
245
|
+
fail(`ownership scan commit ${ownershipEvidence.commit ?? "(missing)"} differs from release commit ${commit}; regenerate with right-release ownership at release commit ${commit}`);
|
|
248
246
|
}
|
|
249
247
|
for (const check of preflight) {
|
|
250
248
|
if (check.status !== "ok") console.error(`[right-release] preflight ${check.name}: ${check.detail}`);
|
package/ownership-evidence.mjs
CHANGED
|
@@ -41,7 +41,7 @@ function createEvidence(repoRoot, {
|
|
|
41
41
|
let enrollmentPending = trustState.enrolled !== true;
|
|
42
42
|
if (committed && enrollmentPending && !allowPendingAdvisory && !trustState.trust?.rootFingerprint) throw new Error("protected ownership evidence requires both distinct, unrevoked approval keys to be enrolled");
|
|
43
43
|
// A valid protected registry with incomplete key enrollment can still drive
|
|
44
|
-
// a useful local backlog scan. It cannot
|
|
44
|
+
// a useful local backlog scan. It cannot assert a protected source binding or verify
|
|
45
45
|
// approval signatures, so this result is deliberately advisory.
|
|
46
46
|
const useCommittedSource = committed && (!enrollmentPending || Boolean(trustState.trust?.rootFingerprint));
|
|
47
47
|
if (useCommittedSource && requireCleanCheckout) assertCommittedCheckoutClean(root);
|
|
@@ -68,6 +68,7 @@ function createEvidence(repoRoot, {
|
|
|
68
68
|
}
|
|
69
69
|
if (useCommittedSource && requireCleanCheckout) assertCommittedCheckoutClean(root, { expectedCommit: source.commit, expectedTree: source.tree });
|
|
70
70
|
const commit = useCommittedSource ? source.commit : git(root, ["rev-parse", "HEAD"], gitRunner);
|
|
71
|
+
if (!commit) throw new Error("ownership evidence: scan source did not produce a HEAD commit");
|
|
71
72
|
const tree = useCommittedSource ? source.tree : git(root, ["rev-parse", `${commit}^{tree}`], gitRunner);
|
|
72
73
|
const scannerVersion = SCANNER_VERSION;
|
|
73
74
|
const catalogVersion = manifest.version ?? String(manifest.schema ?? "1");
|
|
@@ -154,7 +155,8 @@ function createEvidence(repoRoot, {
|
|
|
154
155
|
schema: 1,
|
|
155
156
|
repositoryIdentity: repositoryIdentity ? canonicalRepositoryIdentity(result.metadata?.repositoryIdentity) : null,
|
|
156
157
|
source: enrollmentPending ? "enrollment-pending-advisory" : useCommittedSource ? "committed-git-tree" : "mutable-checkout-test-seam",
|
|
157
|
-
|
|
158
|
+
commit,
|
|
159
|
+
...(enrollmentPending ? {} : { tree }),
|
|
158
160
|
catalogDigest,
|
|
159
161
|
catalogVersion,
|
|
160
162
|
scannerVersion,
|
package/ownership-report.mjs
CHANGED
|
@@ -37,6 +37,7 @@ function statusFor(row, report) {
|
|
|
37
37
|
if (report.enrollmentPending && (row.status === "blocked" || row.status === "blocked-by-findings")) return "enrollment-pending";
|
|
38
38
|
if (row.status === "blocked" || row.status === "blocked-by-findings") return "blocked";
|
|
39
39
|
if (row.status === "excepted") return "excepted";
|
|
40
|
+
if (row.status === "qa-only") return "qa-only";
|
|
40
41
|
if (row.status === "verified-integration") return "verified-integration";
|
|
41
42
|
if (row.status === "declared-dependency" || row.uses === true) return "declared-dependency";
|
|
42
43
|
if (report.coverage?.length) return "unscanned";
|
|
@@ -59,7 +60,7 @@ function exceptionRequests(root, app, report) {
|
|
|
59
60
|
|
|
60
61
|
function asRecord(app, root, scope, report, manifest, versions, scannedAt) {
|
|
61
62
|
const meta = report.metadata ?? {};
|
|
62
|
-
const findings = (report.findings ?? []).map((f) => ({ fingerprint: f.fingerprint, capability: f.capability, file: f.file, line: f.line ?? 0, occurrenceHash: f.occurrenceHash ?? sha256(canonicalJson(f)) }));
|
|
63
|
+
const findings = (report.findings ?? []).map((f) => ({ fingerprint: f.fingerprint, capability: f.capability, file: f.file, line: f.line ?? 0, occurrenceHash: f.occurrenceHash ?? sha256(canonicalJson(f)), ...(f.qaOnly ? { qaOnly: true } : {}) }));
|
|
63
64
|
const files = [...new Set(findings.map((f) => f.file))].sort();
|
|
64
65
|
const contentDigestSource = meta.contentDigests ? "scanner-inputs" : "finding-occurrences-only";
|
|
65
66
|
const contentDigests = meta.contentDigests ?? findings.map((f) => ({ path: f.file, digest: f.occurrenceHash, kind: "finding-occurrence" }));
|
package/ownership.mjs
CHANGED
|
@@ -15,7 +15,7 @@ export const EXCEPTIONS_FILE = path.join(".rightkit", "ownership-exceptions.json
|
|
|
15
15
|
const CODE_EXT = new Set([".rs", ".ts", ".tsx", ".js", ".mjs", ".cjs", ".jsx", ".swift", ".m", ".mm", ".c", ".cc", ".cpp", ".h", ".py", ".go", ".kt", ".java", ".cs"]);
|
|
16
16
|
// Listed installed/build inputs retain explicit coverage diagnostics.
|
|
17
17
|
const OPAQUE_DIRS = new Set(["target", ".build", "DerivedData", "Pods"]);
|
|
18
|
-
export const SCANNER_VERSION = "2.
|
|
18
|
+
export const SCANNER_VERSION = "2.3.0";
|
|
19
19
|
export const catalogDigest = (manifest) => sha256(manifest);
|
|
20
20
|
const byteDigest = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
21
21
|
const inputDigest = (file) => file.digest ?? byteDigest(file.bytes);
|
|
@@ -249,7 +249,8 @@ function dependenciesFromInventory(appRoot, inventory) {
|
|
|
249
249
|
const alias = spec.match(/^(?:npm:)(@[^/]+\/[^@]+|[^@]+)(?:@|$)/)?.[1];
|
|
250
250
|
const localPath = spec.match(/^(?:file:|link:)(.+)$/)?.[1];
|
|
251
251
|
const isGit = /^(git\+|https?:.*\.git)/i.test(spec);
|
|
252
|
-
|
|
252
|
+
const devOnly = field === "devDependencies" && !["dependencies", "optionalDependencies", "peerDependencies"].some((section) => Object.hasOwn(pkg[section] ?? {}, name));
|
|
253
|
+
addDependency(deps, "npm", alias ?? name, rel, { alias: alias ? name : undefined, local: !!localPath, source: localPath ? spec.split(":")[0] : isGit ? "git" : spec.startsWith("workspace:") ? "workspace" : undefined, ...(devOnly ? { devOnly: true } : {}) });
|
|
253
254
|
if (localPath) {
|
|
254
255
|
resolveLocalPath(appRoot, rel, localPath, coverage, rel, inventory, "local-npm-dependency", name);
|
|
255
256
|
} else if (isGit) coverage.push({ path: rel, status: "unscanned", reason: `git-npm-dependency-source:${name}` });
|
|
@@ -359,13 +360,29 @@ function trustedProvenance(appRoot, inventory, trust, repositoryIdentity, covera
|
|
|
359
360
|
return { excluded, classifications };
|
|
360
361
|
}
|
|
361
362
|
|
|
363
|
+
function npmImportUsage(inventory) {
|
|
364
|
+
const usage = new Map(), qaSegments = new Set(["test", "tests", "__tests__", "e2e", "qa", "fixtures"]);
|
|
365
|
+
for (const { rel, bytes } of inventory.files) {
|
|
366
|
+
if (!CODE_EXT.has(path.posix.extname(rel).toLowerCase()) || !bytes) continue;
|
|
367
|
+
const qaPath = rel.split("/").some((segment) => qaSegments.has(segment)) || /(?:\.(?:test|spec)\.|^qa-.*\.)/.test(path.posix.basename(rel));
|
|
368
|
+
for (const match of bytes.toString("utf8").matchAll(/\bimport\b\s*(?:[\w$*{},\s]+?\bfrom\s*)?["']([^"']+)["']|\b(?:import|require)\s*\(\s*["']([^"']+)["']/g)) {
|
|
369
|
+
const specifier = match[1] ?? match[2];
|
|
370
|
+
const name = specifier.split("/").slice(0, specifier.startsWith("@") ? 2 : 1).join("/");
|
|
371
|
+
usage.set(name, (usage.get(name) ?? true) && qaPath);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return usage;
|
|
375
|
+
}
|
|
376
|
+
|
|
362
377
|
function scanInternal(appRoot, manifest, { inventory = fileInventory(appRoot), trust, repositoryIdentity, repositoryRoot = appRoot, pathPrefix = "" } = {}) {
|
|
363
378
|
const findings = [], coverage = [...inventory.coverage], deps = dependenciesFromInventory(appRoot, inventory), contentDigests = [...(deps.contentDigests ?? [])];
|
|
364
379
|
coverage.push(...(deps.coverage ?? []));
|
|
365
380
|
const provenance = trustedProvenance(appRoot, inventory, trust, repositoryIdentity, coverage, repositoryRoot, pathPrefix);
|
|
381
|
+
const npmUsage = npmImportUsage(inventory);
|
|
366
382
|
for (const c of manifest.capabilities) for (const f of c.fingerprints.filter((x) => x.type === "dependency")) for (const d of deps) {
|
|
367
383
|
if (d.ecosystem === f.ecosystem && (d.name === f.name || d.alias === f.name)) {
|
|
368
|
-
|
|
384
|
+
const qaOnly = c.capability !== "desktop-qa" && d.ecosystem === "npm" && d.devOnly && npmUsage.get(d.alias ?? d.name) === true;
|
|
385
|
+
findings.push({ fingerprint: f.id, capability: c.capability, package: c.package, file: d.file, line: 0, text: `${d.ecosystem} dependency ${d.alias ? `${d.alias} aliases ` : ""}${d.name}`, local: d.local, provenance: d.source, ...(qaOnly ? { qaOnly: true } : {}) });
|
|
369
386
|
}
|
|
370
387
|
}
|
|
371
388
|
const matchers = sourceMatchers(manifest);
|
|
@@ -469,16 +486,18 @@ export function evaluate(findings, exceptions, options = {}) {
|
|
|
469
486
|
}
|
|
470
487
|
const pending = enrollmentPending(options.trust, options.trustEnrollment);
|
|
471
488
|
if (pending) warnings.push("approval enrollment pending; detected findings remain open backlog requests until distinct Windows & macOS keys are enrolled");
|
|
472
|
-
|
|
473
|
-
|
|
489
|
+
const qaOnly = findings.filter((f) => f.qaOnly === true && f.capability !== "desktop-qa");
|
|
490
|
+
for (const f of findings) if (!qaOnly.includes(f) && !covered.some((x) => x.occurrenceHash === occurrenceHash(f))) violations.push({ ...f, occurrenceHash: occurrenceHash(f) });
|
|
491
|
+
return { violations, covered, qaOnly, problems, warnings, enrollmentPending: pending };
|
|
474
492
|
}
|
|
475
493
|
|
|
476
494
|
export function capabilityTable(manifest, deps, result) {
|
|
477
495
|
return [...new Map(manifest.capabilities.map((c) => [c.capability, c.package])).entries()].map(([capability, owner]) => {
|
|
478
496
|
const declared = deps.some((d) => d.name === owner || d.name === owner.replace(/^rightkit-/, "@rightkit/"));
|
|
479
497
|
const own = result.violations.filter((x) => x.capability === capability), covered = result.covered.filter((x) => x.capability === capability);
|
|
480
|
-
const
|
|
481
|
-
|
|
498
|
+
const qaOnly = (result.qaOnly ?? []).filter((x) => x.capability === capability && !covered.some((item) => item.occurrenceHash === x.occurrenceHash));
|
|
499
|
+
const status = own.length ? "blocked" : covered.length ? "excepted" : qaOnly.length ? "qa-only" : declared ? "declared-dependency" : result.coverage?.length ? "unscanned" : "not-detected";
|
|
500
|
+
return { capability, owner, uses: declared, status, count: own.length + covered.length + qaOnly.length };
|
|
482
501
|
});
|
|
483
502
|
}
|
|
484
503
|
export function formatTable(rows) { return ["capability owner status", ...rows.map((x) => `${x.capability} ${x.owner} ${x.status}`)].join("\n"); }
|
package/package.json
CHANGED
package/rightkit-versions.json
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"@rightkit/platform-ui": "0.1.2",
|
|
30
30
|
"@rightkit/qa": "0.3.0",
|
|
31
31
|
"@rightkit/qa-chrome": "0.1.2",
|
|
32
|
-
"@rightkit/release": "0.2.
|
|
32
|
+
"@rightkit/release": "0.2.124",
|
|
33
33
|
"@rightkit/settings-ui": "0.2.7",
|
|
34
34
|
"@rightkit/shell": "0.1.0",
|
|
35
35
|
"@rightkit/tauri": "0.1.1",
|
|
@@ -145,7 +145,8 @@
|
|
|
145
145
|
"0.2.119",
|
|
146
146
|
"0.2.120",
|
|
147
147
|
"0.2.121",
|
|
148
|
-
"0.2.122"
|
|
148
|
+
"0.2.122",
|
|
149
|
+
"0.2.123"
|
|
149
150
|
],
|
|
150
151
|
"@rightkit/qa": [
|
|
151
152
|
"0.1.0",
|
|
@@ -245,6 +246,9 @@
|
|
|
245
246
|
"rightkit-playback": "0.1.0",
|
|
246
247
|
"rightkit-process": "0.2.1",
|
|
247
248
|
"rightkit-qa": "0.2.7",
|
|
249
|
+
"rightkit-qts": "0.1.1",
|
|
250
|
+
"rightkit-qts-ggml": "0.1.0",
|
|
251
|
+
"rightkit-qts-ggml-sys": "0.1.0",
|
|
248
252
|
"rightkit-sandbox": "0.1.0",
|
|
249
253
|
"rightkit-search": "0.1.0",
|
|
250
254
|
"rightkit-secrets": "0.1.0",
|
|
@@ -292,6 +296,9 @@
|
|
|
292
296
|
"rightkit-playback": "0.1.0",
|
|
293
297
|
"rightkit-process": "0.2.1",
|
|
294
298
|
"rightkit-qa": "0.2.7",
|
|
299
|
+
"rightkit-qts": "0.1.1",
|
|
300
|
+
"rightkit-qts-ggml": "0.1.0",
|
|
301
|
+
"rightkit-qts-ggml-sys": "0.1.0",
|
|
295
302
|
"rightkit-sandbox": "0.1.0",
|
|
296
303
|
"rightkit-search": "0.1.0",
|
|
297
304
|
"rightkit-secrets": "0.1.0",
|
|
@@ -319,7 +326,7 @@
|
|
|
319
326
|
"@rightkit/platform-ui": "0.1.2",
|
|
320
327
|
"@rightkit/qa": "0.3.0",
|
|
321
328
|
"@rightkit/qa-chrome": "0.1.2",
|
|
322
|
-
"@rightkit/release": "0.2.
|
|
329
|
+
"@rightkit/release": "0.2.123",
|
|
323
330
|
"@rightkit/settings-ui": "0.2.7",
|
|
324
331
|
"@rightkit/shell": "0.1.0",
|
|
325
332
|
"@rightkit/tauri": "0.1.1",
|