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,220 @@
|
|
|
1
|
+
// cspell:ignore retarget retargeted
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Stacked pull requests: one whose base is another open pull request's
|
|
5
|
+
* branch rather than the default branch. Which pull request is on which is
|
|
6
|
+
* `pr-report-core`'s `stack.mts`, because the reports draw the same stacks;
|
|
7
|
+
* what this script does about them is decided here.
|
|
8
|
+
*
|
|
9
|
+
* The life of a layer, and what this script does at each point:
|
|
10
|
+
*
|
|
11
|
+
* 1. **Stacked.** Its base is the layer below, so its diff is its own and its
|
|
12
|
+
* turn has not come: it is reported as waiting, and nothing else. It has
|
|
13
|
+
* no auto-merge, and must not: nothing holds a pull request onto an
|
|
14
|
+
* unprotected branch, so armed it would land in the layer below rather
|
|
15
|
+
* than on the default branch. Nothing arms it — `open-pr` arms nothing,
|
|
16
|
+
* and this script arms a pull request only when it picks it
|
|
17
|
+
* (`armOnPick` in `rebase.mts`), which a stacked one never is.
|
|
18
|
+
* 2. **The layer below moves.** When this script rebases a pull request, it
|
|
19
|
+
* replays every layer stacked on it onto the new head with
|
|
20
|
+
* `git rebase --onto <new head> <old head>`, which carries exactly the
|
|
21
|
+
* layer's own commits. Otherwise each layer would show the old commits of
|
|
22
|
+
* the one below in its diff, and GitHub's own rebase, when that one
|
|
23
|
+
* merges, would start from a head the layer does not contain.
|
|
24
|
+
* 3. **The layer below merges.** GitHub moves this one onto the default
|
|
25
|
+
* branch. From then on it is an ordinary pull request, picked and armed
|
|
26
|
+
* in its turn like any other, save one thing: it still carries the merged
|
|
27
|
+
* layer's commits, so its diff shows that layer's changes as its own. The
|
|
28
|
+
* next survey rebases them off with `--onto`, from the head that layer
|
|
29
|
+
* merged at to its squash commit, and carries the layers above along —
|
|
30
|
+
* without waiting for it to be queued, because that is an undoing of
|
|
31
|
+
* GitHub's move, not a turn: it adds nothing from the base, and needs no
|
|
32
|
+
* review to have finished. The ruleset merges a branch only when it is up
|
|
33
|
+
* to date and only by squash, so that commit's tree is the merged head's,
|
|
34
|
+
* and the rebased layer's tree is the one it had: only its history, and
|
|
35
|
+
* so its diff, changes. A queued layer is left to its own rebase when it
|
|
36
|
+
* is picked, which drops the same commits the same way on its way onto
|
|
37
|
+
* the tip.
|
|
38
|
+
*
|
|
39
|
+
* A stack here is made by the base alone. GitHub's native stacks are not
|
|
40
|
+
* landed: GitHub refuses auto-merge on a pull request in one ("Auto-merge is
|
|
41
|
+
* not supported for stacked pull requests"), and keeps a layer in its stack
|
|
42
|
+
* after the one below has merged and it has been moved onto the default
|
|
43
|
+
* branch — so one is reported rather than picked, and merged by hand.
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
import {
|
|
47
|
+
findStackParents,
|
|
48
|
+
isSafeRefName,
|
|
49
|
+
type NativeStackEntry,
|
|
50
|
+
type QueueOwner,
|
|
51
|
+
} from 'pr-report-core';
|
|
52
|
+
import { isMergeQueued, isSkipCiLabelled, wantsAutoRebase } from './labels.mjs';
|
|
53
|
+
import type {
|
|
54
|
+
BaseChange,
|
|
55
|
+
BranchOf,
|
|
56
|
+
Classification,
|
|
57
|
+
PullRequest,
|
|
58
|
+
RetargetedLayer,
|
|
59
|
+
StackedOn,
|
|
60
|
+
TriageContext,
|
|
61
|
+
} from './types.mjs';
|
|
62
|
+
import { isVersionPullRequest } from './version-pr.mjs';
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The branch the pull request was stacked on, if the last change of its base
|
|
66
|
+
* moved it off that branch and onto the default one.
|
|
67
|
+
*/
|
|
68
|
+
export const retargetedFrom = (
|
|
69
|
+
changes: readonly BaseChange[],
|
|
70
|
+
defaultBranch: string,
|
|
71
|
+
): string | undefined => {
|
|
72
|
+
const last = changes.at(-1);
|
|
73
|
+
|
|
74
|
+
return last?.to === defaultBranch && last.from !== defaultBranch
|
|
75
|
+
? last.from
|
|
76
|
+
: undefined;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/** What to report for a stacked pull request in place of anything else. */
|
|
80
|
+
export const stackedNote = (
|
|
81
|
+
pr: PullRequest,
|
|
82
|
+
parent: number,
|
|
83
|
+
context: TriageContext,
|
|
84
|
+
): Classification =>
|
|
85
|
+
({
|
|
86
|
+
kind: 'note',
|
|
87
|
+
note: `#${pr.number}: stacked on #${parent}, which merges first${
|
|
88
|
+
context.cyclic.has(pr.number)
|
|
89
|
+
? ' — and on a cycle of stacks and Merge-After, so nothing here will move it'
|
|
90
|
+
: ''
|
|
91
|
+
}`,
|
|
92
|
+
}) as const;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* What to report instead of picking a pull request in one of GitHub's native
|
|
96
|
+
* stacks, which GitHub will not let this script arm.
|
|
97
|
+
*/
|
|
98
|
+
export const nativeStackNote = (
|
|
99
|
+
pr: PullRequest,
|
|
100
|
+
entry: NativeStackEntry,
|
|
101
|
+
): Classification =>
|
|
102
|
+
({
|
|
103
|
+
kind: 'note',
|
|
104
|
+
note: `#${pr.number}: layer ${entry.position} of ${entry.size} of GitHub's native stack #${entry.stack}, on which GitHub refuses auto-merge; merge it by hand (a stack made by the base alone needs no native stack)`,
|
|
105
|
+
}) as const;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Why a layer cannot be carried along when the one below it moves, or
|
|
109
|
+
* `undefined` when it can. A fork's branch is not this repository's to push
|
|
110
|
+
* to, whatever its name.
|
|
111
|
+
*/
|
|
112
|
+
export const restackable = (pr: BranchOf): string | undefined =>
|
|
113
|
+
pr.state !== 'OPEN'
|
|
114
|
+
? (`it is ${pr.state}` as const)
|
|
115
|
+
: pr.isCrossRepository
|
|
116
|
+
? 'its branch is in a fork'
|
|
117
|
+
: !isSafeRefName(pr.headRefName)
|
|
118
|
+
? (`branch name ${JSON.stringify(pr.headRefName)} will not be passed to a shell` as const)
|
|
119
|
+
: undefined;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The pull requests this script rebases onto the default branch ahead of any
|
|
123
|
+
* turn: the bottom layer of a stack, or a lone pull request, that asked for it
|
|
124
|
+
* (`wantsAutoRebase`) and is paused by `skip-ci`, so the push costs no
|
|
125
|
+
* matrix. Whether it is actually behind is for the caller to ask
|
|
126
|
+
* git: `skip-ci` leaves the merge state at `BLOCKED` whatever the branch is.
|
|
127
|
+
* The version pull request is the release workflow's. Only the worker's own
|
|
128
|
+
* are moved (`owner.mts`): one set aside or claimed is a skill's or a
|
|
129
|
+
* person's until its comment is resolved or the claim ends.
|
|
130
|
+
*/
|
|
131
|
+
export const autoRebaseTargets = (
|
|
132
|
+
pullRequests: readonly PullRequest[],
|
|
133
|
+
defaultBranch: string,
|
|
134
|
+
{
|
|
135
|
+
owners,
|
|
136
|
+
}: Readonly<{
|
|
137
|
+
/** Who moves each queued pull request next (`owner.mts`). */
|
|
138
|
+
owners: ReadonlyMap<number, QueueOwner>;
|
|
139
|
+
}>,
|
|
140
|
+
): readonly PullRequest[] =>
|
|
141
|
+
pullRequests.filter(
|
|
142
|
+
(pr) =>
|
|
143
|
+
pr.baseRefName === defaultBranch &&
|
|
144
|
+
!pr.isDraft &&
|
|
145
|
+
wantsAutoRebase(pr) &&
|
|
146
|
+
isSkipCiLabelled(pr) &&
|
|
147
|
+
!isVersionPullRequest(pr, defaultBranch) &&
|
|
148
|
+
restackable(pr) === undefined &&
|
|
149
|
+
owners.get(pr.number)?.owner === 'worker',
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
/** Which open pull request each stacked one is on: child → parent. */
|
|
153
|
+
export const stackParentsOf = (
|
|
154
|
+
pullRequests: readonly PullRequest[],
|
|
155
|
+
defaultBranch: string,
|
|
156
|
+
): ReadonlyMap<number, number> =>
|
|
157
|
+
findStackParents(
|
|
158
|
+
pullRequests.map((pr) => ({
|
|
159
|
+
number: pr.number,
|
|
160
|
+
headRef: pr.headRefName,
|
|
161
|
+
baseRef: pr.baseRefName,
|
|
162
|
+
fromFork: pr.isCrossRepository,
|
|
163
|
+
})),
|
|
164
|
+
defaultBranch,
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* What to remember of this survey's stacks for the next one: every layer, by
|
|
169
|
+
* the branch it is on, and every layer remembered before that is still on
|
|
170
|
+
* that branch. The second is a layer whose parent has merged while GitHub
|
|
171
|
+
* has yet to move it — no open pull request heads its base any longer, so
|
|
172
|
+
* the bases alone no longer say it is a layer, but it has still to come off
|
|
173
|
+
* one.
|
|
174
|
+
*/
|
|
175
|
+
export const stackedOnAfter = (
|
|
176
|
+
before: StackedOn | undefined,
|
|
177
|
+
pullRequests: readonly PullRequest[],
|
|
178
|
+
stackParents: ReadonlyMap<number, number>,
|
|
179
|
+
): StackedOn =>
|
|
180
|
+
new Map(
|
|
181
|
+
pullRequests
|
|
182
|
+
.filter(
|
|
183
|
+
(pr) =>
|
|
184
|
+
stackParents.has(pr.number) ||
|
|
185
|
+
before?.get(pr.number) === pr.baseRefName,
|
|
186
|
+
)
|
|
187
|
+
.map((pr) => [pr.number, pr.baseRefName] as const),
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* The pull requests the last survey saw stacked and this one sees on the
|
|
192
|
+
* default branch — before the first survey, every pull request on the
|
|
193
|
+
* default branch, whose timeline then says whether it came off a stack.
|
|
194
|
+
*
|
|
195
|
+
* A queued one is not among them: its own rebase, when it is picked, drops
|
|
196
|
+
* the merged layer's commits on its way onto the tip. Nor is anything that
|
|
197
|
+
* cannot be pushed to, or the version pull request, which was never a layer.
|
|
198
|
+
*/
|
|
199
|
+
export const retargetedLayers = (
|
|
200
|
+
before: StackedOn | undefined,
|
|
201
|
+
pullRequests: readonly PullRequest[],
|
|
202
|
+
defaultBranch: string,
|
|
203
|
+
): readonly RetargetedLayer[] =>
|
|
204
|
+
pullRequests
|
|
205
|
+
.filter(
|
|
206
|
+
(pr) =>
|
|
207
|
+
pr.baseRefName === defaultBranch &&
|
|
208
|
+
!isMergeQueued(pr) &&
|
|
209
|
+
!isVersionPullRequest(pr, defaultBranch) &&
|
|
210
|
+
restackable(pr) === undefined,
|
|
211
|
+
)
|
|
212
|
+
.flatMap((pr): readonly RetargetedLayer[] => {
|
|
213
|
+
if (before === undefined) {
|
|
214
|
+
return [{ pr, from: undefined }];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const from = before.get(pr.number);
|
|
218
|
+
|
|
219
|
+
return from === undefined ? [] : [{ pr, from }];
|
|
220
|
+
});
|
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
// cspell:ignore retarget retargeted
|
|
2
|
+
|
|
3
|
+
import type { Claim } from 'pr-report-core';
|
|
4
|
+
import { Arr } from 'ts-data-forge';
|
|
5
|
+
import { ownerOf } from './owner.mjs';
|
|
6
|
+
import {
|
|
7
|
+
autoRebaseTargets,
|
|
8
|
+
nativeStackNote,
|
|
9
|
+
restackable,
|
|
10
|
+
retargetedFrom,
|
|
11
|
+
retargetedLayers,
|
|
12
|
+
stackedNote,
|
|
13
|
+
stackedOnAfter,
|
|
14
|
+
stackParentsOf,
|
|
15
|
+
} from './stack.mjs';
|
|
16
|
+
import type {
|
|
17
|
+
BaseChange,
|
|
18
|
+
PullRequest,
|
|
19
|
+
SkipRecord,
|
|
20
|
+
TriageContext,
|
|
21
|
+
} from './types.mjs';
|
|
22
|
+
|
|
23
|
+
const NOW = Date.parse('2026-10-15T00:30:00Z');
|
|
24
|
+
|
|
25
|
+
const pullRequest = (
|
|
26
|
+
fields: Partial<PullRequest> & Readonly<{ number: number }>,
|
|
27
|
+
): PullRequest =>
|
|
28
|
+
({
|
|
29
|
+
id: `PR_${fields.number}`,
|
|
30
|
+
title: `pull request #${fields.number}`,
|
|
31
|
+
body: '',
|
|
32
|
+
state: 'OPEN',
|
|
33
|
+
headRefName: `feature/${fields.number}`,
|
|
34
|
+
headRefOid: 'a'.repeat(40),
|
|
35
|
+
baseRefName: 'main',
|
|
36
|
+
isCrossRepository: false,
|
|
37
|
+
isDraft: false,
|
|
38
|
+
mergeStateStatus: 'BLOCKED',
|
|
39
|
+
autoMergeRequest: null,
|
|
40
|
+
labels: [],
|
|
41
|
+
...fields,
|
|
42
|
+
}) as const;
|
|
43
|
+
|
|
44
|
+
const context = (
|
|
45
|
+
stackParents: ReadonlyMap<number, number>,
|
|
46
|
+
cyclic: ReadonlySet<number> = new Set(),
|
|
47
|
+
): TriageContext =>
|
|
48
|
+
({
|
|
49
|
+
defaultBranch: 'main',
|
|
50
|
+
baseSha: 'b'.repeat(40),
|
|
51
|
+
skipped: new Map(),
|
|
52
|
+
claimed: new Map(),
|
|
53
|
+
owners: new Map(),
|
|
54
|
+
demoted: new Map(),
|
|
55
|
+
requiredContexts: [],
|
|
56
|
+
issueOrder: { closes: new Map(), startAfter: new Map() },
|
|
57
|
+
reviewRequirements: {
|
|
58
|
+
requireCodeOwnerReview: false,
|
|
59
|
+
requireConversationResolution: false,
|
|
60
|
+
},
|
|
61
|
+
openNumbers: new Set(),
|
|
62
|
+
dependencies: new Map(),
|
|
63
|
+
inherited: new Map(),
|
|
64
|
+
stackParents,
|
|
65
|
+
cycles: [],
|
|
66
|
+
cyclic,
|
|
67
|
+
releaseBlockers: [],
|
|
68
|
+
}) as const;
|
|
69
|
+
|
|
70
|
+
const retarget = (from: string, to = 'main'): BaseChange =>
|
|
71
|
+
({ from, to }) as const;
|
|
72
|
+
|
|
73
|
+
describe(retargetedFrom, () => {
|
|
74
|
+
test('names the branch the last change of base moved it off', () => {
|
|
75
|
+
assert.strictEqual(
|
|
76
|
+
retargetedFrom([retarget('feature/1')], 'main'),
|
|
77
|
+
'feature/1',
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('is nothing for a pull request that never changed base', () => {
|
|
82
|
+
assert.isUndefined(retargetedFrom([], 'main'));
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('is nothing when the last change was away from the default branch', () => {
|
|
86
|
+
assert.isUndefined(
|
|
87
|
+
retargetedFrom(
|
|
88
|
+
[retarget('feature/1'), retarget('main', 'feature/2')],
|
|
89
|
+
'main',
|
|
90
|
+
),
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('reads the last change of base, not the first', () => {
|
|
95
|
+
assert.strictEqual(
|
|
96
|
+
retargetedFrom(
|
|
97
|
+
[retarget('feature/1', 'feature/2'), retarget('feature/2')],
|
|
98
|
+
'main',
|
|
99
|
+
),
|
|
100
|
+
'feature/2',
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe(stackedNote, () => {
|
|
106
|
+
test('says which layer it waits for', () => {
|
|
107
|
+
assert.deepStrictEqual(
|
|
108
|
+
stackedNote(pullRequest({ number: 2 }), 1, context(new Map([[2, 1]]))),
|
|
109
|
+
{
|
|
110
|
+
kind: 'note',
|
|
111
|
+
note: '#2: stacked on #1, which merges first',
|
|
112
|
+
},
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test('says when the stack loops back on itself', () => {
|
|
117
|
+
const note = stackedNote(
|
|
118
|
+
pullRequest({ number: 2 }),
|
|
119
|
+
1,
|
|
120
|
+
context(
|
|
121
|
+
new Map([
|
|
122
|
+
[2, 1],
|
|
123
|
+
[1, 2],
|
|
124
|
+
]),
|
|
125
|
+
new Set([1, 2]),
|
|
126
|
+
),
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
assert.isTrue(note.kind === 'note' && note.note.includes('cycle'));
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe(restackable, () => {
|
|
134
|
+
test('keeps a layer this repository can push to', () => {
|
|
135
|
+
assert.isUndefined(restackable(pullRequest({ number: 2 })));
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("refuses a fork's branch", () => {
|
|
139
|
+
assert.isDefined(
|
|
140
|
+
restackable(pullRequest({ number: 2, isCrossRepository: true })),
|
|
141
|
+
);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('refuses a branch name it will not pass to a shell', () => {
|
|
145
|
+
assert.isDefined(
|
|
146
|
+
restackable(pullRequest({ number: 2, headRefName: "it's" })),
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('refuses one that is not open', () => {
|
|
151
|
+
assert.isDefined(restackable(pullRequest({ number: 2, state: 'CLOSED' })));
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe(nativeStackNote, () => {
|
|
156
|
+
test('says why one in a native stack is not armed, and what to do', () => {
|
|
157
|
+
// GitHub keeps the layer in the stack after the one below it merged and
|
|
158
|
+
// it was moved onto `main`, and refuses auto-merge for as long.
|
|
159
|
+
assert.deepStrictEqual(
|
|
160
|
+
nativeStackNote(pullRequest({ number: 2 }), {
|
|
161
|
+
stack: 3,
|
|
162
|
+
position: 2,
|
|
163
|
+
size: 2,
|
|
164
|
+
}),
|
|
165
|
+
{
|
|
166
|
+
kind: 'note',
|
|
167
|
+
note: "#2: layer 2 of 2 of GitHub's native stack #3, on which GitHub refuses auto-merge; merge it by hand (a stack made by the base alone needs no native stack)",
|
|
168
|
+
},
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe(stackParentsOf, () => {
|
|
174
|
+
test('reads which open pull request each layer is on from the bases', () => {
|
|
175
|
+
assert.deepStrictEqual(
|
|
176
|
+
stackParentsOf(
|
|
177
|
+
[
|
|
178
|
+
pullRequest({ number: 1 }),
|
|
179
|
+
pullRequest({ number: 2, baseRefName: 'feature/1' }),
|
|
180
|
+
pullRequest({ number: 3, baseRefName: 'feature/2' }),
|
|
181
|
+
],
|
|
182
|
+
'main',
|
|
183
|
+
),
|
|
184
|
+
new Map([
|
|
185
|
+
[2, 1],
|
|
186
|
+
[3, 2],
|
|
187
|
+
]),
|
|
188
|
+
);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe(stackedOnAfter, () => {
|
|
193
|
+
test('remembers the branch each layer is stacked on', () => {
|
|
194
|
+
const pulls = [
|
|
195
|
+
pullRequest({ number: 1 }),
|
|
196
|
+
pullRequest({ number: 2, baseRefName: 'feature/1' }),
|
|
197
|
+
] as const;
|
|
198
|
+
|
|
199
|
+
assert.deepStrictEqual(
|
|
200
|
+
stackedOnAfter(undefined, pulls, stackParentsOf(pulls, 'main')),
|
|
201
|
+
new Map([[2, 'feature/1']]),
|
|
202
|
+
);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test('keeps a layer whose parent merged until GitHub moves it', () => {
|
|
206
|
+
// #1 merged and left the open list, and GitHub has not moved #2 yet: the
|
|
207
|
+
// bases no longer make a stack, but #2 has still to come off one.
|
|
208
|
+
const pulls = [
|
|
209
|
+
pullRequest({ number: 2, baseRefName: 'feature/1' }),
|
|
210
|
+
] as const;
|
|
211
|
+
|
|
212
|
+
assert.deepStrictEqual(
|
|
213
|
+
stackedOnAfter(
|
|
214
|
+
new Map([[2, 'feature/1']]),
|
|
215
|
+
pulls,
|
|
216
|
+
stackParentsOf(pulls, 'main'),
|
|
217
|
+
),
|
|
218
|
+
new Map([[2, 'feature/1']]),
|
|
219
|
+
);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test('forgets a layer once it is on another base', () => {
|
|
223
|
+
const pulls = [pullRequest({ number: 2 })] as const;
|
|
224
|
+
|
|
225
|
+
assert.deepStrictEqual(
|
|
226
|
+
stackedOnAfter(
|
|
227
|
+
new Map([[2, 'feature/1']]),
|
|
228
|
+
pulls,
|
|
229
|
+
stackParentsOf(pulls, 'main'),
|
|
230
|
+
),
|
|
231
|
+
new Map(),
|
|
232
|
+
);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test('forgets a layer that is no longer open', () => {
|
|
236
|
+
assert.deepStrictEqual(
|
|
237
|
+
stackedOnAfter(new Map([[2, 'feature/1']]), [], new Map()),
|
|
238
|
+
new Map(),
|
|
239
|
+
);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
describe(retargetedLayers, () => {
|
|
244
|
+
test('names a layer that was stacked and is now on the default branch', () => {
|
|
245
|
+
const layer = pullRequest({ number: 2 });
|
|
246
|
+
|
|
247
|
+
assert.deepStrictEqual(
|
|
248
|
+
retargetedLayers(new Map([[2, 'feature/1']]), [layer], 'main'),
|
|
249
|
+
[{ pr: layer, from: 'feature/1' }],
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test('is nothing for a layer GitHub has not moved yet', () => {
|
|
254
|
+
assert.deepStrictEqual(
|
|
255
|
+
retargetedLayers(
|
|
256
|
+
new Map([[2, 'feature/1']]),
|
|
257
|
+
[pullRequest({ number: 2, baseRefName: 'feature/1' })],
|
|
258
|
+
'main',
|
|
259
|
+
),
|
|
260
|
+
[],
|
|
261
|
+
);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test('is nothing for a pull request that was never seen stacked', () => {
|
|
265
|
+
assert.deepStrictEqual(
|
|
266
|
+
retargetedLayers(new Map(), [pullRequest({ number: 2 })], 'main'),
|
|
267
|
+
[],
|
|
268
|
+
);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test('before the first survey, offers every pull request on the default branch, with the branch to be read from its timeline', () => {
|
|
272
|
+
const layer = pullRequest({ number: 2 });
|
|
273
|
+
|
|
274
|
+
assert.deepStrictEqual(
|
|
275
|
+
retargetedLayers(
|
|
276
|
+
undefined,
|
|
277
|
+
[layer, pullRequest({ number: 3, baseRefName: 'feature/2' })],
|
|
278
|
+
'main',
|
|
279
|
+
),
|
|
280
|
+
[{ pr: layer, from: undefined }],
|
|
281
|
+
);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
test('leaves a queued pull request to its own rebase when it is picked', () => {
|
|
285
|
+
assert.deepStrictEqual(
|
|
286
|
+
retargetedLayers(
|
|
287
|
+
new Map([[2, 'feature/1']]),
|
|
288
|
+
[pullRequest({ number: 2, labels: [{ name: 'merge-queued' }] })],
|
|
289
|
+
'main',
|
|
290
|
+
),
|
|
291
|
+
[],
|
|
292
|
+
);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test("leaves a fork's branch and the version pull request alone", () => {
|
|
296
|
+
assert.deepStrictEqual(
|
|
297
|
+
retargetedLayers(
|
|
298
|
+
undefined,
|
|
299
|
+
[
|
|
300
|
+
pullRequest({ number: 2, isCrossRepository: true }),
|
|
301
|
+
pullRequest({ number: 3, headRefName: 'changeset-release/main' }),
|
|
302
|
+
],
|
|
303
|
+
'main',
|
|
304
|
+
),
|
|
305
|
+
[],
|
|
306
|
+
);
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
const labelled = (...names: readonly string[]): PullRequest['labels'] =>
|
|
311
|
+
names.map((name) => ({ name }));
|
|
312
|
+
|
|
313
|
+
const targets = (
|
|
314
|
+
pullRequests: readonly PullRequest[],
|
|
315
|
+
skipped: ReadonlyMap<number, SkipRecord> = new Map(),
|
|
316
|
+
claimed: ReadonlyMap<number, Claim> = new Map(),
|
|
317
|
+
): readonly number[] =>
|
|
318
|
+
autoRebaseTargets(pullRequests, 'main', {
|
|
319
|
+
owners: new Map(
|
|
320
|
+
Arr.generate(function* () {
|
|
321
|
+
for (const pr of pullRequests) {
|
|
322
|
+
const owner = ownerOf(pr, {
|
|
323
|
+
defaultBranch: 'main',
|
|
324
|
+
skipped,
|
|
325
|
+
claimed,
|
|
326
|
+
stackParents: new Map(),
|
|
327
|
+
cyclic: new Set(),
|
|
328
|
+
nowEpochMs: NOW,
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
if (owner !== undefined) {
|
|
332
|
+
yield [pr.number, owner] as const;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}),
|
|
336
|
+
),
|
|
337
|
+
}).map((pr) => pr.number);
|
|
338
|
+
|
|
339
|
+
describe(autoRebaseTargets, () => {
|
|
340
|
+
test('takes a paused pull request labelled auto-rebase, and a paused queued one', () => {
|
|
341
|
+
assert.deepStrictEqual(
|
|
342
|
+
targets([
|
|
343
|
+
pullRequest({ number: 1, labels: labelled('skip-ci', 'auto-rebase') }),
|
|
344
|
+
pullRequest({ number: 2, labels: labelled('skip-ci', 'merge-queued') }),
|
|
345
|
+
]),
|
|
346
|
+
[1, 2],
|
|
347
|
+
);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
test('leaves a paused pull request that asked for neither', () => {
|
|
351
|
+
assert.deepStrictEqual(
|
|
352
|
+
targets([pullRequest({ number: 1, labels: labelled('skip-ci') })]),
|
|
353
|
+
[],
|
|
354
|
+
);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
test('leaves one that skip-ci is not on, since a push would run the matrix', () => {
|
|
358
|
+
assert.deepStrictEqual(
|
|
359
|
+
targets([
|
|
360
|
+
pullRequest({ number: 1, labels: labelled('auto-rebase') }),
|
|
361
|
+
pullRequest({ number: 2, labels: labelled('merge-queued') }),
|
|
362
|
+
]),
|
|
363
|
+
[],
|
|
364
|
+
);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
test('leaves a draft, a layer, a fork and the version pull request', () => {
|
|
368
|
+
assert.deepStrictEqual(
|
|
369
|
+
targets([
|
|
370
|
+
pullRequest({
|
|
371
|
+
number: 1,
|
|
372
|
+
isDraft: true,
|
|
373
|
+
labels: labelled('skip-ci', 'auto-rebase'),
|
|
374
|
+
}),
|
|
375
|
+
pullRequest({
|
|
376
|
+
number: 2,
|
|
377
|
+
baseRefName: 'feature/1',
|
|
378
|
+
labels: labelled('skip-ci', 'auto-rebase'),
|
|
379
|
+
}),
|
|
380
|
+
pullRequest({
|
|
381
|
+
number: 3,
|
|
382
|
+
isCrossRepository: true,
|
|
383
|
+
labels: labelled('skip-ci', 'auto-rebase'),
|
|
384
|
+
}),
|
|
385
|
+
pullRequest({
|
|
386
|
+
number: 4,
|
|
387
|
+
headRefName: 'changeset-release/main',
|
|
388
|
+
labels: labelled('skip-ci', 'merge-queued'),
|
|
389
|
+
}),
|
|
390
|
+
]),
|
|
391
|
+
[],
|
|
392
|
+
);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
test('leaves one set aside at its head, whatever the base', () => {
|
|
396
|
+
const given = pullRequest({
|
|
397
|
+
number: 1,
|
|
398
|
+
labels: labelled('skip-ci', 'auto-rebase'),
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
assert.deepStrictEqual(
|
|
402
|
+
targets(
|
|
403
|
+
[given],
|
|
404
|
+
new Map([
|
|
405
|
+
[
|
|
406
|
+
1,
|
|
407
|
+
{
|
|
408
|
+
number: 1,
|
|
409
|
+
headSha: given.headRefOid,
|
|
410
|
+
baseSha: 'b'.repeat(40),
|
|
411
|
+
reason: 'rebase-failed',
|
|
412
|
+
detail: 'conflict',
|
|
413
|
+
},
|
|
414
|
+
],
|
|
415
|
+
]),
|
|
416
|
+
),
|
|
417
|
+
[],
|
|
418
|
+
);
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
test('leaves one a session has claimed, until its claim ends', () => {
|
|
422
|
+
assert.deepStrictEqual(
|
|
423
|
+
targets(
|
|
424
|
+
[
|
|
425
|
+
pullRequest({
|
|
426
|
+
number: 1,
|
|
427
|
+
labels: labelled('skip-ci', 'auto-rebase'),
|
|
428
|
+
}),
|
|
429
|
+
pullRequest({
|
|
430
|
+
number: 2,
|
|
431
|
+
labels: labelled('skip-ci', 'auto-rebase'),
|
|
432
|
+
}),
|
|
433
|
+
],
|
|
434
|
+
new Map(),
|
|
435
|
+
new Map([
|
|
436
|
+
[
|
|
437
|
+
1,
|
|
438
|
+
{
|
|
439
|
+
headSha: pullRequest({ number: 1 }).headRefOid,
|
|
440
|
+
by: 'unblock-wren',
|
|
441
|
+
at: '2026-10-15T00:00:00Z',
|
|
442
|
+
},
|
|
443
|
+
],
|
|
444
|
+
]),
|
|
445
|
+
),
|
|
446
|
+
[2],
|
|
447
|
+
);
|
|
448
|
+
});
|
|
449
|
+
});
|