@rmartz/merge-safety 0.14.0 → 0.16.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 +9 -2
- package/dist/bin/merge-safety.d.ts +1 -43
- package/dist/bin/merge-safety.js +75 -425
- package/dist/chunk-2QWRQ4Q5.js +614 -0
- package/dist/index.d.ts +360 -3
- package/dist/index.js +19 -3
- package/package.json +1 -1
- package/dist/chunk-ITPKHZGC.js +0 -12
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,367 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Breaking-change detection over a PR's **own** unified diff (#53).
|
|
3
|
+
*
|
|
4
|
+
* Until now `prIsBreaking` was read entirely from the PR title's `!` marker or a
|
|
5
|
+
* `breaking change` label an LLM turn applies (`/review` Step 5 in the coordinator,
|
|
6
|
+
* which shells to `breaking-change.py check-diff`). That made a *required,
|
|
7
|
+
* merge-gating* check depend on a non-deterministic producer, and it failed in the
|
|
8
|
+
* **permissive** direction: no `/review` run, or a reviewer who misread the diff,
|
|
9
|
+
* and the PR was evaluated as non-breaking. These detectors are pure functions over
|
|
10
|
+
* a diff, so the check derives the fact itself; the title `!` marker is the only
|
|
11
|
+
* declared input alongside it — the label is never read.
|
|
12
|
+
*
|
|
13
|
+
* Ported from `claude/scripts/breaking_change.py` in `rmartz/dotfiles`
|
|
14
|
+
* (`has_major_version_bump` / `has_sensitive_package_bump` /
|
|
15
|
+
* `has_material_test_changes`). Each returns the *specific* packages or files that
|
|
16
|
+
* tripped it rather than a bare boolean, so the check-run can name them — the same
|
|
17
|
+
* "one computation, two views" shape the base-commit and file-overlap facts use.
|
|
18
|
+
*
|
|
19
|
+
* Deliberately NOT ported: `add_breaking_marker` / `remove_breaking_marker` /
|
|
20
|
+
* `reconcile_breaking_title`, which rewrite the PR *title* inside the squash-merge
|
|
21
|
+
* transaction (with the release-please exemption). Detection moves here; stamping
|
|
22
|
+
* stays in `merge-pr.py`, at a moment a PR-event action cannot reach.
|
|
23
|
+
*/
|
|
24
|
+
/** The kinds of breaking signal a PR's own diff can carry. */
|
|
25
|
+
declare const BREAKING_DIFF_KINDS: readonly ["major-version-bump", "sensitive-package-bump", "material-test-changes"];
|
|
26
|
+
type BreakingDiffKind = (typeof BREAKING_DIFF_KINDS)[number];
|
|
27
|
+
/** One fired detector, with the packages / files that tripped it. */
|
|
28
|
+
interface BreakingDiffSignal {
|
|
29
|
+
kind: BreakingDiffKind;
|
|
30
|
+
/** The specific package names or file paths, surfaced in the check-run reason. */
|
|
31
|
+
detail: string[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Reason presentation for the merge-safety check-run — how a verdict's facts are
|
|
36
|
+
* rendered as the human-readable lines the check-run output carries.
|
|
37
|
+
*
|
|
38
|
+
* Split from `merge-safety.ts` (the verdict *logic*) so each stays under its
|
|
39
|
+
* `max-lines` cap as axes accumulate (#53 added the diff-derived breaking
|
|
40
|
+
* signals). Presentation is a genuinely separate concern —
|
|
41
|
+
* nothing here decides anything, it only formats.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/** A base commit surfaced in a reason so the report names *which* commit triggered it. */
|
|
45
|
+
interface BaseCommit {
|
|
46
|
+
/** The full commit SHA (rendered abbreviated in the report). */
|
|
47
|
+
sha: string;
|
|
48
|
+
/** The commit subject (first line of its message). */
|
|
49
|
+
subject: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Base-health classification — "is the base branch's own CI failing?", the
|
|
54
|
+
* PR-independent axis of the merge-safety verdict.
|
|
55
|
+
*
|
|
56
|
+
* Extracted from `merge-safety.ts` when the self-derived breaking-change signal
|
|
57
|
+
* (#53) pushed that file past its 480-line cap. It is a genuinely separate
|
|
58
|
+
* concern: every other clause of the verdict reads *this PR* against *its base*,
|
|
59
|
+
* while these functions read only the base tip's check-runs and know nothing
|
|
60
|
+
* about any PR. Pure — the network boundary stays in the bin's probes.
|
|
61
|
+
*/
|
|
62
|
+
/**
|
|
63
|
+
* A base-tip check-run reduced to what base-health classification needs: its name
|
|
64
|
+
* (matched against the base branch's required status checks), its conclusion, and
|
|
65
|
+
* the slug of the producing GitHub App (used by the fallback heuristic to tell a
|
|
66
|
+
* GitHub Actions CI run from an external deploy integration).
|
|
67
|
+
*/
|
|
68
|
+
interface BaseCheckRun {
|
|
69
|
+
/** The check-run name (e.g. the workflow / job name), matched to a required context. */
|
|
70
|
+
name: string;
|
|
71
|
+
/** The check-run conclusion, or `null` while still in progress. */
|
|
72
|
+
conclusion: string | null;
|
|
73
|
+
/** The producing GitHub App's slug (e.g. `github-actions`, `vercel`), or `null`. */
|
|
74
|
+
appSlug: string | null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Merge-safety predicate — "is this PR safe to merge *as it stands*, or must it
|
|
79
|
+
* first be brought current against its base branch and re-run through CI?"
|
|
80
|
+
*
|
|
81
|
+
* This is the coordinator's `_pr_needs_branch_update` decision, lifted into a
|
|
82
|
+
* pure, side-effect-free function so it can be shared by two callers: PR Shepherd
|
|
83
|
+
* (which merges) and the `merge-safety` GitHub check (which *surfaces* the
|
|
84
|
+
* verdict as a required status so it's visible on the PR and can gate auto-merge).
|
|
85
|
+
* It knows nothing about posting, check-runs, or PR Shepherd's gate labels — it
|
|
86
|
+
* maps gathered *facts* to a verdict, and the caller owns the emission channel.
|
|
87
|
+
*
|
|
88
|
+
* The rule (a PR **must be brought current** when it is not already current AND):
|
|
89
|
+
* 1. a **breaking** commit landed on the base since the PR's merge-base **and the
|
|
90
|
+
* PR is not a (non-breaking) `docs` PR**, OR
|
|
91
|
+
* 2. a **CI** commit landed on the base since merge-base — `ci`-typed, or one
|
|
92
|
+
* that changed `.github/workflows/**` / `.github/actions/**` (#67) — (the
|
|
93
|
+
* coordinator rebases in-flight PRs past CI changes, by prefix or path), OR
|
|
94
|
+
* 3. the PR is **itself** a breaking change **and the base has moved by more than
|
|
95
|
+
* `docs` commits alone**, OR
|
|
96
|
+
* 4. the PR is **itself** a CI change (`ci`-typed, or its diff touches those
|
|
97
|
+
* paths) **and is not a hotfix** — a CI
|
|
98
|
+
* guard is only as good as the base it last ran against, so a CI PR that was
|
|
99
|
+
* clean when it was opened must be re-tested against the current base to
|
|
100
|
+
* catch a pattern the guard protects against that has regressed on the base
|
|
101
|
+
* since (the symmetric partner to clause 2's base-side detection). The
|
|
102
|
+
* `hotfix` label exempts *this* clause — a CI fix for a base whose own CI is
|
|
103
|
+
* red can be caught in a bind (base health blocks it unless it is a hotfix,
|
|
104
|
+
* and being forced current may itself be impossible or pointless while the
|
|
105
|
+
* base is broken), so `hotfix` frees it here just as it frees the base-health
|
|
106
|
+
* axis. The exemption is deliberately *this-PR-is-itself-CI*-only: it does
|
|
107
|
+
* not touch clause 3 (`prIsBreaking`) or the base-side clauses (1, 2, 5),
|
|
108
|
+
* which guard against real incompatibilities a hotfix must still rebase past,
|
|
109
|
+
* OR
|
|
110
|
+
* 5. the PR's changed files **intersect** the files changed on the base since
|
|
111
|
+
* merge-base.
|
|
112
|
+
*
|
|
113
|
+
* The `docs` carve-outs on clauses 1 and 3 (a breaking and a docs change interact
|
|
114
|
+
* only through a shared file; CI changes get no such carve-out) are explained in
|
|
115
|
+
* `staleness.ts`, which evaluates clauses 1–5.
|
|
116
|
+
*
|
|
117
|
+
* Clause 5 is a *narrowing* guard, not a widening one: it only ever forces more
|
|
118
|
+
* PRs current. Git can merge two disjoint-looking diffs cleanly and still produce
|
|
119
|
+
* invalid code (an earlier PR deletes a symbol this PR still references), so any
|
|
120
|
+
* file-level overlap forces a rebase + re-CI to catch the semantic conflict a
|
|
121
|
+
* clean textual merge hides.
|
|
122
|
+
*
|
|
123
|
+
* A hard git **conflict** is a separate axis from staleness — GitHub blocks such
|
|
124
|
+
* a merge regardless — but the verdict folds it in so a single check answers "is
|
|
125
|
+
* it safe to merge right now?", with a distinct label for visibility.
|
|
126
|
+
*
|
|
127
|
+
* **Base health** is a third, PR-independent axis: when the base branch's own CI
|
|
128
|
+
* is failing, merging anything but a fix onto it only compounds a broken base, so
|
|
129
|
+
* a PR is held **unless it is a hotfix** (the `hotfix` label is the escape hatch
|
|
130
|
+
* that lets the fix for broken main through). "Failing CI" is read expansively —
|
|
131
|
+
* any failing GitHub Actions run on the base counts — but a failing *deploy* does
|
|
132
|
+
* not: a red deploy under green Actions is likelier an external/environmental
|
|
133
|
+
* fault no code change can fix, so it must not wedge the whole merge queue.
|
|
134
|
+
*
|
|
135
|
+
* **The breaking verdict reads the title and the diff, never a label** (#82).
|
|
136
|
+
* `prIsBreaking` is the PR title's `!` marker or a breaking signal in the PR's own
|
|
137
|
+
* diff (`breaking-diff.ts`, #53). Whether a PR *should* carry `!` — the
|
|
138
|
+
* `breaking change` label, a dependency major bump, a linter bump's `ci` type — is
|
|
139
|
+
* title policy, enforced by pr-policy's title check; this check only consumes the
|
|
140
|
+
* resulting title and never reads or writes a label for it.
|
|
141
|
+
*/
|
|
142
|
+
|
|
143
|
+
/** Labels the check drives on a PR. Structural (`as const`) per repo convention. */
|
|
144
|
+
declare const MERGE_SAFETY_LABELS: readonly ["update required", "merge conflict"];
|
|
145
|
+
type MergeSafetyLabel = (typeof MERGE_SAFETY_LABELS)[number];
|
|
146
|
+
/**
|
|
147
|
+
* The gathered facts a merge-safety verdict is computed from. The `*SinceMergeBase`
|
|
148
|
+
* / `fileOverlap` booleans drive the verdict; the parallel `baseBreakingCommits` /
|
|
149
|
+
* `baseCiCommits` / `overlappingFiles` detail lists name *which* base commits and
|
|
150
|
+
* files triggered each, so the report can surface the specifics. Each boolean is
|
|
151
|
+
* exactly `list.length > 0` — the gatherer derives it from the list.
|
|
152
|
+
*/
|
|
153
|
+
interface MergeSafetyFacts {
|
|
154
|
+
/** The PR's merge-base is the current base-branch tip — nothing is stale. */
|
|
155
|
+
isCurrent: boolean;
|
|
156
|
+
/** A breaking commit landed on the base since the PR's merge-base. */
|
|
157
|
+
baseBreakingSinceMergeBase: boolean;
|
|
158
|
+
/** A CI commit (`ci`-typed, or touching a CI path) landed on the base since merge-base. */
|
|
159
|
+
baseCiSinceMergeBase: boolean;
|
|
160
|
+
/** The PR is itself a breaking change: its title's `!` marker or a diff-derived signal. */
|
|
161
|
+
prIsBreaking: boolean;
|
|
162
|
+
/**
|
|
163
|
+
* The PR title is a `docs:` / `docs(scope):` conventional commit. A non-breaking
|
|
164
|
+
* docs PR is exempt from the base-breaking clause (a breaking base change only
|
|
165
|
+
* forces it current through a shared file, via `fileOverlap`).
|
|
166
|
+
*/
|
|
167
|
+
prIsDocs: boolean;
|
|
168
|
+
/**
|
|
169
|
+
* The base moved since merge-base, and **every** commit it moved by is
|
|
170
|
+
* `docs`-typed. Exempts the `prIsBreaking` clause: a breaking PR behind only docs
|
|
171
|
+
* commits need not be brought current unless the files overlap.
|
|
172
|
+
*/
|
|
173
|
+
baseOnlyDocsSinceMergeBase: boolean;
|
|
174
|
+
/**
|
|
175
|
+
* The PR is itself a CI change: title `ci:` / `ci(scope):`, or its own diff
|
|
176
|
+
* changes `.github/workflows/**` / `.github/actions/**` (#67).
|
|
177
|
+
*/
|
|
178
|
+
prIsCi: boolean;
|
|
179
|
+
/** The PR's changed files intersect the base's changed files since merge-base. */
|
|
180
|
+
fileOverlap: boolean;
|
|
181
|
+
/** Git reports the PR as conflicting (`mergeable === 'CONFLICTING'`). */
|
|
182
|
+
hasConflict: boolean;
|
|
183
|
+
/** The base branch tip has at least one failing GitHub Actions CI check. */
|
|
184
|
+
baseCiFailing: boolean;
|
|
185
|
+
/**
|
|
186
|
+
* The PR carries the `hotfix` label, exempting it from the base-CI-failing axis
|
|
187
|
+
* *and* from the `prIsCi` update-required clause (a hotfix CI fix for a red base
|
|
188
|
+
* is not forced current). It does not exempt `prIsBreaking` or the base-side
|
|
189
|
+
* clauses.
|
|
190
|
+
*/
|
|
191
|
+
prIsHotfix: boolean;
|
|
192
|
+
/** The base commits since merge-base whose message marks a breaking change. */
|
|
193
|
+
baseBreakingCommits: readonly BaseCommit[];
|
|
194
|
+
/** The CI base commits (`ci`-typed, or touching a CI path) since merge-base. */
|
|
195
|
+
baseCiCommits: readonly BaseCommit[];
|
|
196
|
+
/** The PR's changed files that also changed on the base since merge-base. */
|
|
197
|
+
overlappingFiles: readonly string[];
|
|
198
|
+
/** The names of the failing base CI checks, surfaced in the base-health reason. */
|
|
199
|
+
failingBaseChecks: readonly string[];
|
|
200
|
+
/**
|
|
201
|
+
* The breaking signals the PR's **own diff** carries (#53) — a dependency major
|
|
202
|
+
* bump, a CI-sensitive linter/formatter version change, or material changes to
|
|
203
|
+
* existing tests. These feed `prIsBreaking` alongside the title marker, so a PR
|
|
204
|
+
* that changes something risky is re-tested against a moved base even when its
|
|
205
|
+
* title is unmarked.
|
|
206
|
+
*/
|
|
207
|
+
prBreakingDiffSignals: readonly BreakingDiffSignal[];
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* `pending` is posted as an incomplete (`in_progress`) check-run: it blocks the
|
|
211
|
+
* merge like `failure` but renders as a yellow dot, not a red ✗ (#58).
|
|
212
|
+
*/
|
|
213
|
+
type MergeSafetyConclusion = 'success' | 'failure' | 'pending';
|
|
214
|
+
interface MergeSafetyDecision {
|
|
215
|
+
/**
|
|
216
|
+
* The check-run conclusion: `success` iff safe to merge as-is; `pending` when the
|
|
217
|
+
* only problem is staleness, which a branch update clears; otherwise `failure`.
|
|
218
|
+
*/
|
|
219
|
+
conclusion: MergeSafetyConclusion;
|
|
220
|
+
/** The PR must be brought current before merge (the staleness axis). */
|
|
221
|
+
needsUpdate: boolean;
|
|
222
|
+
/** The PR has a hard git conflict (the mergeability axis). */
|
|
223
|
+
hasConflict: boolean;
|
|
224
|
+
/** Blocked because the base's CI is failing and this PR is not a hotfix (the base-health axis). */
|
|
225
|
+
baseUnhealthy: boolean;
|
|
226
|
+
/**
|
|
227
|
+
* Short state phrase for the check-run title — the verdict at a glance. One of
|
|
228
|
+
* `No update required` / `Update required` / `Merge conflict` / `Base CI
|
|
229
|
+
* failing` / `Could not evaluate`. The check-run
|
|
230
|
+
* *name* stays the stable `merge-safety` (so branch
|
|
231
|
+
* protection can match it); this varies with the outcome instead.
|
|
232
|
+
*/
|
|
233
|
+
title: string;
|
|
234
|
+
/** Ordered most→least severe, human-readable — the check-run output detail. */
|
|
235
|
+
reasons: string[];
|
|
236
|
+
/** One-line check-run summary. */
|
|
237
|
+
summary: string;
|
|
238
|
+
/**
|
|
239
|
+
* Labels the check drives.
|
|
240
|
+
*
|
|
241
|
+
* `add` / `remove` are **reconciled**: the check owns `update required` and
|
|
242
|
+
* `merge conflict` outright, adding the ones that apply and removing the rest.
|
|
243
|
+
*/
|
|
244
|
+
labels: {
|
|
245
|
+
add: MergeSafetyLabel[];
|
|
246
|
+
remove: MergeSafetyLabel[];
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Map gathered {@link MergeSafetyFacts} to a {@link MergeSafetyDecision}. Pure:
|
|
251
|
+
* the same facts always yield the same verdict. A PR that is already current is
|
|
252
|
+
* always safe on the staleness axis — a stale trigger can only matter when the
|
|
253
|
+
* PR is behind — so the update clauses are gated on `!isCurrent`.
|
|
254
|
+
*/
|
|
255
|
+
declare function evaluateMergeSafety(facts: MergeSafetyFacts): MergeSafetyDecision;
|
|
256
|
+
/**
|
|
257
|
+
* The verdict for a PR whose facts could not be gathered (bad merge-base, a git
|
|
258
|
+
* command that failed, an unreadable PR). Fail-safe: `failure`, so a consumer
|
|
259
|
+
* that trusts the verdict treats an ungatherable PR as unsafe rather than green.
|
|
260
|
+
* `needsUpdate` / `hasConflict` stay `false` because they are genuinely unknown —
|
|
261
|
+
* the `failure` conclusion is what carries the safety, not a fabricated axis. No
|
|
262
|
+
* labels are proposed: an ungatherable state is not evidence for adding or
|
|
263
|
+
* removing either label.
|
|
264
|
+
*/
|
|
265
|
+
declare function errorMergeSafetyDecision(message: string): MergeSafetyDecision;
|
|
266
|
+
|
|
267
|
+
/** Runs a `git` argv and yields stdout, or `null` on non-zero exit / failure. */
|
|
268
|
+
type GitRunner = (args: string[]) => Promise<string | null>;
|
|
269
|
+
/**
|
|
270
|
+
* Fetches the base branch tip's check-runs (deduped to the latest per name) so
|
|
271
|
+
* base-health can be judged. Injected like {@link GitRunner} so the assembly logic
|
|
272
|
+
* stays testable with a fake and the network boundary lives in the bin. Soft-fails
|
|
273
|
+
* to `[]` (an unreadable base is treated as *not* failing) so a transient `gh`
|
|
274
|
+
* error never wedges the whole merge queue on a base-health false positive.
|
|
275
|
+
*/
|
|
276
|
+
type BaseChecksProbe = (baseSha: string) => Promise<readonly BaseCheckRun[]>;
|
|
277
|
+
/**
|
|
278
|
+
* Fetches the base branch's **required status check** contexts — the set base-health
|
|
279
|
+
* scopes failures to (#40). Returns `null` when protection can't be read (transient
|
|
280
|
+
* `gh` error, no ruleset, missing scope); a `null` or empty set means base-health
|
|
281
|
+
* reports no failure, so a probe hiccup never wedges the merge queue. Injected like
|
|
282
|
+
* {@link BaseChecksProbe}, with the network boundary in the bin.
|
|
283
|
+
*/
|
|
284
|
+
type RequiredChecksProbe = (baseBranch: string) => Promise<readonly string[] | null>;
|
|
285
|
+
/** The real `git` runner, bounded and rooted at `cwd`. */
|
|
286
|
+
declare function makeGitRunner(cwd?: string): GitRunner;
|
|
287
|
+
/** PR metadata the gatherer needs beyond what git derives (from `gh pr view --json`). */
|
|
288
|
+
interface PrMergeMeta {
|
|
289
|
+
headSha: string;
|
|
290
|
+
title: string;
|
|
291
|
+
labels: readonly string[];
|
|
292
|
+
/** `gh`'s `mergeable`: `MERGEABLE` | `CONFLICTING` | `UNKNOWN`. */
|
|
293
|
+
mergeable: string;
|
|
294
|
+
}
|
|
295
|
+
interface GatherOptions {
|
|
296
|
+
/** Base branch ref to compare against (default `origin/main`). */
|
|
297
|
+
baseRef?: string;
|
|
298
|
+
git: GitRunner;
|
|
299
|
+
/** Probe for the base tip's check-runs, used to judge base health. */
|
|
300
|
+
baseChecks: BaseChecksProbe;
|
|
301
|
+
/** Probe for the base branch's required status checks, scoping base health (#40). */
|
|
302
|
+
requiredChecks: RequiredChecksProbe;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Assemble {@link MergeSafetyFacts} for one PR. Throws when the merge-base or base
|
|
306
|
+
* tip can't be resolved — the caller must treat an ungatherable PR as unsafe
|
|
307
|
+
* (fail the check) rather than let a stale green through.
|
|
308
|
+
*/
|
|
309
|
+
declare function gatherMergeSafetyFacts(meta: PrMergeMeta, { baseRef, git, baseChecks, requiredChecks }: GatherOptions): Promise<MergeSafetyFacts>;
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* The API-backed fact source: a {@link GitRunner} that answers the `git` queries
|
|
313
|
+
* {@link gatherMergeSafetyFacts} makes from the GitHub REST API instead of a local
|
|
314
|
+
* checkout, so merge-safety can run where there is no `git` binary (a hosted
|
|
315
|
+
* GitHub App's serverless function). The git-backed runner stays the default; this
|
|
316
|
+
* one is opt-in through the same `git` injection point.
|
|
317
|
+
*
|
|
318
|
+
* Every query maps onto two compares and, for CI-path detection, one commit read
|
|
319
|
+
* per base commit:
|
|
320
|
+
* - `compare/{head}...{tip}` — the merge base, the base commits since it, and the
|
|
321
|
+
* base-side changed files;
|
|
322
|
+
* - `compare/{tip}...{head}` — the PR-side changed files and their patches;
|
|
323
|
+
* - `commits/{sha}` — each base commit's own files, for the CI-path signal.
|
|
324
|
+
*
|
|
325
|
+
* The compare API truncates silently (250 commits, 300 files) and omits `patch`
|
|
326
|
+
* for oversized files. Each of those **fails closed** by throwing, so the caller
|
|
327
|
+
* reports `Could not evaluate` rather than a verdict built on partial data.
|
|
328
|
+
*/
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* A GitHub REST `GET` for a path relative to the API root (e.g.
|
|
332
|
+
* `repos/o/r/compare/a...b`), yielding the parsed JSON body, or `null` when the
|
|
333
|
+
* read fails. A caller that wants a transient error to abort the run (rather than
|
|
334
|
+
* fail the check) may throw instead; the error propagates unchanged.
|
|
335
|
+
*/
|
|
336
|
+
type GitHubApiGet = (path: string) => Promise<unknown>;
|
|
337
|
+
interface ApiGitRunnerOptions {
|
|
338
|
+
/** `owner/repo` the PR and its base live in. */
|
|
339
|
+
repo: string;
|
|
340
|
+
api: GitHubApiGet;
|
|
341
|
+
/** Concurrent `commits/{sha}` reads during CI-path detection (default 8). */
|
|
342
|
+
concurrency?: number;
|
|
343
|
+
}
|
|
344
|
+
/** The compare API's caps: past these its lists are silently truncated. */
|
|
345
|
+
declare const COMPARE_MAX_COMMITS = 250;
|
|
346
|
+
declare const COMPARE_MAX_FILES = 300;
|
|
347
|
+
/** A commit read lists at most this many files without pagination. */
|
|
348
|
+
declare const COMMIT_MAX_FILES = 300;
|
|
349
|
+
/**
|
|
350
|
+
* A {@link GitRunner} backed by the GitHub REST API rather than a checkout. It
|
|
351
|
+
* answers exactly the queries {@link gatherMergeSafetyFacts} makes and throws on
|
|
352
|
+
* any other, so a new git query added there fails closed until it is mapped here.
|
|
353
|
+
* Comparisons are cached for the runner's lifetime, so create one per evaluation.
|
|
354
|
+
*/
|
|
355
|
+
declare function makeApiGitRunner({ repo, api, concurrency }: ApiGitRunnerOptions): GitRunner;
|
|
356
|
+
|
|
1
357
|
/**
|
|
2
358
|
* @rmartz/merge-safety — public entry point.
|
|
3
359
|
*
|
|
4
360
|
* Encodes the package's stable public contract: the `merge-safety` check-run
|
|
5
361
|
* name and the CLI command surface. The evaluate/invalidate implementation
|
|
6
362
|
* (`merge-safety.ts` + `merge-safety-facts.ts` and the `merge-safety` bin)
|
|
7
|
-
* lives alongside it
|
|
8
|
-
*
|
|
363
|
+
* lives alongside it. The check-run name and command surface below are frozen;
|
|
364
|
+
* the re-exported library surface lets a consumer run the verdict in-process.
|
|
9
365
|
*/
|
|
10
366
|
/**
|
|
11
367
|
* The name of the check-run merge-safety posts for each PR.
|
|
@@ -18,6 +374,7 @@
|
|
|
18
374
|
* docs/check-run-contract.md.
|
|
19
375
|
*/
|
|
20
376
|
declare const MERGE_SAFETY_CHECK_NAME = "merge-safety";
|
|
377
|
+
|
|
21
378
|
/**
|
|
22
379
|
* The two operations the `merge-safety` CLI dispatches:
|
|
23
380
|
* - `evaluate` — gather base-currency + breaking-change + conflict facts for one
|
|
@@ -32,4 +389,4 @@ type MergeSafetyCommand = (typeof MERGE_SAFETY_COMMANDS)[number];
|
|
|
32
389
|
/** Type guard for the CLI command surface. */
|
|
33
390
|
declare function isMergeSafetyCommand(value: string | undefined): value is MergeSafetyCommand;
|
|
34
391
|
|
|
35
|
-
export { MERGE_SAFETY_CHECK_NAME, MERGE_SAFETY_COMMANDS, type MergeSafetyCommand, isMergeSafetyCommand };
|
|
392
|
+
export { type ApiGitRunnerOptions, type BaseCheckRun, type BaseChecksProbe, COMMIT_MAX_FILES, COMPARE_MAX_COMMITS, COMPARE_MAX_FILES, type GatherOptions, type GitHubApiGet, type GitRunner, MERGE_SAFETY_CHECK_NAME, MERGE_SAFETY_COMMANDS, type MergeSafetyCommand, type MergeSafetyDecision, type MergeSafetyFacts, type PrMergeMeta, type RequiredChecksProbe, errorMergeSafetyDecision, evaluateMergeSafety, gatherMergeSafetyFacts, isMergeSafetyCommand, makeApiGitRunner, makeGitRunner };
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
import {
|
|
2
|
+
COMMIT_MAX_FILES,
|
|
3
|
+
COMPARE_MAX_COMMITS,
|
|
4
|
+
COMPARE_MAX_FILES,
|
|
2
5
|
MERGE_SAFETY_CHECK_NAME,
|
|
3
6
|
MERGE_SAFETY_COMMANDS,
|
|
4
|
-
|
|
5
|
-
|
|
7
|
+
errorMergeSafetyDecision,
|
|
8
|
+
evaluateMergeSafety,
|
|
9
|
+
gatherMergeSafetyFacts,
|
|
10
|
+
isMergeSafetyCommand,
|
|
11
|
+
makeApiGitRunner,
|
|
12
|
+
makeGitRunner
|
|
13
|
+
} from "./chunk-2QWRQ4Q5.js";
|
|
6
14
|
export {
|
|
15
|
+
COMMIT_MAX_FILES,
|
|
16
|
+
COMPARE_MAX_COMMITS,
|
|
17
|
+
COMPARE_MAX_FILES,
|
|
7
18
|
MERGE_SAFETY_CHECK_NAME,
|
|
8
19
|
MERGE_SAFETY_COMMANDS,
|
|
9
|
-
|
|
20
|
+
errorMergeSafetyDecision,
|
|
21
|
+
evaluateMergeSafety,
|
|
22
|
+
gatherMergeSafetyFacts,
|
|
23
|
+
isMergeSafetyCommand,
|
|
24
|
+
makeApiGitRunner,
|
|
25
|
+
makeGitRunner
|
|
10
26
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rmartz/merge-safety",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "The pre-auto-merge safety verdict for a PR (base-currency, breaking-change, and conflict facts), posted as the `merge-safety` check-run and distributed through the `rmartz/merge-safety-action` composite action kept current by Dependabot.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ci",
|
package/dist/chunk-ITPKHZGC.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
// src/index.ts
|
|
2
|
-
var MERGE_SAFETY_CHECK_NAME = "merge-safety";
|
|
3
|
-
var MERGE_SAFETY_COMMANDS = ["evaluate", "invalidate"];
|
|
4
|
-
function isMergeSafetyCommand(value) {
|
|
5
|
-
return value !== void 0 && MERGE_SAFETY_COMMANDS.includes(value);
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export {
|
|
9
|
-
MERGE_SAFETY_CHECK_NAME,
|
|
10
|
-
MERGE_SAFETY_COMMANDS,
|
|
11
|
-
isMergeSafetyCommand
|
|
12
|
-
};
|