pr-report-core 0.1.0 → 0.2.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/dist/checks.d.mts.map +1 -1
- package/dist/checks.mjs +8 -8
- package/dist/checks.mjs.map +1 -1
- package/dist/code-owners.d.mts +24 -24
- package/dist/code-owners.d.mts.map +1 -1
- package/dist/code-owners.mjs +42 -42
- package/dist/code-owners.mjs.map +1 -1
- package/dist/merge-after.d.mts.map +1 -1
- package/dist/merge-after.mjs +13 -13
- package/dist/merge-after.mjs.map +1 -1
- package/dist/queue-order.d.mts +14 -1
- package/dist/queue-order.d.mts.map +1 -1
- package/dist/queue-order.mjs +34 -17
- package/dist/queue-order.mjs.map +1 -1
- package/dist/queue-owner.d.mts +17 -17
- package/dist/queue-owner.d.mts.map +1 -1
- package/dist/queue-owner.mjs +51 -51
- package/dist/queue-owner.mjs.map +1 -1
- package/dist/report.d.mts +5 -5
- package/dist/report.d.mts.map +1 -1
- package/dist/report.mjs +23 -23
- package/dist/report.mjs.map +1 -1
- package/package.json +5 -5
- package/src/checks.mts +10 -10
- package/src/code-owners.mts +81 -81
- package/src/merge-after.mts +23 -23
- package/src/queue-order.mts +49 -27
- package/src/queue-order.test.mts +37 -0
- package/src/queue-owner.mts +84 -84
- package/src/report.mts +43 -43
package/dist/queue-owner.mjs
CHANGED
|
@@ -43,56 +43,6 @@ export const SET_ASIDE_REASONS = [
|
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const setAsideOwner = (reason) => {
|
|
51
|
-
switch (reason) {
|
|
52
|
-
case 'checks-failed':
|
|
53
|
-
case 'rebase-failed':
|
|
54
|
-
return { owner: 'skill', why: 'the skill fixes it' };
|
|
55
|
-
case 'already-in-base':
|
|
56
|
-
return {
|
|
57
|
-
owner: 'human',
|
|
58
|
-
why: 'every commit is already in the base; close it',
|
|
59
|
-
};
|
|
60
|
-
case 'arm-failed':
|
|
61
|
-
return {
|
|
62
|
-
owner: 'human',
|
|
63
|
-
why: 'auto-merge could not be armed; a setting or a permission holds it',
|
|
64
|
-
};
|
|
65
|
-
case 'awaiting-bypass':
|
|
66
|
-
return {
|
|
67
|
-
owner: 'human',
|
|
68
|
-
why: 'green, and only a ruleset bypass merges it',
|
|
69
|
-
};
|
|
70
|
-
case 'not-merging':
|
|
71
|
-
return {
|
|
72
|
-
owner: 'human',
|
|
73
|
-
why: 'green but not merging; something the worker cannot read holds it',
|
|
74
|
-
};
|
|
75
|
-
case 'push-failed':
|
|
76
|
-
return {
|
|
77
|
-
owner: 'human',
|
|
78
|
-
why: 'the push was refused; a setting or a permission holds it',
|
|
79
|
-
};
|
|
80
|
-
case 'unlabel-failed':
|
|
81
|
-
return {
|
|
82
|
-
owner: 'human',
|
|
83
|
-
why: '`skip-ci` could not be taken off; a permission holds it',
|
|
84
|
-
};
|
|
85
|
-
case 'watch-timeout':
|
|
86
|
-
return {
|
|
87
|
-
owner: 'human',
|
|
88
|
-
why: 'its checks ran past the watch; look at the run',
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
46
|
export const queueOwner = (pr) => {
|
|
97
47
|
const queued = pr.labels.includes(MERGE_QUEUED_LABEL);
|
|
98
48
|
if (!queued &&
|
|
@@ -164,6 +114,57 @@ export const queueOwnerOfEntry = (entry, comments, context) => queueOwner({
|
|
|
164
114
|
claim: comments.claim?.says,
|
|
165
115
|
nowEpochMs: context.nowEpochMs,
|
|
166
116
|
});
|
|
117
|
+
export const isSetAsideReason = (reason) => KNOWN_REASONS.has(reason);
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
const setAsideOwner = (reason) => {
|
|
127
|
+
switch (reason) {
|
|
128
|
+
case 'checks-failed':
|
|
129
|
+
case 'rebase-failed':
|
|
130
|
+
return { owner: 'skill', why: 'the skill fixes it' };
|
|
131
|
+
case 'already-in-base':
|
|
132
|
+
return {
|
|
133
|
+
owner: 'human',
|
|
134
|
+
why: 'every commit is already in the base; close it',
|
|
135
|
+
};
|
|
136
|
+
case 'arm-failed':
|
|
137
|
+
return {
|
|
138
|
+
owner: 'human',
|
|
139
|
+
why: 'auto-merge could not be armed; a setting or a permission holds it',
|
|
140
|
+
};
|
|
141
|
+
case 'awaiting-bypass':
|
|
142
|
+
return {
|
|
143
|
+
owner: 'human',
|
|
144
|
+
why: 'green, and only a ruleset bypass merges it',
|
|
145
|
+
};
|
|
146
|
+
case 'not-merging':
|
|
147
|
+
return {
|
|
148
|
+
owner: 'human',
|
|
149
|
+
why: 'green but not merging; something the worker cannot read holds it',
|
|
150
|
+
};
|
|
151
|
+
case 'push-failed':
|
|
152
|
+
return {
|
|
153
|
+
owner: 'human',
|
|
154
|
+
why: 'the push was refused; a setting or a permission holds it',
|
|
155
|
+
};
|
|
156
|
+
case 'unlabel-failed':
|
|
157
|
+
return {
|
|
158
|
+
owner: 'human',
|
|
159
|
+
why: '`skip-ci` could not be taken off; a permission holds it',
|
|
160
|
+
};
|
|
161
|
+
case 'watch-timeout':
|
|
162
|
+
return {
|
|
163
|
+
owner: 'human',
|
|
164
|
+
why: 'its checks ran past the watch; look at the run',
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
};
|
|
167
168
|
|
|
168
169
|
|
|
169
170
|
|
|
@@ -173,7 +174,6 @@ const standingAt = (comment, headSha) => comment?.kind === 'standing' &&
|
|
|
173
174
|
comment.setAside.headSha === headSha
|
|
174
175
|
? comment.setAside
|
|
175
176
|
: undefined;
|
|
176
|
-
export const isSetAsideReason = (reason) => KNOWN_REASONS.has(reason);
|
|
177
177
|
const KNOWN_REASONS = new Set(SET_ASIDE_REASONS);
|
|
178
178
|
|
|
179
179
|
|
package/dist/queue-owner.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queue-owner.mjs","sourceRoot":"","sources":["../src/queue-owner.mts"],"names":[],"mappings":"AAAA,iCAAiC;AAEjC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,YAAY,EAAqB,MAAM,aAAa,CAAC;AAC9D,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,aAAa,GACd,MAAM,cAAc,CAAC;AAKtB,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAU,CAAC;AAa1E,+DAA+D;AAC/D,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,iBAAiB;IACjB,YAAY;IACZ,iBAAiB;IACjB,eAAe;IACf,aAAa;IACb,aAAa;IACb,eAAe;IACf,gBAAgB;IAChB,eAAe;CACP,CAAC;AAIX
|
|
1
|
+
{"version":3,"file":"queue-owner.mjs","sourceRoot":"","sources":["../src/queue-owner.mts"],"names":[],"mappings":"AAAA,iCAAiC;AAEjC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,YAAY,EAAqB,MAAM,aAAa,CAAC;AAC9D,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,aAAa,GACd,MAAM,cAAc,CAAC;AAKtB,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAU,CAAC;AAa1E,+DAA+D;AAC/D,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,iBAAiB;IACjB,YAAY;IACZ,iBAAiB;IACjB,eAAe;IACf,aAAa;IACb,aAAa;IACb,eAAe;IACf,gBAAgB;IAChB,eAAe;CACP,CAAC;AAIX;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,EAAmB,EAA0B,EAAE;IACxE,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;IAEtD,IACE,CAAC,MAAM;QACP,CAAC,CACC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,CAC3E,EACD,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IACE,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,UAAU;QAC7B,YAAY,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,EACvD,CAAC;QACD,OAAO;YACL,KAAK,EAAE,OAAO;YACd,MAAM,EAAE,cAAc,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE;SAClE,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,eAAe,CAAC,EAAE,CAAC,CAAC;IAEtC,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAC/C,CAAC;IAED,2EAA2E;IAC3E,gCAAgC;IAChC,IAAI,EAAE,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO;YACL,KAAK,EAAE,QAAQ;YACf,MAAM,EAAE,eAAe,EAAE,CAAC,SAAS,+BAA+B;SACnE,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC;IAErD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,OAAO;YACL,KAAK,EAAE,QAAQ;YACf,MAAM,EAAE,MAAM;gBACZ,CAAC,CAAC,+CAA+C;gBACjD,CAAC,CAAC,6CAA6C;SAClD,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACvC,OAAO;YACL,KAAK,EAAE,MAAM;YACb,MAAM,EAAE,mBAAmB,QAAQ,CAAC,MAAM,8EAA8E;SACzH,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAEtD,MAAM,IAAI,GAAG,eAAe,QAAQ,CAAC,MAAM,gBAAyB,CAAC;IAErE,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;QACtB,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,KAAK,GAAG,EAAE,EAAE,CAAC;IAC9C,CAAC;IAED,MAAM,SAAS,GAAG,YAAY,CAAC,EAAE,EAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE5D,OAAO,SAAS,KAAK,SAAS;QAC5B,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,KAAK,GAAG,EAAE,EAAE;QAC/C,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,KAAK,SAAS,EAAE,EAAE,CAAC;AAC1D,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,KAAkB,EAClB,QAAuB,EACvB,OAIE,EACsB,EAAE,CAC1B,UAAU,CAAC;IACT,MAAM,EAAE,KAAK,CAAC,MAAM;IACpB,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC;IAC5C,OAAO,EAAE,KAAK,CAAC,OAAO;IACtB,QAAQ,EAAE,KAAK,CAAC,QAAQ;IACxB,OAAO,EAAE,KAAK,CAAC,OAAO;IACtB,OAAO,EAAE,KAAK,CAAC,OAAO;IACtB,OAAO,EAAE,KAAK,CAAC,OAAO;IACtB,aAAa,EAAE,OAAO,CAAC,aAAa;IACpC,SAAS,EAAE,KAAK,CAAC,SAAS;IAC1B,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACrE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,IAAI;IACjC,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,IAAI;IAC3B,UAAU,EAAE,OAAO,CAAC,UAAU;CAC/B,CAAC,CAAC;AAEL,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,MAAc,EAA4B,EAAE,CAC3E,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAE5B;;;;;;;GAOG;AACH,MAAM,aAAa,GAAG,CACpB,MAAsB,EAC+B,EAAE;IACvD,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,eAAe,CAAC;QACrB,KAAK,eAAe;YAClB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,oBAAoB,EAAE,CAAC;QAEvD,KAAK,iBAAiB;YACpB,OAAO;gBACL,KAAK,EAAE,OAAO;gBACd,GAAG,EAAE,+CAA+C;aACrD,CAAC;QAEJ,KAAK,YAAY;YACf,OAAO;gBACL,KAAK,EAAE,OAAO;gBACd,GAAG,EAAE,mEAAmE;aACzE,CAAC;QAEJ,KAAK,iBAAiB;YACpB,OAAO;gBACL,KAAK,EAAE,OAAO;gBACd,GAAG,EAAE,4CAA4C;aAClD,CAAC;QAEJ,KAAK,aAAa;YAChB,OAAO;gBACL,KAAK,EAAE,OAAO;gBACd,GAAG,EAAE,kEAAkE;aACxE,CAAC;QAEJ,KAAK,aAAa;YAChB,OAAO;gBACL,KAAK,EAAE,OAAO;gBACd,GAAG,EAAE,0DAA0D;aAChE,CAAC;QAEJ,KAAK,gBAAgB;YACnB,OAAO;gBACL,KAAK,EAAE,OAAO;gBACd,GAAG,EAAE,yDAAyD;aAC/D,CAAC;QAEJ,KAAK,eAAe;YAClB,OAAO;gBACL,KAAK,EAAE,OAAO;gBACd,GAAG,EAAE,gDAAgD;aACtD,CAAC;IACN,CAAC;AACH,CAAC,CAAC;AAuBF;;;GAGG;AACH,MAAM,UAAU,GAAG,CACjB,OAAoC,EACpC,OAAe,EACO,EAAE,CACxB,OAAO,EAAE,IAAI,KAAK,UAAU;IAC5B,CAAC,OAAO,CAAC,KAAK;IACd,OAAO,CAAC,QAAQ,CAAC,OAAO,KAAK,OAAO;IAClC,CAAC,CAAC,OAAO,CAAC,QAAQ;IAClB,CAAC,CAAC,SAAS,CAAC;AAEhB,MAAM,aAAa,GAAwB,IAAI,GAAG,CAAC,iBAAiB,CAAC,CAAC;AAEtE;;;GAGG;AACH,MAAM,eAAe,GAAG,CAAC,EAAmB,EAAsB,EAAE,CAClE,EAAE,CAAC,OAAO;IACR,CAAC,CAAC,mCAAmC;IACrC,CAAC,CAAC,EAAE,CAAC,QAAQ;QACX,CAAC,CAAC,kDAAkD;QACpD,CAAC,CAAC,EAAE,CAAC,OAAO;YACV,CAAC,CAAC,yDAAyD;YAC3D,CAAC,CAAC,EAAE,CAAC,OAAO,KAAK,EAAE,CAAC,aAAa,IAAI,EAAE,CAAC,SAAS,KAAK,SAAS;gBAC7D,CAAC,CAAE,YAAY,EAAE,CAAC,OAAO,aAAa,EAAE,CAAC,aAAa,iDAA2D;gBACjH,CAAC,CAAC,SAAS,CAAC;AAEtB;;;;GAIG;AACH,MAAM,YAAY,GAAG,CACnB,EAAmB,EACnB,MAAsB,EACtB,MAAe,EACK,EAAE,CACtB,EAAE,CAAC,OAAO,KAAK,qBAAqB,EAAE,CAAC,aAAa,EAAE;IACpD,CAAC,CAAC,yEAAyE;IAC3E,CAAC,CAAC,MAAM,KAAK,eAAe,IAAI,CAAC,MAAM;QACrC,CAAC,CAAC,mDAAmD;QACrD,CAAC,CAAC,SAAS,CAAC"}
|
package/dist/report.d.mts
CHANGED
|
@@ -26,11 +26,6 @@ export declare const buildReport: ({ repo, defaultBranch, generatedAt, required,
|
|
|
26
26
|
*/
|
|
27
27
|
references?: ReadonlyMap<number, ReferenceStatus>;
|
|
28
28
|
}>) => PrReport;
|
|
29
|
-
export type DecidedEntries = Readonly<{
|
|
30
|
-
entries: readonly ReportEntry[];
|
|
31
|
-
roots: readonly TreeNode[];
|
|
32
|
-
cycles: readonly (readonly number[])[];
|
|
33
|
-
}>;
|
|
34
29
|
/**
|
|
35
30
|
* The part of a report that is decided rather than read: each open pull
|
|
36
31
|
* request's declared order, the stack it is in, its check verdict, and the
|
|
@@ -58,4 +53,9 @@ export declare const buildEntries: ({ required, defaultBranch, pulls, issues, sk
|
|
|
58
53
|
*/
|
|
59
54
|
skipCiPauses?: boolean;
|
|
60
55
|
}>) => DecidedEntries;
|
|
56
|
+
export type DecidedEntries = Readonly<{
|
|
57
|
+
entries: readonly ReportEntry[];
|
|
58
|
+
roots: readonly TreeNode[];
|
|
59
|
+
cycles: readonly (readonly number[])[];
|
|
60
|
+
}>;
|
|
61
61
|
//# sourceMappingURL=report.d.mts.map
|
package/dist/report.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.d.mts","sourceRoot":"","sources":["../src/report.mts"],"names":[],"mappings":"AAAA,yDAAyD;AAOzD,OAAO,EAML,KAAK,eAAe,EACrB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,OAAO,EACP,WAAW,EAEX,QAAQ,EACT,MAAM,aAAa,CAAC;AAErB;;;;;GAKG;AACH,eAAO,MAAM,WAAW,wJAarB,QAAQ,CAAC;IACV,IAAI,EAAE,OAAO,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,aAAa,EAAE,OAAO,CAAC;IACvB,KAAK,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACnC,MAAM,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACrC,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC;;;OAGG;IACH,UAAU,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CACnD,CAAC,KAAG,QAiBQ,CAAC;
|
|
1
|
+
{"version":3,"file":"report.d.mts","sourceRoot":"","sources":["../src/report.mts"],"names":[],"mappings":"AAAA,yDAAyD;AAOzD,OAAO,EAML,KAAK,eAAe,EACrB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,OAAO,EACP,WAAW,EAEX,QAAQ,EACT,MAAM,aAAa,CAAC;AAErB;;;;;GAKG;AACH,eAAO,MAAM,WAAW,wJAarB,QAAQ,CAAC;IACV,IAAI,EAAE,OAAO,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,aAAa,EAAE,OAAO,CAAC;IACvB,KAAK,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACnC,MAAM,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACrC,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC;;;OAGG;IACH,UAAU,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CACnD,CAAC,KAAG,QAiBQ,CAAC;AAEd;;;;GAIG;AACH,eAAO,MAAM,YAAY,8DAMtB,QAAQ,CAAC;IACV;;;;OAIG;IACH,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACnC;;;OAGG;IACH,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC,KAAG,cA6EJ,CAAC;AAuCF,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;IACpC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IAChC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;IAC3B,MAAM,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC;CACxC,CAAC,CAAC"}
|
package/dist/report.mjs
CHANGED
|
@@ -34,29 +34,6 @@ export const buildReport = ({ repo, defaultBranch, generatedAt, required, authen
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
const reportIssues = (issues, pulls, references) => {
|
|
39
|
-
const listed = new Set(issues.map(({ number }) => number));
|
|
40
|
-
const open = new Set(pulls.map(({ number }) => number));
|
|
41
|
-
const statusOf = (number) => listed.has(number) || open.has(number)
|
|
42
|
-
? 'waiting'
|
|
43
|
-
: (references.get(number) ?? 'unknown');
|
|
44
|
-
const states = decideStartStates(issues, statusOf);
|
|
45
|
-
const worked = issuesInProgress(pulls);
|
|
46
|
-
return issues.map((issue) => ({
|
|
47
|
-
...issue,
|
|
48
|
-
start: withProgress(states.get(issue.number) ?? 'free', progressOf(issue.labels, worked.has(issue.number))),
|
|
49
|
-
named: issue.startAfter.map((number) => ({
|
|
50
|
-
number,
|
|
51
|
-
status: statusOf(number),
|
|
52
|
-
})),
|
|
53
|
-
}));
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
37
|
export const buildEntries = ({ required, defaultBranch, pulls, issues = NO_DECLARATIONS, skipCiPauses = true, }) => {
|
|
61
38
|
const open = new Set(pulls.map(({ number }) => number));
|
|
62
39
|
const parents = findStackParents(pulls, defaultBranch);
|
|
@@ -113,6 +90,29 @@ export const buildEntries = ({ required, defaultBranch, pulls, issues = NO_DECLA
|
|
|
113
90
|
})));
|
|
114
91
|
return { entries, roots, cycles };
|
|
115
92
|
};
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
const reportIssues = (issues, pulls, references) => {
|
|
100
|
+
const listed = new Set(issues.map(({ number }) => number));
|
|
101
|
+
const open = new Set(pulls.map(({ number }) => number));
|
|
102
|
+
const statusOf = (number) => listed.has(number) || open.has(number)
|
|
103
|
+
? 'waiting'
|
|
104
|
+
: (references.get(number) ?? 'unknown');
|
|
105
|
+
const states = decideStartStates(issues, statusOf);
|
|
106
|
+
const worked = issuesInProgress(pulls);
|
|
107
|
+
return issues.map((issue) => ({
|
|
108
|
+
...issue,
|
|
109
|
+
start: withProgress(states.get(issue.number) ?? 'free', progressOf(issue.labels, worked.has(issue.number))),
|
|
110
|
+
named: issue.startAfter.map((number) => ({
|
|
111
|
+
number,
|
|
112
|
+
status: statusOf(number),
|
|
113
|
+
})),
|
|
114
|
+
}));
|
|
115
|
+
};
|
|
116
116
|
const NO_DECLARATIONS = {
|
|
117
117
|
startAfter: new Map(),
|
|
118
118
|
open: new Set(),
|
package/dist/report.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.mjs","sourceRoot":"","sources":["../src/report.mts"],"names":[],"mappings":"AAAA,yDAAyD;AAEzD,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACV,YAAY,GAEb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAanD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,EAC1B,IAAI,EACJ,aAAa,EACb,WAAW,EACX,QAAQ,EACR,aAAa,EACb,KAAK,EACL,MAAM,EACN,gBAAgB,EAChB,MAAM,EACN,WAAW,EACX,iBAAiB,EACjB,UAAU,GAAG,IAAI,GAAG,EAA2B,GAmB/C,EAAY,EAAE,CACd,CAAC;IACC,IAAI;IACJ,aAAa;IACb,WAAW;IACX,aAAa;IACb,QAAQ;IACR,GAAG,YAAY,CAAC;QACd,QAAQ;QACR,aAAa;QACb,KAAK;QACL,GAAG,CAAC,iBAAiB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC;KAC1E,CAAC;IACF,MAAM;IACN,gBAAgB;IAChB,MAAM,EAAE,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC;IAC/C,WAAW;CACZ,CAAU,CAAC;AAEd
|
|
1
|
+
{"version":3,"file":"report.mjs","sourceRoot":"","sources":["../src/report.mts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pr-report-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "What a GitHub merge queue driven by labels and `Merge-After:` trailers decides about the open pull requests — verdicts, merge order, stacks, owners — with no I/O, shared by the GitHub Pull Requests Manager page and the unblock-prs worker",
|
|
6
6
|
"keywords": [
|
|
@@ -49,10 +49,10 @@
|
|
|
49
49
|
"cspell": "10.4.0",
|
|
50
50
|
"dedent": "1.7.2",
|
|
51
51
|
"eslint": "10.13.0",
|
|
52
|
-
"eslint-config-typed": "7.1
|
|
53
|
-
"eslint-plugin-ts-data-forge": "0.10.
|
|
54
|
-
"eslint-plugin-ts-fortress": "1.3.
|
|
55
|
-
"eslint-plugin-ts-type-forge": "0.8.
|
|
52
|
+
"eslint-config-typed": "7.2.1",
|
|
53
|
+
"eslint-plugin-ts-data-forge": "0.10.6",
|
|
54
|
+
"eslint-plugin-ts-fortress": "1.3.11",
|
|
55
|
+
"eslint-plugin-ts-type-forge": "0.8.16",
|
|
56
56
|
"jiti": "2.7.0",
|
|
57
57
|
"npm-run-all2": "9.0.4",
|
|
58
58
|
"prettier": "3.9.10",
|
package/src/checks.mts
CHANGED
|
@@ -153,16 +153,6 @@ export const anyRunInProgress = (runs: readonly CheckRunReport[]): boolean =>
|
|
|
153
153
|
(run) => run.status !== 'completed' || run.round?.completed === false,
|
|
154
154
|
);
|
|
155
155
|
|
|
156
|
-
/**
|
|
157
|
-
* Which of two runs of one name GitHub would answer with: the one in the
|
|
158
|
-
* later-created suite, and within one suite the later run of it — a re-run
|
|
159
|
-
* keeps the suite and takes a new run id.
|
|
160
|
-
*/
|
|
161
|
-
const outranks = (run: CheckRunReport, previous: CheckRunReport): boolean =>
|
|
162
|
-
run.checkSuiteId === previous.checkSuiteId
|
|
163
|
-
? run.id > previous.id
|
|
164
|
-
: run.checkSuiteId > previous.checkSuiteId;
|
|
165
|
-
|
|
166
156
|
/**
|
|
167
157
|
* What every context has reported on one commit, from both places GitHub
|
|
168
158
|
* keeps them. The aggregate jobs are check runs; `no-skip-ci-label` is a
|
|
@@ -308,3 +298,13 @@ export const summarizeChecks = ({
|
|
|
308
298
|
required: required.length,
|
|
309
299
|
};
|
|
310
300
|
};
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Which of two runs of one name GitHub would answer with: the one in the
|
|
304
|
+
* later-created suite, and within one suite the later run of it — a re-run
|
|
305
|
+
* keeps the suite and takes a new run id.
|
|
306
|
+
*/
|
|
307
|
+
const outranks = (run: CheckRunReport, previous: CheckRunReport): boolean =>
|
|
308
|
+
run.checkSuiteId === previous.checkSuiteId
|
|
309
|
+
? run.id > previous.id
|
|
310
|
+
: run.checkSuiteId > previous.checkSuiteId;
|
package/src/code-owners.mts
CHANGED
|
@@ -55,87 +55,6 @@ export const ownersOf = (
|
|
|
55
55
|
): readonly string[] =>
|
|
56
56
|
rules.findLast((rule) => toRegExp(rule.pattern).test(path))?.owners ?? [];
|
|
57
57
|
|
|
58
|
-
const stripComment = (line: string): string => {
|
|
59
|
-
const at = line.indexOf('#');
|
|
60
|
-
|
|
61
|
-
return at === -1 ? line : line.slice(0, at);
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const toRegExp = (pattern: string): RegExp => {
|
|
65
|
-
const directory = pattern.endsWith('/');
|
|
66
|
-
|
|
67
|
-
const trimmed = pattern.replaceAll(/^\/|\/$/gu, '');
|
|
68
|
-
|
|
69
|
-
// `.gitignore`'s rule: a slash anywhere but at the end ties the pattern to
|
|
70
|
-
// the root, and a pattern without one may match at any depth.
|
|
71
|
-
const anchored = pattern.startsWith('/') || trimmed.includes('/');
|
|
72
|
-
|
|
73
|
-
const body = globToRegExpSource(trimmed);
|
|
74
|
-
|
|
75
|
-
// A directory owns what is under it and never matches as a file. A name
|
|
76
|
-
// with no trailing slash may be either, so it matches itself and what is
|
|
77
|
-
// under it — unless its last segment is a wildcard, which GitHub reads as
|
|
78
|
-
// files at that level only: `docs/*` owns `docs/a.md` and not
|
|
79
|
-
// `docs/build/a.md`.
|
|
80
|
-
const tail = directory
|
|
81
|
-
? '/.+'
|
|
82
|
-
: (trimmed.split('/').at(-1) ?? '').includes('*')
|
|
83
|
-
? ''
|
|
84
|
-
: '(?:/.+)?';
|
|
85
|
-
|
|
86
|
-
// eslint-disable-next-line security/detect-non-literal-regexp
|
|
87
|
-
return new RegExp(`^${anchored ? '' : '(?:.+/)?'}${body}${tail}$`, 'u');
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const globToRegExpSource = (glob: string): string =>
|
|
91
|
-
glob
|
|
92
|
-
.split('/')
|
|
93
|
-
.map((segment, index, segments) =>
|
|
94
|
-
segment === '**'
|
|
95
|
-
? // `a/**/b` is `a/b` as well as `a/x/y/b`, so the separator that
|
|
96
|
-
// follows is part of what `**` may leave out.
|
|
97
|
-
index === segments.length - 1
|
|
98
|
-
? '.*'
|
|
99
|
-
: '(?:.*/)?'
|
|
100
|
-
: `${segment
|
|
101
|
-
.split('')
|
|
102
|
-
.map((char) =>
|
|
103
|
-
char === '*' ? '[^/]*' : char === '?' ? '[^/]' : escapeChar(char),
|
|
104
|
-
)
|
|
105
|
-
.join('')}${index === segments.length - 1 ? '' : '/'}`,
|
|
106
|
-
)
|
|
107
|
-
.join('');
|
|
108
|
-
|
|
109
|
-
// Not `RegExp.escape`: it arrived in Node 24, and `ws:check:test` also runs
|
|
110
|
-
// on the `engines.node` floor (`test-node-versions (minimum)`), which is 22.
|
|
111
|
-
// Under the `u` flag only the syntax characters and `/` may be escaped, and
|
|
112
|
-
// everything else is a literal as it stands.
|
|
113
|
-
const escapeChar = (char: string): string =>
|
|
114
|
-
String.raw`^$\.*+?()[]{}|/`.includes(char) ? (`\\${char}` as const) : char;
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Where a pull request stands with the code owners.
|
|
118
|
-
*
|
|
119
|
-
* `required` lists what is still waiting — the owned paths no owner has
|
|
120
|
-
* approved yet, and who could approve them. `authorOwns` is the case that
|
|
121
|
-
* waits for ever: GitHub does not let an author approve their own pull
|
|
122
|
-
* request, so when the author is the only owner nothing but a ruleset bypass
|
|
123
|
-
* will merge it. `unknown` is a pull request whose changed files were more
|
|
124
|
-
* than could be read, where the unread ones might be owned — or, with
|
|
125
|
-
* `teams`, one approved by someone these teams might have as a member.
|
|
126
|
-
*/
|
|
127
|
-
export type CodeOwnerReview = Readonly<
|
|
128
|
-
| { state: 'approved' }
|
|
129
|
-
| { state: 'not-required' }
|
|
130
|
-
| { state: 'unknown'; teams?: readonly string[] }
|
|
131
|
-
| {
|
|
132
|
-
state: 'required';
|
|
133
|
-
paths: readonly string[];
|
|
134
|
-
owners: readonly string[];
|
|
135
|
-
authorOwns: boolean;
|
|
136
|
-
}
|
|
137
|
-
>;
|
|
138
|
-
|
|
139
58
|
/**
|
|
140
59
|
* What GitHub would say, worked out the way GitHub does: every changed path
|
|
141
60
|
* that has owners needs an approval from one of them. An approval is the
|
|
@@ -225,5 +144,86 @@ export const codeOwnerReview = ({
|
|
|
225
144
|
return owned ? { state: 'approved' } : { state: 'not-required' };
|
|
226
145
|
};
|
|
227
146
|
|
|
147
|
+
const stripComment = (line: string): string => {
|
|
148
|
+
const at = line.indexOf('#');
|
|
149
|
+
|
|
150
|
+
return at === -1 ? line : line.slice(0, at);
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// Not `RegExp.escape`: it arrived in Node 24, and `ws:check:test` also runs
|
|
154
|
+
// on the `engines.node` floor (`test-node-versions (minimum)`), which is 22.
|
|
155
|
+
// Under the `u` flag only the syntax characters and `/` may be escaped, and
|
|
156
|
+
// everything else is a literal as it stands.
|
|
157
|
+
const escapeChar = (char: string): string =>
|
|
158
|
+
String.raw`^$\.*+?()[]{}|/`.includes(char) ? (`\\${char}` as const) : char;
|
|
159
|
+
|
|
160
|
+
const globToRegExpSource = (glob: string): string =>
|
|
161
|
+
glob
|
|
162
|
+
.split('/')
|
|
163
|
+
.map((segment, index, segments) =>
|
|
164
|
+
segment === '**'
|
|
165
|
+
? // `a/**/b` is `a/b` as well as `a/x/y/b`, so the separator that
|
|
166
|
+
// follows is part of what `**` may leave out.
|
|
167
|
+
index === segments.length - 1
|
|
168
|
+
? '.*'
|
|
169
|
+
: '(?:.*/)?'
|
|
170
|
+
: `${segment
|
|
171
|
+
.split('')
|
|
172
|
+
.map((char) =>
|
|
173
|
+
char === '*' ? '[^/]*' : char === '?' ? '[^/]' : escapeChar(char),
|
|
174
|
+
)
|
|
175
|
+
.join('')}${index === segments.length - 1 ? '' : '/'}`,
|
|
176
|
+
)
|
|
177
|
+
.join('');
|
|
178
|
+
|
|
179
|
+
const toRegExp = (pattern: string): RegExp => {
|
|
180
|
+
const directory = pattern.endsWith('/');
|
|
181
|
+
|
|
182
|
+
const trimmed = pattern.replaceAll(/^\/|\/$/gu, '');
|
|
183
|
+
|
|
184
|
+
// `.gitignore`'s rule: a slash anywhere but at the end ties the pattern to
|
|
185
|
+
// the root, and a pattern without one may match at any depth.
|
|
186
|
+
const anchored = pattern.startsWith('/') || trimmed.includes('/');
|
|
187
|
+
|
|
188
|
+
const body = globToRegExpSource(trimmed);
|
|
189
|
+
|
|
190
|
+
// A directory owns what is under it and never matches as a file. A name
|
|
191
|
+
// with no trailing slash may be either, so it matches itself and what is
|
|
192
|
+
// under it — unless its last segment is a wildcard, which GitHub reads as
|
|
193
|
+
// files at that level only: `docs/*` owns `docs/a.md` and not
|
|
194
|
+
// `docs/build/a.md`.
|
|
195
|
+
const tail = directory
|
|
196
|
+
? '/.+'
|
|
197
|
+
: (trimmed.split('/').at(-1) ?? '').includes('*')
|
|
198
|
+
? ''
|
|
199
|
+
: '(?:/.+)?';
|
|
200
|
+
|
|
201
|
+
// eslint-disable-next-line security/detect-non-literal-regexp
|
|
202
|
+
return new RegExp(`^${anchored ? '' : '(?:.+/)?'}${body}${tail}$`, 'u');
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Where a pull request stands with the code owners.
|
|
207
|
+
*
|
|
208
|
+
* `required` lists what is still waiting — the owned paths no owner has
|
|
209
|
+
* approved yet, and who could approve them. `authorOwns` is the case that
|
|
210
|
+
* waits for ever: GitHub does not let an author approve their own pull
|
|
211
|
+
* request, so when the author is the only owner nothing but a ruleset bypass
|
|
212
|
+
* will merge it. `unknown` is a pull request whose changed files were more
|
|
213
|
+
* than could be read, where the unread ones might be owned — or, with
|
|
214
|
+
* `teams`, one approved by someone these teams might have as a member.
|
|
215
|
+
*/
|
|
216
|
+
export type CodeOwnerReview = Readonly<
|
|
217
|
+
| { state: 'approved' }
|
|
218
|
+
| { state: 'not-required' }
|
|
219
|
+
| { state: 'unknown'; teams?: readonly string[] }
|
|
220
|
+
| {
|
|
221
|
+
state: 'required';
|
|
222
|
+
paths: readonly string[];
|
|
223
|
+
owners: readonly string[];
|
|
224
|
+
authorOwns: boolean;
|
|
225
|
+
}
|
|
226
|
+
>;
|
|
227
|
+
|
|
228
228
|
/** `org/team`, as opposed to a person's login or an email address. */
|
|
229
229
|
const isTeam = (owner: string): boolean => owner.includes('/');
|
package/src/merge-after.mts
CHANGED
|
@@ -43,29 +43,6 @@ export const parseMergeAfter = (body: string): readonly number[] =>
|
|
|
43
43
|
export const parseStartAfter = (body: string): readonly number[] =>
|
|
44
44
|
parseDeclaration(body, 'start-after');
|
|
45
45
|
|
|
46
|
-
/**
|
|
47
|
-
* `<Key>: #1901, #1903` in a body, anywhere in it, on as many lines as it
|
|
48
|
-
* likes. Only this exact trailer is read — a sentence mentioning another pull
|
|
49
|
-
* request is prose, and treating it as a declaration would make every
|
|
50
|
-
* cross-reference a constraint.
|
|
51
|
-
*/
|
|
52
|
-
const parseDeclaration = (
|
|
53
|
-
body: string,
|
|
54
|
-
key: 'merge-after' | 'start-after',
|
|
55
|
-
): readonly number[] =>
|
|
56
|
-
Arr.uniq(
|
|
57
|
-
outsideCodeFences(body)
|
|
58
|
-
.matchAll(DECLARATION_LINE[key])
|
|
59
|
-
.flatMap((line) =>
|
|
60
|
-
(line.groups?.['refs'] ?? '').matchAll(NUMBER_REF).flatMap((ref) => {
|
|
61
|
-
const parsed = Num.safeParseInt(ref.groups?.['number'] ?? '');
|
|
62
|
-
|
|
63
|
-
return Result.isOk(parsed) ? [parsed.value] : [];
|
|
64
|
-
}),
|
|
65
|
-
)
|
|
66
|
-
.toArray(),
|
|
67
|
-
);
|
|
68
|
-
|
|
69
46
|
/**
|
|
70
47
|
* The body with the contents of its fenced code blocks blanked out, line
|
|
71
48
|
* count preserved.
|
|
@@ -149,6 +126,29 @@ export const findMergeAfterCycles = (
|
|
|
149
126
|
return Array.from(mut_cycles.values());
|
|
150
127
|
};
|
|
151
128
|
|
|
129
|
+
/**
|
|
130
|
+
* `<Key>: #1901, #1903` in a body, anywhere in it, on as many lines as it
|
|
131
|
+
* likes. Only this exact trailer is read — a sentence mentioning another pull
|
|
132
|
+
* request is prose, and treating it as a declaration would make every
|
|
133
|
+
* cross-reference a constraint.
|
|
134
|
+
*/
|
|
135
|
+
const parseDeclaration = (
|
|
136
|
+
body: string,
|
|
137
|
+
key: 'merge-after' | 'start-after',
|
|
138
|
+
): readonly number[] =>
|
|
139
|
+
Arr.uniq(
|
|
140
|
+
outsideCodeFences(body)
|
|
141
|
+
.matchAll(DECLARATION_LINE[key])
|
|
142
|
+
.flatMap((line) =>
|
|
143
|
+
(line.groups?.['refs'] ?? '').matchAll(NUMBER_REF).flatMap((ref) => {
|
|
144
|
+
const parsed = Num.safeParseInt(ref.groups?.['number'] ?? '');
|
|
145
|
+
|
|
146
|
+
return Result.isOk(parsed) ? [parsed.value] : [];
|
|
147
|
+
}),
|
|
148
|
+
)
|
|
149
|
+
.toArray(),
|
|
150
|
+
);
|
|
151
|
+
|
|
152
152
|
const rotateToLowest = (cycle: readonly number[]): readonly number[] => {
|
|
153
153
|
if (!Arr.isNonEmpty(cycle)) {
|
|
154
154
|
return cycle;
|
package/src/queue-order.mts
CHANGED
|
@@ -422,6 +422,26 @@ export const priorityRankOfLabels = (labels: readonly string[]): 0 | 1 | 2 => {
|
|
|
422
422
|
}
|
|
423
423
|
};
|
|
424
424
|
|
|
425
|
+
/**
|
|
426
|
+
* {@link priorityRankOfLabels}, for a pull request that `bypass` says only a
|
|
427
|
+
* ruleset bypass merges ({@link ReviewHold}): that one ranks with
|
|
428
|
+
* `priority:low` unless labelled `priority:high` — alone, since the two
|
|
429
|
+
* together count as neither. Picked, it is released, goes green and is set
|
|
430
|
+
* aside for a person, so the cycle spent on it lands nothing; picked last,
|
|
431
|
+
* it still has its checks run. `priority:high` is a person saying they want
|
|
432
|
+
* that verdict now, and is the one way to ask for it, so it still counts.
|
|
433
|
+
*/
|
|
434
|
+
export const pickPriorityRank = (
|
|
435
|
+
labels: readonly string[],
|
|
436
|
+
bypass: boolean,
|
|
437
|
+
): 0 | 1 | 2 => {
|
|
438
|
+
const rank = priorityRankOfLabels(labels);
|
|
439
|
+
|
|
440
|
+
return bypass && rank !== 0
|
|
441
|
+
? priorityRankOfLabels([PRIORITY_LOW_LABEL])
|
|
442
|
+
: rank;
|
|
443
|
+
};
|
|
444
|
+
|
|
425
445
|
/**
|
|
426
446
|
* The order the worker picks candidates in. The declared order has already
|
|
427
447
|
* been applied, as a gate rather than a sort, so this is lowest number first
|
|
@@ -432,7 +452,9 @@ export const priorityRankOfLabels = (labels: readonly string[]): 0 | 1 | 2 => {
|
|
|
432
452
|
* else is not known to sit without merging, and this is.
|
|
433
453
|
* 2. `priority:high` before the rest, `priority:low` after them. It is a
|
|
434
454
|
* person's declaration, so it outranks the two guesses below — and the
|
|
435
|
-
* version pull request reads it too.
|
|
455
|
+
* version pull request reads it too. One only a ruleset bypass merges
|
|
456
|
+
* counts as `priority:low` unless labelled `priority:high`
|
|
457
|
+
* ({@link pickPriorityRank}).
|
|
436
458
|
* 3. What GitHub only *thinks* conflicts goes after the rest: a rebase the
|
|
437
459
|
* worker is sure of is worth spending the cycle on before one it is
|
|
438
460
|
* guessing at.
|
|
@@ -450,6 +472,7 @@ export const orderCandidates = <P,>(
|
|
|
450
472
|
context: Readonly<{
|
|
451
473
|
defaultBranch: string;
|
|
452
474
|
demoted: (candidate: P) => boolean;
|
|
475
|
+
bypass: (candidate: P) => boolean;
|
|
453
476
|
}>,
|
|
454
477
|
): readonly P[] => {
|
|
455
478
|
const rank = (candidate: P): number => {
|
|
@@ -462,19 +485,28 @@ export const orderCandidates = <P,>(
|
|
|
462
485
|
: 0;
|
|
463
486
|
};
|
|
464
487
|
|
|
488
|
+
const priorityRank = (candidate: P): number =>
|
|
489
|
+
pickPriorityRank(factsOf(candidate).labels, context.bypass(candidate));
|
|
490
|
+
|
|
465
491
|
return candidates
|
|
466
492
|
.toSorted((a, b) => factsOf(a).number - factsOf(b).number)
|
|
467
493
|
.toSorted((a, b) => rank(a) - rank(b))
|
|
468
|
-
.toSorted(
|
|
469
|
-
(a, b) =>
|
|
470
|
-
priorityRankOfLabels(factsOf(a).labels) -
|
|
471
|
-
priorityRankOfLabels(factsOf(b).labels),
|
|
472
|
-
)
|
|
494
|
+
.toSorted((a, b) => priorityRank(a) - priorityRank(b))
|
|
473
495
|
.toSorted(
|
|
474
496
|
(a, b) => Number(context.demoted(a)) - Number(context.demoted(b)),
|
|
475
497
|
);
|
|
476
498
|
};
|
|
477
499
|
|
|
500
|
+
/**
|
|
501
|
+
* Whether `pr` carries the label `name`. A function rather than `includes`
|
|
502
|
+
* in place, which narrows the label constant itself to `never` where it says
|
|
503
|
+
* no.
|
|
504
|
+
*/
|
|
505
|
+
const hasLabel = (
|
|
506
|
+
pr: Readonly<{ labels: readonly string[] }>,
|
|
507
|
+
name: string,
|
|
508
|
+
): boolean => pr.labels.includes(name);
|
|
509
|
+
|
|
478
510
|
/** Why a pull request is none of the queue's business, if it is not. */
|
|
479
511
|
const outOfScopeReason = (
|
|
480
512
|
pr: QueueFacts,
|
|
@@ -521,6 +553,17 @@ const waitingOn = (
|
|
|
521
553
|
: undefined;
|
|
522
554
|
};
|
|
523
555
|
|
|
556
|
+
/**
|
|
557
|
+
* Pending — including a required context that has not reported at all — or
|
|
558
|
+
* green and about to merge is in flight; a failed required check is failing.
|
|
559
|
+
*/
|
|
560
|
+
const byChecks = (reads: QueueReads): QueueStanding =>
|
|
561
|
+
reads.checksFailed === undefined
|
|
562
|
+
? ({ kind: 'needs', read: 'checksFailed' } as const)
|
|
563
|
+
: reads.checksFailed
|
|
564
|
+
? ({ kind: 'failing' } as const)
|
|
565
|
+
: ({ kind: 'in-flight' } as const);
|
|
566
|
+
|
|
524
567
|
/**
|
|
525
568
|
* The version pull request: held while anything labelled `blocks-release`
|
|
526
569
|
* is open, and while the branch was built before the tip of the base, then
|
|
@@ -547,24 +590,3 @@ const versionStanding = (
|
|
|
547
590
|
? { kind: 'candidate' }
|
|
548
591
|
: byChecks(reads);
|
|
549
592
|
};
|
|
550
|
-
|
|
551
|
-
/**
|
|
552
|
-
* Pending — including a required context that has not reported at all — or
|
|
553
|
-
* green and about to merge is in flight; a failed required check is failing.
|
|
554
|
-
*/
|
|
555
|
-
const byChecks = (reads: QueueReads): QueueStanding =>
|
|
556
|
-
reads.checksFailed === undefined
|
|
557
|
-
? ({ kind: 'needs', read: 'checksFailed' } as const)
|
|
558
|
-
: reads.checksFailed
|
|
559
|
-
? ({ kind: 'failing' } as const)
|
|
560
|
-
: ({ kind: 'in-flight' } as const);
|
|
561
|
-
|
|
562
|
-
/**
|
|
563
|
-
* Whether `pr` carries the label `name`. A function rather than `includes`
|
|
564
|
-
* in place, which narrows the label constant itself to `never` where it says
|
|
565
|
-
* no.
|
|
566
|
-
*/
|
|
567
|
-
const hasLabel = (
|
|
568
|
-
pr: Readonly<{ labels: readonly string[] }>,
|
|
569
|
-
name: string,
|
|
570
|
-
): boolean => pr.labels.includes(name);
|