@cejel/cejel 0.6.11 → 0.7.0-rc.2
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.
|
@@ -739,20 +739,31 @@ function isWitanHistoryDepthUnknownAbstention(report) {
|
|
|
739
739
|
function isWitanNoGitHistoryAbstention(report) {
|
|
740
740
|
return report.verdict === "insufficient_source" && report.insufficientSourceReason === noGitHistoryAbstentionReason(report.rubricVersion);
|
|
741
741
|
}
|
|
742
|
-
function
|
|
743
|
-
const result = execGit(["rev-parse", "--is-shallow-repository"], { cwd: repoPath });
|
|
744
|
-
return result.ok && result.stdout.trim() === "true";
|
|
745
|
-
}
|
|
746
|
-
function readHistoryDepth(repoPath) {
|
|
742
|
+
function readHistoryDepthAnswer(repoPath) {
|
|
747
743
|
const result = execGit(["rev-parse", "--is-shallow-repository"], { cwd: repoPath });
|
|
748
744
|
if (result.ok) {
|
|
749
745
|
const answer = result.stdout.trim();
|
|
750
|
-
if (answer === "true") return "shallow";
|
|
751
|
-
if (answer === "false") return "complete";
|
|
746
|
+
if (answer === "true") return { depth: "shallow", answeredByGit: true };
|
|
747
|
+
if (answer === "false") return { depth: "complete", answeredByGit: true };
|
|
752
748
|
}
|
|
749
|
+
const failure = result.ok ? { noAnswer: true } : isExpectedGitAbsence(result) ? {} : { failure: result.reason };
|
|
753
750
|
const entry = findVisibleGitMetadata(repoPath);
|
|
754
|
-
if (entry === void 0)
|
|
755
|
-
|
|
751
|
+
if (entry === void 0) {
|
|
752
|
+
return { depth: result.ok ? "unknown" : "not_a_repository", answeredByGit: false, ...failure };
|
|
753
|
+
}
|
|
754
|
+
return { depth: historyDepthFromFiles(entry), answeredByGit: false, ...failure };
|
|
755
|
+
}
|
|
756
|
+
function isShallowHistoryDepth(answer) {
|
|
757
|
+
return answer.depth === "shallow";
|
|
758
|
+
}
|
|
759
|
+
function isHistoryDepthUnsettledByFailure(answer) {
|
|
760
|
+
return answer.failure !== void 0 && answer.depth !== "shallow" && answer.depth !== "complete";
|
|
761
|
+
}
|
|
762
|
+
var HISTORY_DEPTH_NO_ANSWER = "Cejel: local git history-depth check gave no answer (git printed neither true nor false).";
|
|
763
|
+
function historyDepthFailureLimitation(answer) {
|
|
764
|
+
if (answer.failure === void 0 && answer.noAnswer !== true) return void 0;
|
|
765
|
+
const failed = answer.failure !== void 0 ? describeGitFailure(answer.failure, "history-depth check") : HISTORY_DEPTH_NO_ANSWER;
|
|
766
|
+
return answer.depth !== "shallow" && answer.depth !== "complete" ? `${failed} Cejel could not read the history depth from the repository's files either.` : `${failed} Cejel read the history depth from the repository's own shallow file, which is the file git reads to answer.`;
|
|
756
767
|
}
|
|
757
768
|
function isDefinitelyAbsent(error) {
|
|
758
769
|
const code = typeof error === "object" && error !== null ? error.code : void 0;
|
|
@@ -820,9 +831,8 @@ function historyDepthAbstentionReason(depth, rubricVersion, v27Wording) {
|
|
|
820
831
|
if (depth === "not_a_repository") return noGitHistoryAbstentionReason(rubricVersion);
|
|
821
832
|
return void 0;
|
|
822
833
|
}
|
|
823
|
-
function detectHistoryLimitations(repoPath) {
|
|
824
|
-
|
|
825
|
-
return [SHALLOW_HISTORY_V27_ABSTENTION];
|
|
834
|
+
function detectHistoryLimitations(repoPath, answer = readHistoryDepthAnswer(repoPath)) {
|
|
835
|
+
return answer.answeredByGit && answer.depth === "shallow" ? [SHALLOW_HISTORY_V27_ABSTENTION] : void 0;
|
|
826
836
|
}
|
|
827
837
|
|
|
828
838
|
// src/witan/abstention.ts
|
|
@@ -885,6 +895,172 @@ function witanHistoryAbstentionInstruction(label) {
|
|
|
885
895
|
// src/witan/certificate-presentation.ts
|
|
886
896
|
init_esm_shims();
|
|
887
897
|
|
|
898
|
+
// src/witan/path-limits.ts
|
|
899
|
+
init_esm_shims();
|
|
900
|
+
|
|
901
|
+
// src/witan/finding-limits.ts
|
|
902
|
+
init_esm_shims();
|
|
903
|
+
var RULE_ID_MAX = 200;
|
|
904
|
+
var MESSAGE_MAX = 500;
|
|
905
|
+
var LOCATION_MAX = 700;
|
|
906
|
+
function clamp(value, max) {
|
|
907
|
+
if (value.length <= max) return value;
|
|
908
|
+
const ellipsis = "...";
|
|
909
|
+
return `${value.slice(0, Math.max(max - ellipsis.length, 0))}${ellipsis}`;
|
|
910
|
+
}
|
|
911
|
+
function clampFinding(finding) {
|
|
912
|
+
return {
|
|
913
|
+
...finding,
|
|
914
|
+
ruleId: clamp(finding.ruleId, RULE_ID_MAX),
|
|
915
|
+
message: clamp(finding.message, MESSAGE_MAX),
|
|
916
|
+
...finding.location ? { location: clamp(finding.location, LOCATION_MAX) } : {}
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// src/witan/path-limits.ts
|
|
921
|
+
var REPORT_PATH_MAX = 700;
|
|
922
|
+
var MARKER_PREFIX = "\0cejel:path-over-report-limit:";
|
|
923
|
+
var MARKER_SUFFIX = "\0";
|
|
924
|
+
var OVER_LIMIT_ANCHOR_LABEL = `Path longer than the report's ${REPORT_PATH_MAX}-character limit; not cited`;
|
|
925
|
+
var OVER_LIMIT_ANCHOR = Object.freeze({
|
|
926
|
+
kind: "repository",
|
|
927
|
+
label: OVER_LIMIT_ANCHOR_LABEL,
|
|
928
|
+
path: "."
|
|
929
|
+
});
|
|
930
|
+
function overReportPathLimit(path) {
|
|
931
|
+
return path !== void 0 && path.length > REPORT_PATH_MAX;
|
|
932
|
+
}
|
|
933
|
+
function markSignal(signal, markerFor) {
|
|
934
|
+
const marks = (signal.positiveEvidence ?? []).some(({ path }) => overReportPathLimit(path)) || (signal.findings ?? []).some(({ evidence }) => overReportPathLimit(evidence.path));
|
|
935
|
+
if (!marks) return signal;
|
|
936
|
+
const markPointer = (pointer) => pointer.path !== void 0 && overReportPathLimit(pointer.path) ? { ...pointer, path: markerFor(pointer.path) } : pointer;
|
|
937
|
+
return {
|
|
938
|
+
...signal,
|
|
939
|
+
...signal.positiveEvidence ? { positiveEvidence: signal.positiveEvidence.map(markPointer) } : {},
|
|
940
|
+
...signal.findings ? {
|
|
941
|
+
findings: signal.findings.map(
|
|
942
|
+
(finding) => overReportPathLimit(finding.evidence.path) ? { ...finding, evidence: markPointer(finding.evidence) } : finding
|
|
943
|
+
)
|
|
944
|
+
} : {}
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
function markOverLimitPaths(input) {
|
|
948
|
+
const markers = /* @__PURE__ */ new Map();
|
|
949
|
+
const markerFor = (path) => {
|
|
950
|
+
let marker = markers.get(path);
|
|
951
|
+
if (marker === void 0) {
|
|
952
|
+
marker = `${MARKER_PREFIX}${markers.size}${MARKER_SUFFIX}`;
|
|
953
|
+
markers.set(path, marker);
|
|
954
|
+
}
|
|
955
|
+
return marker;
|
|
956
|
+
};
|
|
957
|
+
const signals = input.signals?.map((signal) => markSignal(signal, markerFor));
|
|
958
|
+
const signalsMarked = signals?.some((signal, index) => signal !== input.signals?.[index]) ?? false;
|
|
959
|
+
const droppedWithheldPaths = (input.withheldPaths ?? []).filter(({ path }) => overReportPathLimit(path));
|
|
960
|
+
if (!signalsMarked && droppedWithheldPaths.length === 0) return { input, droppedWithheldPaths };
|
|
961
|
+
return {
|
|
962
|
+
input: {
|
|
963
|
+
...input,
|
|
964
|
+
...signalsMarked ? { signals } : {},
|
|
965
|
+
...droppedWithheldPaths.length > 0 ? { withheldPaths: input.withheldPaths?.filter(({ path }) => !overReportPathLimit(path)) } : {}
|
|
966
|
+
},
|
|
967
|
+
droppedWithheldPaths
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
function isMarked(pointer) {
|
|
971
|
+
return pointer.path?.startsWith(MARKER_PREFIX) === true;
|
|
972
|
+
}
|
|
973
|
+
function isCitablePointer(pointer) {
|
|
974
|
+
return !isMarked(pointer);
|
|
975
|
+
}
|
|
976
|
+
var NO_SHORTENING = "No path is shortened, since a shortened path would name a file that does not exist.";
|
|
977
|
+
function overLimitCitationDisclosure(count) {
|
|
978
|
+
const one = count === 1;
|
|
979
|
+
return `Cejel did not cite ${count} repository ${one ? "path" : "paths"} here because ${one ? "it is" : "they are"} longer than the report's ${REPORT_PATH_MAX}-character path limit. Every finding that cited ${one ? "it" : "one"} is kept, with that citation left out, and the score and status were computed with ${one ? "it" : "them"}. ${NO_SHORTENING}`;
|
|
980
|
+
}
|
|
981
|
+
var OVER_LIMIT_WITHHELD_MARK = "not listed in withheldPaths because";
|
|
982
|
+
function overLimitWithheldPathsDisclosure(count, signals) {
|
|
983
|
+
const one = count === 1;
|
|
984
|
+
const it = one ? "it" : "them";
|
|
985
|
+
const head = `${count} withheld repository ${one ? "path is" : "paths are"} ${OVER_LIMIT_WITHHELD_MARK} ${one ? "it is" : "they are"} longer than the report's ${REPORT_PATH_MAX}-character path limit; contentReadSummary still counts ${it}.`;
|
|
986
|
+
const named = signals.length > 0 ? ` Checks that would have read ${it}: ${signals.join(", ")}.` : "";
|
|
987
|
+
const counted = signals.length > 0 ? ` ${signals.length} ${signals.length === 1 ? "check" : "checks"} would have read ${it}.` : "";
|
|
988
|
+
const withNames = `${head}${named} ${NO_SHORTENING}`;
|
|
989
|
+
return withNames.length <= STATEMENT_MAX ? withNames : `${head}${counted} ${NO_SHORTENING}`;
|
|
990
|
+
}
|
|
991
|
+
function statesUnlistedWithheldPaths(criteria) {
|
|
992
|
+
return criteria.some(
|
|
993
|
+
({ disclosures, notes }) => (disclosures ?? []).some((sentence) => sentence.includes(OVER_LIMIT_WITHHELD_MARK)) || notes?.includes(OVER_LIMIT_WITHHELD_MARK) === true
|
|
994
|
+
);
|
|
995
|
+
}
|
|
996
|
+
var DISCLOSURES_MAX = 4;
|
|
997
|
+
var STATEMENT_MAX = 1e3;
|
|
998
|
+
function overLimitCitationNote(count) {
|
|
999
|
+
const one = count === 1;
|
|
1000
|
+
return `${count} ${one ? "path" : "paths"} over the report's ${REPORT_PATH_MAX}-character limit ${one ? "is" : "are"} not cited; scoring counted ${one ? "it" : "them"}.`;
|
|
1001
|
+
}
|
|
1002
|
+
function overLimitWithheldPathsNote(count) {
|
|
1003
|
+
const one = count === 1;
|
|
1004
|
+
return `${count} withheld ${one ? "path is" : "paths are"} ${OVER_LIMIT_WITHHELD_MARK} ${one ? "it is" : "they are"} over the report's ${REPORT_PATH_MAX}-character limit.`;
|
|
1005
|
+
}
|
|
1006
|
+
function stateOn(criterion, statements) {
|
|
1007
|
+
const disclosures = criterion.disclosures ?? [];
|
|
1008
|
+
const room = Math.max(0, DISCLOSURES_MAX - disclosures.length);
|
|
1009
|
+
const disclosed = statements.slice(0, room).map(({ full }) => full);
|
|
1010
|
+
const rest = statements.slice(room);
|
|
1011
|
+
const withDisclosures = disclosed.length > 0 ? { ...criterion, disclosures: [...disclosures, ...disclosed] } : criterion;
|
|
1012
|
+
if (rest.length === 0) return withDisclosures;
|
|
1013
|
+
const join5 = (notes, sentences) => notes ? `${notes} ${sentences}` : sentences;
|
|
1014
|
+
const whole = join5(criterion.notes, rest.map(({ full }) => full).join(" "));
|
|
1015
|
+
if (whole.length <= STATEMENT_MAX) return { ...withDisclosures, notes: whole };
|
|
1016
|
+
const countOnly = rest.map(({ countOnly: sentence }) => sentence).join(" ");
|
|
1017
|
+
const counted = join5(criterion.notes, countOnly);
|
|
1018
|
+
if (counted.length <= STATEMENT_MAX) return { ...withDisclosures, notes: counted };
|
|
1019
|
+
const kept = clamp(criterion.notes, STATEMENT_MAX - countOnly.length - 1);
|
|
1020
|
+
return { ...withDisclosures, notes: `${kept} ${countOnly}` };
|
|
1021
|
+
}
|
|
1022
|
+
function dropMarkedCitations(criteria, droppedWithheldPaths = []) {
|
|
1023
|
+
const statements = criteria.map(() => []);
|
|
1024
|
+
const cited = criteria.map((criterion, index) => {
|
|
1025
|
+
const dropped = /* @__PURE__ */ new Set();
|
|
1026
|
+
const evidence = criterion.evidence.filter((pointer) => {
|
|
1027
|
+
if (!isMarked(pointer)) return true;
|
|
1028
|
+
dropped.add(pointer.path);
|
|
1029
|
+
return false;
|
|
1030
|
+
});
|
|
1031
|
+
const findings = criterion.findings.map((finding) => {
|
|
1032
|
+
if (!isMarked(finding.evidence)) return finding;
|
|
1033
|
+
dropped.add(finding.evidence.path);
|
|
1034
|
+
return { ...finding, evidence: { ...OVER_LIMIT_ANCHOR } };
|
|
1035
|
+
});
|
|
1036
|
+
if (dropped.size === 0) return criterion;
|
|
1037
|
+
statements[index]?.push({
|
|
1038
|
+
full: overLimitCitationDisclosure(dropped.size),
|
|
1039
|
+
countOnly: overLimitCitationNote(dropped.size)
|
|
1040
|
+
});
|
|
1041
|
+
return { ...criterion, evidence, findings };
|
|
1042
|
+
});
|
|
1043
|
+
if (droppedWithheldPaths.length > 0) {
|
|
1044
|
+
if (cited.length === 0) {
|
|
1045
|
+
throw new Error("Cejel invariant: a report with withheld paths has at least one criterion");
|
|
1046
|
+
}
|
|
1047
|
+
const signals = [...new Set(droppedWithheldPaths.flatMap(({ signalsAdmitting }) => signalsAdmitting))].sort();
|
|
1048
|
+
const admitting = new Set(signals.map((signal) => signal.split(".")[0]));
|
|
1049
|
+
const index = Math.max(
|
|
1050
|
+
0,
|
|
1051
|
+
cited.findIndex(({ id }) => admitting.has(id))
|
|
1052
|
+
);
|
|
1053
|
+
statements[index]?.push({
|
|
1054
|
+
full: overLimitWithheldPathsDisclosure(droppedWithheldPaths.length, signals),
|
|
1055
|
+
countOnly: overLimitWithheldPathsNote(droppedWithheldPaths.length)
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
return cited.map((criterion, index) => {
|
|
1059
|
+
const own = statements[index] ?? [];
|
|
1060
|
+
return own.length === 0 ? criterion : stateOn(criterion, own);
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
|
|
888
1064
|
// src/witan/repo-signals.ts
|
|
889
1065
|
init_esm_shims();
|
|
890
1066
|
import { createHash } from "crypto";
|
|
@@ -979,6 +1155,51 @@ function isShellScriptPath(text) {
|
|
|
979
1155
|
function execOnTerminatorFreeLine(pattern, line) {
|
|
980
1156
|
return LINE_TERMINATOR_PATTERN.test(line) ? null : pattern.exec(line);
|
|
981
1157
|
}
|
|
1158
|
+
function firstLineStart(text, from, to) {
|
|
1159
|
+
for (let index = from; index < to; index += 1) {
|
|
1160
|
+
if (index === 0 || LINE_TERMINATOR_PATTERN.test(text[index - 1] ?? "")) return index;
|
|
1161
|
+
}
|
|
1162
|
+
return to;
|
|
1163
|
+
}
|
|
1164
|
+
function stripLineLedHashCommentLines(text) {
|
|
1165
|
+
const pattern = /^[^\S\n\r\u2028\u2029]*#.*$/gm;
|
|
1166
|
+
let result = "";
|
|
1167
|
+
let kept = 0;
|
|
1168
|
+
for (let match = pattern.exec(text); match !== null; match = pattern.exec(text)) {
|
|
1169
|
+
let blankStart = match.index;
|
|
1170
|
+
while (blankStart > kept && WHITESPACE_PATTERN.test(text[blankStart - 1] ?? "")) blankStart -= 1;
|
|
1171
|
+
result += text.slice(kept, firstLineStart(text, blankStart, match.index));
|
|
1172
|
+
kept = match.index + match[0].length;
|
|
1173
|
+
}
|
|
1174
|
+
return result + text.slice(kept);
|
|
1175
|
+
}
|
|
1176
|
+
function widenToLeadingBlankRun(match, text) {
|
|
1177
|
+
if (match === null || text[match.index] === "{" || text[match.index] === ",") return match;
|
|
1178
|
+
const end = match.index + match[0].length;
|
|
1179
|
+
let blankStart = match.index;
|
|
1180
|
+
while (blankStart > 0 && WHITESPACE_PATTERN.test(text[blankStart - 1] ?? "")) blankStart -= 1;
|
|
1181
|
+
const before = text[blankStart - 1];
|
|
1182
|
+
const start = before === "{" || before === "," ? blankStart - 1 : firstLineStart(text, blankStart, match.index);
|
|
1183
|
+
match[0] = text.slice(start, end);
|
|
1184
|
+
match.index = start;
|
|
1185
|
+
return match;
|
|
1186
|
+
}
|
|
1187
|
+
var IDENTIFIER_START_PATTERN = /[A-Za-z_$]/;
|
|
1188
|
+
var IDENTIFIER_CHARACTER_PATTERN = /[\w$.-]/;
|
|
1189
|
+
function execFromIdentifierRunStarts(sticky, text, from) {
|
|
1190
|
+
let runHasStart = false;
|
|
1191
|
+
for (let index = from; index < text.length; index += 1) {
|
|
1192
|
+
const character = text[index] ?? "";
|
|
1193
|
+
if (character === '"' || character === "'" || !runHasStart && IDENTIFIER_START_PATTERN.test(character)) {
|
|
1194
|
+
sticky.lastIndex = index;
|
|
1195
|
+
const match = sticky.exec(text);
|
|
1196
|
+
if (match !== null) return match;
|
|
1197
|
+
}
|
|
1198
|
+
if (!IDENTIFIER_CHARACTER_PATTERN.test(character)) runHasStart = false;
|
|
1199
|
+
else if (IDENTIFIER_START_PATTERN.test(character)) runHasStart = true;
|
|
1200
|
+
}
|
|
1201
|
+
return null;
|
|
1202
|
+
}
|
|
982
1203
|
var VERSION_OPERATOR_CHARACTERS = /* @__PURE__ */ new Set([">", "<", "=", "~", "!", "^"]);
|
|
983
1204
|
var VERSION_DIGIT_PATTERN = /[\d.]/;
|
|
984
1205
|
function firstVersionConstraint(text) {
|
|
@@ -1308,7 +1529,8 @@ function buildWitanInputFromRepoUntracked(options) {
|
|
|
1308
1529
|
const usesV26SettlingEvidence = rubricVersion === WITAN_RUBRIC_VERSION_V26;
|
|
1309
1530
|
const usesV26BuildStepEvidence = rubricVersion === WITAN_RUBRIC_VERSION_V26;
|
|
1310
1531
|
const usesV27Closure = usesV27FalseAssuranceClosure(rubricVersion);
|
|
1311
|
-
const
|
|
1532
|
+
const historyDepth = options.historyDepth ?? readHistoryDepthAnswer(options.repoPath);
|
|
1533
|
+
const unreadHistory = unreadHistoryDepth(historyDepth.depth);
|
|
1312
1534
|
const structuralArchetype = classifyRepoArchetype(inventoryFiles, rubricVersion);
|
|
1313
1535
|
const readableArchetype = rubricVersion === WITAN_RUBRIC_VERSION_V13 || rubricVersion === WITAN_RUBRIC_VERSION_V14 || rubricVersion === WITAN_RUBRIC_VERSION_V15 || rubricVersion === WITAN_RUBRIC_VERSION_V16 || usesV17DetectorClosure(rubricVersion) ? applyReadableSourceRepresentationGate(
|
|
1314
1536
|
options.repoPath,
|
|
@@ -1330,6 +1552,8 @@ function buildWitanInputFromRepoUntracked(options) {
|
|
|
1330
1552
|
const usesV47Detectors = rubricVersion === WITAN_RUBRIC_VERSION_V15 || rubricVersion === WITAN_RUBRIC_VERSION_V16 || usesV17DetectorClosure(rubricVersion);
|
|
1331
1553
|
const reviewableSourceProof = usesV17Detectors ? buildReviewableSourceProof(options.repoPath, inventoryFiles, headSha, rubricVersion) : void 0;
|
|
1332
1554
|
const scanLimitations = new Set(fileInventory.scanLimitations);
|
|
1555
|
+
const historyDepthLimitation = historyDepthFailureLimitation(historyDepth);
|
|
1556
|
+
if (historyDepthLimitation !== void 0) scanLimitations.add(historyDepthLimitation);
|
|
1333
1557
|
const coreSignals = collectRepoSignals(
|
|
1334
1558
|
options.repoPath,
|
|
1335
1559
|
generatedAt,
|
|
@@ -1355,7 +1579,8 @@ function buildWitanInputFromRepoUntracked(options) {
|
|
|
1355
1579
|
reviewableSourceProof,
|
|
1356
1580
|
inventoryAbsenceContext,
|
|
1357
1581
|
scanLimitations,
|
|
1358
|
-
unreadHistory
|
|
1582
|
+
unreadHistory,
|
|
1583
|
+
historyDepth
|
|
1359
1584
|
);
|
|
1360
1585
|
const archetype = usesV17Detectors && reviewableSourceProof?.passes === true && isV17RescuableAbstention(gatedArchetype.abstentionKind) && coreSignals.some(isMeasuredCriterionSignal) ? {
|
|
1361
1586
|
archetype: isLikelyMonorepo(inventoryFiles) ? "monorepo" : "source",
|
|
@@ -1626,7 +1851,7 @@ var SEMANTIC_AUXILIARY_PATH_PATTERN_V14 = /(^|\/)(?:locales?|i18n|l10n|translati
|
|
|
1626
1851
|
var HTML_SOURCE_EXTENSION_PATTERN = /\.(?:html?|xhtml)$/i;
|
|
1627
1852
|
var HTML_META_REFRESH_PATTERN = /<meta\b(?=[^>]*\bhttp-equiv\s*=\s*["']?\s*refresh\b)(?=[^>]*\bcontent\s*=\s*["'][^"']*\burl\s*=)[^>]*>/i;
|
|
1628
1853
|
var RUBY_PACKAGE_RECIPE_CLASS_PATTERN = /\bclass\s+[A-Za-z0-9_:]+\s*<\s*(?:Formula|Cask)\b/;
|
|
1629
|
-
var RUBY_PACKAGE_RECIPE_URL_PATTERN =
|
|
1854
|
+
var RUBY_PACKAGE_RECIPE_URL_PATTERN = /^[^\S\n\r\u2028\u2029]*url\s+["'][^"']+["']/m;
|
|
1630
1855
|
var RUBY_PACKAGE_RECIPE_PATH_PATTERN = /(^|\/)(?:Formula|Casks?)(\/|$)/;
|
|
1631
1856
|
function selectProportionalRepresentativeSourceFiles(files, rankKey, limit) {
|
|
1632
1857
|
const eligibleFiles = [...new Set(files)];
|
|
@@ -1846,7 +2071,7 @@ function isHtmlRedirectMetadataSource(repoPath, file) {
|
|
|
1846
2071
|
function isRubyPackageRecipeMetadataSource(repoPath, file) {
|
|
1847
2072
|
if (!/\.rb$/i.test(file) || !RUBY_PACKAGE_RECIPE_PATH_PATTERN.test(file)) return false;
|
|
1848
2073
|
const contents = readSemanticMetadataPrefix(repoPath, file);
|
|
1849
|
-
const operativeContents = contents
|
|
2074
|
+
const operativeContents = contents === null ? void 0 : stripLineLedHashCommentLines(contents);
|
|
1850
2075
|
return operativeContents !== void 0 && RUBY_PACKAGE_RECIPE_CLASS_PATTERN.test(operativeContents) && RUBY_PACKAGE_RECIPE_URL_PATTERN.test(operativeContents);
|
|
1851
2076
|
}
|
|
1852
2077
|
function readSemanticMetadataPrefix(repoPath, file) {
|
|
@@ -1968,7 +2193,7 @@ function buildNotApplicableSignal(criterionId, reason) {
|
|
|
1968
2193
|
notApplicable: true
|
|
1969
2194
|
};
|
|
1970
2195
|
}
|
|
1971
|
-
function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, useV39Detectors, useV47Detectors, useV18NativeRls, useV19CommitYear, useV20A3ExplicitGaps, useV21ExecutedEscalations, useV22PackageStartEntrypoint, useV23StrictCoverageAndRecipeRunners, useV23PemPrivateKeyGrammar, useV23WithheldPathAbstention, useV24SecretContentContext, useV25InlineHealthRoute, useV26SettlingEvidence, useV26BuildStepEvidence, useV27FalseAssuranceClosure, reviewableSourceProof, inventoryAbsenceContext = LEGACY_INVENTORY_ABSENCE_CONTEXT, scanLimitations = /* @__PURE__ */ new Set(), unreadHistory) {
|
|
2196
|
+
function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, useV39Detectors, useV47Detectors, useV18NativeRls, useV19CommitYear, useV20A3ExplicitGaps, useV21ExecutedEscalations, useV22PackageStartEntrypoint, useV23StrictCoverageAndRecipeRunners, useV23PemPrivateKeyGrammar, useV23WithheldPathAbstention, useV24SecretContentContext, useV25InlineHealthRoute, useV26SettlingEvidence, useV26BuildStepEvidence, useV27FalseAssuranceClosure, reviewableSourceProof, inventoryAbsenceContext = LEGACY_INVENTORY_ABSENCE_CONTEXT, scanLimitations = /* @__PURE__ */ new Set(), unreadHistory, historyDepth = COMPLETE_HISTORY_DEPTH) {
|
|
1972
2197
|
const signals = [];
|
|
1973
2198
|
const mono = resolveMonorepoContext(repoPath, scanLimitations);
|
|
1974
2199
|
const collectCriterion = (criterionId, collect) => withContentReadCriterion(criterionId, collect);
|
|
@@ -1983,7 +2208,8 @@ function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, u
|
|
|
1983
2208
|
useV23StrictCoverageAndRecipeRunners,
|
|
1984
2209
|
reviewableSourceProof,
|
|
1985
2210
|
inventoryAbsenceContext,
|
|
1986
|
-
useV27FalseAssuranceClosure
|
|
2211
|
+
useV27FalseAssuranceClosure,
|
|
2212
|
+
scanLimitations
|
|
1987
2213
|
)
|
|
1988
2214
|
);
|
|
1989
2215
|
const a2Signal = collectCriterion(
|
|
@@ -2003,7 +2229,8 @@ function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, u
|
|
|
2003
2229
|
scanLimitations,
|
|
2004
2230
|
useV26SettlingEvidence,
|
|
2005
2231
|
useV27FalseAssuranceClosure,
|
|
2006
|
-
unreadHistory
|
|
2232
|
+
unreadHistory,
|
|
2233
|
+
historyDepth
|
|
2007
2234
|
)
|
|
2008
2235
|
);
|
|
2009
2236
|
const a3Signal = collectCriterion(
|
|
@@ -2049,7 +2276,7 @@ function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, u
|
|
|
2049
2276
|
);
|
|
2050
2277
|
const b2Signal = collectCriterion(
|
|
2051
2278
|
"B2",
|
|
2052
|
-
() => collectB2PrTraceEvidence(repoPath, repoFiles, useV27FalseAssuranceClosure)
|
|
2279
|
+
() => collectB2PrTraceEvidence(repoPath, repoFiles, useV27FalseAssuranceClosure, historyDepth)
|
|
2053
2280
|
);
|
|
2054
2281
|
const b3Signal = collectCriterion(
|
|
2055
2282
|
"B3",
|
|
@@ -2095,7 +2322,7 @@ function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, u
|
|
|
2095
2322
|
}
|
|
2096
2323
|
return signals;
|
|
2097
2324
|
}
|
|
2098
|
-
function collectA1TestIntegrityEvidence(repoPath, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, useV23StrictCoverageAndRecipeRunners, reviewableSourceProof, inventoryAbsenceContext = LEGACY_INVENTORY_ABSENCE_CONTEXT, useV27FalseAssuranceClosure = false) {
|
|
2325
|
+
function collectA1TestIntegrityEvidence(repoPath, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, useV23StrictCoverageAndRecipeRunners, reviewableSourceProof, inventoryAbsenceContext = LEGACY_INVENTORY_ABSENCE_CONTEXT, useV27FalseAssuranceClosure = false, scanLimitations = /* @__PURE__ */ new Set()) {
|
|
2099
2326
|
const evidence = [];
|
|
2100
2327
|
const findings = [];
|
|
2101
2328
|
const v17TestFiles = findConcreteTestFiles(
|
|
@@ -2107,7 +2334,7 @@ function collectA1TestIntegrityEvidence(repoPath, repoFiles, useV27Detectors, us
|
|
|
2107
2334
|
const allTestFiles = pytestSettings !== void 0 ? v17TestFiles.filter(
|
|
2108
2335
|
(file) => !PYTEST_CONFTEST_PATTERN.test(file) || pytestSettings.forFile(file).pythonFiles !== void 0
|
|
2109
2336
|
) : v17TestFiles;
|
|
2110
|
-
const runWideHooks = pytestSettings ? findPytestRunWideSkips(repoPath, repoFiles) : [];
|
|
2337
|
+
const runWideHooks = pytestSettings ? findPytestRunWideSkips(repoPath, repoFiles, scanLimitations) : [];
|
|
2111
2338
|
const runWideSkips = runWideHooks.filter((skip) => skip.withheld === void 0);
|
|
2112
2339
|
const hookWithheldFor = (skip, file) => {
|
|
2113
2340
|
if (skip.withheld !== void 0) {
|
|
@@ -2609,7 +2836,7 @@ function buildA1AuthenticatedAbsenceSignal(repoPath, proof, inventoryCount, inve
|
|
|
2609
2836
|
notes: `V17 authenticated absence: ${proof.eligibleSourceFileCount} criterion-ratable of ${proof.sourceShapedFileCount} source-shaped files; sampled ${proof.sampledFileCount}; ${proof.readableSampledFileCount} representative-text files. Verification-script signals (lint/typecheck/coverage/CI commands) are independent of test-file presence and are still credited above where detected.`
|
|
2610
2837
|
};
|
|
2611
2838
|
}
|
|
2612
|
-
function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, useV39Detectors, useV47Detectors, useV23PemPrivateKeyGrammar, useV24SecretContentContext, useV18NativeRls, inventoryAbsenceContext, scanLimitations, useV26SettlingEvidence = false, useV27FalseAssuranceClosure = false, unreadHistory) {
|
|
2839
|
+
function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, useV39Detectors, useV47Detectors, useV23PemPrivateKeyGrammar, useV24SecretContentContext, useV18NativeRls, inventoryAbsenceContext, scanLimitations, useV26SettlingEvidence = false, useV27FalseAssuranceClosure = false, unreadHistory, historyDepth = COMPLETE_HISTORY_DEPTH) {
|
|
2613
2840
|
const abstainsOnIncompleteHistory = useV27FalseAssuranceClosure || unreadHistory !== void 0;
|
|
2614
2841
|
const noGitHistory = unreadHistory === "not_a_repository";
|
|
2615
2842
|
const evidence = [];
|
|
@@ -2724,7 +2951,8 @@ function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33
|
|
|
2724
2951
|
const historyIncompleteness = a2HistoryIncompleteness(
|
|
2725
2952
|
repoPath,
|
|
2726
2953
|
a2HistoryReads,
|
|
2727
|
-
historySecretScan?.truncated === true
|
|
2954
|
+
historySecretScan?.truncated === true,
|
|
2955
|
+
historyDepth
|
|
2728
2956
|
);
|
|
2729
2957
|
const dbLayerImportFile = migrationFiles.length === 0 ? implFiles.slice(0, 30).find((f) => fileContains(repoPath, f, DB_CLIENT_PATTERN)) : void 0;
|
|
2730
2958
|
const hasDataLayer = migrationFiles.length > 0 || dbLayerImportFile !== void 0;
|
|
@@ -3444,7 +3672,7 @@ function collectA3ProdReadinessEvidence(repoPath, repoFiles, useV27Detectors, us
|
|
|
3444
3672
|
const hasActiveHealthcheck = withContentReadSignal(
|
|
3445
3673
|
"A3",
|
|
3446
3674
|
"dockerfile_healthcheck",
|
|
3447
|
-
() => fileContains(repoPath, runtimeContainer,
|
|
3675
|
+
() => fileContains(repoPath, runtimeContainer, /^[^\S\n\r\u2028\u2029]*HEALTHCHECK\s+(?!NONE\b)/im)
|
|
3448
3676
|
);
|
|
3449
3677
|
if (!hasActiveHealthcheck) {
|
|
3450
3678
|
findings.push({
|
|
@@ -4764,8 +4992,11 @@ function unreadHistoryA2Sentence(depth) {
|
|
|
4764
4992
|
}
|
|
4765
4993
|
return depth === "shallow" ? "This repository is a shallow clone, so history secret scanning read only the commits present (reason: shallow_history); the critical finding stands on what was read." : "Cejel could not read the history depth of this checkout, so history secret scanning may have read only part of it (reason: history_depth_unknown); the critical finding stands on what was read.";
|
|
4766
4994
|
}
|
|
4767
|
-
function a2HistoryIncompleteness(repoPath, reads, blobCapHit) {
|
|
4768
|
-
|
|
4995
|
+
function a2HistoryIncompleteness(repoPath, reads, blobCapHit, historyDepth) {
|
|
4996
|
+
if (isHistoryDepthUnsettledByFailure(historyDepth) && historyDepth.failure !== void 0) {
|
|
4997
|
+
reads.noteFailure(historyDepth.failure);
|
|
4998
|
+
}
|
|
4999
|
+
const shallow = isShallowHistoryDepth(historyDepth);
|
|
4769
5000
|
const limitHits = [...reads.limitHits].sort();
|
|
4770
5001
|
const controlCharacterSkip = reads.controlCharacterCandidate;
|
|
4771
5002
|
const gitUnavailable = reads.gitAbsent && hasVisibleGitMetadata(repoPath);
|
|
@@ -4906,6 +5137,8 @@ function a2HistoryApplicabilityDisclosure(incompleteness) {
|
|
|
4906
5137
|
}
|
|
4907
5138
|
var B2_SHALLOW_HISTORY_NOTE = `B2 abstained: this repository is a shallow clone with fewer than ${RECENT_COMMIT_WINDOW} commits visible, so the pull-request merge ratio cannot be read over its window. B2 is left out of the composite rather than scored on the commits present. Re-scan a full clone (for actions/checkout, set fetch-depth: 0).`;
|
|
4908
5139
|
var B2_SHALLOW_RATIO_WITHHELD_NOTE = `B2 abstained from the pull-request merge ratio only: this repository is a shallow clone with fewer than ${RECENT_COMMIT_WINDOW} commits visible, so the pull-request merge ratio cannot be read over its window. B2 is scored on the CI workflows, pull-request template and review-gate record in the checked-out files, with the ratio left out of its score rather than scored on the commits present. Re-scan a full clone (for actions/checkout, set fetch-depth: 0).`;
|
|
5140
|
+
var B2_SHALLOW_CHECK_FAILED_NOTE = `B2 abstained: Cejel could not tell whether this clone is shallow, because git failed to answer and the repository's files did not settle it (the scan limitations name the failed read), and fewer than ${RECENT_COMMIT_WINDOW} commits are visible, so the pull-request merge ratio may be read over a truncated window. Under v27 B2 is not scored on that ratio; it stays in the composite at 0. Re-scan once the local git failure is resolved.`;
|
|
5141
|
+
var COMPLETE_HISTORY_DEPTH = { depth: "complete", answeredByGit: true };
|
|
4909
5142
|
function shallowHistoryAbstention(criterionId, notes) {
|
|
4910
5143
|
return {
|
|
4911
5144
|
criterionId,
|
|
@@ -4916,7 +5149,7 @@ function shallowHistoryAbstention(criterionId, notes) {
|
|
|
4916
5149
|
notes
|
|
4917
5150
|
};
|
|
4918
5151
|
}
|
|
4919
|
-
function collectB2PrTraceEvidence(repoPath, repoFiles, useV27FalseAssuranceClosure = false) {
|
|
5152
|
+
function collectB2PrTraceEvidence(repoPath, repoFiles, useV27FalseAssuranceClosure = false, historyDepth = COMPLETE_HISTORY_DEPTH) {
|
|
4920
5153
|
const workflows = repoFiles.filter(isCiWorkflow);
|
|
4921
5154
|
const prTemplate = repoFiles.find(
|
|
4922
5155
|
(file) => /(^|\/)(pull_request_template|PULL_REQUEST_TEMPLATE)(\.md$|\/[^/]+\.md$)/.test(file)
|
|
@@ -4958,12 +5191,19 @@ function collectB2PrTraceEvidence(repoPath, repoFiles, useV27FalseAssuranceClosu
|
|
|
4958
5191
|
];
|
|
4959
5192
|
if (evidence.length === 0) return null;
|
|
4960
5193
|
const prTracePrimitives = creditedWorkflowCount + (prTemplate ? 1 : 0) + (branchProtectionDoc ? 1 : 0);
|
|
4961
|
-
const ratioWithheld =
|
|
5194
|
+
const ratioWithheld = isShallowHistoryDepth(historyDepth) && readRecentCommits(repoPath).length < RECENT_COMMIT_WINDOW;
|
|
5195
|
+
const shallowCheckDecidesRatio = useV27FalseAssuranceClosure && isHistoryDepthUnsettledByFailure(historyDepth) && readRecentCommits(repoPath).length < RECENT_COMMIT_WINDOW;
|
|
4962
5196
|
const uncreditedDisclosures = uncredited.size > 0 ? {
|
|
4963
5197
|
disclosures: [uncreditedCiWorkflowDisclosure(WITAN_B2_UNCREDITED_CI_WORKFLOW_PREFIX, uncredited)]
|
|
4964
5198
|
} : {};
|
|
4965
5199
|
const uncreditedNote = uncredited.size > 0 ? uncreditedCiWorkflowNote(uncredited, "a PR-trace CI workflow") : void 0;
|
|
4966
5200
|
const withUncreditedNote = (note) => uncreditedNote ? `${note} ${uncreditedCiWorkflowNote(uncredited, "a PR-trace CI workflow", CRITERION_NOTES_MAX_LENGTH - note.length - 1)}` : note;
|
|
5201
|
+
if (shallowCheckDecidesRatio) {
|
|
5202
|
+
return {
|
|
5203
|
+
...shallowHistoryAbstention("B2", withUncreditedNote(B2_SHALLOW_CHECK_FAILED_NOTE)),
|
|
5204
|
+
...uncreditedDisclosures
|
|
5205
|
+
};
|
|
5206
|
+
}
|
|
4967
5207
|
if (ratioWithheld && prTracePrimitives === 0) {
|
|
4968
5208
|
return {
|
|
4969
5209
|
...shallowHistoryAbstention("B2", withUncreditedNote(B2_SHALLOW_HISTORY_NOTE)),
|
|
@@ -11344,6 +11584,8 @@ function readShellCommands(text, directory, addopts, visit, unknownDirectory, me
|
|
|
11344
11584
|
let current = directory;
|
|
11345
11585
|
let why = directory === null ? unknownDirectory : void 0;
|
|
11346
11586
|
let exported = addopts;
|
|
11587
|
+
let exports = 0;
|
|
11588
|
+
let exportReachesOther = false;
|
|
11347
11589
|
const joined = stripShellHeredocBodies(text).replace(/\\\r?\n/g, " ");
|
|
11348
11590
|
for (const segment of splitShellCommandSegments(joined)) {
|
|
11349
11591
|
const all = shellWords(segment.trim());
|
|
@@ -11371,7 +11613,7 @@ function readShellCommands(text, directory, addopts, visit, unknownDirectory, me
|
|
|
11371
11613
|
for (const word of words.slice(1)) {
|
|
11372
11614
|
if (word.startsWith(PYTEST_ADDOPTS_ASSIGNMENT)) {
|
|
11373
11615
|
exported = shellWords(word.slice(PYTEST_ADDOPTS_ASSIGNMENT.length));
|
|
11374
|
-
|
|
11616
|
+
exports += 1;
|
|
11375
11617
|
}
|
|
11376
11618
|
}
|
|
11377
11619
|
continue;
|
|
@@ -11392,8 +11634,23 @@ function readShellCommands(text, directory, addopts, visit, unknownDirectory, me
|
|
|
11392
11634
|
current = null;
|
|
11393
11635
|
continue;
|
|
11394
11636
|
}
|
|
11637
|
+
if (mentions !== void 0 && makeCommandOf(words) === void 0 && !writesGithubEnvOnly(words)) {
|
|
11638
|
+
mentions.reachesOther = true;
|
|
11639
|
+
if (exports > 0) exportReachesOther = true;
|
|
11640
|
+
}
|
|
11395
11641
|
visit(words, current, local, current === null ? why : void 0);
|
|
11396
11642
|
}
|
|
11643
|
+
if (mentions !== void 0 && !exportReachesOther) mentions.read += exports;
|
|
11644
|
+
}
|
|
11645
|
+
function writesGithubEnvOnly(words) {
|
|
11646
|
+
const name = basename(words[0] ?? "");
|
|
11647
|
+
if (name !== "echo" && name !== "printf") return false;
|
|
11648
|
+
let names = false;
|
|
11649
|
+
for (const word of words) {
|
|
11650
|
+
if (word.includes("$(") || word.includes("`")) return false;
|
|
11651
|
+
if (word.includes("GITHUB_ENV")) names = true;
|
|
11652
|
+
}
|
|
11653
|
+
return names;
|
|
11397
11654
|
}
|
|
11398
11655
|
var PYTHON_INTERPRETER_PATTERN = /^(?:python(?:\d+(?:\.\d+)*)?|pypy\d*(?:\.\d+)?|py)(?:\.exe)?$/i;
|
|
11399
11656
|
var PYTHON_INTERPRETER_VARIABLE_PATTERN = /^(?:\{envpython\}|\$[({]?[A-Za-z_]*PY[A-Za-z_]*[)}]?)$/i;
|
|
@@ -11641,7 +11898,7 @@ function yamlPlainScalar(value) {
|
|
|
11641
11898
|
const comment = trailingCommentStart(value);
|
|
11642
11899
|
return (comment === -1 ? value : value.slice(0, comment)).trim();
|
|
11643
11900
|
}
|
|
11644
|
-
function readGithubRunSteps(text) {
|
|
11901
|
+
function readGithubRunSteps(text, usesSteps) {
|
|
11645
11902
|
const lines = readYamlLines(text);
|
|
11646
11903
|
const raw = text.split(/\r?\n/);
|
|
11647
11904
|
const steps = [];
|
|
@@ -11710,6 +11967,7 @@ function readGithubRunSteps(text) {
|
|
|
11710
11967
|
const firstRootLine = lines.findIndex((line) => !line.blank);
|
|
11711
11968
|
const root = factsOf(firstRootLine === -1 ? /* @__PURE__ */ new Map() : mappingKeys(firstRootLine, lines[firstRootLine]?.keyColumn ?? 0));
|
|
11712
11969
|
const jobs = /* @__PURE__ */ new Map();
|
|
11970
|
+
const stepKeysAt = /* @__PURE__ */ new Map();
|
|
11713
11971
|
const jobFacts = (header, column) => {
|
|
11714
11972
|
let facts = jobs.get(header);
|
|
11715
11973
|
if (facts === void 0) {
|
|
@@ -11722,21 +11980,35 @@ function readGithubRunSteps(text) {
|
|
|
11722
11980
|
};
|
|
11723
11981
|
for (let index = 0; index < lines.length; index += 1) {
|
|
11724
11982
|
const line = lines[index];
|
|
11725
|
-
if (!line || line.blank || line.key !== "run" || line.value === "") continue;
|
|
11983
|
+
if (!line || line.blank || line.key !== "run" && line.key !== "uses" || line.value === "") continue;
|
|
11726
11984
|
const column = line.keyColumn;
|
|
11727
11985
|
const stepStart = itemStart[index] ?? -1;
|
|
11728
11986
|
if (stepStart === -1) continue;
|
|
11729
|
-
|
|
11987
|
+
let stepKeys = stepKeysAt.get(stepStart);
|
|
11988
|
+
if (stepKeys === void 0) {
|
|
11989
|
+
stepKeys = mappingKeys(stepStart, column);
|
|
11990
|
+
stepKeysAt.set(stepStart, stepKeys);
|
|
11991
|
+
}
|
|
11730
11992
|
const list = parent[stepStart] ?? -1;
|
|
11731
11993
|
const jobHeader = list === -1 ? -1 : parent[list] ?? -1;
|
|
11732
11994
|
const job = jobHeader === -1 ? { never: false } : jobFacts(jobHeader, lines[list]?.keyColumn ?? 0);
|
|
11733
11995
|
if (isFalse(scalarAt(stepKeys.get("if"))) || job.never) continue;
|
|
11996
|
+
if (line.key === "uses") {
|
|
11997
|
+
if (!stepKeys.has("run")) {
|
|
11998
|
+
usesSteps?.push({
|
|
11999
|
+
line: index,
|
|
12000
|
+
addoptsKeyLines: [root.addoptsLine, job.addoptsLine, addoptsLineIn(stepKeys)].filter((at) => at !== void 0)
|
|
12001
|
+
});
|
|
12002
|
+
}
|
|
12003
|
+
continue;
|
|
12004
|
+
}
|
|
11734
12005
|
const directoryValue = scalarAt(stepKeys.get("working-directory")) ?? job.directory ?? root.directory;
|
|
11735
12006
|
const directory = directoryValue === void 0 ? "" : directoryValue.includes("${{") ? null : resolveCommandPath("", directoryValue);
|
|
11736
12007
|
const stepAddopts = addoptsIn(stepKeys);
|
|
11737
12008
|
const addoptsValue = stepAddopts ?? job.addopts ?? root.addopts;
|
|
11738
12009
|
const addoptsLine = stepAddopts !== void 0 ? addoptsLineIn(stepKeys) : job.addopts !== void 0 ? job.addoptsLine : root.addoptsLine;
|
|
11739
12010
|
let command;
|
|
12011
|
+
let lastLine = index;
|
|
11740
12012
|
if (/^[|>]/.test(line.value)) {
|
|
11741
12013
|
const block = [];
|
|
11742
12014
|
for (let next = index + 1; next < raw.length; next += 1) {
|
|
@@ -11744,6 +12016,7 @@ function readGithubRunSteps(text) {
|
|
|
11744
12016
|
if (text2.trim() !== "" && text2.length - text2.trimStart().length <= column) break;
|
|
11745
12017
|
block.push(text2);
|
|
11746
12018
|
}
|
|
12019
|
+
lastLine = index + block.length;
|
|
11747
12020
|
let indent = Number.POSITIVE_INFINITY;
|
|
11748
12021
|
for (const text2 of block) if (text2.trim() !== "") indent = Math.min(indent, text2.length - text2.trimStart().length);
|
|
11749
12022
|
const dedented = block.map((text2) => text2.slice(Number.isFinite(indent) ? indent : 0));
|
|
@@ -11757,6 +12030,7 @@ function readGithubRunSteps(text) {
|
|
|
11757
12030
|
command,
|
|
11758
12031
|
directory,
|
|
11759
12032
|
line: index,
|
|
12033
|
+
lastLine,
|
|
11760
12034
|
addoptsKeyLines: [root.addoptsLine, job.addoptsLine, stepAddoptsLine].filter((at) => at !== void 0),
|
|
11761
12035
|
...directory === null && directoryValue !== void 0 ? { unknownDirectory: `working-directory: ${directoryValue}` } : {},
|
|
11762
12036
|
...addoptsValue === void 0 || expression ? {} : { addopts: shellWords(addoptsValue) },
|
|
@@ -11778,9 +12052,13 @@ function readPytestInvocations(repoFiles, readText) {
|
|
|
11778
12052
|
let hatchScripts;
|
|
11779
12053
|
let hatchExpansionsLeft = HATCH_EXPANSION_BUDGET;
|
|
11780
12054
|
const environmentTexts = [];
|
|
11781
|
-
let addoptsMentionNotRead
|
|
11782
|
-
const noteAddoptsMentions = (text, read) => {
|
|
11783
|
-
if (wordCount(text, "PYTEST_ADDOPTS")
|
|
12055
|
+
let addoptsMentionNotRead;
|
|
12056
|
+
const noteAddoptsMentions = (text, read, readLines, file) => {
|
|
12057
|
+
if (addoptsMentionNotRead !== void 0 || wordCount(text, "PYTEST_ADDOPTS") === read) return;
|
|
12058
|
+
const lines = text.split(/\r?\n/);
|
|
12059
|
+
let at = lines.findIndex((line, index) => !readLines.has(index) && containsWord(line, "PYTEST_ADDOPTS"));
|
|
12060
|
+
if (at === -1) at = lines.findIndex((line) => containsWord(line, "PYTEST_ADDOPTS"));
|
|
12061
|
+
addoptsMentionNotRead = { text: (lines[at] ?? "").trim(), source: a1NotePath(file), file, line: Math.max(at, 0) };
|
|
11784
12062
|
};
|
|
11785
12063
|
const makeEnvironments = [];
|
|
11786
12064
|
let readingCi = true;
|
|
@@ -11883,23 +12161,47 @@ function readPytestInvocations(repoFiles, readText) {
|
|
|
11883
12161
|
const writesAddopts = text.includes("GITHUB_ENV") && text.includes("PYTEST_ADDOPTS") ? "PYTEST_ADDOPTS written to $GITHUB_ENV" : void 0;
|
|
11884
12162
|
const unreadWhy = writesAddopts ?? remote.checkoutPath;
|
|
11885
12163
|
const keyLines = /* @__PURE__ */ new Set();
|
|
11886
|
-
const
|
|
11887
|
-
|
|
12164
|
+
const keysReachingOther = /* @__PURE__ */ new Set();
|
|
12165
|
+
const readLines = /* @__PURE__ */ new Set();
|
|
12166
|
+
let fileReachesOther = false;
|
|
12167
|
+
let shellRead = 0;
|
|
12168
|
+
const mentioned = text.includes("PYTEST_ADDOPTS");
|
|
12169
|
+
const usesSteps = [];
|
|
12170
|
+
for (const step of readGithubRunSteps(text, usesSteps)) {
|
|
11888
12171
|
for (const at of step.addoptsKeyLines) keyLines.add(at);
|
|
12172
|
+
const shellMentions = mentioned ? { read: 0, reachesOther: false } : void 0;
|
|
11889
12173
|
const envAddopts = step.addopts !== void 0 && step.addoptsLine !== void 0 ? { words: step.addopts, line: step.addoptsLine } : void 0;
|
|
11890
12174
|
const notKnownReason = writesAddopts ?? step.addoptsNotKnown;
|
|
11891
12175
|
const envNotKnown = notKnownReason === void 0 ? void 0 : { reason: notKnownReason, source, file, line: writesAddopts === void 0 && step.addoptsLine !== void 0 ? step.addoptsLine : step.line };
|
|
11892
12176
|
if (unreadWhy !== void 0) readShellCommands(step.command, null, step.addopts, visitor(source, file, step.line, 0, envAddopts, envNotKnown), unreadWhy, shellMentions);
|
|
11893
12177
|
else readShellCommands(step.command, step.directory, step.addopts, visitor(source, file, step.line, 0, envAddopts, envNotKnown), step.unknownDirectory, shellMentions);
|
|
12178
|
+
if (shellMentions === void 0) continue;
|
|
12179
|
+
if (shellMentions.reachesOther) {
|
|
12180
|
+
fileReachesOther = true;
|
|
12181
|
+
for (const at of step.addoptsKeyLines) keysReachingOther.add(at);
|
|
12182
|
+
}
|
|
12183
|
+
if (writesAddopts !== void 0) continue;
|
|
12184
|
+
shellRead += shellMentions.read;
|
|
12185
|
+
if (!shellMentions.reachesOther && wordCount(step.command, "PYTEST_ADDOPTS") === shellMentions.read) {
|
|
12186
|
+
for (let at = step.line; at <= step.lastLine; at += 1) readLines.add(at);
|
|
12187
|
+
}
|
|
11894
12188
|
}
|
|
11895
|
-
if (
|
|
11896
|
-
|
|
11897
|
-
|
|
12189
|
+
if (mentioned) {
|
|
12190
|
+
for (const step of usesSteps) {
|
|
12191
|
+
fileReachesOther = true;
|
|
12192
|
+
for (const at of step.addoptsKeyLines) keysReachingOther.add(at);
|
|
12193
|
+
}
|
|
12194
|
+
for (const at of keyLines) if (!keysReachingOther.has(at)) readLines.add(at);
|
|
12195
|
+
let read = shellRead;
|
|
12196
|
+
for (const at of keyLines) if (!keysReachingOther.has(at)) read += 1;
|
|
12197
|
+
if (writesAddopts !== void 0 && !fileReachesOther) {
|
|
11898
12198
|
text.split(/\r?\n/).forEach((line, index) => {
|
|
11899
|
-
if (
|
|
12199
|
+
if (keyLines.has(index) || !line.includes("GITHUB_ENV")) return;
|
|
12200
|
+
read += wordCount(line, "PYTEST_ADDOPTS");
|
|
12201
|
+
readLines.add(index);
|
|
11900
12202
|
});
|
|
11901
12203
|
}
|
|
11902
|
-
noteAddoptsMentions(text, read);
|
|
12204
|
+
noteAddoptsMentions(text, read, readLines, file);
|
|
11903
12205
|
}
|
|
11904
12206
|
for (const step of remote.pytestInputs) {
|
|
11905
12207
|
invocations.push({ source, file, anchor: step.line, directory: "", args: [], text: step.uses, unread: step.uses });
|
|
@@ -11934,13 +12236,15 @@ function readPytestInvocations(repoFiles, readText) {
|
|
|
11934
12236
|
environmentTexts.push(text);
|
|
11935
12237
|
const jenkinsDir = file === "Jenkinsfile" ? jenkinsDirCall(text) : void 0;
|
|
11936
12238
|
const addopts = readCiAddopts(text);
|
|
11937
|
-
const shellMentions = text.includes("PYTEST_ADDOPTS") ? { read: 0 } : void 0;
|
|
12239
|
+
const shellMentions = text.includes("PYTEST_ADDOPTS") ? { read: 0, reachesOther: false } : void 0;
|
|
11938
12240
|
for (const command of ciShellTexts(text)) {
|
|
11939
12241
|
readShellCommands(command, jenkinsDir === void 0 ? "" : null, addopts, visitor(source, file, 0), jenkinsDir, shellMentions);
|
|
11940
12242
|
}
|
|
11941
12243
|
if (shellMentions !== void 0) {
|
|
11942
|
-
const
|
|
11943
|
-
|
|
12244
|
+
const lines = text.split(/\r?\n/);
|
|
12245
|
+
const keyAt = lines.findIndex((line) => PYTEST_ADDOPTS_LINE_PATTERN.test(line));
|
|
12246
|
+
const keyRead = keyAt !== -1 && PYTEST_ADDOPTS_KEY_PATTERN.test(lines[keyAt] ?? "") && !shellMentions.reachesOther;
|
|
12247
|
+
noteAddoptsMentions(text, shellMentions.read + (keyRead ? 1 : 0), new Set(keyRead ? [keyAt] : []), file);
|
|
11944
12248
|
}
|
|
11945
12249
|
}
|
|
11946
12250
|
const noxAt = invocations.length;
|
|
@@ -11952,7 +12256,7 @@ function readPytestInvocations(repoFiles, readText) {
|
|
|
11952
12256
|
const unreadPosargs = () => hidden ??= [...partitioned.unread, ...noxUnread, ...noxCommandsInUnreadFiles(repoFiles, readText, readActions)];
|
|
11953
12257
|
const passedSets = [[], ...literal.map((entry) => entry.words)];
|
|
11954
12258
|
const callCost = passedSets.reduce((sum, words) => sum + 1 + words.length, 0);
|
|
11955
|
-
let noxLeft =
|
|
12259
|
+
let noxLeft = POSARGS_BUDGET;
|
|
11956
12260
|
let unreadSaid = false;
|
|
11957
12261
|
for (const file of usable.filter((candidate) => basename(candidate) === "noxfile.py")) {
|
|
11958
12262
|
const text = readText(file);
|
|
@@ -12003,7 +12307,7 @@ function readPytestInvocations(repoFiles, readText) {
|
|
|
12003
12307
|
const just = /[Jj]ustfile$/.test(file);
|
|
12004
12308
|
const targets = just ? readJustRecipes(text) : readRecipeTargets(text);
|
|
12005
12309
|
const headers = just ? new Map([...readJustRecipes(text)].map(([name, recipe]) => [name, recipe.line])) : makeTargetLines(text);
|
|
12006
|
-
const setByEnvironment = (name) => name === "PYTEST_ADDOPTS" ? addoptsMentionNotRead : environmentTexts.some((ciText) => containsWord(ciText, name));
|
|
12310
|
+
const setByEnvironment = (name) => name === "PYTEST_ADDOPTS" ? addoptsMentionNotRead !== void 0 : environmentTexts.some((ciText) => containsWord(ciText, name));
|
|
12007
12311
|
const justVariables = just ? readJustVariables(text) : void 0;
|
|
12008
12312
|
const make = just ? void 0 : readMakeVariables(text, setByEnvironment);
|
|
12009
12313
|
const budget = { left: MAKE_EXPANSION_BUDGET };
|
|
@@ -12013,6 +12317,7 @@ function readPytestInvocations(repoFiles, readText) {
|
|
|
12013
12317
|
const expand = expandWith(make);
|
|
12014
12318
|
let addoptsUnknown = false;
|
|
12015
12319
|
let addoptsText;
|
|
12320
|
+
let addoptsNotKnownAt;
|
|
12016
12321
|
if (justVariables !== void 0) {
|
|
12017
12322
|
const exportsAll = /^set[ \t]+export(?:[ \t]*:=[ \t]*true)?[ \t]*$/m.test(text);
|
|
12018
12323
|
const assigned = /^(?:export[ \t]+)?PYTEST_ADDOPTS[ \t]*:=/m.test(text);
|
|
@@ -12022,8 +12327,13 @@ function readPytestInvocations(repoFiles, readText) {
|
|
|
12022
12327
|
addoptsUnknown = addoptsText === void 0;
|
|
12023
12328
|
}
|
|
12024
12329
|
} else {
|
|
12025
|
-
const exported = /^(?:export[ \t]*|\.EXPORT_ALL_VARIABLES[ \t]*:.*|(?:override[ \t]+)?export[ \t]+PYTEST_ADDOPTS\b.*)$/m.test(text);
|
|
12026
|
-
|
|
12330
|
+
const exported = /^ *(?:export[ \t]*|\.EXPORT_ALL_VARIABLES[ \t]*:.*|(?:override[ \t]+)?export[ \t]+PYTEST_ADDOPTS\b.*)$/m.test(text);
|
|
12331
|
+
const assigns = make?.assigned.has("PYTEST_ADDOPTS") === true || make?.defaulted.has("PYTEST_ADDOPTS") === true;
|
|
12332
|
+
const mentionAt = exported || !assigns || makeOverrides.has("PYTEST_ADDOPTS") ? void 0 : addoptsMentionNotRead;
|
|
12333
|
+
if (mentionAt !== void 0) {
|
|
12334
|
+
addoptsUnknown = true;
|
|
12335
|
+
addoptsNotKnownAt = mentionAt;
|
|
12336
|
+
} else if (isUnknown("PYTEST_ADDOPTS")) addoptsUnknown = true;
|
|
12027
12337
|
else if (exported && make?.values.has("PYTEST_ADDOPTS")) {
|
|
12028
12338
|
const expanded = expand(make.values.get("PYTEST_ADDOPTS") ?? "");
|
|
12029
12339
|
if (expanded.unknown) addoptsUnknown = true;
|
|
@@ -12033,7 +12343,8 @@ function readPytestInvocations(repoFiles, readText) {
|
|
|
12033
12343
|
const addopts = addoptsText === void 0 ? void 0 : shellWords(shellWords(addoptsText.trim()).join(" "));
|
|
12034
12344
|
const unknownRunsPytest = (command, expanded) => command.toLowerCase().includes("pytest") || expanded.toLowerCase().includes("pytest") || makeReferencesIn(expanded).some((name) => make?.pytestNames.has(name) === true);
|
|
12035
12345
|
const firstTarget = [...targets.keys()][0];
|
|
12036
|
-
const readRecipes = (names, variables, inForceAddopts, notKnown, addoptsAt) => {
|
|
12346
|
+
const readRecipes = (names, variables, inForceAddopts, notKnown, addoptsAt, notKnownAt) => {
|
|
12347
|
+
const pointAt = notKnown === void 0 ? void 0 : notKnownAt;
|
|
12037
12348
|
const expandHere = variables === make ? expand : expandWith(variables);
|
|
12038
12349
|
const reached = /* @__PURE__ */ new Set();
|
|
12039
12350
|
const queue = names.filter((name) => name !== void 0 && targets.has(name));
|
|
@@ -12053,14 +12364,21 @@ function readPytestInvocations(repoFiles, readText) {
|
|
|
12053
12364
|
}
|
|
12054
12365
|
readShellCommands(expanded.text, repoDirectory(file), inForceAddopts, (words, directory, inForce, why) => {
|
|
12055
12366
|
queue.push(...recipeTargetsNamed(words).filter((named) => targets.has(named)));
|
|
12056
|
-
const visit = visitor(a1NotePath(file), file, headers.get(name) ?? 0, 0, void 0, void 0, addoptsAt);
|
|
12367
|
+
const visit = visitor(pointAt?.source ?? a1NotePath(file), pointAt?.file ?? file, pointAt?.line ?? headers.get(name) ?? 0, 0, void 0, void 0, addoptsAt);
|
|
12057
12368
|
if (notKnown !== void 0) visit(words, null, inForce, notKnown);
|
|
12058
12369
|
else visit(words, directory, inForce, why);
|
|
12059
12370
|
});
|
|
12060
12371
|
}
|
|
12061
12372
|
}
|
|
12062
12373
|
};
|
|
12063
|
-
readRecipes(
|
|
12374
|
+
readRecipes(
|
|
12375
|
+
[firstTarget, "test", "tests", "check", ...namedTargets],
|
|
12376
|
+
make,
|
|
12377
|
+
addopts,
|
|
12378
|
+
addoptsUnknown ? addoptsNotKnownAt?.text ?? "PYTEST_ADDOPTS" : void 0,
|
|
12379
|
+
void 0,
|
|
12380
|
+
addoptsNotKnownAt
|
|
12381
|
+
);
|
|
12064
12382
|
if (make === void 0 || addoptsUnknown) continue;
|
|
12065
12383
|
const here = repoDirectory(file);
|
|
12066
12384
|
const baseKey = addopts?.join("\0");
|
|
@@ -12093,11 +12411,12 @@ function readPytestInvocations(repoFiles, readText) {
|
|
|
12093
12411
|
}
|
|
12094
12412
|
const toxenv = readToxenvValues(environmentTexts);
|
|
12095
12413
|
for (const name of toxenv.names) namedToxEnvironments.add(name);
|
|
12414
|
+
const toxBudget = { left: POSARGS_BUDGET, characters: POSARGS_CHARACTER_BUDGET };
|
|
12096
12415
|
for (const file of usable.filter((candidate) => basename(candidate) === "tox.ini")) {
|
|
12097
12416
|
const text = readText(file);
|
|
12098
12417
|
if (text === void 0) continue;
|
|
12099
12418
|
const notRead = (entry) => invocations.push({ source: entry.source, file: entry.file, anchor: entry.anchor, directory: "", args: [], text: entry.text, unread: entry.text });
|
|
12100
|
-
readToxCommands(file, text, namedToxEnvironments, (anchor) => visitor(a1NotePath(file), file, anchor), toxPosargs, notRead, toxenv.unknown);
|
|
12419
|
+
readToxCommands(file, text, namedToxEnvironments, (anchor) => visitor(a1NotePath(file), file, anchor), toxPosargs, notRead, toxenv.unknown, toxBudget);
|
|
12101
12420
|
}
|
|
12102
12421
|
const after = invocations.splice(noxAt);
|
|
12103
12422
|
for (const run of readNoxfiles()) invocations.push(run);
|
|
@@ -12364,6 +12683,8 @@ function readMakeVariables(text, setByEnvironment = () => false, environment) {
|
|
|
12364
12683
|
const simple = /* @__PURE__ */ new Set();
|
|
12365
12684
|
const unknown = /* @__PURE__ */ new Set();
|
|
12366
12685
|
const pytestNames = /* @__PURE__ */ new Set();
|
|
12686
|
+
const assigned = /* @__PURE__ */ new Map();
|
|
12687
|
+
const defaulted = /* @__PURE__ */ new Set();
|
|
12367
12688
|
const budget = { left: MAKE_EXPANSION_BUDGET };
|
|
12368
12689
|
let fromEnvironment = environment !== void 0;
|
|
12369
12690
|
if (environment !== void 0) values.set(environment.name, environment.value);
|
|
@@ -12373,7 +12694,9 @@ function readMakeVariables(text, setByEnvironment = () => false, environment) {
|
|
|
12373
12694
|
};
|
|
12374
12695
|
let conditional = 0;
|
|
12375
12696
|
let defining;
|
|
12697
|
+
let lineIndex = -1;
|
|
12376
12698
|
for (const line of text.split(/\r?\n/)) {
|
|
12699
|
+
lineIndex += 1;
|
|
12377
12700
|
const trimmed = line.trim();
|
|
12378
12701
|
if (defining !== void 0) {
|
|
12379
12702
|
if (/^endef\b/.test(trimmed)) defining = void 0;
|
|
@@ -12381,6 +12704,7 @@ function readMakeVariables(text, setByEnvironment = () => false, environment) {
|
|
|
12381
12704
|
continue;
|
|
12382
12705
|
}
|
|
12383
12706
|
if (line.startsWith(" ")) continue;
|
|
12707
|
+
const body = line.replace(/^[ \t]+/, "");
|
|
12384
12708
|
const define = /^(?:override[ \t]+)?(?:export[ \t]+)?define[ \t]+([A-Za-z_][A-Za-z0-9_.-]*)/.exec(trimmed);
|
|
12385
12709
|
if (define) {
|
|
12386
12710
|
defining = define[1] ?? "";
|
|
@@ -12395,14 +12719,14 @@ function readMakeVariables(text, setByEnvironment = () => false, environment) {
|
|
|
12395
12719
|
conditional = Math.max(0, conditional - 1);
|
|
12396
12720
|
continue;
|
|
12397
12721
|
}
|
|
12398
|
-
const targetSpecific = /^[^#=:\t][^#=:]*:[ \t]*(?:(?:override|export|private)[ \t]+)*([A-Za-z_][A-Za-z0-9_.-]*)[ \t]*(?:\?=|:::=|::=|:=|\+=|!=|=)(.*)$/.exec(
|
|
12722
|
+
const targetSpecific = /^[^#=:\t][^#=:]*:[ \t]*(?:(?:override|export|private)[ \t]+)*([A-Za-z_][A-Za-z0-9_.-]*)[ \t]*(?:\?=|:::=|::=|:=|\+=|!=|=)(.*)$/.exec(body);
|
|
12399
12723
|
if (targetSpecific) {
|
|
12400
12724
|
const name2 = targetSpecific[1] ?? "";
|
|
12401
12725
|
if ((targetSpecific[2] ?? "").toLowerCase().includes("pytest")) pytestNames.add(name2);
|
|
12402
12726
|
forget(name2);
|
|
12403
12727
|
continue;
|
|
12404
12728
|
}
|
|
12405
|
-
const match = /^(?:override[ \t]+)?(?:export[ \t]+)?([A-Za-z_][A-Za-z0-9_.-]*)[ \t]*(\?=|:::=|::=|:=|\+=|!=|=)(.*)$/.exec(
|
|
12729
|
+
const match = /^(?:override[ \t]+)?(?:export[ \t]+)?([A-Za-z_][A-Za-z0-9_.-]*)[ \t]*(\?=|:::=|::=|:=|\+=|!=|=)(.*)$/.exec(body);
|
|
12406
12730
|
if (!match) continue;
|
|
12407
12731
|
const name = match[1] ?? "";
|
|
12408
12732
|
const operator = match[2] ?? "=";
|
|
@@ -12410,12 +12734,18 @@ function readMakeVariables(text, setByEnvironment = () => false, environment) {
|
|
|
12410
12734
|
if (value.toLowerCase().includes("pytest")) pytestNames.add(name);
|
|
12411
12735
|
if (unknown.has(name)) continue;
|
|
12412
12736
|
const fromTheEnvironment = environment !== void 0 && name === environment.name;
|
|
12413
|
-
if (fromTheEnvironment && environment.overrides && !/^override[ \t]/.test(
|
|
12414
|
-
if (conditional > 0 || operator === "!=" || operator === ":::="
|
|
12737
|
+
if (fromTheEnvironment && environment.overrides && !/^override[ \t]/.test(body)) continue;
|
|
12738
|
+
if (conditional > 0 || operator === "!=" || operator === ":::=") {
|
|
12739
|
+
forget(name);
|
|
12740
|
+
continue;
|
|
12741
|
+
}
|
|
12742
|
+
if (operator === "?=" && setByEnvironment(name)) {
|
|
12743
|
+
defaulted.add(name);
|
|
12415
12744
|
forget(name);
|
|
12416
12745
|
continue;
|
|
12417
12746
|
}
|
|
12418
12747
|
if (fromTheEnvironment && operator !== "?=") fromEnvironment = false;
|
|
12748
|
+
if (operator !== "?=" && !assigned.has(name)) assigned.set(name, { text: line.trim(), line: lineIndex });
|
|
12419
12749
|
if (operator === "?=") {
|
|
12420
12750
|
if (!values.has(name)) {
|
|
12421
12751
|
values.set(name, value);
|
|
@@ -12439,7 +12769,7 @@ function readMakeVariables(text, setByEnvironment = () => false, environment) {
|
|
|
12439
12769
|
simple.delete(name);
|
|
12440
12770
|
}
|
|
12441
12771
|
}
|
|
12442
|
-
return { values, unknown, pytestNames, fromEnvironment };
|
|
12772
|
+
return { values, unknown, pytestNames, fromEnvironment, assigned, defaulted };
|
|
12443
12773
|
}
|
|
12444
12774
|
function expandMakeVariables(command, values, isUnknown, budget, depth = 0) {
|
|
12445
12775
|
if (!command.includes("$")) return { text: command, unknown: false };
|
|
@@ -12632,7 +12962,8 @@ function substituteHatchArguments(command, args) {
|
|
|
12632
12962
|
return out + command.slice(index);
|
|
12633
12963
|
}
|
|
12634
12964
|
var NOX_POSARGS_WORD = "\0session.posargs";
|
|
12635
|
-
var
|
|
12965
|
+
var POSARGS_BUDGET = 4096;
|
|
12966
|
+
var POSARGS_CHARACTER_BUDGET = 262144;
|
|
12636
12967
|
function readNoxRunCalls(text) {
|
|
12637
12968
|
const calls = [];
|
|
12638
12969
|
const pattern = /\bsession\.run(?:_always)?[ \t]*\(/g;
|
|
@@ -12720,7 +13051,7 @@ function readCiAddopts(text) {
|
|
|
12720
13051
|
}
|
|
12721
13052
|
return void 0;
|
|
12722
13053
|
}
|
|
12723
|
-
function readToxCommands(file, text, named, visitAt, posargs = [], notRead = () => void 0, factorsUnknown = false) {
|
|
13054
|
+
function readToxCommands(file, text, named, visitAt, posargs = [], notRead = () => void 0, factorsUnknown = false, budget = { left: POSARGS_BUDGET, characters: POSARGS_CHARACTER_BUDGET }) {
|
|
12724
13055
|
const sections = readIniSections(text);
|
|
12725
13056
|
const toxDirectory = repoDirectory(file);
|
|
12726
13057
|
const tox = sections.get("tox");
|
|
@@ -12740,6 +13071,12 @@ function readToxCommands(file, text, named, visitAt, posargs = [], notRead = ()
|
|
|
12740
13071
|
if (section) environments.push({ section, names: [name] });
|
|
12741
13072
|
}
|
|
12742
13073
|
const { literal: literalPosargs, unread: unreadPosargs } = partitionPosargs(posargs);
|
|
13074
|
+
let posargsWords = 0;
|
|
13075
|
+
let posargsCharacters = 0;
|
|
13076
|
+
for (const entry of literalPosargs) {
|
|
13077
|
+
posargsWords += entry.words.length;
|
|
13078
|
+
for (const word of entry.words) posargsCharacters += word.length + 1;
|
|
13079
|
+
}
|
|
12743
13080
|
let unreadSaid = false;
|
|
12744
13081
|
const seen = /* @__PURE__ */ new Set();
|
|
12745
13082
|
for (const { section, names } of environments) {
|
|
@@ -12766,6 +13103,16 @@ function readToxCommands(file, text, named, visitAt, posargs = [], notRead = ()
|
|
|
12766
13103
|
const anchor = lineOfText.get(raw.trim()) ?? 0;
|
|
12767
13104
|
const relative3 = directory === null ? "." : posix.relative(directory === "" ? "." : directory, toxDirectory === "" ? "." : toxDirectory) || ".";
|
|
12768
13105
|
const substitute = (passed) => line.replace(/\{posargs\}/g, passed === void 0 ? "" : passed.map((word) => /\s/.test(word) ? `'${word}'` : word).join(" ")).replace(/\{posargs:([^}]*)\}/g, (_, fallback) => passed === void 0 ? fallback : passed.join(" ")).replace(/\{(?:toxinidir|tox_root)\}/g, relative3);
|
|
13106
|
+
if (line.includes("{posargs")) {
|
|
13107
|
+
let occurrences = 0;
|
|
13108
|
+
for (let at = line.indexOf("{posargs"); at !== -1; at = line.indexOf("{posargs", at + 1)) occurrences += 1;
|
|
13109
|
+
budget.left -= 1 + literalPosargs.length + occurrences * posargsWords;
|
|
13110
|
+
budget.characters -= occurrences * posargsCharacters;
|
|
13111
|
+
if (budget.left < 0 || budget.characters < 0) {
|
|
13112
|
+
notRead({ text: line, source: a1NotePath(file), file, anchor });
|
|
13113
|
+
continue;
|
|
13114
|
+
}
|
|
13115
|
+
}
|
|
12769
13116
|
readShellCommands(substitute(void 0), directory, addopts, visitAt(anchor));
|
|
12770
13117
|
if (!line.includes("{posargs")) continue;
|
|
12771
13118
|
for (const entry of literalPosargs) readShellCommands(substitute(entry.words), directory, addopts, visitAt(anchor));
|
|
@@ -14038,10 +14385,15 @@ var PYTHON_BUILTIN_COPY_CALL_PATTERN = /^(?:list|tuple)[ \t]*\(/;
|
|
|
14038
14385
|
var PYTHON_BUILTINS_REACH_PATTERN = /builtins|(?<![A-Za-z0-9_])(?:modules|__dict__|__globals__|__getattr__|__getattribute__|__setattr__|__delattr__|__import__|importlib|setattr|delattr|getattr|attrgetter|_getframe|currentframe|f_globals|f_locals|f_back|tb_frame|gi_frame|cr_frame|ag_frame|gc|ctypes|__subclasses__|pickle|marshal|runpy|__loader__|__spec__)(?![A-Za-z0-9_])|(?<![A-Za-z0-9_.])(?:exec|eval|compile|globals|vars|locals)(?![A-Za-z0-9_])|\.[ \t]*(?:list|tuple)(?![A-Za-z0-9_])(?![ \t]*\()/;
|
|
14039
14386
|
var PYTHON_SOURCE_FILE_PATTERN = /\.(?:py|pyw|pyx|pxd|pxi)$/i;
|
|
14040
14387
|
var PYTHON_UNREAD_MODULE_FILE_PATTERN = /\.(?:pyc|pyo|so|pyd|egg|whl|zip|pth)$/i;
|
|
14041
|
-
function filesIncludingExcluded(repoPath) {
|
|
14388
|
+
function filesIncludingExcluded(repoPath, scanLimitations) {
|
|
14042
14389
|
const tracked = readGitTrackedFileOutput(repoPath);
|
|
14043
14390
|
if (tracked.ok) return tracked.stdout.split("\n").filter((line) => line !== "");
|
|
14044
|
-
if (!isExpectedGitAbsence(tracked))
|
|
14391
|
+
if (!isExpectedGitAbsence(tracked)) {
|
|
14392
|
+
scanLimitations.add(
|
|
14393
|
+
`${describeGitFailure(tracked.reason, "full tracked-file inventory")} A1 credited no copy of pytest's collected items made through a builtin, because the files that may replace the builtin could not be listed.`
|
|
14394
|
+
);
|
|
14395
|
+
return void 0;
|
|
14396
|
+
}
|
|
14045
14397
|
const files = [];
|
|
14046
14398
|
const pending = [""];
|
|
14047
14399
|
for (let next = pending.pop(); next !== void 0; next = pending.pop()) {
|
|
@@ -14060,8 +14412,8 @@ function filesIncludingExcluded(repoPath) {
|
|
|
14060
14412
|
}
|
|
14061
14413
|
return files;
|
|
14062
14414
|
}
|
|
14063
|
-
function builtinCopyReplacedBy(repoPath) {
|
|
14064
|
-
const files = filesIncludingExcluded(repoPath);
|
|
14415
|
+
function builtinCopyReplacedBy(repoPath, scanLimitations) {
|
|
14416
|
+
const files = filesIncludingExcluded(repoPath, scanLimitations);
|
|
14065
14417
|
if (files === void 0) return "and the repository's files could not be listed";
|
|
14066
14418
|
for (const listed of files) {
|
|
14067
14419
|
const quoted = listed.startsWith('"') && listed.endsWith('"') && listed.length > 1;
|
|
@@ -14398,7 +14750,7 @@ function readPytestCollectionHook(source, index, builtinsNamedIn = () => void 0)
|
|
|
14398
14750
|
return void 0;
|
|
14399
14751
|
}
|
|
14400
14752
|
var A1_HOOK_NOT_READ = "hook not read: ";
|
|
14401
|
-
function findPytestRunWideSkips(repoPath, repoFiles) {
|
|
14753
|
+
function findPytestRunWideSkips(repoPath, repoFiles, scanLimitations = /* @__PURE__ */ new Set()) {
|
|
14402
14754
|
const found = [];
|
|
14403
14755
|
let ignoresCollection = false;
|
|
14404
14756
|
let builtinsRead = false;
|
|
@@ -14406,7 +14758,7 @@ function findPytestRunWideSkips(repoPath, repoFiles) {
|
|
|
14406
14758
|
const builtinsNamedIn = () => {
|
|
14407
14759
|
if (!builtinsRead) {
|
|
14408
14760
|
builtinsRead = true;
|
|
14409
|
-
builtinsFile = builtinCopyReplacedBy(repoPath);
|
|
14761
|
+
builtinsFile = builtinCopyReplacedBy(repoPath, scanLimitations);
|
|
14410
14762
|
}
|
|
14411
14763
|
return builtinsFile;
|
|
14412
14764
|
};
|
|
@@ -15310,10 +15662,10 @@ function isRecord(value) {
|
|
|
15310
15662
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15311
15663
|
}
|
|
15312
15664
|
function isTestRunnerConfig(repoPath, file) {
|
|
15313
|
-
return /(^|\/)(vitest|jest|pytest|playwright|cypress|mocha)\.config\.[cm]?[jt]s$/.test(file) || /(^|\/)(pytest\.ini|tox\.ini)$/.test(file) || /(^|\/)phpunit\.xml(\.dist)?$/.test(file) || /(^|\/)pyproject\.toml$/.test(file) && fileContains(repoPath, file, /^\[tool\.pytest(?:\.|\])/m) || /(^|\/)CMakeLists\.txt$/.test(file) && fileContains(repoPath, file, /\b(?:enable_testing|add_test|ctest_add_tests)\s*\(/i) || /(^|\/)Makefile$/.test(file) && fileContains(repoPath, file,
|
|
15665
|
+
return /(^|\/)(vitest|jest|pytest|playwright|cypress|mocha)\.config\.[cm]?[jt]s$/.test(file) || /(^|\/)(pytest\.ini|tox\.ini)$/.test(file) || /(^|\/)phpunit\.xml(\.dist)?$/.test(file) || /(^|\/)pyproject\.toml$/.test(file) && fileContains(repoPath, file, /^\[tool\.pytest(?:\.|\])/m) || /(^|\/)CMakeLists\.txt$/.test(file) && fileContains(repoPath, file, /\b(?:enable_testing|add_test|ctest_add_tests)\s*\(/i) || /(^|\/)Makefile$/.test(file) && fileContains(repoPath, file, /^[^\S\n\r\u2028\u2029]*(?:test|tests|check)\s*:/m);
|
|
15314
15666
|
}
|
|
15315
15667
|
function isRecipeTestEntry(repoPath, file) {
|
|
15316
|
-
return isRecipeFile(file) && fileContains(repoPath, file,
|
|
15668
|
+
return isRecipeFile(file) && fileContains(repoPath, file, /^[^\S\n\r\u2028\u2029]*(?:test|tests|check)\s*:/m);
|
|
15317
15669
|
}
|
|
15318
15670
|
function findCoverageConfigFiles(repoPath, repoFiles, authoredOnly = false) {
|
|
15319
15671
|
return repoFiles.filter(
|
|
@@ -16014,7 +16366,7 @@ function markdownClaimArtifactDeclarations(file, contents) {
|
|
|
16014
16366
|
var WORKFLOW_WRITE_OPERATOR_PATTERN = />|(?:^|[\s|;&(])(?:tee|-o|--out[\w-]*)(?=[\s=]|$)/;
|
|
16015
16367
|
var WORKFLOW_ARTIFACT_ACTION_PATTERN = /^\s*(?:-\s+)?uses\s*:\s*["']?actions\/(?:upload|download)-artifact(?:[/@]|["']?\s*$)/i;
|
|
16016
16368
|
function workflowClaimArtifactDeclarations(contents) {
|
|
16017
|
-
if (
|
|
16369
|
+
if (/^[^\S\n\r\u2028\u2029]*(?:-\s+)?working-directory\s*:/m.test(contents)) return [];
|
|
16018
16370
|
const declarations = [];
|
|
16019
16371
|
let stepsIndent = null;
|
|
16020
16372
|
let stepItemIndent = null;
|
|
@@ -16093,13 +16445,13 @@ function isCoverageConfig(repoPath, file) {
|
|
|
16093
16445
|
return true;
|
|
16094
16446
|
}
|
|
16095
16447
|
if (/(^|\/)(?:setup\.cfg|tox\.ini)$/.test(file)) {
|
|
16096
|
-
return fileContains(repoPath, file,
|
|
16448
|
+
return fileContains(repoPath, file, /^[^\S\n\r\u2028\u2029]*\[coverage:(?:run|report)\]\s*$/m);
|
|
16097
16449
|
}
|
|
16098
16450
|
if (/(^|\/)vite\.config\.[cm]?[jt]s$/.test(file)) {
|
|
16099
16451
|
return viteConfigCollectsCoverage(repoPath, file);
|
|
16100
16452
|
}
|
|
16101
16453
|
if (/(^|\/)\.simplecov$/.test(file)) {
|
|
16102
|
-
return fileContains(repoPath, file, /^(
|
|
16454
|
+
return fileContains(repoPath, file, /^(?![^\S\n\r\u2028\u2029]*#).*\bSimpleCov\b/m);
|
|
16103
16455
|
}
|
|
16104
16456
|
if (/(^|\/)phpunit\.xml(?:\.dist)?$/.test(file)) {
|
|
16105
16457
|
return /<coverage(?=[\s>/])/.test(stripXmlComments(readRepoText(join2(repoPath, file), "utf8")));
|
|
@@ -16144,7 +16496,10 @@ function packageJsonHasCoverageTooling(repoPath, file) {
|
|
|
16144
16496
|
return packageJsonHasStaticCoverageTooling(repoPath, file);
|
|
16145
16497
|
}
|
|
16146
16498
|
function vitestCoveragePropertyCollects(contents) {
|
|
16147
|
-
const property =
|
|
16499
|
+
const property = widenToLeadingBlankRun(
|
|
16500
|
+
/(?:^|[{,])[^\S\n\r\u2028\u2029]*["']?coverage["']?\s*:\s*([^,\r\n}]*)/im.exec(contents),
|
|
16501
|
+
contents
|
|
16502
|
+
)?.[1]?.trim();
|
|
16148
16503
|
if (!property || /^(?:false\b|null\b|undefined\b)/i.test(property)) return false;
|
|
16149
16504
|
if (/^\{/.test(property) && /\benabled\s*:\s*false\b/i.test(contents)) return false;
|
|
16150
16505
|
return true;
|
|
@@ -16158,7 +16513,10 @@ function viteConfigCollectsCoverage(repoPath, file) {
|
|
|
16158
16513
|
if (!/["']\.\/vite\.config(?:\.[cm]?[jt]s)?["']/.test(vitestConfig)) return false;
|
|
16159
16514
|
}
|
|
16160
16515
|
const contents = stripJavaScriptComments(readRepoText(join2(repoPath, file), "utf8"));
|
|
16161
|
-
const testProperty =
|
|
16516
|
+
const testProperty = widenToLeadingBlankRun(
|
|
16517
|
+
/(?:^|[{,])[^\S\n\r\u2028\u2029]*["']?test["']?\s*:\s*\{/m.exec(contents),
|
|
16518
|
+
contents
|
|
16519
|
+
);
|
|
16162
16520
|
if (!testProperty) return false;
|
|
16163
16521
|
const block = balancedBraceBlock(contents, testProperty.index + testProperty[0].length - 1);
|
|
16164
16522
|
return vitestCoveragePropertyCollects(block);
|
|
@@ -16214,7 +16572,7 @@ function isV23CoverageConfig(repoPath, file) {
|
|
|
16214
16572
|
}
|
|
16215
16573
|
if (/(^|\/)jest\.config\.[cm]?[jt]s$/.test(file)) {
|
|
16216
16574
|
const contents = stripJavaScriptComments(readRepoText(join2(repoPath, file), "utf8"));
|
|
16217
|
-
return /(?:^|[{,])\
|
|
16575
|
+
return /(?:^|[{,])[^\S\n\r\u2028\u2029]*["']?collectCoverage["']?\s*:\s*true\b/m.test(contents) || /(?:^|[{,])[^\S\n\r\u2028\u2029]*["']?(?:coverageThreshold|coverageProvider|coverageDirectory)["']?\s*:/m.test(
|
|
16218
16576
|
contents
|
|
16219
16577
|
);
|
|
16220
16578
|
}
|
|
@@ -16228,7 +16586,10 @@ function coverageConfigurationDecision(repoPath, file) {
|
|
|
16228
16586
|
if (/(^|\/)vitest\.config\.[cm]?[jt]s$/.test(file)) {
|
|
16229
16587
|
const rawContents = readRepoText(join2(repoPath, file), "utf8");
|
|
16230
16588
|
const contents = stripJavaScriptComments(rawContents);
|
|
16231
|
-
const match =
|
|
16589
|
+
const match = widenToLeadingBlankRun(
|
|
16590
|
+
/(?:^|[{,])[^\S\n\r\u2028\u2029]*["']?coverage["']?\s*:\s*([^,\r\n}]*)/im.exec(contents),
|
|
16591
|
+
contents
|
|
16592
|
+
);
|
|
16232
16593
|
if (!match) return null;
|
|
16233
16594
|
return {
|
|
16234
16595
|
path: file,
|
|
@@ -16242,7 +16603,10 @@ function coverageConfigurationDecision(repoPath, file) {
|
|
|
16242
16603
|
if (/(^|\/)jest\.config\.[cm]?[jt]s$/.test(file)) {
|
|
16243
16604
|
const rawContents = readRepoText(join2(repoPath, file), "utf8");
|
|
16244
16605
|
const contents = stripJavaScriptComments(rawContents);
|
|
16245
|
-
const match =
|
|
16606
|
+
const match = widenToLeadingBlankRun(
|
|
16607
|
+
/(?:^|[{,])[^\S\n\r\u2028\u2029]*["']?(collectCoverage|coverageThreshold|coverageProvider|coverageDirectory)["']?\s*:\s*([^,\r\n}]*)/m.exec(
|
|
16608
|
+
contents
|
|
16609
|
+
),
|
|
16246
16610
|
contents
|
|
16247
16611
|
);
|
|
16248
16612
|
if (!match) return null;
|
|
@@ -17297,8 +17661,8 @@ var EXPRESS_ERROR_MIDDLEWARE_PATTERN = /\(\s*(?:_?err|_?error)(?:\s*:\s*(?:[^,()
|
|
|
17297
17661
|
var EXPRESS_MIDDLEWARE_REACHABLE_PATTERN = /\.use\s*\(|\bmodule\.exports\b|\bexport\s+(?:default\b|function\b|const\b|class\b)/;
|
|
17298
17662
|
var ERROR_BOUNDARY_FILENAME_PATTERN = /(^|\/)(?:error-boundary|error)\.(?:tsx|jsx|ts|js)$/;
|
|
17299
17663
|
var RACK_SERVER_ENTRYPOINT_PATTERN = /Rack::(?:Server|Handler(?:::\w+)?)\.(?:start|run)\s*\(/;
|
|
17300
|
-
var RACK_CONFIG_RUN_PATTERN =
|
|
17301
|
-
var RUNTIME_CONTAINER_COMMAND_PATTERN =
|
|
17664
|
+
var RACK_CONFIG_RUN_PATTERN = /^[^\S\n\r\u2028\u2029]*run\s+(?:(?:[A-Z]\w*(?:::\w+)*(?:\.new)?|lambda)\b|->)/m;
|
|
17665
|
+
var RUNTIME_CONTAINER_COMMAND_PATTERN = /^[^\S\n\r\u2028\u2029]*(?:CMD|ENTRYPOINT)\s+.*(?:\b(?:start|serve|server|qgis|nginx|apache|gunicorn|uvicorn)\b|manage\.py\s+runserver).*$/im;
|
|
17302
17666
|
var NON_RUNTIME_CONTAINER_COMMAND_PATTERN = /\b(?:test|lint|build|compile|package|check)\b/i;
|
|
17303
17667
|
var V20_NODE_RUNTIME_CONTAINER_COMMAND_PATTERN = /\bnode\b.*\b(?:main|server|app|service|index)\.(?:[cm]?js|ts)\b/i;
|
|
17304
17668
|
var CI_DEPLOY_JOB_PATTERN = /(?:^\s{2,4}deploy\s*:|\b(?:fly|heroku|vercel|netlify|railway|render)\s+deploy\b|kubectl\s+apply\b|helm\s+(?:install|upgrade|deploy)\b|docker\s+push\b)/im;
|
|
@@ -18047,7 +18411,7 @@ function findFirstMatchingLineOutsideComments(repoPath, file, pattern) {
|
|
|
18047
18411
|
const index = lines.findIndex((line) => pattern.test(line));
|
|
18048
18412
|
return index === -1 ? null : index + 1;
|
|
18049
18413
|
}
|
|
18050
|
-
var PEM_PRIVATE_KEY_ASSIGNMENT_PATTERN = /(?:["'])?([A-Za-z_$][\w$.-]*)(?:["'])?\s*[:=]\s*(?:\|[-+0-9]*\s*\r?\n[ \t]*)?["'`]?-----BEGIN((?: (?:RSA|DSA|EC|OPENSSH|ENCRYPTED))? PRIVATE KEY)-----([\s\S]{0,32768}?)-----END\2-----/
|
|
18414
|
+
var PEM_PRIVATE_KEY_ASSIGNMENT_PATTERN = /(?:["'])?([A-Za-z_$][\w$.-]*)(?:["'])?\s*[:=]\s*(?:\|[-+0-9]*\s*\r?\n[ \t]*)?["'`]?-----BEGIN((?: (?:RSA|DSA|EC|OPENSSH|ENCRYPTED))? PRIVATE KEY)-----([\s\S]{0,32768}?)-----END\2-----/y;
|
|
18051
18415
|
var BARE_PEM_PRIVATE_KEY_PATTERN = /["'`]?-----BEGIN((?: (?:RSA|DSA|EC|OPENSSH|ENCRYPTED))? PRIVATE KEY)-----([\s\S]{0,32768}?)-----END\1-----/g;
|
|
18052
18416
|
var BARE_PEM_KEY_FILE_PATTERN = /(?:^|\/)id_(?:rsa|dsa|ecdsa|ed25519)$|\.(?:pem|key)$/i;
|
|
18053
18417
|
function isBarePemKeyFile(file) {
|
|
@@ -18062,9 +18426,13 @@ function isPlausiblePemKeyBody(rawBody) {
|
|
|
18062
18426
|
return !isPlaceholderSecretValue(normalized);
|
|
18063
18427
|
}
|
|
18064
18428
|
function scanForPemPrivateKeyMatch(pattern, contents, lines, hasIdentifierGroup) {
|
|
18065
|
-
|
|
18429
|
+
const next = (from) => {
|
|
18430
|
+
if (pattern.sticky) return execFromIdentifierRunStarts(pattern, contents, from);
|
|
18431
|
+
pattern.lastIndex = from;
|
|
18432
|
+
return pattern.exec(contents);
|
|
18433
|
+
};
|
|
18066
18434
|
let match;
|
|
18067
|
-
|
|
18435
|
+
for (let from = 0; (match = next(from)) !== null; from = match.index + match[0].length) {
|
|
18068
18436
|
const full = match[0];
|
|
18069
18437
|
const identifier = hasIdentifierGroup ? match[1] ?? "" : "";
|
|
18070
18438
|
const body = (hasIdentifierGroup ? match[3] : match[2]) ?? "";
|
|
@@ -19302,21 +19670,26 @@ init_esm_shims();
|
|
|
19302
19670
|
init_esm_shims();
|
|
19303
19671
|
import { createHash as createHash2 } from "crypto";
|
|
19304
19672
|
var WITAN_RUBRIC_BEHAVIOUR_FINGERPRINTS = Object.freeze({
|
|
19305
|
-
|
|
19306
|
-
|
|
19307
|
-
|
|
19308
|
-
|
|
19309
|
-
|
|
19310
|
-
[
|
|
19311
|
-
[
|
|
19312
|
-
[
|
|
19313
|
-
[
|
|
19673
|
+
// Re-pinned 2026-11-11 (#622), every entry: the corpus gained `history-shallow-depth-1` (a
|
|
19674
|
+
// depth-1 clone of a repository whose full clone scores A2 and B2 from history) and
|
|
19675
|
+
// `publish-after-check-workflow` (B3's check-before-publish shape, #620). No rubric's behaviour
|
|
19676
|
+
// changed: scored without those two specimens, the corpus reproduces every previous value
|
|
19677
|
+
// (rubric-behaviour-fingerprint.test.ts, "#622: the corpus extension ...").
|
|
19678
|
+
[WITAN_RUBRIC_VERSION_V17]: "sha256:1017cbaa7a77b4cdf50177bd20536480d874e191b46a1fe65cd7aaedc794a375",
|
|
19679
|
+
[WITAN_RUBRIC_VERSION_V18]: "sha256:d4479647b2ea561db907f2a0e890e1ea789aeb0ba93e8367a0c115df0084ba01",
|
|
19680
|
+
[WITAN_RUBRIC_VERSION_V19]: "sha256:afc007665ad29b348054699e560fb8b51ccfa9909964dd299a5eccf9156e7652",
|
|
19681
|
+
[WITAN_RUBRIC_VERSION_V20]: "sha256:2f81463b7b97591bfb5b22e25654d688947f19cc5a384f8b217a85a342475001",
|
|
19682
|
+
[WITAN_RUBRIC_VERSION_V21]: "sha256:4afda89876135bc742511257e17f8c0b6db8c88ae90818adfc60ff382b8a49da",
|
|
19683
|
+
[WITAN_RUBRIC_VERSION_V22]: "sha256:804ff598f785ef3a290d37db0617278f811755af49f9f14b1d68fa3d6299dfb2",
|
|
19684
|
+
[WITAN_RUBRIC_VERSION_V23]: "sha256:3d5a16189c49fc10d9c08e1f309f4966ba1c7a3d1dea86ec1e841f67752fab7e",
|
|
19685
|
+
[WITAN_RUBRIC_VERSION_V24]: "sha256:2b822b556a0ca073a0d7dc1ec7690a0b5b2b3e1581c00b03b4aebd987a4cbc0a",
|
|
19686
|
+
[WITAN_RUBRIC_VERSION_V25]: "sha256:86da3a1603ddff9635033721fa41029d7eea06c4b2e39ed8b1da5dd703f177a2",
|
|
19314
19687
|
// v25's closure plus v26 item 6 (docs/calibration/v26/ruling-item-6.md and its amendment 1).
|
|
19315
19688
|
// The corpus holds no declared claim-reconciliation artifact, so the v25 A5 finding no longer
|
|
19316
19689
|
// fires on it and, by amendment 1, A5 is scored without reconciliation_artifact_depth. The A5
|
|
19317
19690
|
// component and the report components of the specimens whose code-trust score that moves differ
|
|
19318
19691
|
// from v25's. A2 does not move on this corpus.
|
|
19319
|
-
[WITAN_RUBRIC_VERSION_V26]: "sha256:
|
|
19692
|
+
[WITAN_RUBRIC_VERSION_V26]: "sha256:0870e977698cf9fa3863c534007c8442fb9c61f274cbefa45fd724801bcc146f",
|
|
19320
19693
|
// v17's closure plus the v27 false-assurance gate (#517-#520, #442, #527, #535). On this corpus
|
|
19321
19694
|
// A4, B2 and B3 move against v17: specimens whose CI runs `npm ci` with no committed lockfile
|
|
19322
19695
|
// score lockfile_coverage 0 under library norms, and `workspace-package-scripts`, whose only
|
|
@@ -19324,8 +19697,9 @@ var WITAN_RUBRIC_BEHAVIOUR_FINGERPRINTS = Object.freeze({
|
|
|
19324
19697
|
// leaderboard/RUBRIC_CHANGELOG.md). That specimen's report component differs from v17's.
|
|
19325
19698
|
// Re-pinned 2026-10-26 (#517, #520 follow-up): the specimen whose CI runs `npm ci` with no
|
|
19326
19699
|
// committed lockfile now also carries A4's warning finding citing that line, so the A4 component
|
|
19327
|
-
// moves; no score, status or report component does. B3's publishing rule
|
|
19328
|
-
|
|
19700
|
+
// moves; no score, status or report component does. Until 2026-11-11 B3's publishing rule reached
|
|
19701
|
+
// no specimen; since #622, `publish-after-check-workflow` holds the shape it counts.
|
|
19702
|
+
[WITAN_RUBRIC_VERSION_V27]: "sha256:8fa9d87e3047afb80e8e09d08c5cb88a651c8e8b69b6b35184d2ab98ddd3a8fb"
|
|
19329
19703
|
});
|
|
19330
19704
|
function rubricBehaviourFingerprint(rubricVersion) {
|
|
19331
19705
|
return WITAN_RUBRIC_BEHAVIOUR_FINGERPRINTS[rubricVersion];
|
|
@@ -19397,7 +19771,8 @@ function usesV17DetectorClosure2(rubricVersion) {
|
|
|
19397
19771
|
}
|
|
19398
19772
|
var MAX_SIGNAL_ADJUSTMENT = 0.8;
|
|
19399
19773
|
function createWitanReport(input, inputSignals, rubric = WITAN_RUBRIC, toolVersion) {
|
|
19400
|
-
const
|
|
19774
|
+
const marked = markOverLimitPaths(input);
|
|
19775
|
+
const parsedInput = WitanReportInputSchema.parse(marked.input);
|
|
19401
19776
|
const signalsByCriterion = mergeSignalsByCriterion(parsedInput.signals);
|
|
19402
19777
|
const readFailureAbstentions = new Set(
|
|
19403
19778
|
[...signalsByCriterion.values()].filter((signal) => signal.insufficientData === true && !isCompositeExcludedAbstention(signal)).map((signal) => signal.criterionId)
|
|
@@ -19412,7 +19787,7 @@ function createWitanReport(input, inputSignals, rubric = WITAN_RUBRIC, toolVersi
|
|
|
19412
19787
|
}
|
|
19413
19788
|
}
|
|
19414
19789
|
const consumedSignals = [];
|
|
19415
|
-
const
|
|
19790
|
+
const scoredCriteria = rubric.map((rubricCriterion) => {
|
|
19416
19791
|
const id = rubricCriterion.id;
|
|
19417
19792
|
const signal = signalsByCriterion.get(id);
|
|
19418
19793
|
const scored = scoreCriterion(signal, parsedInput.rubricVersion);
|
|
@@ -19462,6 +19837,7 @@ function createWitanReport(input, inputSignals, rubric = WITAN_RUBRIC, toolVersi
|
|
|
19462
19837
|
...signal?.disclosures ? { disclosures: signal.disclosures } : {}
|
|
19463
19838
|
};
|
|
19464
19839
|
});
|
|
19840
|
+
const criteria = dropMarkedCitations(scoredCriteria, marked.droppedWithheldPaths);
|
|
19465
19841
|
const categoryOrder = [];
|
|
19466
19842
|
for (const rubricCriterion of rubric) {
|
|
19467
19843
|
if (!categoryOrder.includes(rubricCriterion.category)) {
|
|
@@ -20030,7 +20406,7 @@ function ensureFindingsExplainStatus(criterionId, score, status, findings, evide
|
|
|
20030
20406
|
)) {
|
|
20031
20407
|
return [...findings];
|
|
20032
20408
|
}
|
|
20033
|
-
const anchor = evidence
|
|
20409
|
+
const anchor = evidence.find(isCitablePointer) ?? {
|
|
20034
20410
|
kind: "repository",
|
|
20035
20411
|
label: `${criterionId} metric-derived score`,
|
|
20036
20412
|
path: "."
|
|
@@ -20659,11 +21035,13 @@ function describeWithheldReasons(entries) {
|
|
|
20659
21035
|
function buildWithheldPathsGapSentence(report) {
|
|
20660
21036
|
const withheldPaths = report.withheldPaths;
|
|
20661
21037
|
if (withheldPaths === void 0) return void 0;
|
|
21038
|
+
const unlisted = statesUnlistedWithheldPaths(report.criteria);
|
|
20662
21039
|
if (withheldPaths.length === 0) {
|
|
20663
|
-
return "No content was withheld from any signal.";
|
|
21040
|
+
return unlisted ? "Content was withheld at a path longer than the report's 700-character path limit; withheldPaths does not list it, and a criterion's disclosures or notes say how much." : "No content was withheld from any signal.";
|
|
20664
21041
|
}
|
|
20665
21042
|
const count = withheldPaths.length;
|
|
20666
|
-
const
|
|
21043
|
+
const listed = unlisted ? "listed " : "";
|
|
21044
|
+
const entryWord = count === 1 ? `${listed}content entry was` : `${listed}content entries were`;
|
|
20667
21045
|
const pronoun = count === 1 ? "it" : "them";
|
|
20668
21046
|
const reasonLabel = describeWithheldReasons(withheldPaths);
|
|
20669
21047
|
const intersecting = withheldPaths.filter((entry) => entry.signalsAdmitting.length > 0);
|
|
@@ -23417,25 +23795,6 @@ init_esm_shims();
|
|
|
23417
23795
|
import { lstatSync as lstatSync4, opendirSync, readFileSync as readFileSync4, realpathSync as realpathSync4 } from "fs";
|
|
23418
23796
|
import { join as join3, resolve as resolve4 } from "path";
|
|
23419
23797
|
|
|
23420
|
-
// src/witan/finding-limits.ts
|
|
23421
|
-
init_esm_shims();
|
|
23422
|
-
var RULE_ID_MAX = 200;
|
|
23423
|
-
var MESSAGE_MAX = 500;
|
|
23424
|
-
var LOCATION_MAX = 700;
|
|
23425
|
-
function clamp(value, max) {
|
|
23426
|
-
if (value.length <= max) return value;
|
|
23427
|
-
const ellipsis = "...";
|
|
23428
|
-
return `${value.slice(0, Math.max(max - ellipsis.length, 0))}${ellipsis}`;
|
|
23429
|
-
}
|
|
23430
|
-
function clampFinding(finding) {
|
|
23431
|
-
return {
|
|
23432
|
-
...finding,
|
|
23433
|
-
ruleId: clamp(finding.ruleId, RULE_ID_MAX),
|
|
23434
|
-
message: clamp(finding.message, MESSAGE_MAX),
|
|
23435
|
-
...finding.location ? { location: clamp(finding.location, LOCATION_MAX) } : {}
|
|
23436
|
-
};
|
|
23437
|
-
}
|
|
23438
|
-
|
|
23439
23798
|
// src/witan/generic-adapter.ts
|
|
23440
23799
|
init_esm_shims();
|
|
23441
23800
|
import { readFileSync } from "fs";
|
|
@@ -23945,14 +24304,16 @@ function scoreRepoWithPublicCejel(options) {
|
|
|
23945
24304
|
if (options.rubricVersion !== void 0) {
|
|
23946
24305
|
assertSelectableRubricVersion(options.rubricVersion);
|
|
23947
24306
|
}
|
|
24307
|
+
const historyDepth = readHistoryDepthAnswer(options.repoPath);
|
|
23948
24308
|
const input = buildWitanInputFromRepo({
|
|
23949
24309
|
productSlug: options.productSlug,
|
|
23950
24310
|
productDisplayName: options.productDisplayName,
|
|
23951
24311
|
repoPath: options.repoPath,
|
|
24312
|
+
historyDepth,
|
|
23952
24313
|
...options.generatedAt ? { generatedAt: options.generatedAt } : {},
|
|
23953
24314
|
...options.rubricVersion ? { rubricVersion: options.rubricVersion } : {}
|
|
23954
24315
|
});
|
|
23955
|
-
const scannedSubject =
|
|
24316
|
+
const { subject: scannedSubject, commitTimeLimitation } = readScannedSubjectAnswer(
|
|
23956
24317
|
options.repoPath,
|
|
23957
24318
|
input.repo.headSha,
|
|
23958
24319
|
options.subjectRepository
|
|
@@ -23961,12 +24322,12 @@ function scoreRepoWithPublicCejel(options) {
|
|
|
23961
24322
|
const inputBinding = documents.length > 0 ? buildInputBinding(documents, scannedSubject, options.strictIngest ? "strict" : "record-only") : void 0;
|
|
23962
24323
|
if (options.strictIngest && inputBinding) assertStrictIngestBinding(documents, inputBinding);
|
|
23963
24324
|
const scored = createWitanReport(
|
|
23964
|
-
input,
|
|
24325
|
+
withScanLimitations(input, [documents.length > 0 ? commitTimeLimitation : void 0]),
|
|
23965
24326
|
inputSignals.length > 0 ? inputSignals : void 0,
|
|
23966
24327
|
void 0,
|
|
23967
24328
|
options.toolVersion
|
|
23968
24329
|
);
|
|
23969
|
-
const historyLimitations = detectHistoryLimitations(options.repoPath);
|
|
24330
|
+
const historyLimitations = detectHistoryLimitations(options.repoPath, historyDepth);
|
|
23970
24331
|
const withHistory = historyLimitations ? { ...scored, historyLimitations } : scored;
|
|
23971
24332
|
const report = inputBinding ? { ...withHistory, inputBinding } : withHistory;
|
|
23972
24333
|
if (report.verdict !== "insufficient_source" || !isWitanNoMeasurementAbstention(report)) {
|
|
@@ -23995,7 +24356,7 @@ function assertReportVerifiable(report) {
|
|
|
23995
24356
|
].join("\n")
|
|
23996
24357
|
);
|
|
23997
24358
|
}
|
|
23998
|
-
function
|
|
24359
|
+
function readScannedSubjectAnswer(repoPath, headSha, subjectRepository) {
|
|
23999
24360
|
let repository;
|
|
24000
24361
|
if (subjectRepository !== void 0) {
|
|
24001
24362
|
repository = normalizeRepositoryIdentity(subjectRepository);
|
|
@@ -24006,19 +24367,34 @@ function readScannedSubject(repoPath, headSha, subjectRepository) {
|
|
|
24006
24367
|
}
|
|
24007
24368
|
}
|
|
24008
24369
|
let commitTime;
|
|
24370
|
+
let commitTimeLimitation;
|
|
24009
24371
|
if (headSha) {
|
|
24010
24372
|
const result = execGit(["show", "-s", "--format=%ct", "HEAD"], { cwd: repoPath });
|
|
24373
|
+
if (!result.ok) {
|
|
24374
|
+
commitTimeLimitation = `${describeGitFailure(result.reason, "HEAD committer-time discovery")} Cejel recorded each ingested input's freshness as unverifiable.`;
|
|
24375
|
+
}
|
|
24011
24376
|
const seconds = result.ok ? Number(result.stdout.trim()) : Number.NaN;
|
|
24012
24377
|
if (Number.isSafeInteger(seconds) && seconds >= 0) {
|
|
24013
24378
|
commitTime = plainIsoInstant(seconds * 1e3);
|
|
24014
24379
|
}
|
|
24015
24380
|
}
|
|
24016
24381
|
return {
|
|
24017
|
-
|
|
24018
|
-
|
|
24019
|
-
|
|
24382
|
+
subject: {
|
|
24383
|
+
...repository !== void 0 ? { repository } : {},
|
|
24384
|
+
...headSha ? { commit: headSha.toLowerCase() } : {},
|
|
24385
|
+
...commitTime !== void 0 ? { commitTime } : {}
|
|
24386
|
+
},
|
|
24387
|
+
...commitTimeLimitation !== void 0 ? { commitTimeLimitation } : {}
|
|
24020
24388
|
};
|
|
24021
24389
|
}
|
|
24390
|
+
function withScanLimitations(input, extra) {
|
|
24391
|
+
const existing = input.scanLimitations ?? [];
|
|
24392
|
+
const added = [...new Set(extra.filter((entry) => entry !== void 0))].filter(
|
|
24393
|
+
(entry) => !existing.includes(entry)
|
|
24394
|
+
);
|
|
24395
|
+
if (added.length === 0) return input;
|
|
24396
|
+
return { ...input, scanLimitations: [...existing, ...added].slice(0, 16) };
|
|
24397
|
+
}
|
|
24022
24398
|
function resolvePublicIngestInputs(options) {
|
|
24023
24399
|
const seen = /* @__PURE__ */ new Set();
|
|
24024
24400
|
const documents = [];
|