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.
Files changed (331) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +125 -2
  3. package/dist/claim-pr/claim-pr.d.mts +45 -0
  4. package/dist/claim-pr/claim-pr.d.mts.map +1 -0
  5. package/dist/claim-pr/claim-pr.mjs +217 -0
  6. package/dist/claim-pr/claim-pr.mjs.map +1 -0
  7. package/dist/claim-pr/index.d.mts +2 -0
  8. package/dist/claim-pr/index.d.mts.map +1 -0
  9. package/dist/claim-pr/index.mjs +2 -0
  10. package/dist/claim-pr/index.mjs.map +1 -0
  11. package/dist/cmd/claim-pr.d.mts +3 -0
  12. package/dist/cmd/claim-pr.d.mts.map +1 -0
  13. package/dist/cmd/claim-pr.mjs +20 -0
  14. package/dist/cmd/claim-pr.mjs.map +1 -0
  15. package/dist/cmd/open-pr.d.mts +3 -0
  16. package/dist/cmd/open-pr.d.mts.map +1 -0
  17. package/dist/cmd/open-pr.mjs +20 -0
  18. package/dist/cmd/open-pr.mjs.map +1 -0
  19. package/dist/cmd/pr-report.d.mts +3 -0
  20. package/dist/cmd/pr-report.d.mts.map +1 -0
  21. package/dist/cmd/pr-report.mjs +20 -0
  22. package/dist/cmd/pr-report.mjs.map +1 -0
  23. package/dist/cmd/push-fixed-pr.d.mts +3 -0
  24. package/dist/cmd/push-fixed-pr.d.mts.map +1 -0
  25. package/dist/cmd/push-fixed-pr.mjs +20 -0
  26. package/dist/cmd/push-fixed-pr.mjs.map +1 -0
  27. package/dist/cmd/report-unowned-prs.d.mts +3 -0
  28. package/dist/cmd/report-unowned-prs.d.mts.map +1 -0
  29. package/dist/cmd/report-unowned-prs.mjs +20 -0
  30. package/dist/cmd/report-unowned-prs.mjs.map +1 -0
  31. package/dist/cmd/unblock-prs.d.mts +3 -0
  32. package/dist/cmd/unblock-prs.d.mts.map +1 -0
  33. package/dist/cmd/unblock-prs.mjs +19 -0
  34. package/dist/cmd/unblock-prs.mjs.map +1 -0
  35. package/dist/entry-point.d.mts +3 -0
  36. package/dist/entry-point.d.mts.map +1 -0
  37. package/dist/entry-point.mjs +3 -0
  38. package/dist/entry-point.mjs.map +1 -0
  39. package/dist/lib/cli-args.d.mts +27 -0
  40. package/dist/lib/cli-args.d.mts.map +1 -0
  41. package/dist/lib/cli-args.mjs +55 -0
  42. package/dist/lib/cli-args.mjs.map +1 -0
  43. package/dist/lib/github-api.d.mts +153 -0
  44. package/dist/lib/github-api.d.mts.map +1 -0
  45. package/dist/lib/github-api.mjs +291 -0
  46. package/dist/lib/github-api.mjs.map +1 -0
  47. package/dist/lib/index.d.mts +7 -0
  48. package/dist/lib/index.d.mts.map +1 -0
  49. package/dist/lib/index.mjs +7 -0
  50. package/dist/lib/index.mjs.map +1 -0
  51. package/dist/lib/json.d.mts +12 -0
  52. package/dist/lib/json.d.mts.map +1 -0
  53. package/dist/lib/json.mjs +28 -0
  54. package/dist/lib/json.mjs.map +1 -0
  55. package/dist/lib/log.d.mts +10 -0
  56. package/dist/lib/log.d.mts.map +1 -0
  57. package/dist/lib/log.mjs +13 -0
  58. package/dist/lib/log.mjs.map +1 -0
  59. package/dist/lib/shell.d.mts +18 -0
  60. package/dist/lib/shell.d.mts.map +1 -0
  61. package/dist/lib/shell.mjs +42 -0
  62. package/dist/lib/shell.mjs.map +1 -0
  63. package/dist/lib/time.d.mts +17 -0
  64. package/dist/lib/time.d.mts.map +1 -0
  65. package/dist/lib/time.mjs +35 -0
  66. package/dist/lib/time.mjs.map +1 -0
  67. package/dist/open-pr/github.d.mts +72 -0
  68. package/dist/open-pr/github.d.mts.map +1 -0
  69. package/dist/open-pr/github.mjs +249 -0
  70. package/dist/open-pr/github.mjs.map +1 -0
  71. package/dist/open-pr/index.d.mts +6 -0
  72. package/dist/open-pr/index.d.mts.map +1 -0
  73. package/dist/open-pr/index.mjs +6 -0
  74. package/dist/open-pr/index.mjs.map +1 -0
  75. package/dist/open-pr/main.d.mts +36 -0
  76. package/dist/open-pr/main.d.mts.map +1 -0
  77. package/dist/open-pr/main.mjs +204 -0
  78. package/dist/open-pr/main.mjs.map +1 -0
  79. package/dist/open-pr/options.d.mts +20 -0
  80. package/dist/open-pr/options.d.mts.map +1 -0
  81. package/dist/open-pr/options.mjs +66 -0
  82. package/dist/open-pr/options.mjs.map +1 -0
  83. package/dist/open-pr/steps.d.mts +23 -0
  84. package/dist/open-pr/steps.d.mts.map +1 -0
  85. package/dist/open-pr/steps.mjs +38 -0
  86. package/dist/open-pr/steps.mjs.map +1 -0
  87. package/dist/open-pr/types.d.mts +179 -0
  88. package/dist/open-pr/types.d.mts.map +1 -0
  89. package/dist/open-pr/types.mjs +20 -0
  90. package/dist/open-pr/types.mjs.map +1 -0
  91. package/dist/pr-report/github.d.mts +59 -0
  92. package/dist/pr-report/github.d.mts.map +1 -0
  93. package/dist/pr-report/github.mjs +561 -0
  94. package/dist/pr-report/github.mjs.map +1 -0
  95. package/dist/pr-report/index.d.mts +8 -0
  96. package/dist/pr-report/index.d.mts.map +1 -0
  97. package/dist/pr-report/index.mjs +8 -0
  98. package/dist/pr-report/index.mjs.map +1 -0
  99. package/dist/pr-report/linked-issues.d.mts +15 -0
  100. package/dist/pr-report/linked-issues.d.mts.map +1 -0
  101. package/dist/pr-report/linked-issues.mjs +39 -0
  102. package/dist/pr-report/linked-issues.mjs.map +1 -0
  103. package/dist/pr-report/main.d.mts +42 -0
  104. package/dist/pr-report/main.d.mts.map +1 -0
  105. package/dist/pr-report/main.mjs +163 -0
  106. package/dist/pr-report/main.mjs.map +1 -0
  107. package/dist/pr-report/options.d.mts +36 -0
  108. package/dist/pr-report/options.d.mts.map +1 -0
  109. package/dist/pr-report/options.mjs +90 -0
  110. package/dist/pr-report/options.mjs.map +1 -0
  111. package/dist/pr-report/queue.d.mts +28 -0
  112. package/dist/pr-report/queue.d.mts.map +1 -0
  113. package/dist/pr-report/queue.mjs +74 -0
  114. package/dist/pr-report/queue.mjs.map +1 -0
  115. package/dist/pr-report/render.d.mts +14 -0
  116. package/dist/pr-report/render.d.mts.map +1 -0
  117. package/dist/pr-report/render.mjs +318 -0
  118. package/dist/pr-report/render.mjs.map +1 -0
  119. package/dist/pr-report/repo-settings.d.mts +12 -0
  120. package/dist/pr-report/repo-settings.d.mts.map +1 -0
  121. package/dist/pr-report/repo-settings.mjs +22 -0
  122. package/dist/pr-report/repo-settings.mjs.map +1 -0
  123. package/dist/push-fixed-pr/index.d.mts +2 -0
  124. package/dist/push-fixed-pr/index.d.mts.map +1 -0
  125. package/dist/push-fixed-pr/index.mjs +2 -0
  126. package/dist/push-fixed-pr/index.mjs.map +1 -0
  127. package/dist/push-fixed-pr/push-fixed-pr.d.mts +54 -0
  128. package/dist/push-fixed-pr/push-fixed-pr.d.mts.map +1 -0
  129. package/dist/push-fixed-pr/push-fixed-pr.mjs +172 -0
  130. package/dist/push-fixed-pr/push-fixed-pr.mjs.map +1 -0
  131. package/dist/report-unowned-prs/index.d.mts +2 -0
  132. package/dist/report-unowned-prs/index.d.mts.map +1 -0
  133. package/dist/report-unowned-prs/index.mjs +2 -0
  134. package/dist/report-unowned-prs/index.mjs.map +1 -0
  135. package/dist/report-unowned-prs/report-unowned-prs.d.mts +49 -0
  136. package/dist/report-unowned-prs/report-unowned-prs.d.mts.map +1 -0
  137. package/dist/report-unowned-prs/report-unowned-prs.mjs +127 -0
  138. package/dist/report-unowned-prs/report-unowned-prs.mjs.map +1 -0
  139. package/dist/tsconfig.json +1 -0
  140. package/dist/unblock-prs/auto-fix.d.mts +45 -0
  141. package/dist/unblock-prs/auto-fix.d.mts.map +1 -0
  142. package/dist/unblock-prs/auto-fix.mjs +207 -0
  143. package/dist/unblock-prs/auto-fix.mjs.map +1 -0
  144. package/dist/unblock-prs/checks.d.mts +307 -0
  145. package/dist/unblock-prs/checks.d.mts.map +1 -0
  146. package/dist/unblock-prs/checks.mjs +372 -0
  147. package/dist/unblock-prs/checks.mjs.map +1 -0
  148. package/dist/unblock-prs/claims.d.mts +21 -0
  149. package/dist/unblock-prs/claims.d.mts.map +1 -0
  150. package/dist/unblock-prs/claims.mjs +23 -0
  151. package/dist/unblock-prs/claims.mjs.map +1 -0
  152. package/dist/unblock-prs/config.d.mts +487 -0
  153. package/dist/unblock-prs/config.d.mts.map +1 -0
  154. package/dist/unblock-prs/config.mjs +119 -0
  155. package/dist/unblock-prs/config.mjs.map +1 -0
  156. package/dist/unblock-prs/constants.d.mts +28 -0
  157. package/dist/unblock-prs/constants.d.mts.map +1 -0
  158. package/dist/unblock-prs/constants.mjs +28 -0
  159. package/dist/unblock-prs/constants.mjs.map +1 -0
  160. package/dist/unblock-prs/demotions.d.mts +42 -0
  161. package/dist/unblock-prs/demotions.d.mts.map +1 -0
  162. package/dist/unblock-prs/demotions.mjs +50 -0
  163. package/dist/unblock-prs/demotions.mjs.map +1 -0
  164. package/dist/unblock-prs/github.d.mts +739 -0
  165. package/dist/unblock-prs/github.d.mts.map +1 -0
  166. package/dist/unblock-prs/github.mjs +626 -0
  167. package/dist/unblock-prs/github.mjs.map +1 -0
  168. package/dist/unblock-prs/index.d.mts +25 -0
  169. package/dist/unblock-prs/index.d.mts.map +1 -0
  170. package/dist/unblock-prs/index.mjs +25 -0
  171. package/dist/unblock-prs/index.mjs.map +1 -0
  172. package/dist/unblock-prs/labels.d.mts +49 -0
  173. package/dist/unblock-prs/labels.d.mts.map +1 -0
  174. package/dist/unblock-prs/labels.mjs +49 -0
  175. package/dist/unblock-prs/labels.mjs.map +1 -0
  176. package/dist/unblock-prs/main.d.mts +179 -0
  177. package/dist/unblock-prs/main.d.mts.map +1 -0
  178. package/dist/unblock-prs/main.mjs +640 -0
  179. package/dist/unblock-prs/main.mjs.map +1 -0
  180. package/dist/unblock-prs/merge-after.d.mts +46 -0
  181. package/dist/unblock-prs/merge-after.d.mts.map +1 -0
  182. package/dist/unblock-prs/merge-after.mjs +65 -0
  183. package/dist/unblock-prs/merge-after.mjs.map +1 -0
  184. package/dist/unblock-prs/options.d.mts +35 -0
  185. package/dist/unblock-prs/options.d.mts.map +1 -0
  186. package/dist/unblock-prs/options.mjs +88 -0
  187. package/dist/unblock-prs/options.mjs.map +1 -0
  188. package/dist/unblock-prs/owner.d.mts +32 -0
  189. package/dist/unblock-prs/owner.d.mts.map +1 -0
  190. package/dist/unblock-prs/owner.mjs +63 -0
  191. package/dist/unblock-prs/owner.mjs.map +1 -0
  192. package/dist/unblock-prs/quiet.d.mts +21 -0
  193. package/dist/unblock-prs/quiet.d.mts.map +1 -0
  194. package/dist/unblock-prs/quiet.mjs +42 -0
  195. package/dist/unblock-prs/quiet.mjs.map +1 -0
  196. package/dist/unblock-prs/rebase.d.mts +120 -0
  197. package/dist/unblock-prs/rebase.d.mts.map +1 -0
  198. package/dist/unblock-prs/rebase.mjs +576 -0
  199. package/dist/unblock-prs/rebase.mjs.map +1 -0
  200. package/dist/unblock-prs/release.d.mts +62 -0
  201. package/dist/unblock-prs/release.d.mts.map +1 -0
  202. package/dist/unblock-prs/release.mjs +94 -0
  203. package/dist/unblock-prs/release.mjs.map +1 -0
  204. package/dist/unblock-prs/set-aside-comment.d.mts +20 -0
  205. package/dist/unblock-prs/set-aside-comment.d.mts.map +1 -0
  206. package/dist/unblock-prs/set-aside-comment.mjs +93 -0
  207. package/dist/unblock-prs/set-aside-comment.mjs.map +1 -0
  208. package/dist/unblock-prs/set-aside-detail.d.mts +41 -0
  209. package/dist/unblock-prs/set-aside-detail.d.mts.map +1 -0
  210. package/dist/unblock-prs/set-aside-detail.mjs +56 -0
  211. package/dist/unblock-prs/set-aside-detail.mjs.map +1 -0
  212. package/dist/unblock-prs/skips.d.mts +85 -0
  213. package/dist/unblock-prs/skips.d.mts.map +1 -0
  214. package/dist/unblock-prs/skips.mjs +141 -0
  215. package/dist/unblock-prs/skips.mjs.map +1 -0
  216. package/dist/unblock-prs/stack.d.mts +98 -0
  217. package/dist/unblock-prs/stack.d.mts.map +1 -0
  218. package/dist/unblock-prs/stack.mjs +141 -0
  219. package/dist/unblock-prs/stack.mjs.map +1 -0
  220. package/dist/unblock-prs/triage.d.mts +28 -0
  221. package/dist/unblock-prs/triage.d.mts.map +1 -0
  222. package/dist/unblock-prs/triage.mjs +396 -0
  223. package/dist/unblock-prs/triage.mjs.map +1 -0
  224. package/dist/unblock-prs/types.d.mts +793 -0
  225. package/dist/unblock-prs/types.d.mts.map +1 -0
  226. package/dist/unblock-prs/types.mjs +26 -0
  227. package/dist/unblock-prs/types.mjs.map +1 -0
  228. package/dist/unblock-prs/util.d.mts +23 -0
  229. package/dist/unblock-prs/util.d.mts.map +1 -0
  230. package/dist/unblock-prs/util.mjs +60 -0
  231. package/dist/unblock-prs/util.mjs.map +1 -0
  232. package/dist/unblock-prs/version-pr.d.mts +57 -0
  233. package/dist/unblock-prs/version-pr.d.mts.map +1 -0
  234. package/dist/unblock-prs/version-pr.mjs +79 -0
  235. package/dist/unblock-prs/version-pr.mjs.map +1 -0
  236. package/dist/unblock-prs/watch.d.mts +17 -0
  237. package/dist/unblock-prs/watch.d.mts.map +1 -0
  238. package/dist/unblock-prs/watch.mjs +128 -0
  239. package/dist/unblock-prs/watch.mjs.map +1 -0
  240. package/package.json +118 -3
  241. package/src/claim-pr/claim-pr.mts +380 -0
  242. package/src/claim-pr/claim-pr.test.mts +176 -0
  243. package/src/claim-pr/index.mts +1 -0
  244. package/src/cmd/claim-pr.mts +32 -0
  245. package/src/cmd/open-pr.mts +28 -0
  246. package/src/cmd/pr-report.mts +28 -0
  247. package/src/cmd/push-fixed-pr.mts +36 -0
  248. package/src/cmd/report-unowned-prs.mts +37 -0
  249. package/src/cmd/unblock-prs.mts +24 -0
  250. package/src/entry-point.mts +3 -0
  251. package/src/lib/cli-args.mts +86 -0
  252. package/src/lib/cli-args.test.mts +92 -0
  253. package/src/lib/github-api.mts +463 -0
  254. package/src/lib/github-api.test.mts +40 -0
  255. package/src/lib/index.mts +6 -0
  256. package/src/lib/json.mts +43 -0
  257. package/src/lib/json.test.mts +34 -0
  258. package/src/lib/log.mts +19 -0
  259. package/src/lib/log.test.mts +45 -0
  260. package/src/lib/shell.mts +52 -0
  261. package/src/lib/time.mts +49 -0
  262. package/src/open-pr/README.md +237 -0
  263. package/src/open-pr/github.mts +444 -0
  264. package/src/open-pr/index.mts +5 -0
  265. package/src/open-pr/main.mts +327 -0
  266. package/src/open-pr/options.mts +101 -0
  267. package/src/open-pr/options.test.mts +67 -0
  268. package/src/open-pr/steps.mts +57 -0
  269. package/src/open-pr/steps.test.mts +59 -0
  270. package/src/open-pr/types.mts +42 -0
  271. package/src/pr-report/README.md +244 -0
  272. package/src/pr-report/github.mts +911 -0
  273. package/src/pr-report/index.mts +7 -0
  274. package/src/pr-report/linked-issues.mts +51 -0
  275. package/src/pr-report/linked-issues.test.mts +65 -0
  276. package/src/pr-report/main.mts +257 -0
  277. package/src/pr-report/options.mts +140 -0
  278. package/src/pr-report/queue.mts +125 -0
  279. package/src/pr-report/render.mts +428 -0
  280. package/src/pr-report/render.test.mts +379 -0
  281. package/src/pr-report/repo-settings.mts +34 -0
  282. package/src/push-fixed-pr/index.mts +1 -0
  283. package/src/push-fixed-pr/push-fixed-pr.mts +276 -0
  284. package/src/push-fixed-pr/push-fixed-pr.test.mts +228 -0
  285. package/src/report-unowned-prs/index.mts +1 -0
  286. package/src/report-unowned-prs/report-unowned-prs.mts +190 -0
  287. package/src/report-unowned-prs/report-unowned-prs.test.mts +87 -0
  288. package/src/unblock-prs/README.md +1616 -0
  289. package/src/unblock-prs/auto-fix.mts +334 -0
  290. package/src/unblock-prs/auto-fix.test.mts +159 -0
  291. package/src/unblock-prs/checks.mts +538 -0
  292. package/src/unblock-prs/checks.test.mts +507 -0
  293. package/src/unblock-prs/claims.mts +33 -0
  294. package/src/unblock-prs/claims.test.mts +64 -0
  295. package/src/unblock-prs/config.mts +187 -0
  296. package/src/unblock-prs/config.schema.json +71 -0
  297. package/src/unblock-prs/config.test.mts +139 -0
  298. package/src/unblock-prs/constants.mts +33 -0
  299. package/src/unblock-prs/demotions.mts +73 -0
  300. package/src/unblock-prs/demotions.test.mts +100 -0
  301. package/src/unblock-prs/github.mts +1042 -0
  302. package/src/unblock-prs/github.test.mts +84 -0
  303. package/src/unblock-prs/index.mts +24 -0
  304. package/src/unblock-prs/labels.mts +70 -0
  305. package/src/unblock-prs/main.mts +1051 -0
  306. package/src/unblock-prs/merge-after.mts +117 -0
  307. package/src/unblock-prs/merge-after.test.mts +120 -0
  308. package/src/unblock-prs/options.mts +157 -0
  309. package/src/unblock-prs/owner.mts +91 -0
  310. package/src/unblock-prs/owner.test.mts +114 -0
  311. package/src/unblock-prs/quiet.mts +62 -0
  312. package/src/unblock-prs/quiet.test.mts +175 -0
  313. package/src/unblock-prs/rebase.mts +962 -0
  314. package/src/unblock-prs/release.mts +149 -0
  315. package/src/unblock-prs/release.test.mts +209 -0
  316. package/src/unblock-prs/set-aside-comment.mts +143 -0
  317. package/src/unblock-prs/set-aside-comment.test.mts +148 -0
  318. package/src/unblock-prs/set-aside-detail.mts +82 -0
  319. package/src/unblock-prs/set-aside-detail.test.mts +131 -0
  320. package/src/unblock-prs/skips.mts +215 -0
  321. package/src/unblock-prs/skips.test.mts +280 -0
  322. package/src/unblock-prs/stack.mts +220 -0
  323. package/src/unblock-prs/stack.test.mts +449 -0
  324. package/src/unblock-prs/triage.mts +669 -0
  325. package/src/unblock-prs/triage.test.mts +100 -0
  326. package/src/unblock-prs/types.mts +358 -0
  327. package/src/unblock-prs/util.mts +78 -0
  328. package/src/unblock-prs/util.test.mts +26 -0
  329. package/src/unblock-prs/version-pr.mts +112 -0
  330. package/src/unblock-prs/version-pr.test.mts +78 -0
  331. package/src/unblock-prs/watch.mts +219 -0
