@reneturcios/greenlight 0.13.0 → 0.15.0
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 +148 -212
- package/dist/brief.d.ts +94 -0
- package/dist/brief.js +283 -0
- package/dist/brief.js.map +1 -0
- package/dist/ci-prep.d.ts +109 -0
- package/dist/ci-prep.js +768 -0
- package/dist/ci-prep.js.map +1 -0
- package/dist/cli.js +89 -1
- package/dist/cli.js.map +1 -1
- package/dist/help.js +35 -0
- package/dist/help.js.map +1 -1
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -1
- package/dist/radar.d.ts +84 -0
- package/dist/radar.js +370 -0
- package/dist/radar.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -5
package/dist/brief.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { type CiTriageDeps, type CiTriageResult } from "./ci-triage.js";
|
|
2
|
+
import { type ReviewTriageDeps, type ReviewTriageResult } from "./review-triage.js";
|
|
3
|
+
export type BriefFileArea = "source" | "tests" | "config" | "docs" | "generated" | "other";
|
|
4
|
+
export interface BriefFile {
|
|
5
|
+
filename: string;
|
|
6
|
+
status: "added" | "modified" | "removed" | "renamed" | "other";
|
|
7
|
+
additions: number;
|
|
8
|
+
deletions: number;
|
|
9
|
+
area: BriefFileArea;
|
|
10
|
+
}
|
|
11
|
+
export interface BriefResult {
|
|
12
|
+
pr: string;
|
|
13
|
+
number: number;
|
|
14
|
+
title: string;
|
|
15
|
+
author: string;
|
|
16
|
+
state: string;
|
|
17
|
+
merged: boolean;
|
|
18
|
+
draft: boolean;
|
|
19
|
+
url: string;
|
|
20
|
+
createdAt: string;
|
|
21
|
+
ageDays: number;
|
|
22
|
+
baseRef: string;
|
|
23
|
+
size: {
|
|
24
|
+
additions: number;
|
|
25
|
+
deletions: number;
|
|
26
|
+
filesChanged: number;
|
|
27
|
+
};
|
|
28
|
+
areas: Array<{
|
|
29
|
+
area: BriefFileArea;
|
|
30
|
+
files: string[];
|
|
31
|
+
}>;
|
|
32
|
+
ci: CiTriageResult;
|
|
33
|
+
review: ReviewTriageResult;
|
|
34
|
+
testEvidence: {
|
|
35
|
+
newOrChangedTestFiles: string[];
|
|
36
|
+
/** Plain statement: what test evidence exists, or that none does. */
|
|
37
|
+
note: string;
|
|
38
|
+
};
|
|
39
|
+
/** Concrete merge blockers found, each with evidence. */
|
|
40
|
+
blockers: string[];
|
|
41
|
+
/** Things not verified, stated plainly. */
|
|
42
|
+
unverified: string[];
|
|
43
|
+
/** One line: what the reviewer should look at first. */
|
|
44
|
+
lookFirst: string;
|
|
45
|
+
/** API calls consumed (measured, for budget honesty). */
|
|
46
|
+
apiCalls: number;
|
|
47
|
+
}
|
|
48
|
+
export interface BriefDeps {
|
|
49
|
+
/** Test seam / alternate PR detail fetch. */
|
|
50
|
+
fetchPullDetail?: (owner: string, repo: string, number: number) => Promise<PullDetailFull>;
|
|
51
|
+
/** Test seam / alternate file list fetch. */
|
|
52
|
+
fetchPullFiles?: (owner: string, repo: string, number: number) => Promise<BriefFile[]>;
|
|
53
|
+
/** Test seam / alternate reviews fetch (for change-request blockers). */
|
|
54
|
+
fetchReviews?: (owner: string, repo: string, number: number) => Promise<ReviewStateItem[]>;
|
|
55
|
+
/** Test seam / alternate contributing-text fetch (changeset signal). */
|
|
56
|
+
fetchContributingText?: (owner: string, repo: string) => Promise<string | undefined>;
|
|
57
|
+
/** Forwarded to the CI triage engine. */
|
|
58
|
+
ciDeps?: CiTriageDeps;
|
|
59
|
+
/** Forwarded to the review triage engine. */
|
|
60
|
+
reviewDeps?: ReviewTriageDeps;
|
|
61
|
+
}
|
|
62
|
+
export interface PullDetailFull {
|
|
63
|
+
title: string;
|
|
64
|
+
author: string;
|
|
65
|
+
state: string;
|
|
66
|
+
merged: boolean;
|
|
67
|
+
draft: boolean;
|
|
68
|
+
url: string;
|
|
69
|
+
createdAt: string;
|
|
70
|
+
baseRef: string;
|
|
71
|
+
mergeable: boolean | null;
|
|
72
|
+
mergeableState: string;
|
|
73
|
+
}
|
|
74
|
+
export interface ReviewStateItem {
|
|
75
|
+
state: string;
|
|
76
|
+
author: string;
|
|
77
|
+
dismissed: boolean;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Parse a PR target: full /pull/ URL, or owner/repo#N shorthand.
|
|
81
|
+
* Throws a usage error otherwise.
|
|
82
|
+
*/
|
|
83
|
+
export declare function parseBriefTarget(target: string): {
|
|
84
|
+
owner: string;
|
|
85
|
+
repo: string;
|
|
86
|
+
number: number;
|
|
87
|
+
};
|
|
88
|
+
/** Area classification for a file path. Pure. */
|
|
89
|
+
export declare function fileArea(path: string): BriefFileArea;
|
|
90
|
+
/**
|
|
91
|
+
* The brief pipeline. Best-effort per source: a failed fetch degrades to
|
|
92
|
+
* an "unverified" note, never to silence or a fabricated verdict.
|
|
93
|
+
*/
|
|
94
|
+
export declare function briefPR(target: string, deps?: BriefDeps): Promise<BriefResult>;
|
package/dist/brief.js
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PR Brief: a verified review brief for ANY pull request.
|
|
3
|
+
*
|
|
4
|
+
* Generalizes the existing review/CI triage (built for our own PRs) into a
|
|
5
|
+
* one-screen brief any reviewer — or maintainer — can read before opening
|
|
6
|
+
* the diff. This is the overlap into review tooling (CodeRabbit/Greptile
|
|
7
|
+
* territory) done Greenlight's way: every claim carries quoted evidence
|
|
8
|
+
* or a link, classifications come from the existing deterministic triage
|
|
9
|
+
* engines, and anything unverified is marked as such. No generic praise,
|
|
10
|
+
* no invented findings.
|
|
11
|
+
*
|
|
12
|
+
* Sections:
|
|
13
|
+
* 1. What changed: files grouped by area/risk (source, tests, config,
|
|
14
|
+
* docs, generated, other), plus size and shape.
|
|
15
|
+
* 2. CI: the existing ci_triage base-vs-head classification, reused.
|
|
16
|
+
* 3. Review findings: the existing review-triage classification, reused
|
|
17
|
+
* (real vs false-positive vs already-addressed, each with evidence).
|
|
18
|
+
* 4. Test evidence present in the PR (new/changed test files), or
|
|
19
|
+
* explicitly marked absent.
|
|
20
|
+
* 5. Merge-readiness blockers: conflicts, unresolved change requests,
|
|
21
|
+
* missing changeset where the repo requires one.
|
|
22
|
+
* 6. A one-line "look at this first" for the reviewer.
|
|
23
|
+
*
|
|
24
|
+
* Budget: PR detail (1) + files (1-2) + reviews (1-2) + the triage
|
|
25
|
+
* engines' own fetches + house-rules (cached, 24h TTL) for the changeset
|
|
26
|
+
* signal. All reads ride the existing file cache.
|
|
27
|
+
*/
|
|
28
|
+
import { apiCallsUsed, apiGet, cacheGet, cacheSet, fetchContributing, parsePullUrl, } from "./github.js";
|
|
29
|
+
import { triagePr } from "./ci-triage.js";
|
|
30
|
+
import { triageReview } from "./review-triage.js";
|
|
31
|
+
/**
|
|
32
|
+
* Parse a PR target: full /pull/ URL, or owner/repo#N shorthand.
|
|
33
|
+
* Throws a usage error otherwise.
|
|
34
|
+
*/
|
|
35
|
+
export function parseBriefTarget(target) {
|
|
36
|
+
const t = target.trim();
|
|
37
|
+
try {
|
|
38
|
+
return parsePullUrl(t);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
const m = /^([^/]+)\/([^/#]+)#(\d+)$/.exec(t);
|
|
42
|
+
if (!m) {
|
|
43
|
+
throw new Error(`Not a PR target: ${target} (expected a pull-request URL or owner/repo#N)`);
|
|
44
|
+
}
|
|
45
|
+
return { owner: m[1], repo: m[2], number: Number(m[3]) };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** Area classification for a file path. Pure. */
|
|
49
|
+
export function fileArea(path) {
|
|
50
|
+
const p = path.toLowerCase();
|
|
51
|
+
if (/\/tests?\/|\/__tests__\/|\.test\.|\.spec\.|_test\.|-test\.|test_\b/.test(p) ||
|
|
52
|
+
p.startsWith("test/") ||
|
|
53
|
+
p.startsWith("tests/")) {
|
|
54
|
+
return "tests";
|
|
55
|
+
}
|
|
56
|
+
if (p.startsWith(".github/") ||
|
|
57
|
+
p.endsWith("package-lock.json") ||
|
|
58
|
+
p.endsWith("yarn.lock") ||
|
|
59
|
+
p.endsWith("pnpm-lock.yaml") ||
|
|
60
|
+
p.endsWith(".lock") ||
|
|
61
|
+
/(^|\/)dist\//.test(p) ||
|
|
62
|
+
p.endsWith(".min.js") ||
|
|
63
|
+
p.endsWith(".map") ||
|
|
64
|
+
/\/generated\//.test(p)) {
|
|
65
|
+
return "generated";
|
|
66
|
+
}
|
|
67
|
+
if (p.startsWith(".") ||
|
|
68
|
+
/(^|\/)(config|conf|settings?|scripts?|tools?|tooling|build|ci|infra|docker|k8s|kubernetes)\//.test(p) ||
|
|
69
|
+
/\.(ya?ml|toml|ini|cfg|env|example)$/.test(p) ||
|
|
70
|
+
p === "package.json" ||
|
|
71
|
+
p === "tsconfig.json" ||
|
|
72
|
+
p === "dockerfile") {
|
|
73
|
+
return "config";
|
|
74
|
+
}
|
|
75
|
+
if (p.endsWith(".md") || p.endsWith(".mdx") || p.startsWith("docs/") || p.startsWith("doc/")) {
|
|
76
|
+
return "docs";
|
|
77
|
+
}
|
|
78
|
+
if (/\.(ts|tsx|js|jsx|mjs|cjs|py|go|rs|rb|php|java|kt|swift|c|cpp|h|hpp|cs|vue|svelte)$/.test(p)) {
|
|
79
|
+
return "source";
|
|
80
|
+
}
|
|
81
|
+
return "other";
|
|
82
|
+
}
|
|
83
|
+
function toBriefFile(item) {
|
|
84
|
+
const filename = String(item.filename ?? "");
|
|
85
|
+
const rawStatus = String(item.status ?? "other").toLowerCase();
|
|
86
|
+
const status = rawStatus === "added" || rawStatus === "modified" || rawStatus === "removed" || rawStatus === "renamed"
|
|
87
|
+
? rawStatus
|
|
88
|
+
: "other";
|
|
89
|
+
return {
|
|
90
|
+
filename,
|
|
91
|
+
status,
|
|
92
|
+
additions: typeof item.additions === "number" ? item.additions : 0,
|
|
93
|
+
deletions: typeof item.deletions === "number" ? item.deletions : 0,
|
|
94
|
+
area: fileArea(filename),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
async function defaultFetchPullDetail(owner, repo, number) {
|
|
98
|
+
const key = [owner, repo, String(number), "brief-detail"];
|
|
99
|
+
const cached = cacheGet("issue", key);
|
|
100
|
+
if (cached)
|
|
101
|
+
return cached;
|
|
102
|
+
const p = (await apiGet(`/repos/${owner}/${repo}/pulls/${number}`));
|
|
103
|
+
const detail = {
|
|
104
|
+
title: String(p.title ?? ""),
|
|
105
|
+
author: String(p.user?.login ?? "unknown"),
|
|
106
|
+
state: String(p.state ?? "open"),
|
|
107
|
+
merged: p.merged_at != null,
|
|
108
|
+
draft: Boolean(p.draft),
|
|
109
|
+
url: String(p.html_url ?? `https://github.com/${owner}/${repo}/pull/${number}`),
|
|
110
|
+
createdAt: String(p.created_at ?? ""),
|
|
111
|
+
baseRef: String(p.base?.ref ?? ""),
|
|
112
|
+
mergeable: p.mergeable ?? null,
|
|
113
|
+
mergeableState: String(p.mergeable_state ?? "unknown"),
|
|
114
|
+
};
|
|
115
|
+
cacheSet("issue", key, detail);
|
|
116
|
+
return detail;
|
|
117
|
+
}
|
|
118
|
+
async function defaultFetchPullFiles(owner, repo, number) {
|
|
119
|
+
const key = [owner, repo, String(number), "brief-files"];
|
|
120
|
+
const cached = cacheGet("issue", key);
|
|
121
|
+
if (cached)
|
|
122
|
+
return cached;
|
|
123
|
+
const files = [];
|
|
124
|
+
for (let page = 1; page <= 5; page++) {
|
|
125
|
+
const data = (await apiGet(`/repos/${owner}/${repo}/pulls/${number}/files?per_page=100&page=${page}`));
|
|
126
|
+
for (const item of data ?? [])
|
|
127
|
+
files.push(toBriefFile(item));
|
|
128
|
+
if ((data ?? []).length < 100)
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
cacheSet("issue", key, files);
|
|
132
|
+
return files;
|
|
133
|
+
}
|
|
134
|
+
async function defaultFetchReviews(owner, repo, number) {
|
|
135
|
+
const data = (await apiGet(`/repos/${owner}/${repo}/pulls/${number}/reviews?per_page=100`));
|
|
136
|
+
return (data ?? []).map((r) => ({
|
|
137
|
+
state: String(r.state ?? ""),
|
|
138
|
+
author: String(r.user?.login ?? "unknown"),
|
|
139
|
+
dismissed: String(r.state ?? "") === "DISMISSED",
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
const ageDays = (iso) => {
|
|
143
|
+
const t = Date.parse(iso);
|
|
144
|
+
if (!Number.isFinite(t))
|
|
145
|
+
return 0;
|
|
146
|
+
return Math.floor((Date.now() - t) / (24 * 60 * 60 * 1000));
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
* The brief pipeline. Best-effort per source: a failed fetch degrades to
|
|
150
|
+
* an "unverified" note, never to silence or a fabricated verdict.
|
|
151
|
+
*/
|
|
152
|
+
export async function briefPR(target, deps = {}) {
|
|
153
|
+
const callsBefore = apiCallsUsed();
|
|
154
|
+
const { owner, repo, number } = parseBriefTarget(target);
|
|
155
|
+
const prUrl = `https://github.com/${owner}/${repo}/pull/${number}`;
|
|
156
|
+
const unverified = [];
|
|
157
|
+
const blockers = [];
|
|
158
|
+
const fetchPullDetail = deps.fetchPullDetail ?? defaultFetchPullDetail;
|
|
159
|
+
const fetchPullFiles = deps.fetchPullFiles ?? defaultFetchPullFiles;
|
|
160
|
+
const fetchReviews = deps.fetchReviews ?? defaultFetchReviews;
|
|
161
|
+
let detail;
|
|
162
|
+
try {
|
|
163
|
+
detail = await fetchPullDetail(owner, repo, number);
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
throw new Error(`could not fetch PR ${prUrl}: ${err instanceof Error ? err.message : String(err)}`);
|
|
167
|
+
}
|
|
168
|
+
let files;
|
|
169
|
+
try {
|
|
170
|
+
files = await fetchPullFiles(owner, repo, number);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
files = [];
|
|
174
|
+
unverified.push("changed-file list unavailable; area breakdown is empty");
|
|
175
|
+
}
|
|
176
|
+
// Reviews for change-request blockers (independent of the triage engine).
|
|
177
|
+
let reviews = [];
|
|
178
|
+
try {
|
|
179
|
+
reviews = await fetchReviews(owner, repo, number);
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
unverified.push("review list unavailable; change-request blocker check skipped");
|
|
183
|
+
}
|
|
184
|
+
const openChangeRequests = reviews.filter((r) => r.state === "CHANGES_REQUESTED" && !r.dismissed && r.author !== detail.author);
|
|
185
|
+
for (const r of openChangeRequests) {
|
|
186
|
+
blockers.push(`unresolved change request from @${r.author}`);
|
|
187
|
+
}
|
|
188
|
+
// Merge conflicts.
|
|
189
|
+
if (detail.mergeable === false || detail.mergeableState === "dirty") {
|
|
190
|
+
blockers.push("merge conflicts (mergeable_state=dirty)");
|
|
191
|
+
}
|
|
192
|
+
else if (detail.mergeable === null && detail.mergeableState === "unknown") {
|
|
193
|
+
unverified.push("mergeable state not reported by GitHub yet");
|
|
194
|
+
}
|
|
195
|
+
// CI classification: reuse the existing triage engine (base vs head).
|
|
196
|
+
let ci;
|
|
197
|
+
try {
|
|
198
|
+
ci = await triagePr(prUrl, deps.ciDeps ?? {});
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
ci = {
|
|
202
|
+
pr: prUrl, title: detail.title, baseRef: detail.baseRef, baseSha: "",
|
|
203
|
+
headSha: "", verdict: "NO FAILURES", checks: [],
|
|
204
|
+
};
|
|
205
|
+
unverified.push("CI triage unavailable");
|
|
206
|
+
}
|
|
207
|
+
// Review findings: reuse the existing deterministic review triage.
|
|
208
|
+
let review;
|
|
209
|
+
try {
|
|
210
|
+
review = await triageReview(prUrl, deps.reviewDeps ?? {});
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
review = {
|
|
214
|
+
pr: prUrl, title: detail.title, verdict: "NO FINDINGS",
|
|
215
|
+
findings: [], counts: { likelyReal: 0, likelyNoise: 0, needsDecision: 0, noise: 0, resolved: 0 },
|
|
216
|
+
};
|
|
217
|
+
unverified.push("review triage unavailable");
|
|
218
|
+
}
|
|
219
|
+
// Changeset convention (remote variant of ci-prep's signal): repo
|
|
220
|
+
// mentions changesets in its policy files, but the PR ships no
|
|
221
|
+
// .changeset file -> blocker.
|
|
222
|
+
try {
|
|
223
|
+
const contributing = deps.fetchContributingText
|
|
224
|
+
? await deps.fetchContributingText(owner, repo)
|
|
225
|
+
: await fetchContributing(owner, repo);
|
|
226
|
+
if (contributing && /changeset/i.test(contributing)) {
|
|
227
|
+
const hasChangeset = files.some((f) => /(^|\/)\.changeset\//.test(f.filename));
|
|
228
|
+
if (!hasChangeset)
|
|
229
|
+
blockers.push("repo requires changesets (CONTRIBUTING), none in this PR");
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
unverified.push("changeset convention check skipped (policy fetch failed)");
|
|
234
|
+
}
|
|
235
|
+
// Area breakdown.
|
|
236
|
+
const areaOrder = ["source", "tests", "config", "docs", "generated", "other"];
|
|
237
|
+
const areas = areaOrder
|
|
238
|
+
.map((area) => ({ area, files: files.filter((f) => f.area === area).map((f) => f.filename) }))
|
|
239
|
+
.filter((g) => g.files.length > 0);
|
|
240
|
+
const additions = files.reduce((s, f) => s + f.additions, 0);
|
|
241
|
+
const deletions = files.reduce((s, f) => s + f.deletions, 0);
|
|
242
|
+
// Test evidence: new or changed test-area files, stated plainly.
|
|
243
|
+
const testFiles = files.filter((f) => f.area === "tests").map((f) => f.filename);
|
|
244
|
+
const testEvidenceNote = testFiles.length > 0
|
|
245
|
+
? `${testFiles.length} test file${testFiles.length === 1 ? "" : "s"} touched — verification is visible in the PR`
|
|
246
|
+
: "no test files touched — verification is not visible in this PR";
|
|
247
|
+
// Look-first: blockers first, then real findings, then CI, else shape.
|
|
248
|
+
const firstReal = review.findings?.find((f) => f.class === "likely-real");
|
|
249
|
+
const firstFailing = ci.checks?.find((c) => c.class === "likely-yours");
|
|
250
|
+
const biggestArea = areas.reduce((best, g) => (best === undefined || g.files.length > best.count ? { area: g.area, count: g.files.length } : best), undefined);
|
|
251
|
+
const lookFirst = blockers.length > 0
|
|
252
|
+
? `Start here: ${blockers[0]}.`
|
|
253
|
+
: firstReal !== undefined
|
|
254
|
+
? `Start here: ${firstReal.author}'s ${firstReal.class} finding${firstReal.path ? ` on ${firstReal.path}` : ""}: ${firstReal.summary}.`
|
|
255
|
+
: firstFailing !== undefined
|
|
256
|
+
? `Start here: CI check "${firstFailing.name}" classified ${firstFailing.class}: ${firstFailing.evidence}.`
|
|
257
|
+
: biggestArea !== undefined
|
|
258
|
+
? `No blockers or real findings. Start here: ${biggestArea.count} ${biggestArea.area} file${biggestArea.count === 1 ? "" : "s"} carry the change.`
|
|
259
|
+
: "No blockers, findings, or files visible. Nothing to review.";
|
|
260
|
+
return {
|
|
261
|
+
pr: prUrl,
|
|
262
|
+
number,
|
|
263
|
+
title: detail.title,
|
|
264
|
+
author: detail.author,
|
|
265
|
+
state: detail.merged ? "merged" : detail.state,
|
|
266
|
+
merged: detail.merged,
|
|
267
|
+
draft: detail.draft,
|
|
268
|
+
url: detail.url,
|
|
269
|
+
createdAt: detail.createdAt,
|
|
270
|
+
ageDays: ageDays(detail.createdAt),
|
|
271
|
+
baseRef: detail.baseRef,
|
|
272
|
+
size: { additions, deletions, filesChanged: files.length },
|
|
273
|
+
areas,
|
|
274
|
+
ci,
|
|
275
|
+
review,
|
|
276
|
+
testEvidence: { newOrChangedTestFiles: testFiles, note: testEvidenceNote },
|
|
277
|
+
blockers,
|
|
278
|
+
unverified,
|
|
279
|
+
lookFirst,
|
|
280
|
+
apiCalls: apiCallsUsed() - callsBefore,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
//# sourceMappingURL=brief.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brief.js","sourceRoot":"","sources":["../src/brief.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI Pre-flight (`greenlight ci-prep` / MCP `ci_prep`).
|
|
3
|
+
*
|
|
4
|
+
* Answers one question with evidence, BEFORE a PR is pushed: would this
|
|
5
|
+
* repo's own pull_request workflows pass on the current tree?
|
|
6
|
+
*
|
|
7
|
+
* Pipeline:
|
|
8
|
+
* 1. DISCOVER: parse .github/workflows/*.yml for workflows triggered on
|
|
9
|
+
* pull_request / pull_request_target; extract jobs and steps.
|
|
10
|
+
* 2. MAP + RUN (native mode): translate each step into a local check.
|
|
11
|
+
* Steps that map to package scripts or common binaries (test, lint,
|
|
12
|
+
* typecheck, build) are EXECUTED with a per-step timeout. Steps that
|
|
13
|
+
* cannot run locally (deploys, secrets, external services, publishes)
|
|
14
|
+
* are SKIPPED with the reason named. A check is never reported PASS
|
|
15
|
+
* unless it actually ran.
|
|
16
|
+
* 3. CONVENTIONS: changeset requirement, conventional-commit subjects,
|
|
17
|
+
* and DCO sign-off are checked against git history when the repo
|
|
18
|
+
* signals that it requires them.
|
|
19
|
+
* 4. ACT MODE (--act): when `act` and a working docker daemon are both
|
|
20
|
+
* present, the discovered PR workflows run through `act` for real;
|
|
21
|
+
* otherwise the tool says so plainly and falls back to native mode.
|
|
22
|
+
*
|
|
23
|
+
* Honesty contract (same as verify.ts): this would rather say SKIPPED with
|
|
24
|
+
* a reason than report a verdict it cannot defend. Verdict is GO only
|
|
25
|
+
* when zero checks FAIL.
|
|
26
|
+
*/
|
|
27
|
+
export type CiPrepStatus = "PASS" | "FAIL" | "SKIPPED";
|
|
28
|
+
export interface CiPrepCheck {
|
|
29
|
+
/** e.g. "test (workflow ci, job build)" or "convention: changesets". */
|
|
30
|
+
name: string;
|
|
31
|
+
kind: "workflow-step" | "convention" | "act";
|
|
32
|
+
status: CiPrepStatus;
|
|
33
|
+
/** One-line evidence: exit code, timing, or the skip reason. */
|
|
34
|
+
detail: string;
|
|
35
|
+
/** Last ~30 lines of captured output when the step was executed. */
|
|
36
|
+
outputTail?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface CiPrepResult {
|
|
39
|
+
repoDir: string;
|
|
40
|
+
base: string | null;
|
|
41
|
+
verdict: "GO" | "NO-GO";
|
|
42
|
+
actMode: "used" | "unavailable" | "not-requested";
|
|
43
|
+
checks: CiPrepCheck[];
|
|
44
|
+
markdown: string;
|
|
45
|
+
}
|
|
46
|
+
export interface CiPrepOptions {
|
|
47
|
+
/** Base ref for convention checks (changesets/commits/DCO). */
|
|
48
|
+
base?: string;
|
|
49
|
+
/** Per-step execution timeout in seconds (default 300). */
|
|
50
|
+
timeoutSecs?: number;
|
|
51
|
+
/** Try act+docker; fall back to native mode when unavailable. */
|
|
52
|
+
useAct?: boolean;
|
|
53
|
+
/** Test seam: override the detected package manager. */
|
|
54
|
+
packageManager?: string;
|
|
55
|
+
/** Test seam: override act/docker detection. */
|
|
56
|
+
actAvailable?: boolean;
|
|
57
|
+
}
|
|
58
|
+
type YamlNode = string | number | boolean | null | YamlNode[] | {
|
|
59
|
+
[k: string]: YamlNode;
|
|
60
|
+
};
|
|
61
|
+
/** Parse a GitHub Actions workflow file into a plain object tree. Throws on structural problems. */
|
|
62
|
+
export declare function parseWorkflowYaml(text: string): Record<string, YamlNode>;
|
|
63
|
+
export interface WorkflowStep {
|
|
64
|
+
name: string;
|
|
65
|
+
uses?: string;
|
|
66
|
+
run?: string;
|
|
67
|
+
if?: string;
|
|
68
|
+
workingDirectory?: string;
|
|
69
|
+
}
|
|
70
|
+
export interface WorkflowJob {
|
|
71
|
+
id: string;
|
|
72
|
+
name: string;
|
|
73
|
+
steps: WorkflowStep[];
|
|
74
|
+
matrix?: string;
|
|
75
|
+
}
|
|
76
|
+
export interface DiscoveredWorkflow {
|
|
77
|
+
file: string;
|
|
78
|
+
jobs: WorkflowJob[];
|
|
79
|
+
/** Why this workflow was skipped entirely (unparseable, no PR trigger). */
|
|
80
|
+
skipReason?: string;
|
|
81
|
+
}
|
|
82
|
+
/** Does this workflow run on pull_request / pull_request_target? */
|
|
83
|
+
export declare function triggersOnPullRequest(doc: Record<string, YamlNode>): boolean;
|
|
84
|
+
export declare function discoverWorkflows(dir: string): DiscoveredWorkflow[];
|
|
85
|
+
export type StepMapping = {
|
|
86
|
+
kind: "execute";
|
|
87
|
+
script: string;
|
|
88
|
+
} | {
|
|
89
|
+
kind: "skip";
|
|
90
|
+
reason: string;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Decide what to do with a `run:` step. Conservative by design: anything
|
|
94
|
+
* that is not clearly a local test/lint/build invocation, or that touches
|
|
95
|
+
* secrets/publishes/deploys, becomes SKIPPED with a named reason.
|
|
96
|
+
*/
|
|
97
|
+
export declare function mapRunStep(script: string): StepMapping;
|
|
98
|
+
export declare function mapUsesStep(uses: string): StepMapping;
|
|
99
|
+
/** Push-only / main-only `if:` conditions make a step irrelevant to PRs. */
|
|
100
|
+
export declare function isPushOnlyCondition(ifExpr: string): boolean;
|
|
101
|
+
export declare function detectPackageManager(dir: string, override?: string): string | null;
|
|
102
|
+
export declare function actAvailable(): boolean;
|
|
103
|
+
export declare function runCiPrep(targetDir: string, opts?: CiPrepOptions): Promise<CiPrepResult>;
|
|
104
|
+
export declare function renderCiPrepMarkdown(r: CiPrepResult): string;
|
|
105
|
+
/** Human-readable console rendering (the --json flag prints the raw result). */
|
|
106
|
+
export declare function formatCiPrepHuman(r: CiPrepResult): string;
|
|
107
|
+
/** Ensure the tmp fixture helper can create scratch dirs outside the repo. */
|
|
108
|
+
export declare function scratchDir(prefix: string): string;
|
|
109
|
+
export {};
|