pr-merge-queue 0.0.0-stage → 0.0.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/LICENSE +201 -0
- package/README.md +125 -2
- package/dist/claim-pr/claim-pr.d.mts +45 -0
- package/dist/claim-pr/claim-pr.d.mts.map +1 -0
- package/dist/claim-pr/claim-pr.mjs +217 -0
- package/dist/claim-pr/claim-pr.mjs.map +1 -0
- package/dist/claim-pr/index.d.mts +2 -0
- package/dist/claim-pr/index.d.mts.map +1 -0
- package/dist/claim-pr/index.mjs +2 -0
- package/dist/claim-pr/index.mjs.map +1 -0
- package/dist/cmd/claim-pr.d.mts +3 -0
- package/dist/cmd/claim-pr.d.mts.map +1 -0
- package/dist/cmd/claim-pr.mjs +20 -0
- package/dist/cmd/claim-pr.mjs.map +1 -0
- package/dist/cmd/open-pr.d.mts +3 -0
- package/dist/cmd/open-pr.d.mts.map +1 -0
- package/dist/cmd/open-pr.mjs +20 -0
- package/dist/cmd/open-pr.mjs.map +1 -0
- package/dist/cmd/pr-report.d.mts +3 -0
- package/dist/cmd/pr-report.d.mts.map +1 -0
- package/dist/cmd/pr-report.mjs +20 -0
- package/dist/cmd/pr-report.mjs.map +1 -0
- package/dist/cmd/push-fixed-pr.d.mts +3 -0
- package/dist/cmd/push-fixed-pr.d.mts.map +1 -0
- package/dist/cmd/push-fixed-pr.mjs +20 -0
- package/dist/cmd/push-fixed-pr.mjs.map +1 -0
- package/dist/cmd/report-unowned-prs.d.mts +3 -0
- package/dist/cmd/report-unowned-prs.d.mts.map +1 -0
- package/dist/cmd/report-unowned-prs.mjs +20 -0
- package/dist/cmd/report-unowned-prs.mjs.map +1 -0
- package/dist/cmd/unblock-prs.d.mts +3 -0
- package/dist/cmd/unblock-prs.d.mts.map +1 -0
- package/dist/cmd/unblock-prs.mjs +19 -0
- package/dist/cmd/unblock-prs.mjs.map +1 -0
- package/dist/entry-point.d.mts +3 -0
- package/dist/entry-point.d.mts.map +1 -0
- package/dist/entry-point.mjs +3 -0
- package/dist/entry-point.mjs.map +1 -0
- package/dist/lib/cli-args.d.mts +27 -0
- package/dist/lib/cli-args.d.mts.map +1 -0
- package/dist/lib/cli-args.mjs +55 -0
- package/dist/lib/cli-args.mjs.map +1 -0
- package/dist/lib/github-api.d.mts +153 -0
- package/dist/lib/github-api.d.mts.map +1 -0
- package/dist/lib/github-api.mjs +291 -0
- package/dist/lib/github-api.mjs.map +1 -0
- package/dist/lib/index.d.mts +7 -0
- package/dist/lib/index.d.mts.map +1 -0
- package/dist/lib/index.mjs +7 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/lib/json.d.mts +12 -0
- package/dist/lib/json.d.mts.map +1 -0
- package/dist/lib/json.mjs +28 -0
- package/dist/lib/json.mjs.map +1 -0
- package/dist/lib/log.d.mts +10 -0
- package/dist/lib/log.d.mts.map +1 -0
- package/dist/lib/log.mjs +13 -0
- package/dist/lib/log.mjs.map +1 -0
- package/dist/lib/shell.d.mts +18 -0
- package/dist/lib/shell.d.mts.map +1 -0
- package/dist/lib/shell.mjs +42 -0
- package/dist/lib/shell.mjs.map +1 -0
- package/dist/lib/time.d.mts +17 -0
- package/dist/lib/time.d.mts.map +1 -0
- package/dist/lib/time.mjs +35 -0
- package/dist/lib/time.mjs.map +1 -0
- package/dist/open-pr/github.d.mts +72 -0
- package/dist/open-pr/github.d.mts.map +1 -0
- package/dist/open-pr/github.mjs +249 -0
- package/dist/open-pr/github.mjs.map +1 -0
- package/dist/open-pr/index.d.mts +6 -0
- package/dist/open-pr/index.d.mts.map +1 -0
- package/dist/open-pr/index.mjs +6 -0
- package/dist/open-pr/index.mjs.map +1 -0
- package/dist/open-pr/main.d.mts +36 -0
- package/dist/open-pr/main.d.mts.map +1 -0
- package/dist/open-pr/main.mjs +204 -0
- package/dist/open-pr/main.mjs.map +1 -0
- package/dist/open-pr/options.d.mts +20 -0
- package/dist/open-pr/options.d.mts.map +1 -0
- package/dist/open-pr/options.mjs +66 -0
- package/dist/open-pr/options.mjs.map +1 -0
- package/dist/open-pr/steps.d.mts +23 -0
- package/dist/open-pr/steps.d.mts.map +1 -0
- package/dist/open-pr/steps.mjs +38 -0
- package/dist/open-pr/steps.mjs.map +1 -0
- package/dist/open-pr/types.d.mts +179 -0
- package/dist/open-pr/types.d.mts.map +1 -0
- package/dist/open-pr/types.mjs +20 -0
- package/dist/open-pr/types.mjs.map +1 -0
- package/dist/pr-report/github.d.mts +59 -0
- package/dist/pr-report/github.d.mts.map +1 -0
- package/dist/pr-report/github.mjs +561 -0
- package/dist/pr-report/github.mjs.map +1 -0
- package/dist/pr-report/index.d.mts +8 -0
- package/dist/pr-report/index.d.mts.map +1 -0
- package/dist/pr-report/index.mjs +8 -0
- package/dist/pr-report/index.mjs.map +1 -0
- package/dist/pr-report/linked-issues.d.mts +15 -0
- package/dist/pr-report/linked-issues.d.mts.map +1 -0
- package/dist/pr-report/linked-issues.mjs +39 -0
- package/dist/pr-report/linked-issues.mjs.map +1 -0
- package/dist/pr-report/main.d.mts +42 -0
- package/dist/pr-report/main.d.mts.map +1 -0
- package/dist/pr-report/main.mjs +163 -0
- package/dist/pr-report/main.mjs.map +1 -0
- package/dist/pr-report/options.d.mts +36 -0
- package/dist/pr-report/options.d.mts.map +1 -0
- package/dist/pr-report/options.mjs +90 -0
- package/dist/pr-report/options.mjs.map +1 -0
- package/dist/pr-report/queue.d.mts +28 -0
- package/dist/pr-report/queue.d.mts.map +1 -0
- package/dist/pr-report/queue.mjs +74 -0
- package/dist/pr-report/queue.mjs.map +1 -0
- package/dist/pr-report/render.d.mts +14 -0
- package/dist/pr-report/render.d.mts.map +1 -0
- package/dist/pr-report/render.mjs +318 -0
- package/dist/pr-report/render.mjs.map +1 -0
- package/dist/pr-report/repo-settings.d.mts +12 -0
- package/dist/pr-report/repo-settings.d.mts.map +1 -0
- package/dist/pr-report/repo-settings.mjs +22 -0
- package/dist/pr-report/repo-settings.mjs.map +1 -0
- package/dist/push-fixed-pr/index.d.mts +2 -0
- package/dist/push-fixed-pr/index.d.mts.map +1 -0
- package/dist/push-fixed-pr/index.mjs +2 -0
- package/dist/push-fixed-pr/index.mjs.map +1 -0
- package/dist/push-fixed-pr/push-fixed-pr.d.mts +54 -0
- package/dist/push-fixed-pr/push-fixed-pr.d.mts.map +1 -0
- package/dist/push-fixed-pr/push-fixed-pr.mjs +172 -0
- package/dist/push-fixed-pr/push-fixed-pr.mjs.map +1 -0
- package/dist/report-unowned-prs/index.d.mts +2 -0
- package/dist/report-unowned-prs/index.d.mts.map +1 -0
- package/dist/report-unowned-prs/index.mjs +2 -0
- package/dist/report-unowned-prs/index.mjs.map +1 -0
- package/dist/report-unowned-prs/report-unowned-prs.d.mts +49 -0
- package/dist/report-unowned-prs/report-unowned-prs.d.mts.map +1 -0
- package/dist/report-unowned-prs/report-unowned-prs.mjs +127 -0
- package/dist/report-unowned-prs/report-unowned-prs.mjs.map +1 -0
- package/dist/tsconfig.json +1 -0
- package/dist/unblock-prs/auto-fix.d.mts +45 -0
- package/dist/unblock-prs/auto-fix.d.mts.map +1 -0
- package/dist/unblock-prs/auto-fix.mjs +207 -0
- package/dist/unblock-prs/auto-fix.mjs.map +1 -0
- package/dist/unblock-prs/checks.d.mts +307 -0
- package/dist/unblock-prs/checks.d.mts.map +1 -0
- package/dist/unblock-prs/checks.mjs +372 -0
- package/dist/unblock-prs/checks.mjs.map +1 -0
- package/dist/unblock-prs/claims.d.mts +21 -0
- package/dist/unblock-prs/claims.d.mts.map +1 -0
- package/dist/unblock-prs/claims.mjs +23 -0
- package/dist/unblock-prs/claims.mjs.map +1 -0
- package/dist/unblock-prs/config.d.mts +487 -0
- package/dist/unblock-prs/config.d.mts.map +1 -0
- package/dist/unblock-prs/config.mjs +119 -0
- package/dist/unblock-prs/config.mjs.map +1 -0
- package/dist/unblock-prs/constants.d.mts +28 -0
- package/dist/unblock-prs/constants.d.mts.map +1 -0
- package/dist/unblock-prs/constants.mjs +28 -0
- package/dist/unblock-prs/constants.mjs.map +1 -0
- package/dist/unblock-prs/demotions.d.mts +42 -0
- package/dist/unblock-prs/demotions.d.mts.map +1 -0
- package/dist/unblock-prs/demotions.mjs +50 -0
- package/dist/unblock-prs/demotions.mjs.map +1 -0
- package/dist/unblock-prs/github.d.mts +739 -0
- package/dist/unblock-prs/github.d.mts.map +1 -0
- package/dist/unblock-prs/github.mjs +626 -0
- package/dist/unblock-prs/github.mjs.map +1 -0
- package/dist/unblock-prs/index.d.mts +25 -0
- package/dist/unblock-prs/index.d.mts.map +1 -0
- package/dist/unblock-prs/index.mjs +25 -0
- package/dist/unblock-prs/index.mjs.map +1 -0
- package/dist/unblock-prs/labels.d.mts +49 -0
- package/dist/unblock-prs/labels.d.mts.map +1 -0
- package/dist/unblock-prs/labels.mjs +49 -0
- package/dist/unblock-prs/labels.mjs.map +1 -0
- package/dist/unblock-prs/main.d.mts +179 -0
- package/dist/unblock-prs/main.d.mts.map +1 -0
- package/dist/unblock-prs/main.mjs +640 -0
- package/dist/unblock-prs/main.mjs.map +1 -0
- package/dist/unblock-prs/merge-after.d.mts +46 -0
- package/dist/unblock-prs/merge-after.d.mts.map +1 -0
- package/dist/unblock-prs/merge-after.mjs +65 -0
- package/dist/unblock-prs/merge-after.mjs.map +1 -0
- package/dist/unblock-prs/options.d.mts +35 -0
- package/dist/unblock-prs/options.d.mts.map +1 -0
- package/dist/unblock-prs/options.mjs +88 -0
- package/dist/unblock-prs/options.mjs.map +1 -0
- package/dist/unblock-prs/owner.d.mts +32 -0
- package/dist/unblock-prs/owner.d.mts.map +1 -0
- package/dist/unblock-prs/owner.mjs +63 -0
- package/dist/unblock-prs/owner.mjs.map +1 -0
- package/dist/unblock-prs/quiet.d.mts +21 -0
- package/dist/unblock-prs/quiet.d.mts.map +1 -0
- package/dist/unblock-prs/quiet.mjs +42 -0
- package/dist/unblock-prs/quiet.mjs.map +1 -0
- package/dist/unblock-prs/rebase.d.mts +120 -0
- package/dist/unblock-prs/rebase.d.mts.map +1 -0
- package/dist/unblock-prs/rebase.mjs +576 -0
- package/dist/unblock-prs/rebase.mjs.map +1 -0
- package/dist/unblock-prs/release.d.mts +62 -0
- package/dist/unblock-prs/release.d.mts.map +1 -0
- package/dist/unblock-prs/release.mjs +94 -0
- package/dist/unblock-prs/release.mjs.map +1 -0
- package/dist/unblock-prs/set-aside-comment.d.mts +20 -0
- package/dist/unblock-prs/set-aside-comment.d.mts.map +1 -0
- package/dist/unblock-prs/set-aside-comment.mjs +93 -0
- package/dist/unblock-prs/set-aside-comment.mjs.map +1 -0
- package/dist/unblock-prs/set-aside-detail.d.mts +41 -0
- package/dist/unblock-prs/set-aside-detail.d.mts.map +1 -0
- package/dist/unblock-prs/set-aside-detail.mjs +56 -0
- package/dist/unblock-prs/set-aside-detail.mjs.map +1 -0
- package/dist/unblock-prs/skips.d.mts +85 -0
- package/dist/unblock-prs/skips.d.mts.map +1 -0
- package/dist/unblock-prs/skips.mjs +141 -0
- package/dist/unblock-prs/skips.mjs.map +1 -0
- package/dist/unblock-prs/stack.d.mts +98 -0
- package/dist/unblock-prs/stack.d.mts.map +1 -0
- package/dist/unblock-prs/stack.mjs +141 -0
- package/dist/unblock-prs/stack.mjs.map +1 -0
- package/dist/unblock-prs/triage.d.mts +28 -0
- package/dist/unblock-prs/triage.d.mts.map +1 -0
- package/dist/unblock-prs/triage.mjs +396 -0
- package/dist/unblock-prs/triage.mjs.map +1 -0
- package/dist/unblock-prs/types.d.mts +793 -0
- package/dist/unblock-prs/types.d.mts.map +1 -0
- package/dist/unblock-prs/types.mjs +26 -0
- package/dist/unblock-prs/types.mjs.map +1 -0
- package/dist/unblock-prs/util.d.mts +23 -0
- package/dist/unblock-prs/util.d.mts.map +1 -0
- package/dist/unblock-prs/util.mjs +60 -0
- package/dist/unblock-prs/util.mjs.map +1 -0
- package/dist/unblock-prs/version-pr.d.mts +57 -0
- package/dist/unblock-prs/version-pr.d.mts.map +1 -0
- package/dist/unblock-prs/version-pr.mjs +79 -0
- package/dist/unblock-prs/version-pr.mjs.map +1 -0
- package/dist/unblock-prs/watch.d.mts +17 -0
- package/dist/unblock-prs/watch.d.mts.map +1 -0
- package/dist/unblock-prs/watch.mjs +128 -0
- package/dist/unblock-prs/watch.mjs.map +1 -0
- package/package.json +118 -3
- package/src/claim-pr/claim-pr.mts +380 -0
- package/src/claim-pr/claim-pr.test.mts +176 -0
- package/src/claim-pr/index.mts +1 -0
- package/src/cmd/claim-pr.mts +32 -0
- package/src/cmd/open-pr.mts +28 -0
- package/src/cmd/pr-report.mts +28 -0
- package/src/cmd/push-fixed-pr.mts +36 -0
- package/src/cmd/report-unowned-prs.mts +37 -0
- package/src/cmd/unblock-prs.mts +24 -0
- package/src/entry-point.mts +3 -0
- package/src/lib/cli-args.mts +86 -0
- package/src/lib/cli-args.test.mts +92 -0
- package/src/lib/github-api.mts +463 -0
- package/src/lib/github-api.test.mts +40 -0
- package/src/lib/index.mts +6 -0
- package/src/lib/json.mts +43 -0
- package/src/lib/json.test.mts +34 -0
- package/src/lib/log.mts +19 -0
- package/src/lib/log.test.mts +45 -0
- package/src/lib/shell.mts +52 -0
- package/src/lib/time.mts +49 -0
- package/src/open-pr/README.md +237 -0
- package/src/open-pr/github.mts +444 -0
- package/src/open-pr/index.mts +5 -0
- package/src/open-pr/main.mts +327 -0
- package/src/open-pr/options.mts +101 -0
- package/src/open-pr/options.test.mts +67 -0
- package/src/open-pr/steps.mts +57 -0
- package/src/open-pr/steps.test.mts +59 -0
- package/src/open-pr/types.mts +42 -0
- package/src/pr-report/README.md +244 -0
- package/src/pr-report/github.mts +911 -0
- package/src/pr-report/index.mts +7 -0
- package/src/pr-report/linked-issues.mts +51 -0
- package/src/pr-report/linked-issues.test.mts +65 -0
- package/src/pr-report/main.mts +257 -0
- package/src/pr-report/options.mts +140 -0
- package/src/pr-report/queue.mts +125 -0
- package/src/pr-report/render.mts +428 -0
- package/src/pr-report/render.test.mts +379 -0
- package/src/pr-report/repo-settings.mts +34 -0
- package/src/push-fixed-pr/index.mts +1 -0
- package/src/push-fixed-pr/push-fixed-pr.mts +276 -0
- package/src/push-fixed-pr/push-fixed-pr.test.mts +228 -0
- package/src/report-unowned-prs/index.mts +1 -0
- package/src/report-unowned-prs/report-unowned-prs.mts +190 -0
- package/src/report-unowned-prs/report-unowned-prs.test.mts +87 -0
- package/src/unblock-prs/README.md +1616 -0
- package/src/unblock-prs/auto-fix.mts +334 -0
- package/src/unblock-prs/auto-fix.test.mts +159 -0
- package/src/unblock-prs/checks.mts +538 -0
- package/src/unblock-prs/checks.test.mts +507 -0
- package/src/unblock-prs/claims.mts +33 -0
- package/src/unblock-prs/claims.test.mts +64 -0
- package/src/unblock-prs/config.mts +187 -0
- package/src/unblock-prs/config.schema.json +71 -0
- package/src/unblock-prs/config.test.mts +139 -0
- package/src/unblock-prs/constants.mts +33 -0
- package/src/unblock-prs/demotions.mts +73 -0
- package/src/unblock-prs/demotions.test.mts +100 -0
- package/src/unblock-prs/github.mts +1042 -0
- package/src/unblock-prs/github.test.mts +84 -0
- package/src/unblock-prs/index.mts +24 -0
- package/src/unblock-prs/labels.mts +70 -0
- package/src/unblock-prs/main.mts +1051 -0
- package/src/unblock-prs/merge-after.mts +117 -0
- package/src/unblock-prs/merge-after.test.mts +120 -0
- package/src/unblock-prs/options.mts +157 -0
- package/src/unblock-prs/owner.mts +91 -0
- package/src/unblock-prs/owner.test.mts +114 -0
- package/src/unblock-prs/quiet.mts +62 -0
- package/src/unblock-prs/quiet.test.mts +175 -0
- package/src/unblock-prs/rebase.mts +962 -0
- package/src/unblock-prs/release.mts +149 -0
- package/src/unblock-prs/release.test.mts +209 -0
- package/src/unblock-prs/set-aside-comment.mts +143 -0
- package/src/unblock-prs/set-aside-comment.test.mts +148 -0
- package/src/unblock-prs/set-aside-detail.mts +82 -0
- package/src/unblock-prs/set-aside-detail.test.mts +131 -0
- package/src/unblock-prs/skips.mts +215 -0
- package/src/unblock-prs/skips.test.mts +280 -0
- package/src/unblock-prs/stack.mts +220 -0
- package/src/unblock-prs/stack.test.mts +449 -0
- package/src/unblock-prs/triage.mts +669 -0
- package/src/unblock-prs/triage.test.mts +100 -0
- package/src/unblock-prs/types.mts +358 -0
- package/src/unblock-prs/util.mts +78 -0
- package/src/unblock-prs/util.test.mts +26 -0
- package/src/unblock-prs/version-pr.mts +112 -0
- package/src/unblock-prs/version-pr.test.mts +78 -0
- package/src/unblock-prs/watch.mts +219 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import {
|
|
2
|
+
queueOwner,
|
|
3
|
+
readQueueComments,
|
|
4
|
+
writeClaimComment,
|
|
5
|
+
writeSetAsideComment,
|
|
6
|
+
type QueueComments,
|
|
7
|
+
type ReportEntry,
|
|
8
|
+
} from 'pr-report-core';
|
|
9
|
+
import { Arr } from 'ts-data-forge';
|
|
10
|
+
import { decidePush, parsePushArgs } from './push-fixed-pr.mjs';
|
|
11
|
+
|
|
12
|
+
const HEAD = 'c'.repeat(40);
|
|
13
|
+
|
|
14
|
+
const FIX = 'f'.repeat(40);
|
|
15
|
+
|
|
16
|
+
const NOW = Date.parse('2026-10-15T00:30:00Z');
|
|
17
|
+
|
|
18
|
+
const entry = (fields: Partial<ReportEntry> = {}): ReportEntry =>
|
|
19
|
+
({
|
|
20
|
+
number: 2267,
|
|
21
|
+
title: 'fix: a thing',
|
|
22
|
+
body: '',
|
|
23
|
+
author: 'someone',
|
|
24
|
+
isDraft: false,
|
|
25
|
+
labels: [{ name: 'merge-queued', color: '', description: '' }],
|
|
26
|
+
autoMerge: false,
|
|
27
|
+
headRef: 'fix/a-thing',
|
|
28
|
+
headSha: HEAD,
|
|
29
|
+
baseRef: 'main',
|
|
30
|
+
fromFork: false,
|
|
31
|
+
url: '',
|
|
32
|
+
updatedAt: '2026-10-15T00:00:00Z',
|
|
33
|
+
headCommittedAt: undefined,
|
|
34
|
+
comparison: undefined,
|
|
35
|
+
reported: new Map(),
|
|
36
|
+
checksRunning: false,
|
|
37
|
+
linkedIssues: [],
|
|
38
|
+
mergeAfter: [],
|
|
39
|
+
stackedOn: undefined,
|
|
40
|
+
inheritedMergeAfter: [],
|
|
41
|
+
notStartedIssues: [],
|
|
42
|
+
blockedBy: [],
|
|
43
|
+
checks: {
|
|
44
|
+
verdict: 'paused',
|
|
45
|
+
passed: [],
|
|
46
|
+
failed: [],
|
|
47
|
+
pending: [],
|
|
48
|
+
skipped: [],
|
|
49
|
+
missing: [],
|
|
50
|
+
notRun: [],
|
|
51
|
+
required: 0,
|
|
52
|
+
},
|
|
53
|
+
...fields,
|
|
54
|
+
}) as const;
|
|
55
|
+
|
|
56
|
+
const SET_ASIDE = writeSetAsideComment(
|
|
57
|
+
{ reason: 'rebase-failed', headSha: HEAD, baseSha: 'a'.repeat(40) },
|
|
58
|
+
'prose',
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const claimedBy = (by: string): string =>
|
|
62
|
+
writeClaimComment({ headSha: HEAD, by, at: '2026-10-15T00:00:00Z' }, 'prose');
|
|
63
|
+
|
|
64
|
+
const read = (...bodies: readonly string[]): QueueComments =>
|
|
65
|
+
readQueueComments(
|
|
66
|
+
bodies.map((body, index) => ({ id: index + 1, body })),
|
|
67
|
+
{ headSha: HEAD, baseTip: 'a'.repeat(40), nowEpochMs: NOW },
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const decide = (
|
|
71
|
+
entries: readonly ReportEntry[],
|
|
72
|
+
...bodies: readonly string[]
|
|
73
|
+
): ReturnType<typeof decidePush> => {
|
|
74
|
+
const comments = read(...bodies);
|
|
75
|
+
|
|
76
|
+
const target = entries.find((e) => e.number === 2267);
|
|
77
|
+
|
|
78
|
+
return decidePush({
|
|
79
|
+
number: 2267,
|
|
80
|
+
by: 'me',
|
|
81
|
+
head: FIX,
|
|
82
|
+
entries,
|
|
83
|
+
owner:
|
|
84
|
+
target === undefined
|
|
85
|
+
? undefined
|
|
86
|
+
: queueOwner({
|
|
87
|
+
number: target.number,
|
|
88
|
+
labels: target.labels.map(({ name }) => name),
|
|
89
|
+
isDraft: target.isDraft,
|
|
90
|
+
fromFork: target.fromFork,
|
|
91
|
+
headRef: target.headRef,
|
|
92
|
+
headSha: target.headSha,
|
|
93
|
+
baseRef: target.baseRef,
|
|
94
|
+
defaultBranch: 'main',
|
|
95
|
+
stackedOn: target.stackedOn,
|
|
96
|
+
inCycle: false,
|
|
97
|
+
setAside: comments.setAside?.says,
|
|
98
|
+
claim: comments.claim?.says,
|
|
99
|
+
nowEpochMs: NOW,
|
|
100
|
+
}),
|
|
101
|
+
comments,
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
describe(decidePush, () => {
|
|
106
|
+
test('pushes a claimed fix leased against the claimed head, putting skip-ci on first', () => {
|
|
107
|
+
assert.deepStrictEqual(decide([entry()], SET_ASIDE, claimedBy('me')), {
|
|
108
|
+
kind: 'push',
|
|
109
|
+
pr: {
|
|
110
|
+
number: 2267,
|
|
111
|
+
state: 'OPEN',
|
|
112
|
+
headRefName: 'fix/a-thing',
|
|
113
|
+
headRefOid: HEAD,
|
|
114
|
+
baseRefName: 'main',
|
|
115
|
+
isCrossRepository: false,
|
|
116
|
+
},
|
|
117
|
+
head: FIX,
|
|
118
|
+
addSkipCi: true,
|
|
119
|
+
layers: [],
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('carries the layers stacked on it along, lowest first', () => {
|
|
124
|
+
const decided = decide(
|
|
125
|
+
[
|
|
126
|
+
entry({
|
|
127
|
+
labels: Arr.toUnshifted({
|
|
128
|
+
name: 'skip-ci',
|
|
129
|
+
color: '',
|
|
130
|
+
description: '',
|
|
131
|
+
})(entry().labels),
|
|
132
|
+
}),
|
|
133
|
+
entry({
|
|
134
|
+
number: 2290,
|
|
135
|
+
headRef: 'feat/top',
|
|
136
|
+
baseRef: 'feat/middle',
|
|
137
|
+
stackedOn: 2280,
|
|
138
|
+
}),
|
|
139
|
+
entry({
|
|
140
|
+
number: 2280,
|
|
141
|
+
headRef: 'feat/middle',
|
|
142
|
+
baseRef: 'fix/a-thing',
|
|
143
|
+
stackedOn: 2267,
|
|
144
|
+
}),
|
|
145
|
+
],
|
|
146
|
+
SET_ASIDE,
|
|
147
|
+
claimedBy('me'),
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
assert.strictEqual(decided.kind, 'push');
|
|
151
|
+
|
|
152
|
+
if (decided.kind !== 'push') {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
assert.isFalse(decided.addSkipCi);
|
|
157
|
+
|
|
158
|
+
assert.deepStrictEqual(
|
|
159
|
+
decided.layers.map(({ number }) => number),
|
|
160
|
+
[2280, 2290],
|
|
161
|
+
);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test('refuses without a claim of its own', () => {
|
|
165
|
+
assert.strictEqual(decide([entry()], SET_ASIDE).kind, 'refuse');
|
|
166
|
+
|
|
167
|
+
assert.strictEqual(
|
|
168
|
+
decide([entry()], SET_ASIDE, claimedBy('other')).kind,
|
|
169
|
+
'refuse',
|
|
170
|
+
);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('refuses one that is not a skill’s', () => {
|
|
174
|
+
assert.strictEqual(decide([entry()]).kind, 'refuse');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test('refuses a conflict on the dependency update, which is rebuilt instead', () => {
|
|
178
|
+
const update = entry({ headRef: 'chore/pnpm-update' });
|
|
179
|
+
|
|
180
|
+
const decided = decide([update], SET_ASIDE, claimedBy('me'));
|
|
181
|
+
|
|
182
|
+
assert.strictEqual(decided.kind, 'refuse');
|
|
183
|
+
|
|
184
|
+
assert.include(
|
|
185
|
+
decided.kind === 'refuse' ? decided.reason : '',
|
|
186
|
+
'rebuild-pnpm-update',
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
assert.strictEqual(
|
|
190
|
+
decide(
|
|
191
|
+
[update],
|
|
192
|
+
writeSetAsideComment(
|
|
193
|
+
{ reason: 'checks-failed', headSha: HEAD, baseSha: 'a'.repeat(40) },
|
|
194
|
+
'prose',
|
|
195
|
+
),
|
|
196
|
+
claimedBy('me'),
|
|
197
|
+
).kind,
|
|
198
|
+
'push',
|
|
199
|
+
);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test('refuses one that is not open', () => {
|
|
203
|
+
assert.strictEqual(decide([], SET_ASIDE, claimedBy('me')).kind, 'refuse');
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
describe(parsePushArgs, () => {
|
|
208
|
+
test('reads the pull request, the claimant and the commit', () => {
|
|
209
|
+
assert.deepStrictEqual(
|
|
210
|
+
parsePushArgs(['--', '2267', '--by', 'me', '--head', FIX]),
|
|
211
|
+
{ number: 2267, by: 'me', head: FIX },
|
|
212
|
+
);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test('refuses what it cannot act on', () => {
|
|
216
|
+
for (const args of [
|
|
217
|
+
[],
|
|
218
|
+
['2267', '--by', 'me'],
|
|
219
|
+
['2267', '--head', FIX],
|
|
220
|
+
['2267', '--by', 'me', '--head', 'HEAD'],
|
|
221
|
+
['2267', '--by', 'me', '--head', `${FIX};rm`],
|
|
222
|
+
['x', '--by', 'me', '--head', FIX],
|
|
223
|
+
['2267', '2268', '--by', 'me', '--head', FIX],
|
|
224
|
+
]) {
|
|
225
|
+
assert.isString(parsePushArgs(args));
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './report-unowned-prs.mjs';
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import type { QueueComments, QueueOwner, ReportEntry } from 'pr-report-core';
|
|
2
|
+
import { Arr, Result } from 'ts-data-forge';
|
|
3
|
+
import * as t from 'ts-fortress';
|
|
4
|
+
import { resolveApiContext, rest, viewerLogin } from '../lib/index.mjs';
|
|
5
|
+
import { readQueueState } from '../pr-report/index.mjs';
|
|
6
|
+
|
|
7
|
+
/** What `report-unowned-prs --help` prints. */
|
|
8
|
+
export const REPORT_UNOWNED_PRS_HELP = [
|
|
9
|
+
'Usage: report-unowned-prs [--dry-run]',
|
|
10
|
+
'',
|
|
11
|
+
'Files an issue for each queued pull request nobody owns: one the',
|
|
12
|
+
'worker, a skill and a person are all waiting on someone else for.',
|
|
13
|
+
'Once per pull request; an open issue this account filed is not filed again.',
|
|
14
|
+
'',
|
|
15
|
+
'Options:',
|
|
16
|
+
' --dry-run say what it would file, and file nothing',
|
|
17
|
+
' -h, --help show this help',
|
|
18
|
+
].join('\n');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Files an issue for each queued pull request nobody owns.
|
|
22
|
+
*
|
|
23
|
+
* Every pull request in the queue is the worker's, a skill's or a person's
|
|
24
|
+
* (`pr-report-core`'s `queue-owner.mts`); `none` means the rules met a state
|
|
25
|
+
* they do not cover, and a pull request in it waits for nothing that will
|
|
26
|
+
* come. That is a bug in the queue's tooling, not in the pull request, so it
|
|
27
|
+
* is reported as one — once per pull request, found again by a hidden line
|
|
28
|
+
* in the body of an open issue this account wrote — rather than left for
|
|
29
|
+
* someone to notice the queue has stopped.
|
|
30
|
+
*
|
|
31
|
+
* ```sh
|
|
32
|
+
* report-unowned-prs # file what is not filed yet
|
|
33
|
+
* report-unowned-prs --dry-run # say what it would file
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* The `/unblock-prs` skill runs it on every round. It reads the owners as
|
|
37
|
+
* every other command does (`pr-report/queue.mts`) and writes as the token's
|
|
38
|
+
* account, which has to be the one `unblock-prs` runs as.
|
|
39
|
+
*/
|
|
40
|
+
export const reportUnownedPrs = async (
|
|
41
|
+
dryRun: boolean,
|
|
42
|
+
): Promise<Result<string, string>> => {
|
|
43
|
+
const state = await readQueueState();
|
|
44
|
+
|
|
45
|
+
if (Result.isErr(state)) {
|
|
46
|
+
return Result.err(`cannot read the queue: ${state.value}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const { decided, owners, queue } = state.value;
|
|
50
|
+
|
|
51
|
+
const unowned = decided.entries.filter(
|
|
52
|
+
(entry) => owners.get(entry.number)?.owner === 'none',
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
if (!Arr.isNonEmpty(unowned)) {
|
|
56
|
+
return Result.ok('Every queued pull request has an owner.');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const api = await resolveApiContext();
|
|
60
|
+
|
|
61
|
+
if (Result.isErr(api)) {
|
|
62
|
+
return api;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const viewer = await viewerLogin(api.value.token);
|
|
66
|
+
|
|
67
|
+
if (Result.isErr(viewer)) {
|
|
68
|
+
return Result.err(`cannot read who the token is: ${viewer.value}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Issues and pull requests come from one endpoint; a pull request has no
|
|
72
|
+
// body this marker could be in that this account wrote as an issue.
|
|
73
|
+
const listed = await rest(api.value, 'GET', '/issues', IssueListSchema, {
|
|
74
|
+
query: { state: 'open', creator: viewer.value, per_page: '100' },
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
if (Result.isErr(listed)) {
|
|
78
|
+
return Result.err(`cannot list the open issues: ${listed.value}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const plan = planUnownedReports(
|
|
82
|
+
unowned.map((entry) => ({
|
|
83
|
+
entry,
|
|
84
|
+
owner: owners.get(entry.number),
|
|
85
|
+
comments: queue.get(entry.number),
|
|
86
|
+
})),
|
|
87
|
+
listed.value.map(({ number, body }) => ({ number, body: body ?? '' })),
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const mut_lines = plan.reported.map(
|
|
91
|
+
({ pr, issue }) => `#${pr}: already reported in #${issue}.`,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
for (const report of plan.toFile) {
|
|
95
|
+
if (dryRun) {
|
|
96
|
+
mut_lines.push(`#${report.pr}: would file "${report.title}".`);
|
|
97
|
+
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const filed = await rest(
|
|
102
|
+
api.value,
|
|
103
|
+
'POST',
|
|
104
|
+
'/issues',
|
|
105
|
+
t.record({ number: t.number() }),
|
|
106
|
+
{ body: { title: report.title, body: report.body } },
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
mut_lines.push(
|
|
110
|
+
Result.isErr(filed)
|
|
111
|
+
? `#${report.pr}: could not file an issue: ${filed.value}`
|
|
112
|
+
: `#${report.pr}: filed #${filed.value.number}.`,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return Result.ok(mut_lines.join('\n'));
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* What to file for each pull request nobody owns, given the open issues this
|
|
121
|
+
* account wrote: one already carrying the pull request's marker is reported
|
|
122
|
+
* and not filed again.
|
|
123
|
+
*/
|
|
124
|
+
export const planUnownedReports = (
|
|
125
|
+
unowned: readonly Readonly<{
|
|
126
|
+
entry: ReportEntry;
|
|
127
|
+
owner: QueueOwner | undefined;
|
|
128
|
+
comments: QueueComments | undefined;
|
|
129
|
+
}>[],
|
|
130
|
+
openIssues: readonly Readonly<{ number: number; body: string }>[],
|
|
131
|
+
): Readonly<{
|
|
132
|
+
toFile: readonly Readonly<{ pr: number; title: string; body: string }>[];
|
|
133
|
+
reported: readonly Readonly<{ pr: number; issue: number }>[];
|
|
134
|
+
}> => {
|
|
135
|
+
const issueOf = (pr: number): number | undefined =>
|
|
136
|
+
openIssues.find(({ body }) => body.includes(markerOf(pr)))?.number;
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
reported: Arr.generate(function* () {
|
|
140
|
+
for (const { entry } of unowned) {
|
|
141
|
+
const issue = issueOf(entry.number);
|
|
142
|
+
|
|
143
|
+
if (issue !== undefined) {
|
|
144
|
+
yield { pr: entry.number, issue };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}),
|
|
148
|
+
toFile: unowned
|
|
149
|
+
.filter(({ entry }) => issueOf(entry.number) === undefined)
|
|
150
|
+
.map(({ entry, owner, comments }) => ({
|
|
151
|
+
pr: entry.number,
|
|
152
|
+
title: `unblock-prs: #${entry.number} is in the queue with no owner`,
|
|
153
|
+
body: reportBody(entry, owner, comments),
|
|
154
|
+
})),
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const markerOf = (pr: number): string =>
|
|
159
|
+
`<!-- unblock-prs:unowned pr=${pr} -->` as const;
|
|
160
|
+
|
|
161
|
+
const reportBody = (
|
|
162
|
+
entry: ReportEntry,
|
|
163
|
+
owner: QueueOwner | undefined,
|
|
164
|
+
comments: QueueComments | undefined,
|
|
165
|
+
): string =>
|
|
166
|
+
[
|
|
167
|
+
markerOf(entry.number),
|
|
168
|
+
'',
|
|
169
|
+
`#${entry.number} is in the merge queue, and \`queueOwner\` (\`libs/pr-report-core/src/queue-owner.mts\`) gives it to nobody: the worker, the skills and the people working the queue each leave it to the others.`,
|
|
170
|
+
'',
|
|
171
|
+
`- Why: ${owner?.reason ?? 'no owner was returned'}`,
|
|
172
|
+
`- Labels: ${Arr.isNonEmpty(entry.labels) ? entry.labels.map(({ name }) => `\`${name}\``).join(', ') : 'none'}`,
|
|
173
|
+
`- Head: \`${entry.headRef}\` at \`${entry.headSha}\`, onto \`${entry.baseRef}\`${entry.stackedOn === undefined ? '' : ` (stacked on #${entry.stackedOn})`}`,
|
|
174
|
+
`- Set-aside comment: ${describeComment(comments?.setAside?.says)}`,
|
|
175
|
+
`- Claim: ${describeComment(comments?.claim?.says)}`,
|
|
176
|
+
'',
|
|
177
|
+
'Give the state an owner in `queue-owner.mts` (with a test), or make the writer that produced it stop producing it. Then the pull request needs whatever its new owner does; until then a person moves it by hand.',
|
|
178
|
+
'',
|
|
179
|
+
'Filed by `report-unowned-prs`.',
|
|
180
|
+
].join('\n');
|
|
181
|
+
|
|
182
|
+
const describeComment = (says: unknown): string =>
|
|
183
|
+
says === undefined ? 'none' : (`\`${JSON.stringify(says)}\`` as const);
|
|
184
|
+
|
|
185
|
+
const IssueListSchema = t.array(
|
|
186
|
+
t.record({
|
|
187
|
+
number: t.number(),
|
|
188
|
+
body: t.union([t.string(), t.nullType]),
|
|
189
|
+
}),
|
|
190
|
+
);
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { ReportEntry } from 'pr-report-core';
|
|
2
|
+
import { planUnownedReports } from './report-unowned-prs.mjs';
|
|
3
|
+
|
|
4
|
+
const entry = (number: number): ReportEntry =>
|
|
5
|
+
({
|
|
6
|
+
number,
|
|
7
|
+
title: 'fix: a thing',
|
|
8
|
+
body: '',
|
|
9
|
+
author: 'someone',
|
|
10
|
+
isDraft: false,
|
|
11
|
+
labels: [{ name: 'merge-queued', color: '', description: '' }],
|
|
12
|
+
autoMerge: false,
|
|
13
|
+
headRef: `fix/${number}`,
|
|
14
|
+
headSha: 'c'.repeat(40),
|
|
15
|
+
baseRef: 'main',
|
|
16
|
+
fromFork: false,
|
|
17
|
+
url: '',
|
|
18
|
+
updatedAt: '2026-10-15T00:00:00Z',
|
|
19
|
+
headCommittedAt: undefined,
|
|
20
|
+
comparison: undefined,
|
|
21
|
+
reported: new Map(),
|
|
22
|
+
checksRunning: false,
|
|
23
|
+
linkedIssues: [],
|
|
24
|
+
mergeAfter: [],
|
|
25
|
+
stackedOn: undefined,
|
|
26
|
+
inheritedMergeAfter: [],
|
|
27
|
+
notStartedIssues: [],
|
|
28
|
+
blockedBy: [],
|
|
29
|
+
checks: {
|
|
30
|
+
verdict: 'paused',
|
|
31
|
+
passed: [],
|
|
32
|
+
failed: [],
|
|
33
|
+
pending: [],
|
|
34
|
+
skipped: [],
|
|
35
|
+
missing: [],
|
|
36
|
+
notRun: [],
|
|
37
|
+
required: 0,
|
|
38
|
+
},
|
|
39
|
+
}) as const;
|
|
40
|
+
|
|
41
|
+
const unowned = (number: number) =>
|
|
42
|
+
({
|
|
43
|
+
entry: entry(number),
|
|
44
|
+
owner: { owner: 'none', reason: 'set aside for `mystery`' },
|
|
45
|
+
comments: undefined,
|
|
46
|
+
}) as const;
|
|
47
|
+
|
|
48
|
+
describe(planUnownedReports, () => {
|
|
49
|
+
test('files one issue per pull request, marked so it is found again', () => {
|
|
50
|
+
const plan = planUnownedReports([unowned(2267)], []);
|
|
51
|
+
|
|
52
|
+
assert.deepStrictEqual(plan.reported, []);
|
|
53
|
+
|
|
54
|
+
assert.lengthOf(plan.toFile, 1);
|
|
55
|
+
|
|
56
|
+
const [report] = plan.toFile;
|
|
57
|
+
|
|
58
|
+
assert.strictEqual(
|
|
59
|
+
report?.title,
|
|
60
|
+
'unblock-prs: #2267 is in the queue with no owner',
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
assert.match(
|
|
64
|
+
report?.body ?? '',
|
|
65
|
+
/^<!-- unblock-prs:unowned pr=2267 -->\n/u,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
assert.include(report?.body ?? '', 'set aside for `mystery`');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('does not file again what an open issue already reports', () => {
|
|
72
|
+
const plan = planUnownedReports(
|
|
73
|
+
[unowned(2267), unowned(2273)],
|
|
74
|
+
[
|
|
75
|
+
{ number: 2300, body: '<!-- unblock-prs:unowned pr=2267 -->\n\nprose' },
|
|
76
|
+
{ number: 2301, body: '<!-- unblock-prs:unowned pr=22670 -->' },
|
|
77
|
+
],
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
assert.deepStrictEqual(plan.reported, [{ pr: 2267, issue: 2300 }]);
|
|
81
|
+
|
|
82
|
+
assert.deepStrictEqual(
|
|
83
|
+
plan.toFile.map(({ pr }) => pr),
|
|
84
|
+
[2273],
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
});
|