@@ -0,0 +1,444 @@
1
+ /** Everything that talks to GitHub or to `git`, and nothing that decides. */
2
+
3
+ import dedent from 'dedent';
4
+ import {
5
+ closingKeywordIssuesIn,
6
+ inheritStartAfter,
7
+ isSafeRefName,
8
+ parseMergeAfter,
9
+ waitsFor,
10
+ } from 'pr-report-core';
11
+ import { Arr, Result } from 'ts-data-forge';
12
+ import * as t from 'ts-fortress';
13
+ import {
14
+ git,
15
+ graphql,
16
+ resolveRepo,
17
+ resolveToken,
18
+ rest,
19
+ sh,
20
+ type ApiContext,
21
+ } from '../lib/index.mjs';
22
+ import {
23
+ PullRequestListResponseSchema,
24
+ PullRequestResponseSchema,
25
+ toPullRequest,
26
+ type PullRequest,
27
+ } from './types.mjs';
28
+
29
+ /**
30
+ * There is a credential and a repository to use it on, and the branch about
31
+ * to be pushed is not the branch everything targets.
32
+ */
33
+ export const preflight = async (): Promise<
34
+ Result<
35
+ Readonly<{ api: ApiContext; branch: string; defaultBranch: string }>,
36
+ string
37
+ >
38
+ > => {
39
+ const token = await resolveToken();
40
+
41
+ if (Result.isErr(token)) {
42
+ return token;
43
+ }
44
+
45
+ const repo = await resolveRepo();
46
+
47
+ if (Result.isErr(repo)) {
48
+ return repo;
49
+ }
50
+
51
+ const api: ApiContext = { repo: repo.value, token: token.value } as const;
52
+
53
+ const branch = await readRefName('git rev-parse --abbrev-ref HEAD');
54
+
55
+ if (Result.isErr(branch)) {
56
+ return branch;
57
+ }
58
+
59
+ if (branch.value === 'HEAD') {
60
+ return Result.err('HEAD is detached; check a branch out first');
61
+ }
62
+
63
+ const repository = await rest(
64
+ api,
65
+ 'GET',
66
+ '',
67
+ t.record({ default_branch: t.string() }),
68
+ );
69
+
70
+ if (Result.isErr(repository)) {
71
+ return Result.err(`cannot read the repository: ${repository.value}`);
72
+ }
73
+
74
+ const defaultBranch = repository.value.default_branch;
75
+
76
+ if (branch.value === defaultBranch) {
77
+ return Result.err(
78
+ `the current branch is ${branch.value}, which is what pull requests target; make a branch first`,
79
+ );
80
+ }
81
+
82
+ return Result.ok({ api, branch: branch.value, defaultBranch });
83
+ };
84
+
85
+ /** The subject of the branch tip, which is the default pull request title. */
86
+ export const lastCommitSubject = async (): Promise<Result<string, string>> => {
87
+ const subject = await git('git log -1 --format=%s');
88
+
89
+ if (Result.isErr(subject)) {
90
+ return subject;
91
+ }
92
+
93
+ const trimmed = subject.value.trim();
94
+
95
+ return trimmed === ''
96
+ ? Result.err('the branch tip has no commit subject to use as a title')
97
+ : Result.ok(trimmed);
98
+ };
99
+
100
+ /** Pushes and sets the upstream, so the branch exists to open against. */
101
+ export const pushBranch = async (
102
+ branch: string,
103
+ ): Promise<Result<undefined, string>> => {
104
+ const pushed = await git(`git push -u origin ${sh(branch)}`);
105
+
106
+ return Result.isErr(pushed) ? pushed : Result.ok(undefined);
107
+ };
108
+
109
+ /**
110
+ * The open pull request for this branch, or `undefined`.
111
+ *
112
+ * `head` is qualified with the owner. A bare branch name matches the same
113
+ * name on a fork, and this command must not mistake someone else's pull
114
+ * request for its own.
115
+ */
116
+ export const findOpenPullRequest = async (
117
+ api: ApiContext,
118
+ branch: string,
119
+ ): Promise<Result<PullRequest | undefined, string>> => {
120
+ const listed = await rest(
121
+ api,
122
+ 'GET',
123
+ '/pulls',
124
+ PullRequestListResponseSchema,
125
+ {
126
+ query: {
127
+ state: 'open',
128
+ per_page: '1',
129
+ head: `${api.repo.owner}:${branch}`,
130
+ },
131
+ },
132
+ );
133
+
134
+ if (Result.isErr(listed)) {
135
+ return listed;
136
+ }
137
+
138
+ const [found] = listed.value;
139
+
140
+ // `noUncheckedIndexedAccess` makes this `… | undefined`, which is the
141
+ // answer: there is one open pull request for the branch, or none.
142
+ return Result.ok(found === undefined ? undefined : toPullRequest(found));
143
+ };
144
+
145
+ /**
146
+ * The open pull request whose branch `base` is, when a new one is to be
147
+ * stacked on it: exactly one, from this repository. Anything else is not a
148
+ * stack `unblock-prs` or the reports would recognize, and is refused rather
149
+ * than opened.
150
+ */
151
+ export const findStackParent = async (
152
+ api: ApiContext,
153
+ base: string,
154
+ ): Promise<Result<number, string>> => {
155
+ const listed = await rest(
156
+ api,
157
+ 'GET',
158
+ '/pulls',
159
+ PullRequestListResponseSchema,
160
+ {
161
+ query: {
162
+ state: 'open',
163
+ per_page: '2',
164
+ head: `${api.repo.owner}:${base}`,
165
+ },
166
+ },
167
+ );
168
+
169
+ if (Result.isErr(listed)) {
170
+ return listed;
171
+ }
172
+
173
+ const [parent, second] = listed.value;
174
+
175
+ return parent === undefined
176
+ ? Result.err(
177
+ `${base} is neither the default branch nor the branch of an open pull request; a stacked pull request targets the branch of the one below it`,
178
+ )
179
+ : second === undefined
180
+ ? Result.ok(parent.number)
181
+ : Result.err(
182
+ `${base} is the branch of more than one open pull request (#${parent.number}, #${second.number}), so which one this is stacked on cannot be told`,
183
+ );
184
+ };
185
+
186
+ /**
187
+ * Whether the branch contains the tip of `base`, as a layer stacked on it
188
+ * has to: one that does not shows the layer below's old commits in its diff.
189
+ */
190
+ export const containsBase = async (
191
+ base: string,
192
+ ): Promise<Result<undefined, string>> => {
193
+ const fetched = await git(
194
+ `git fetch --quiet origin ${sh(`+refs/heads/${base}:refs/remotes/origin/${base}`)}`,
195
+ );
196
+
197
+ if (Result.isErr(fetched)) {
198
+ return Result.err(`cannot fetch ${base}: ${fetched.value}`);
199
+ }
200
+
201
+ const contained = await git(
202
+ `git merge-base --is-ancestor ${sh(`origin/${base}`)} HEAD`,
203
+ );
204
+
205
+ return Result.isErr(contained)
206
+ ? Result.err(
207
+ `the branch does not contain the tip of ${base}; rebase it onto origin/${base} first, or its diff will carry that layer's old commits`,
208
+ )
209
+ : Result.ok(undefined);
210
+ };
211
+
212
+ export const viewPullRequest = async (
213
+ api: ApiContext,
214
+ prNumber: number,
215
+ ): Promise<Result<PullRequest, string>> => {
216
+ const viewed = await rest(
217
+ api,
218
+ 'GET',
219
+ `/pulls/${prNumber}`,
220
+ PullRequestResponseSchema,
221
+ );
222
+
223
+ return Result.isErr(viewed) ? viewed : Result.ok(toPullRequest(viewed.value));
224
+ };
225
+
226
+ /**
227
+ * Creates it ready for review — never a draft, because `unblock-prs` passes a
228
+ * draft over, and queueing a pull request is meant to be `merge-queued` and
229
+ * nothing else.
230
+ */
231
+ export const createPullRequest = async ({
232
+ api,
233
+ branch,
234
+ base,
235
+ title,
236
+ body,
237
+ }: Readonly<{
238
+ api: ApiContext;
239
+ branch: string;
240
+ base: string;
241
+ title: string;
242
+ body: string;
243
+ }>): Promise<Result<number, string>> => {
244
+ const created = await rest(
245
+ api,
246
+ 'POST',
247
+ '/pulls',
248
+ t.record({ number: t.number() }),
249
+ { body: { head: branch, base, title, body, draft: false } },
250
+ );
251
+
252
+ return Result.isErr(created) ? created : Result.ok(created.value.number);
253
+ };
254
+
255
+ /**
256
+ * What the issues the pull request closes start after, as open pull
257
+ * requests it should merge after (`pr-report-core`'s `inheritStartAfter`),
258
+ * less what its body or `alsoDeclared` — the layer it is stacked on — already
259
+ * says. The readers derive the same each time they read, so this is the
260
+ * body stating it, as it states a stack.
261
+ *
262
+ * Closing issues are read from the keywords GitHub marked in the body, which
263
+ * it marks whatever the base; one linked by hand in the sidebar is not seen.
264
+ */
265
+ export const missingInheritedOrder = async (
266
+ api: ApiContext,
267
+ prNumber: number,
268
+ alsoDeclared: readonly number[],
269
+ ): Promise<
270
+ Result<Readonly<{ body: string; missing: readonly number[] }>, string>
271
+ > => {
272
+ const viewed = await rest(api, 'GET', `/pulls/${prNumber}`, BodiesSchema, {
273
+ withBodyHtml: true,
274
+ });
275
+
276
+ if (Result.isErr(viewed)) {
277
+ return viewed;
278
+ }
279
+
280
+ const body = viewed.value.body ?? '';
281
+
282
+ const closes = closingKeywordIssuesIn(api.repo, viewed.value.body_html ?? '');
283
+
284
+ const mut_startAfter = new Map<number, readonly number[]>();
285
+
286
+ for (const issue of closes) {
287
+ const read = await rest(api, 'GET', `/issues/${issue}`, IssueSchema);
288
+
289
+ if (Result.isErr(read)) {
290
+ return read;
291
+ }
292
+
293
+ // A closed issue holds nothing, and a pull request declares no start.
294
+ if (read.value.state !== 'open' || read.value.pull_request !== undefined) {
295
+ continue;
296
+ }
297
+
298
+ const blockedBy = await blockedByOf(api, issue, read.value);
299
+
300
+ if (Result.isErr(blockedBy)) {
301
+ return blockedBy;
302
+ }
303
+
304
+ mut_startAfter.set(issue, waitsFor(read.value.body ?? '', blockedBy.value));
305
+ }
306
+
307
+ if (!Array.from(mut_startAfter.values()).some(Arr.isNonEmpty)) {
308
+ return Result.ok({ body, missing: [] });
309
+ }
310
+
311
+ const open = await rest(api, 'GET', '/pulls', BodiesListSchema, {
312
+ query: { state: 'open', per_page: '100' },
313
+ withBodyHtml: true,
314
+ });
315
+
316
+ if (Result.isErr(open)) {
317
+ return open;
318
+ }
319
+
320
+ const others = open.value.filter(({ number }) => number !== prNumber);
321
+
322
+ const inherited = inheritStartAfter({
323
+ pulls: Arr.toUnshifted({
324
+ number: prNumber,
325
+ closes: Array.from(mut_startAfter.keys()),
326
+ })(
327
+ others.map(({ number, body_html: html }) => ({
328
+ number,
329
+ closes: closingKeywordIssuesIn(api.repo, html ?? ''),
330
+ })),
331
+ ),
332
+ startAfterOf: mut_startAfter,
333
+ openIssues: new Set(),
334
+ });
335
+
336
+ const declared = new Set(parseMergeAfter(body).concat(alsoDeclared));
337
+
338
+ return Result.ok({
339
+ body,
340
+ missing: (inherited.get(prNumber)?.mergeAfter ?? []).filter(
341
+ (number) => !declared.has(number),
342
+ ),
343
+ });
344
+ };
345
+
346
+ export const updateBody = async (
347
+ api: ApiContext,
348
+ prNumber: number,
349
+ body: string,
350
+ ): Promise<Result<undefined, string>> => {
351
+ const updated = await rest(api, 'PATCH', `/pulls/${prNumber}`, t.unknown(), {
352
+ body: { body },
353
+ });
354
+
355
+ return Result.isErr(updated) ? updated : Result.ok(undefined);
356
+ };
357
+
358
+ const BodiesSchema = t.record({
359
+ number: t.number(),
360
+ body: t.union([t.string(), t.nullType]),
361
+ body_html: t.optional(t.union([t.string(), t.nullType])),
362
+ });
363
+
364
+ const BodiesListSchema = t.array(BodiesSchema);
365
+
366
+ /** An issue, or a pull request, which the issues route answers for too. */
367
+ const IssueSchema = t.record({
368
+ state: t.string(),
369
+ body: t.union([t.string(), t.nullType]),
370
+ pull_request: t.optional(t.record({})),
371
+ /** How many issues block it; absent where GitHub does not send it. */
372
+ issue_dependencies_summary: t.optional(
373
+ t.record({ total_blocked_by: t.number() }),
374
+ ),
375
+ });
376
+
377
+ /**
378
+ * This repository's issues GitHub records as blocking `issue`, read as its
379
+ * `Start-After:` is (`waitsFor`); asked only when its summary says there are
380
+ * any.
381
+ */
382
+ const blockedByOf = async (
383
+ api: ApiContext,
384
+ issue: number,
385
+ read: t.TypeOf<typeof IssueSchema>,
386
+ ): Promise<Result<readonly number[], string>> => {
387
+ if ((read.issue_dependencies_summary?.total_blocked_by ?? 0) === 0) {
388
+ return Result.ok([]);
389
+ }
390
+
391
+ const listed = await rest(
392
+ api,
393
+ 'GET',
394
+ `/issues/${issue}/dependencies/blocked_by`,
395
+ t.array(t.record({ number: t.number(), repository_url: t.string() })),
396
+ { query: { per_page: '100' } },
397
+ );
398
+
399
+ const own =
400
+ `https://api.github.com/repos/${api.repo.owner}/${api.repo.name}`.toLowerCase();
401
+
402
+ return Result.map(listed, (blocking) =>
403
+ blocking
404
+ .filter(({ repository_url: url }) => url.toLowerCase() === own)
405
+ .map(({ number }) => number),
406
+ );
407
+ };
408
+
409
+ /**
410
+ * GraphQL: the REST API cannot take a pull request out of draft. The one
411
+ * GraphQL call this command makes, and only for a pull request someone
412
+ * opened as a draft before running it.
413
+ */
414
+ export const markReady = async (
415
+ api: ApiContext,
416
+ nodeId: string,
417
+ ): Promise<Result<undefined, string>> =>
418
+ graphql(
419
+ api,
420
+ dedent`
421
+ mutation ($id: ID!) {
422
+ markPullRequestReadyForReview(input: { pullRequestId: $id }) {
423
+ clientMutationId
424
+ }
425
+ }
426
+ `,
427
+ { id: nodeId },
428
+ );
429
+
430
+ const readRefName = async (
431
+ command: string,
432
+ ): Promise<Result<string, string>> => {
433
+ const read = await git(command);
434
+
435
+ if (Result.isErr(read)) {
436
+ return Result.err(`${command} failed: ${read.value}`);
437
+ }
438
+
439
+ const name = read.value.trim();
440
+
441
+ return isSafeRefName(name)
442
+ ? Result.ok(name)
443
+ : Result.err(`unexpected branch name: ${JSON.stringify(name)}`);
444
+ };
@@ -0,0 +1,5 @@
1
+ export * from './github.mjs';
2
+ export * from './main.mjs';
3
+ export * from './options.mjs';
4
+ export * from './steps.mjs';
5
+ export * from './types.mjs';