@humanbased/crosscheck 1.2.0-beta.81 → 1.3.0-beta.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/crosscheck.config.example.yml +23 -2
- package/dist/__tests__/adoption.test.d.ts +2 -0
- package/dist/__tests__/adoption.test.d.ts.map +1 -0
- package/dist/__tests__/adoption.test.js +175 -0
- package/dist/__tests__/adoption.test.js.map +1 -0
- package/dist/__tests__/auto-fix-branch.test.d.ts +2 -0
- package/dist/__tests__/auto-fix-branch.test.d.ts.map +1 -0
- package/dist/__tests__/auto-fix-branch.test.js +143 -0
- package/dist/__tests__/auto-fix-branch.test.js.map +1 -0
- package/dist/__tests__/board.test.js +2 -0
- package/dist/__tests__/board.test.js.map +1 -1
- package/dist/__tests__/codex-env.test.d.ts +2 -0
- package/dist/__tests__/codex-env.test.d.ts.map +1 -0
- package/dist/__tests__/codex-env.test.js +70 -0
- package/dist/__tests__/codex-env.test.js.map +1 -0
- package/dist/__tests__/codex.test.js +89 -1
- package/dist/__tests__/codex.test.js.map +1 -1
- package/dist/__tests__/commit-subject.test.d.ts +2 -0
- package/dist/__tests__/commit-subject.test.d.ts.map +1 -0
- package/dist/__tests__/commit-subject.test.js +72 -0
- package/dist/__tests__/commit-subject.test.js.map +1 -0
- package/dist/__tests__/credential-free-origin.test.d.ts +2 -0
- package/dist/__tests__/credential-free-origin.test.d.ts.map +1 -0
- package/dist/__tests__/credential-free-origin.test.js +59 -0
- package/dist/__tests__/credential-free-origin.test.js.map +1 -0
- package/dist/__tests__/detector.test.js +87 -1
- package/dist/__tests__/detector.test.js.map +1 -1
- package/dist/__tests__/error-classification.test.js +34 -0
- package/dist/__tests__/error-classification.test.js.map +1 -1
- package/dist/__tests__/fix.test.js +37 -1
- package/dist/__tests__/fix.test.js.map +1 -1
- package/dist/__tests__/harness-section.test.d.ts +2 -0
- package/dist/__tests__/harness-section.test.d.ts.map +1 -0
- package/dist/__tests__/harness-section.test.js +46 -0
- package/dist/__tests__/harness-section.test.js.map +1 -0
- package/dist/__tests__/kickass.test.js +31 -0
- package/dist/__tests__/kickass.test.js.map +1 -1
- package/dist/__tests__/linear-worker.test.js.map +1 -1
- package/dist/__tests__/loader.test.js +2 -2
- package/dist/__tests__/loader.test.js.map +1 -1
- package/dist/__tests__/onboard-preservation.test.js +0 -1
- package/dist/__tests__/onboard-preservation.test.js.map +1 -1
- package/dist/__tests__/optimize.test.js +1 -1
- package/dist/__tests__/optimize.test.js.map +1 -1
- package/dist/__tests__/pr-status.test.js +213 -3
- package/dist/__tests__/pr-status.test.js.map +1 -1
- package/dist/__tests__/pr-workflow-state.test.js +247 -7
- package/dist/__tests__/pr-workflow-state.test.js.map +1 -1
- package/dist/__tests__/repository-guidance.test.js +21 -21
- package/dist/__tests__/repository-guidance.test.js.map +1 -1
- package/dist/__tests__/reviewed-sha.test.d.ts +2 -0
- package/dist/__tests__/reviewed-sha.test.d.ts.map +1 -0
- package/dist/__tests__/reviewed-sha.test.js +116 -0
- package/dist/__tests__/reviewed-sha.test.js.map +1 -0
- package/dist/__tests__/runner.test.js +178 -2
- package/dist/__tests__/runner.test.js.map +1 -1
- package/dist/__tests__/skill-broker.test.js +44 -2
- package/dist/__tests__/skill-broker.test.js.map +1 -1
- package/dist/__tests__/superseded-fix-pr.test.d.ts +2 -0
- package/dist/__tests__/superseded-fix-pr.test.d.ts.map +1 -0
- package/dist/__tests__/superseded-fix-pr.test.js +225 -0
- package/dist/__tests__/superseded-fix-pr.test.js.map +1 -0
- package/dist/__tests__/webhook.test.js +60 -0
- package/dist/__tests__/webhook.test.js.map +1 -1
- package/dist/cli.js +8 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/adoption.d.ts +6 -0
- package/dist/commands/adoption.d.ts.map +1 -0
- package/dist/commands/adoption.js +132 -0
- package/dist/commands/adoption.js.map +1 -0
- package/dist/commands/detect-step.d.ts.map +1 -1
- package/dist/commands/detect-step.js +5 -1
- package/dist/commands/detect-step.js.map +1 -1
- package/dist/commands/diagnose.d.ts.map +1 -1
- package/dist/commands/diagnose.js +2 -2
- package/dist/commands/diagnose.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +1 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/issue.js +1 -1
- package/dist/commands/issue.js.map +1 -1
- package/dist/commands/kickass.d.ts +1 -1
- package/dist/commands/kickass.d.ts.map +1 -1
- package/dist/commands/kickass.js +36 -22
- package/dist/commands/kickass.js.map +1 -1
- package/dist/commands/linear-test.js.map +1 -1
- package/dist/commands/onboard.d.ts.map +1 -1
- package/dist/commands/onboard.js +30 -3
- package/dist/commands/onboard.js.map +1 -1
- package/dist/commands/optimize.d.ts.map +1 -1
- package/dist/commands/optimize.js +1 -0
- package/dist/commands/optimize.js.map +1 -1
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +1 -1
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +40 -4
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/scan.d.ts.map +1 -1
- package/dist/commands/scan.js +6 -0
- package/dist/commands/scan.js.map +1 -1
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +132 -61
- package/dist/commands/watch.js.map +1 -1
- package/dist/config/loader.js +6 -6
- package/dist/config/loader.js.map +1 -1
- package/dist/config/schema.d.ts +8 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +19 -2
- package/dist/config/schema.js.map +1 -1
- package/dist/github/detector.d.ts.map +1 -1
- package/dist/github/detector.js +28 -1
- package/dist/github/detector.js.map +1 -1
- package/dist/github/merge.d.ts +0 -5
- package/dist/github/merge.d.ts.map +1 -1
- package/dist/github/merge.js +0 -9
- package/dist/github/merge.js.map +1 -1
- package/dist/github/reviewed-sha.d.ts +13 -0
- package/dist/github/reviewed-sha.d.ts.map +1 -0
- package/dist/github/reviewed-sha.js +62 -0
- package/dist/github/reviewed-sha.js.map +1 -0
- package/dist/github/superseded-fix-pr.d.ts +19 -0
- package/dist/github/superseded-fix-pr.d.ts.map +1 -0
- package/dist/github/superseded-fix-pr.js +97 -0
- package/dist/github/superseded-fix-pr.js.map +1 -0
- package/dist/github/webhook.d.ts +4 -1
- package/dist/github/webhook.d.ts.map +1 -1
- package/dist/github/webhook.js +8 -1
- package/dist/github/webhook.js.map +1 -1
- package/dist/lib/adoption.d.ts +64 -0
- package/dist/lib/adoption.d.ts.map +1 -0
- package/dist/lib/adoption.js +165 -0
- package/dist/lib/adoption.js.map +1 -0
- package/dist/lib/annotation.js +0 -7
- package/dist/lib/annotation.js.map +1 -1
- package/dist/lib/auto-fix-branch.d.ts +35 -0
- package/dist/lib/auto-fix-branch.d.ts.map +1 -0
- package/dist/lib/auto-fix-branch.js +88 -0
- package/dist/lib/auto-fix-branch.js.map +1 -0
- package/dist/lib/clone.d.ts +1 -0
- package/dist/lib/clone.d.ts.map +1 -1
- package/dist/lib/clone.js +38 -1
- package/dist/lib/clone.js.map +1 -1
- package/dist/lib/comment-bodies.d.ts.map +1 -1
- package/dist/lib/comment-bodies.js +6 -3
- package/dist/lib/comment-bodies.js.map +1 -1
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +17 -1
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/pr-picker.d.ts +1 -1
- package/dist/lib/pr-picker.d.ts.map +1 -1
- package/dist/lib/pr-picker.js +10 -5
- package/dist/lib/pr-picker.js.map +1 -1
- package/dist/lib/pr-status.d.ts +2 -1
- package/dist/lib/pr-status.d.ts.map +1 -1
- package/dist/lib/pr-status.js +70 -6
- package/dist/lib/pr-status.js.map +1 -1
- package/dist/lib/pr-workflow-state.d.ts +17 -1
- package/dist/lib/pr-workflow-state.d.ts.map +1 -1
- package/dist/lib/pr-workflow-state.js +110 -5
- package/dist/lib/pr-workflow-state.js.map +1 -1
- package/dist/lib/repo-picker.d.ts.map +1 -1
- package/dist/lib/repo-picker.js +0 -1
- package/dist/lib/repo-picker.js.map +1 -1
- package/dist/lib/runner.d.ts +31 -1
- package/dist/lib/runner.d.ts.map +1 -1
- package/dist/lib/runner.js +460 -58
- package/dist/lib/runner.js.map +1 -1
- package/dist/lib/vendor.d.ts +1 -0
- package/dist/lib/vendor.d.ts.map +1 -1
- package/dist/lib/vendor.js +6 -0
- package/dist/lib/vendor.js.map +1 -1
- package/dist/lib/workflow.d.ts +4 -0
- package/dist/lib/workflow.d.ts.map +1 -1
- package/dist/lib/workflow.js +4 -1
- package/dist/lib/workflow.js.map +1 -1
- package/dist/linear/comment.d.ts.map +1 -1
- package/dist/linear/comment.js.map +1 -1
- package/dist/reviewers/address.d.ts +1 -1
- package/dist/reviewers/address.d.ts.map +1 -1
- package/dist/reviewers/address.js +1 -1
- package/dist/reviewers/address.js.map +1 -1
- package/dist/reviewers/codex-env.d.ts +12 -0
- package/dist/reviewers/codex-env.d.ts.map +1 -0
- package/dist/reviewers/codex-env.js +54 -0
- package/dist/reviewers/codex-env.js.map +1 -0
- package/dist/reviewers/codex.d.ts +16 -1
- package/dist/reviewers/codex.d.ts.map +1 -1
- package/dist/reviewers/codex.js +110 -33
- package/dist/reviewers/codex.js.map +1 -1
- package/dist/reviewers/fix.d.ts +1 -1
- package/dist/reviewers/fix.d.ts.map +1 -1
- package/dist/reviewers/fix.js +16 -6
- package/dist/reviewers/fix.js.map +1 -1
- package/dist/skills/broker.d.ts +2 -1
- package/dist/skills/broker.d.ts.map +1 -1
- package/dist/skills/broker.js +28 -2
- package/dist/skills/broker.js.map +1 -1
- package/docs/metrics.md +115 -0
- package/docs/trust.md +159 -0
- package/get-started.md +115 -5
- package/package.json +3 -1
package/dist/github/detector.js
CHANGED
|
@@ -1,7 +1,30 @@
|
|
|
1
1
|
import { getPRCommits } from './client.js';
|
|
2
2
|
import { checkCodexAuth } from '../reviewers/codex.js';
|
|
3
3
|
import { checkClaudeAuth } from '../reviewers/claude.js';
|
|
4
|
-
//
|
|
4
|
+
// Crosscheck's own attribution on work crosscheck wrote. Not part of
|
|
5
|
+
// routing.*_reviews_patterns on purpose: Zod defaults apply only when a field is
|
|
6
|
+
// absent, so an install that pinned its own pattern list would never receive
|
|
7
|
+
// these and would keep reading its own fix PRs as human. This is a fact about
|
|
8
|
+
// output crosscheck produced — same footing as the annotation contract — not a
|
|
9
|
+
// routing preference, so it is always checked.
|
|
10
|
+
//
|
|
11
|
+
// Authorship only. The 'Reviewed' footer says who looked at the code and the
|
|
12
|
+
// 'Attempted' footer marks a fix that failed and produced nothing; neither is
|
|
13
|
+
// evidence of authorship, and matching them would route every reviewed PR back
|
|
14
|
+
// to the other vendor as if it were agent-authored.
|
|
15
|
+
const SELF_AUTHORED_PATTERNS = [
|
|
16
|
+
// Fix-PR body footer — buildAttributionFooter({ action: 'Fixed', ... }).
|
|
17
|
+
{ pattern: /Fixed with \[Claude Code\]/i, origin: 'claude' },
|
|
18
|
+
{ pattern: /Fixed with \[OpenAI Codex\]/i, origin: 'codex' },
|
|
19
|
+
// Commit trailer — buildCommitTrailers on fix and conflict-resolve commits.
|
|
20
|
+
// Anchored to its own line: the field name is quoted in prose (this repo's
|
|
21
|
+
// changelog and docs do it), and prose is not a trailer.
|
|
22
|
+
{ pattern: /^Crosscheck-Reviewer:[^\S\n]*claude[^\S\n]*$/im, origin: 'claude' },
|
|
23
|
+
{ pattern: /^Crosscheck-Reviewer:[^\S\n]*codex[^\S\n]*$/im, origin: 'codex' },
|
|
24
|
+
];
|
|
25
|
+
// Applies codex_reviews_patterns / claude_reviews_patterns against a single text
|
|
26
|
+
// block, then crosscheck's own always-on markers. Configured patterns are
|
|
27
|
+
// checked first: they are explicit routing intent and outrank the default.
|
|
5
28
|
// Returns the detected origin or null if nothing matched.
|
|
6
29
|
function matchPatterns(text, config) {
|
|
7
30
|
for (const pattern of config.routing.codex_reviews_patterns) {
|
|
@@ -12,6 +35,10 @@ function matchPatterns(text, config) {
|
|
|
12
35
|
if (new RegExp(pattern, 'i').test(text))
|
|
13
36
|
return 'codex';
|
|
14
37
|
}
|
|
38
|
+
for (const { pattern, origin } of SELF_AUTHORED_PATTERNS) {
|
|
39
|
+
if (pattern.test(text))
|
|
40
|
+
return origin;
|
|
41
|
+
}
|
|
15
42
|
return null;
|
|
16
43
|
}
|
|
17
44
|
// Step 1 — PR body patterns
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detector.js","sourceRoot":"","sources":["../../src/github/detector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAIxD,
|
|
1
|
+
{"version":3,"file":"detector.js","sourceRoot":"","sources":["../../src/github/detector.ts"],"names":[],"mappings":"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"}
|
package/dist/github/merge.d.ts
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
import type { Octokit } from 'octokit';
|
|
2
2
|
import type { PRMergeSummary } from '../lib/pr-status.js';
|
|
3
|
-
export type MergeMethod = 'merge' | 'squash' | 'rebase';
|
|
4
3
|
export declare function getPRMergeSummary(octokit: Octokit, owner: string, repo: string, pullNumber: number, baseRef: string): Promise<PRMergeSummary>;
|
|
5
|
-
export declare function mergePullRequest(octokit: Octokit, owner: string, repo: string, pullNumber: number, options: {
|
|
6
|
-
method: MergeMethod;
|
|
7
|
-
expectedHeadSha?: string;
|
|
8
|
-
}): Promise<void>;
|
|
9
4
|
//# sourceMappingURL=merge.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../src/github/merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEzD,
|
|
1
|
+
{"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../src/github/merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEzD,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,cAAc,CAAC,CAezB"}
|
package/dist/github/merge.js
CHANGED
|
@@ -12,15 +12,6 @@ export async function getPRMergeSummary(octokit, owner, repo, pullNumber, baseRe
|
|
|
12
12
|
protectedBase,
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
|
-
export async function mergePullRequest(octokit, owner, repo, pullNumber, options) {
|
|
16
|
-
await octokit.rest.pulls.merge({
|
|
17
|
-
owner,
|
|
18
|
-
repo,
|
|
19
|
-
pull_number: pullNumber,
|
|
20
|
-
merge_method: options.method,
|
|
21
|
-
...(options.expectedHeadSha && { sha: options.expectedHeadSha }),
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
15
|
async function getProtectedBase(octokit, owner, repo, branch) {
|
|
25
16
|
try {
|
|
26
17
|
const { data } = await octokit.rest.repos.getBranch({ owner, repo, branch });
|
package/dist/github/merge.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge.js","sourceRoot":"","sources":["../../src/github/merge.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"merge.js","sourceRoot":"","sources":["../../src/github/merge.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAgB,EAChB,KAAa,EACb,IAAY,EACZ,UAAkB,EAClB,OAAe;IAEf,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,aAAa,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACxD,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;QAChE,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC;KAChD,CAAC,CAAA;IAEF,MAAM,gBAAgB,GAAG,OAAO,IAAI,CAAC,eAAe,KAAK,QAAQ;QAC/D,CAAC,CAAC,IAAI,CAAC,eAAe;QACtB,CAAC,CAAC,SAAS,CAAA;IAEb,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,GAAG,CAAC,gBAAgB,IAAI,EAAE,gBAAgB,EAAE,CAAC;QAC7C,aAAa;KACd,CAAA;AACH,CAAC;AAED,KAAK,UAAU,gBAAgB,CAC7B,OAAgB,EAChB,KAAa,EACb,IAAY,EACZ,MAAc;IAEd,IAAI,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAA;QAC5E,OAAO,IAAI,CAAC,SAAS,CAAA;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Octokit } from 'octokit';
|
|
2
|
+
export type ReviewedShaStatus = 'head' | 'ancestor' | 'descendant' | 'unknown_commit' | 'diverged' | 'unverifiable';
|
|
3
|
+
export interface ReviewedShaVerification {
|
|
4
|
+
status: ReviewedShaStatus;
|
|
5
|
+
/** The PR head at verification time, freshly read — `pr.head.sha` goes stale after a fix push. */
|
|
6
|
+
headSha: string | null;
|
|
7
|
+
/** Populated for `unverifiable`, to name the API failure in the error. */
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function isVerifiedReviewedSha(status: ReviewedShaStatus): boolean;
|
|
11
|
+
export declare function verifyReviewedSha(octokit: Octokit, owner: string, repo: string, pullNumber: number, sha: string): Promise<ReviewedShaVerification>;
|
|
12
|
+
export declare function reviewedShaRejection(verification: ReviewedShaVerification, sha: string, verdict: string | null): string;
|
|
13
|
+
//# sourceMappingURL=reviewed-sha.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reviewed-sha.d.ts","sourceRoot":"","sources":["../../src/github/reviewed-sha.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAYtC,MAAM,MAAM,iBAAiB,GAEzB,MAAM,GACN,UAAU,GACV,YAAY,GAQZ,gBAAgB,GAChB,UAAU,GACV,cAAc,CAAA;AAElB,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,iBAAiB,CAAA;IACzB,kGAAkG;IAClG,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAExE;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,uBAAuB,CAAC,CAkClC;AAED,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,uBAAuB,EACrC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,GAAG,IAAI,GACrB,MAAM,CAaR"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export function isVerifiedReviewedSha(status) {
|
|
2
|
+
return status === 'head' || status === 'ancestor' || status === 'descendant';
|
|
3
|
+
}
|
|
4
|
+
export async function verifyReviewedSha(octokit, owner, repo, pullNumber, sha) {
|
|
5
|
+
let headSha;
|
|
6
|
+
try {
|
|
7
|
+
const { data: pull } = await octokit.rest.pulls.get({ owner, repo, pull_number: pullNumber });
|
|
8
|
+
headSha = pull.head.sha;
|
|
9
|
+
}
|
|
10
|
+
catch (err) {
|
|
11
|
+
return { status: 'unverifiable', headSha: null, error: errorText(err) };
|
|
12
|
+
}
|
|
13
|
+
if (sha === headSha)
|
|
14
|
+
return { status: 'head', headSha };
|
|
15
|
+
// compareCommits with the reviewed sha as base answers both questions in one call:
|
|
16
|
+
// a 404 means the sha does not resolve in this repo, and the status says how the two
|
|
17
|
+
// commits are related.
|
|
18
|
+
try {
|
|
19
|
+
const { data: comparison } = await octokit.rest.repos.compareCommits({
|
|
20
|
+
owner, repo, base: sha, head: headSha,
|
|
21
|
+
});
|
|
22
|
+
switch (comparison.status) {
|
|
23
|
+
// 'identical' — short and long sha of the same commit.
|
|
24
|
+
case 'identical':
|
|
25
|
+
// 'ahead' — the head is ahead of the reviewed sha, i.e. the sha is an ancestor.
|
|
26
|
+
case 'ahead':
|
|
27
|
+
return { status: 'ancestor', headSha };
|
|
28
|
+
// 'behind' — the head is behind the reviewed sha, i.e. the sha descends from it.
|
|
29
|
+
case 'behind':
|
|
30
|
+
return { status: 'descendant', headSha };
|
|
31
|
+
default:
|
|
32
|
+
return { status: 'diverged', headSha };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
if (statusCode(err) === 404)
|
|
37
|
+
return { status: 'unknown_commit', headSha };
|
|
38
|
+
return { status: 'unverifiable', headSha, error: errorText(err) };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function reviewedShaRejection(verification, sha, verdict) {
|
|
42
|
+
const subject = `${verdict ?? 'verdict'} not posted`;
|
|
43
|
+
const head = verification.headSha ?? 'unknown';
|
|
44
|
+
switch (verification.status) {
|
|
45
|
+
case 'unknown_commit':
|
|
46
|
+
return `${subject}: reviewed commit ${sha} does not exist in the repository — the reviewed tree was never pushed (PR head is ${head}). Re-run once the commit has landed.`;
|
|
47
|
+
case 'diverged':
|
|
48
|
+
return `${subject}: reviewed commit ${sha} shares no history with the PR head ${head} — the verdict describes code this PR would not merge.`;
|
|
49
|
+
case 'unverifiable':
|
|
50
|
+
return `${subject}: could not verify reviewed commit ${sha} against the repository — ${verification.error ?? 'unknown error'}.`;
|
|
51
|
+
default:
|
|
52
|
+
return `${subject}: reviewed commit ${sha} failed verification.`;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function statusCode(err) {
|
|
56
|
+
const status = err?.status;
|
|
57
|
+
return typeof status === 'number' ? status : undefined;
|
|
58
|
+
}
|
|
59
|
+
function errorText(err) {
|
|
60
|
+
return err instanceof Error ? err.message : String(err);
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=reviewed-sha.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reviewed-sha.js","sourceRoot":"","sources":["../../src/github/reviewed-sha.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Octokit } from 'octokit';
|
|
2
|
+
export declare function autoFixBranchName(sourcePrNumber: number): string;
|
|
3
|
+
export declare function autoFixPRIntro(sourcePrNumber: number): string;
|
|
4
|
+
export declare function isCrosscheckAutoFixPR(body: string | null | undefined, sourcePrNumber: number): boolean;
|
|
5
|
+
export declare function buildSupersededComment(input: {
|
|
6
|
+
owner: string;
|
|
7
|
+
repo: string;
|
|
8
|
+
sourcePrNumber: number;
|
|
9
|
+
mergeCommitSha: string | null;
|
|
10
|
+
}): string;
|
|
11
|
+
export type SupersedeStatus = 'closed' | 'not_crosscheck' | 'failed';
|
|
12
|
+
export interface SupersedeOutcome {
|
|
13
|
+
prNumber: number;
|
|
14
|
+
status: SupersedeStatus;
|
|
15
|
+
reason?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function sourcePRHasMerged(octokit: Octokit, owner: string, repo: string, sourcePrNumber: number): Promise<boolean>;
|
|
18
|
+
export declare function closeSupersededAutoFixPRs(octokit: Octokit, owner: string, repo: string, sourcePrNumber: number, mergeCommitSha: string | null): Promise<SupersedeOutcome[]>;
|
|
19
|
+
//# sourceMappingURL=superseded-fix-pr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"superseded-fix-pr.d.ts","sourceRoot":"","sources":["../../src/github/superseded-fix-pr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAatC,wBAAgB,iBAAiB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAEhE;AAID,wBAAgB,cAAc,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAE7D;AAID,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAEtG;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B,GAAG,MAAM,CAaT;AAED,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,gBAAgB,GAAG,QAAQ,CAAA;AAEpE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,eAAe,CAAA;IACvB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAeD,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,OAAO,CAAC,CAOlB;AAWD,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,MAAM,GAAG,IAAI,GAC5B,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA2B7B"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// An auto-fix PR is a snapshot of the reviewed PR at the moment the fix ran. Once the
|
|
2
|
+
// source PR merges, that snapshot is frozen against a tree that no longer exists: if
|
|
3
|
+
// the author pushed their own fix for the same finding, the two fixes live side by side
|
|
4
|
+
// and merging the auto-fix reintroduces exactly what the author's version removed —
|
|
5
|
+
// while reading, on the PR page, like a routine cleanup merge. Nothing in the auto-fix
|
|
6
|
+
// PR says it was superseded.
|
|
7
|
+
//
|
|
8
|
+
// So when the source PR merges, close its auto-fix PR and say why. The finding is
|
|
9
|
+
// either fixed on the base branch or still open there; either way the stale snapshot
|
|
10
|
+
// is not the vehicle for it.
|
|
11
|
+
export function autoFixBranchName(sourcePrNumber) {
|
|
12
|
+
return `fix/cr-${sourcePrNumber}-review-issues`;
|
|
13
|
+
}
|
|
14
|
+
// First line of every auto-fix PR body. Doubles as the ownership check below, so it is
|
|
15
|
+
// built here rather than inlined at the creation site.
|
|
16
|
+
export function autoFixPRIntro(sourcePrNumber) {
|
|
17
|
+
return `Auto-fix by crosscheck for CR issues found in #${sourcePrNumber}.`;
|
|
18
|
+
}
|
|
19
|
+
// Branch-name matching alone would let a hand-created branch of the same name be
|
|
20
|
+
// closed by crosscheck. Require the body it writes as well.
|
|
21
|
+
export function isCrosscheckAutoFixPR(body, sourcePrNumber) {
|
|
22
|
+
return (body ?? '').includes(autoFixPRIntro(sourcePrNumber));
|
|
23
|
+
}
|
|
24
|
+
export function buildSupersededComment(input) {
|
|
25
|
+
const mergedAs = input.mergeCommitSha
|
|
26
|
+
? ` as [\`${input.mergeCommitSha.slice(0, 9)}\`](https://github.com/${input.owner}/${input.repo}/commit/${input.mergeCommitSha})`
|
|
27
|
+
: '';
|
|
28
|
+
return [
|
|
29
|
+
'### Superseded — closing',
|
|
30
|
+
'',
|
|
31
|
+
`#${input.sourcePrNumber} merged${mergedAs}, so the changes on this branch are a snapshot of a tree that no longer exists. If the author fixed the same finding themselves, merging this PR would reintroduce what their fix removed.`,
|
|
32
|
+
'',
|
|
33
|
+
`Re-run a review against \`${input.owner}/${input.repo}\`'s base branch if the finding still stands there — this branch is not the right vehicle for it.`,
|
|
34
|
+
'',
|
|
35
|
+
'<!-- crosscheck: superseded_auto_fix -->',
|
|
36
|
+
].join('\n');
|
|
37
|
+
}
|
|
38
|
+
// True once the source PR has merged. A PR closed *unmerged* is not a merge: the
|
|
39
|
+
// source branch is still where that finding gets addressed.
|
|
40
|
+
//
|
|
41
|
+
// The merge handler sweeps for auto-fix PRs exactly once, so a fallback auto-fix
|
|
42
|
+
// PR opened after that sweep is never seen by it and stays open and mergeable —
|
|
43
|
+
// the stale-snapshot regression this module exists to prevent. The fix step asks
|
|
44
|
+
// this immediately before it pushes the branch and opens the PR, which closes
|
|
45
|
+
// the window down to that single call.
|
|
46
|
+
//
|
|
47
|
+
// Fails open: an unreadable state returns false and the fix is delivered as
|
|
48
|
+
// usual. A dropped fix is invisible to everyone, whereas today's behaviour is to
|
|
49
|
+
// create unconditionally — so failing open is no regression, and the merge
|
|
50
|
+
// webhook remains the primary mechanism.
|
|
51
|
+
export async function sourcePRHasMerged(octokit, owner, repo, sourcePrNumber) {
|
|
52
|
+
try {
|
|
53
|
+
const { data } = await octokit.rest.pulls.get({ owner, repo, pull_number: sourcePrNumber });
|
|
54
|
+
return data.merged === true;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Closes every open auto-fix PR cut from `sourcePrNumber`, commenting first so the
|
|
61
|
+
// reason survives on the timeline. Returns one outcome per candidate found, in the
|
|
62
|
+
// order GitHub listed them; an empty array means there was nothing cut from that PR
|
|
63
|
+
// (the common case).
|
|
64
|
+
//
|
|
65
|
+
// Candidates are handled concurrently — a comment+close pair is two round-trips,
|
|
66
|
+
// and serialising them makes every later candidate wait on all the earlier ones.
|
|
67
|
+
// Ordering that matters is preserved: comment still precedes close *within* a
|
|
68
|
+
// candidate, and Promise.all keeps the results positional.
|
|
69
|
+
export async function closeSupersededAutoFixPRs(octokit, owner, repo, sourcePrNumber, mergeCommitSha) {
|
|
70
|
+
const branch = autoFixBranchName(sourcePrNumber);
|
|
71
|
+
const { data: candidates } = await octokit.rest.pulls.list({
|
|
72
|
+
owner, repo, state: 'open', head: `${owner}:${branch}`,
|
|
73
|
+
});
|
|
74
|
+
return Promise.all(candidates.map(async (candidate) => {
|
|
75
|
+
if (!isCrosscheckAutoFixPR(candidate.body, sourcePrNumber)) {
|
|
76
|
+
return { prNumber: candidate.number, status: 'not_crosscheck' };
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
await octokit.rest.issues.createComment({
|
|
80
|
+
owner, repo, issue_number: candidate.number,
|
|
81
|
+
body: buildSupersededComment({ owner, repo, sourcePrNumber, mergeCommitSha }),
|
|
82
|
+
});
|
|
83
|
+
await octokit.rest.pulls.update({ owner, repo, pull_number: candidate.number, state: 'closed' });
|
|
84
|
+
return { prNumber: candidate.number, status: 'closed' };
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
// Per-candidate catch, so one failure cannot reject the whole batch and
|
|
88
|
+
// leave the remaining candidates unreported.
|
|
89
|
+
return {
|
|
90
|
+
prNumber: candidate.number,
|
|
91
|
+
status: 'failed',
|
|
92
|
+
reason: err instanceof Error ? err.message : String(err),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=superseded-fix-pr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"superseded-fix-pr.js","sourceRoot":"","sources":["../../src/github/superseded-fix-pr.ts"],"names":[],"mappings":"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"}
|
package/dist/github/webhook.d.ts
CHANGED
|
@@ -27,6 +27,9 @@ export interface PREvent {
|
|
|
27
27
|
labels?: Array<{
|
|
28
28
|
name: string;
|
|
29
29
|
}>;
|
|
30
|
+
merged?: boolean;
|
|
31
|
+
merge_commit_sha?: string | null;
|
|
32
|
+
created_at?: string;
|
|
30
33
|
};
|
|
31
34
|
repository: {
|
|
32
35
|
name: string;
|
|
@@ -65,5 +68,5 @@ export interface WebhookFileLogEntry {
|
|
|
65
68
|
event: string;
|
|
66
69
|
[key: string]: unknown;
|
|
67
70
|
}
|
|
68
|
-
export declare function createWebhookServer(config: Config, webhookSecret: string, onPR: (event: PREvent) => void, onLog: (msg: string) => void, onFileLog?: (entry: WebhookFileLogEntry) => void, onComment?: (event: IssueCommentEvent) => void): import("http").Server<typeof IncomingMessage, typeof ServerResponse>;
|
|
71
|
+
export declare function createWebhookServer(config: Config, webhookSecret: string, onPR: (event: PREvent) => void, onLog: (msg: string) => void, onFileLog?: (entry: WebhookFileLogEntry) => void, onComment?: (event: IssueCommentEvent) => void, onMerged?: (event: PREvent) => void): import("http").Server<typeof IncomingMessage, typeof ServerResponse>;
|
|
69
72
|
//# sourceMappingURL=webhook.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/github/webhook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,MAAM,CAAA;AAC9E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAIjD,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE;QACZ,KAAK,EAAE,MAAM,CAAA;QACb,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE;gBAAE,SAAS,EAAE,MAAM,CAAA;aAAE,GAAG,IAAI,CAAA;SAAE,CAAA;QAItE,IAAI,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE;gBAAE,SAAS,EAAE,MAAM,CAAC;gBAAC,cAAc,CAAC,EAAE,MAAM,CAAA;aAAE,CAAA;SAAE,CAAA;QAC3E,QAAQ,EAAE,MAAM,CAAA;QAChB,IAAI,EAAE;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAA;QAEvB,MAAM,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/github/webhook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,MAAM,CAAA;AAC9E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAIjD,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE;QACZ,KAAK,EAAE,MAAM,CAAA;QACb,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE;gBAAE,SAAS,EAAE,MAAM,CAAA;aAAE,GAAG,IAAI,CAAA;SAAE,CAAA;QAItE,IAAI,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE;gBAAE,SAAS,EAAE,MAAM,CAAC;gBAAC,cAAc,CAAC,EAAE,MAAM,CAAA;aAAE,CAAA;SAAE,CAAA;QAC3E,QAAQ,EAAE,MAAM,CAAA;QAChB,IAAI,EAAE;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAA;QAEvB,MAAM,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;QAGhC,MAAM,CAAC,EAAE,OAAO,CAAA;QAChB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;QAKhC,UAAU,CAAC,EAAE,MAAM,CAAA;KACpB,CAAA;IACD,UAAU,EAAE;QACV,IAAI,EAAE,MAAM,CAAA;QACZ,KAAK,EAAE;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAA;QACxB,SAAS,EAAE,MAAM,CAAA;KAClB,CAAA;CACF;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE;QACL,MAAM,EAAE,MAAM,CAAA;QACd,KAAK,EAAE,MAAM,CAAA;QACb,IAAI,EAAE;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAA;QACvB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KACvC,CAAA;IACD,OAAO,EAAE;QACP,EAAE,EAAE,MAAM,CAAA;QACV,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAA;KACxB,CAAA;IACD,UAAU,EAAE;QACV,IAAI,EAAE,MAAM,CAAA;QACZ,KAAK,EAAE;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAA;KACzB,CAAA;CACF;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,EAC9B,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,EAC5B,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,EAChD,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,EAC9C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,wEA6FpC"}
|
package/dist/github/webhook.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createServer } from 'http';
|
|
2
2
|
import { verifyWebhookSignature } from './client.js';
|
|
3
3
|
import { parseAnnotation } from '../lib/annotation.js';
|
|
4
|
-
export function createWebhookServer(config, webhookSecret, onPR, onLog, onFileLog, onComment) {
|
|
4
|
+
export function createWebhookServer(config, webhookSecret, onPR, onLog, onFileLog, onComment, onMerged) {
|
|
5
5
|
const server = createServer(async (req, res) => {
|
|
6
6
|
const { pathname } = new URL(req.url ?? '/', `http://localhost`);
|
|
7
7
|
if (pathname !== config.server.webhook_path) {
|
|
@@ -77,6 +77,13 @@ export function createWebhookServer(config, webhookSecret, onPR, onLog, onFileLo
|
|
|
77
77
|
// async — don't block the webhook response
|
|
78
78
|
setImmediate(() => onPR(body));
|
|
79
79
|
}
|
|
80
|
+
else if (body.action === 'closed' && body.pull_request.merged === true && onMerged) {
|
|
81
|
+
// A merge freezes any auto-fix PR cut from this one into a snapshot of a tree
|
|
82
|
+
// that no longer exists; the handler closes it. Closed-unmerged is not the same
|
|
83
|
+
// case — the source PR's branch is still the place the finding gets addressed.
|
|
84
|
+
res.writeHead(200).end('ok');
|
|
85
|
+
setImmediate(() => onMerged(body));
|
|
86
|
+
}
|
|
80
87
|
else {
|
|
81
88
|
res.writeHead(200).end('ok');
|
|
82
89
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.js","sourceRoot":"","sources":["../../src/github/webhook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAA6C,MAAM,MAAM,CAAA;AAE9E,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"webhook.js","sourceRoot":"","sources":["../../src/github/webhook.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export interface AdoptionLogLine {
|
|
2
|
+
ts: string;
|
|
3
|
+
event: string;
|
|
4
|
+
repo?: string;
|
|
5
|
+
pr?: number;
|
|
6
|
+
command?: string;
|
|
7
|
+
verdict?: string;
|
|
8
|
+
outcome?: string;
|
|
9
|
+
category?: string;
|
|
10
|
+
step_type?: string;
|
|
11
|
+
applied_count?: number;
|
|
12
|
+
open_to_verdict_ms?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface WeeklyActivity {
|
|
15
|
+
/** Monday of the ISO week, `YYYY-MM-DD`. */
|
|
16
|
+
week: string;
|
|
17
|
+
active_repos: number;
|
|
18
|
+
reviews: number;
|
|
19
|
+
}
|
|
20
|
+
export interface LatencySummary {
|
|
21
|
+
count: number;
|
|
22
|
+
/** Milliseconds from PR open to posted verdict. */
|
|
23
|
+
p50_ms: number;
|
|
24
|
+
p90_ms: number;
|
|
25
|
+
max_ms: number;
|
|
26
|
+
/** Verdicts whose PR event carried no `created_at`, so they have no latency. */
|
|
27
|
+
unmeasured: number;
|
|
28
|
+
}
|
|
29
|
+
export interface AdoptionReport {
|
|
30
|
+
period: {
|
|
31
|
+
from: string;
|
|
32
|
+
to: string;
|
|
33
|
+
log_files: number;
|
|
34
|
+
};
|
|
35
|
+
onboarding: {
|
|
36
|
+
started: number;
|
|
37
|
+
completed: number;
|
|
38
|
+
abandoned: number;
|
|
39
|
+
};
|
|
40
|
+
activity: {
|
|
41
|
+
reviews_started: number;
|
|
42
|
+
reviews_completed: number;
|
|
43
|
+
rechecks_completed: number;
|
|
44
|
+
blocking_findings_posted: number;
|
|
45
|
+
fixes_applied: number;
|
|
46
|
+
/** Distinct repos with at least one completed review in the window. */
|
|
47
|
+
active_repos: number;
|
|
48
|
+
};
|
|
49
|
+
weekly: WeeklyActivity[];
|
|
50
|
+
open_to_verdict: LatencySummary;
|
|
51
|
+
/** Error category of the first failure in each session that never completed a
|
|
52
|
+
* review — the thing that stops a new install from ever getting a verdict. */
|
|
53
|
+
first_run_failures: Record<string, number>;
|
|
54
|
+
}
|
|
55
|
+
export declare function prOpenToVerdictMs(createdAt: string | undefined, verdict: string | null, now?: number): number | undefined;
|
|
56
|
+
export declare function isoWeekMonday(ts: string): string | null;
|
|
57
|
+
export declare function percentile(sorted: number[], fraction: number): number;
|
|
58
|
+
export declare function buildAdoptionReport(lines: AdoptionLogLine[], period: {
|
|
59
|
+
from: string;
|
|
60
|
+
to: string;
|
|
61
|
+
log_files: number;
|
|
62
|
+
}): AdoptionReport;
|
|
63
|
+
export declare function firstRunFailures(lines: AdoptionLogLine[]): Record<string, number>;
|
|
64
|
+
//# sourceMappingURL=adoption.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adoption.d.ts","sourceRoot":"","sources":["../../src/lib/adoption.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,gFAAgF;IAChF,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IACvD,UAAU,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IACrE,QAAQ,EAAE;QACR,eAAe,EAAE,MAAM,CAAA;QACvB,iBAAiB,EAAE,MAAM,CAAA;QACzB,kBAAkB,EAAE,MAAM,CAAA;QAC1B,wBAAwB,EAAE,MAAM,CAAA;QAChC,aAAa,EAAE,MAAM,CAAA;QACrB,uEAAuE;QACvE,YAAY,EAAE,MAAM,CAAA;KACrB,CAAA;IACD,MAAM,EAAE,cAAc,EAAE,CAAA;IACxB,eAAe,EAAE,cAAc,CAAA;IAC/B;mFAC+E;IAC/E,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC3C;AAID,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,GAAG,GAAE,MAAmB,GACvB,MAAM,GAAG,SAAS,CAQpB;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOvD;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMrE;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,eAAe,EAAE,EACxB,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACtD,cAAc,CAsFhB;AAMD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAgCjF"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// Adoption metrics answer "is crosscheck actually being used, and does it reach a
|
|
2
|
+
// verdict fast enough to matter" — a different question from `impact`, which prices
|
|
3
|
+
// the value of reviews that already happened.
|
|
4
|
+
//
|
|
5
|
+
// Every number here is derived from the local NDJSON logs in ~/.crosscheck/logs.
|
|
6
|
+
// Nothing is transmitted: there is no endpoint, no account, and no network call in
|
|
7
|
+
// this file. See docs/metrics.md for the field-by-field inventory.
|
|
8
|
+
// A verdict-bearing latency is only meaningful for a review that produced one; a
|
|
9
|
+
// verdictless review (from `crosscheck review`) has nothing to time to.
|
|
10
|
+
export function prOpenToVerdictMs(createdAt, verdict, now = Date.now()) {
|
|
11
|
+
if (verdict === null || createdAt === undefined)
|
|
12
|
+
return undefined;
|
|
13
|
+
const opened = new Date(createdAt).getTime();
|
|
14
|
+
if (Number.isNaN(opened))
|
|
15
|
+
return undefined;
|
|
16
|
+
const elapsed = now - opened;
|
|
17
|
+
// A negative elapsed time means the clocks disagree, not that the PR was
|
|
18
|
+
// reviewed before it existed. Drop it rather than publish a nonsense number.
|
|
19
|
+
return elapsed >= 0 ? elapsed : undefined;
|
|
20
|
+
}
|
|
21
|
+
export function isoWeekMonday(ts) {
|
|
22
|
+
const d = new Date(ts);
|
|
23
|
+
if (Number.isNaN(d.getTime()))
|
|
24
|
+
return null;
|
|
25
|
+
const day = d.getUTCDay();
|
|
26
|
+
const diff = day === 0 ? -6 : 1 - day;
|
|
27
|
+
return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate() + diff))
|
|
28
|
+
.toISOString().slice(0, 10);
|
|
29
|
+
}
|
|
30
|
+
export function percentile(sorted, fraction) {
|
|
31
|
+
if (sorted.length === 0)
|
|
32
|
+
return 0;
|
|
33
|
+
// Nearest-rank on the sorted sample: no interpolation, so every reported value
|
|
34
|
+
// is one that actually occurred.
|
|
35
|
+
const rank = Math.ceil(fraction * sorted.length) - 1;
|
|
36
|
+
return sorted[Math.min(Math.max(rank, 0), sorted.length - 1)];
|
|
37
|
+
}
|
|
38
|
+
export function buildAdoptionReport(lines, period) {
|
|
39
|
+
const weekly = new Map();
|
|
40
|
+
const activeRepos = new Set();
|
|
41
|
+
const latencies = [];
|
|
42
|
+
let unmeasured = 0;
|
|
43
|
+
const counts = {
|
|
44
|
+
onboard_started: 0,
|
|
45
|
+
onboard_completed: 0,
|
|
46
|
+
reviews_started: 0,
|
|
47
|
+
reviews_completed: 0,
|
|
48
|
+
rechecks_completed: 0,
|
|
49
|
+
blocking: 0,
|
|
50
|
+
fixes: 0,
|
|
51
|
+
};
|
|
52
|
+
for (const line of lines) {
|
|
53
|
+
switch (line.event) {
|
|
54
|
+
case 'onboard_started':
|
|
55
|
+
counts.onboard_started++;
|
|
56
|
+
break;
|
|
57
|
+
case 'onboard_completed':
|
|
58
|
+
// The event is emitted for both outcomes so an abandoned setup is
|
|
59
|
+
// visible; only a success counts as completed.
|
|
60
|
+
if (line.outcome === 'completed')
|
|
61
|
+
counts.onboard_completed++;
|
|
62
|
+
break;
|
|
63
|
+
case 'review_started':
|
|
64
|
+
counts.reviews_started++;
|
|
65
|
+
break;
|
|
66
|
+
case 'review_complete': {
|
|
67
|
+
counts.reviews_completed++;
|
|
68
|
+
if (line.step_type === 'recheck')
|
|
69
|
+
counts.rechecks_completed++;
|
|
70
|
+
if (line.repo) {
|
|
71
|
+
activeRepos.add(line.repo);
|
|
72
|
+
const week = isoWeekMonday(line.ts);
|
|
73
|
+
if (week) {
|
|
74
|
+
const bucket = weekly.get(week) ?? { repos: new Set(), reviews: 0 };
|
|
75
|
+
bucket.repos.add(line.repo);
|
|
76
|
+
bucket.reviews++;
|
|
77
|
+
weekly.set(week, bucket);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (typeof line.open_to_verdict_ms === 'number')
|
|
81
|
+
latencies.push(line.open_to_verdict_ms);
|
|
82
|
+
else if (line.verdict)
|
|
83
|
+
unmeasured++;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
case 'blocking_finding_posted':
|
|
87
|
+
counts.blocking++;
|
|
88
|
+
break;
|
|
89
|
+
case 'fix_complete':
|
|
90
|
+
// delivery: 'comment' still counts — a suggestion the author applied is
|
|
91
|
+
// adoption. A no-op fix (applied_count 0) is not.
|
|
92
|
+
if ((line.applied_count ?? 0) > 0)
|
|
93
|
+
counts.fixes++;
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
latencies.sort((a, b) => a - b);
|
|
98
|
+
return {
|
|
99
|
+
period,
|
|
100
|
+
onboarding: {
|
|
101
|
+
started: counts.onboard_started,
|
|
102
|
+
completed: counts.onboard_completed,
|
|
103
|
+
abandoned: Math.max(0, counts.onboard_started - counts.onboard_completed),
|
|
104
|
+
},
|
|
105
|
+
activity: {
|
|
106
|
+
reviews_started: counts.reviews_started,
|
|
107
|
+
reviews_completed: counts.reviews_completed,
|
|
108
|
+
rechecks_completed: counts.rechecks_completed,
|
|
109
|
+
blocking_findings_posted: counts.blocking,
|
|
110
|
+
fixes_applied: counts.fixes,
|
|
111
|
+
active_repos: activeRepos.size,
|
|
112
|
+
},
|
|
113
|
+
weekly: [...weekly.entries()]
|
|
114
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
115
|
+
.map(([week, { repos, reviews }]) => ({ week, active_repos: repos.size, reviews })),
|
|
116
|
+
open_to_verdict: {
|
|
117
|
+
count: latencies.length,
|
|
118
|
+
p50_ms: percentile(latencies, 0.5),
|
|
119
|
+
p90_ms: percentile(latencies, 0.9),
|
|
120
|
+
max_ms: latencies.at(-1) ?? 0,
|
|
121
|
+
unmeasured,
|
|
122
|
+
},
|
|
123
|
+
first_run_failures: firstRunFailures(lines),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// "First run" is a session that never completed a review. Its first error is the
|
|
127
|
+
// one that stopped a new install from reaching a verdict — the category worth
|
|
128
|
+
// fixing. Sessions that did complete a review are excluded even if they later
|
|
129
|
+
// errored: those are operational failures, not activation failures.
|
|
130
|
+
export function firstRunFailures(lines) {
|
|
131
|
+
const sessions = [];
|
|
132
|
+
let current = null;
|
|
133
|
+
const close = () => {
|
|
134
|
+
if (current)
|
|
135
|
+
sessions.push(current);
|
|
136
|
+
current = null;
|
|
137
|
+
};
|
|
138
|
+
for (const line of lines) {
|
|
139
|
+
if (line.event === 'session_start') {
|
|
140
|
+
close();
|
|
141
|
+
current = { completedReview: false, firstErrorCategory: null };
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
// Logs from before session events existed, or a truncated file, would
|
|
145
|
+
// otherwise drop every entry on the floor.
|
|
146
|
+
if (current === null)
|
|
147
|
+
current = { completedReview: false, firstErrorCategory: null };
|
|
148
|
+
if (line.event === 'review_complete')
|
|
149
|
+
current.completedReview = true;
|
|
150
|
+
if (line.event === 'error' && current.firstErrorCategory === null) {
|
|
151
|
+
current.firstErrorCategory = line.category ?? 'unknown';
|
|
152
|
+
}
|
|
153
|
+
if (line.event === 'session_end')
|
|
154
|
+
close();
|
|
155
|
+
}
|
|
156
|
+
close();
|
|
157
|
+
const failures = {};
|
|
158
|
+
for (const session of sessions) {
|
|
159
|
+
if (session.completedReview || session.firstErrorCategory === null)
|
|
160
|
+
continue;
|
|
161
|
+
failures[session.firstErrorCategory] = (failures[session.firstErrorCategory] ?? 0) + 1;
|
|
162
|
+
}
|
|
163
|
+
return failures;
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=adoption.js.map
|