atris 3.75.0 → 3.77.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/commands/brief.js +219 -36
- package/commands/rating.js +18 -11
- package/commands/task.js +72 -5
- package/commands/worktree.js +27 -6
- package/lib/fleet.js +58 -4
- package/lib/gm-season.js +93 -1
- package/lib/held-pull-requests.js +589 -0
- package/lib/owner-brief.js +1060 -0
- package/lib/pull-request-link.js +25 -0
- package/lib/review-integrity.js +52 -5
- package/lib/task-db.js +519 -135
- package/package.json +1 -1
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// A pull request link, read the one way everything that compares links reads it. The task store
|
|
4
|
+
// (which pull request a job points at) and the held-job sweep (which pull request GitHub answered
|
|
5
|
+
// for) both use it, so the two cannot disagree about what a link names.
|
|
6
|
+
//
|
|
7
|
+
// The link is https://<host>/<owner>/<name>/pull/<number>, however a person decorated it: a
|
|
8
|
+
// trailing slash, /files, a query string, a fragment, any case. A link with no number, or a number
|
|
9
|
+
// that is not a positive whole number, names no pull request.
|
|
10
|
+
//
|
|
11
|
+
// repo the owner and name, in lower case
|
|
12
|
+
// number the pull request number
|
|
13
|
+
// repoLink the repository's own address, as the link spells it
|
|
14
|
+
|
|
15
|
+
const PULL_REQUEST_LINK = /^(https?:\/\/[^/\s]+\/([^/\s]+)\/([^/\s]+))\/pull\/(\d+)(?:[/?#]\S*)?$/i;
|
|
16
|
+
|
|
17
|
+
function pullRequestOfLink(link) {
|
|
18
|
+
const match = PULL_REQUEST_LINK.exec(String(link || '').trim());
|
|
19
|
+
if (!match) return null;
|
|
20
|
+
const number = Number(match[4]);
|
|
21
|
+
if (!Number.isSafeInteger(number) || number <= 0) return null;
|
|
22
|
+
return { repo: `${match[2]}/${match[3]}`.toLowerCase(), number, repoLink: match[1] };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = { pullRequestOfLink };
|
package/lib/review-integrity.js
CHANGED
|
@@ -59,20 +59,65 @@ function reviewEventActors(task) {
|
|
|
59
59
|
return actors;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
//
|
|
62
|
+
// Everyone who ever built the task: the stamped builder, the current claimant, whoever any claim
|
|
63
|
+
// event on file names, and whoever handed work in on it (the proof_ready events that carry a
|
|
64
|
+
// handed_in_by mark; a review pass leaves none) together with the others that hand-in names as
|
|
65
|
+
// having built the work with it (its built_with list: a flight that ran for a member hands in under
|
|
66
|
+
// the member's name, and the engines that ran are named there). Authorship is cumulative and nothing clears it, so
|
|
67
|
+
// sending a job back, letting the claim go or reopening it never makes a past builder look
|
|
68
|
+
// independent again. The system's own names (RESERVED_ACTORS) record passes and checks, never
|
|
69
|
+
// builds, so an event under one of them makes no author. `task.events` is whatever the caller has
|
|
70
|
+
// of the job's history; a caller with the whole history (the store's authorsOfTask) gets the whole
|
|
71
|
+
// answer.
|
|
72
|
+
function taskAuthors(task) {
|
|
73
|
+
const authors = new Set();
|
|
74
|
+
if (!task || typeof task !== 'object') return authors;
|
|
75
|
+
const add = (value, { fromEvent = false } = {}) => {
|
|
76
|
+
const name = normalizeActor(value);
|
|
77
|
+
if (name && !(fromEvent && RESERVED_ACTORS.has(name))) authors.add(name);
|
|
78
|
+
};
|
|
79
|
+
const metadata = task.metadata && typeof task.metadata === 'object' ? task.metadata : {};
|
|
80
|
+
add(metadata.built_by);
|
|
81
|
+
add(task.claimed_by);
|
|
82
|
+
const events = Array.isArray(task.events) ? task.events : [];
|
|
83
|
+
for (const event of events) {
|
|
84
|
+
if (!event || typeof event !== 'object') continue;
|
|
85
|
+
const payload = event.payload && typeof event.payload === 'object' ? event.payload : {};
|
|
86
|
+
if (event.event_type === 'claimed') add(payload.claimed_by || event.actor, { fromEvent: true });
|
|
87
|
+
else if (event.event_type === 'proof_ready') {
|
|
88
|
+
add(payload.handed_in_by, { fromEvent: true });
|
|
89
|
+
// A list of names, not a string to split; anything in it that is not a name is no author.
|
|
90
|
+
if (Array.isArray(payload.built_with)) {
|
|
91
|
+
for (const name of payload.built_with) if (typeof name === 'string') add(name, { fromEvent: true });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return authors;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Whether this actor ever built the task (see taskAuthors), or is the builder the older rules
|
|
99
|
+
// resolve for a row with no other record (taskBuilder).
|
|
100
|
+
function isTaskAuthor(task, actor) {
|
|
101
|
+
const name = normalizeActor(actor);
|
|
102
|
+
if (!name || !task || typeof task !== 'object') return false;
|
|
103
|
+
return taskAuthors(task).has(name) || taskBuilder(task) === name;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Actors who reviewed the task and never built any of it.
|
|
63
107
|
function independentReviewActors(task) {
|
|
64
108
|
const builder = taskBuilder(task);
|
|
65
109
|
const actors = reviewEventActors(task);
|
|
66
110
|
if (builder) actors.delete(builder);
|
|
111
|
+
for (const author of taskAuthors(task)) actors.delete(author);
|
|
67
112
|
return actors;
|
|
68
113
|
}
|
|
69
114
|
|
|
70
|
-
// True when at least one review pass came from someone
|
|
71
|
-
//
|
|
72
|
-
//
|
|
115
|
+
// True when at least one review pass came from someone who never built any of the task. When
|
|
116
|
+
// nobody can be resolved as its builder at all, fall back to requiring two distinct actors.
|
|
117
|
+
// Independence cannot be proven from one.
|
|
73
118
|
function hasIndependentReview(task) {
|
|
74
119
|
const builder = taskBuilder(task);
|
|
75
|
-
if (!builder) return reviewEventActors(task).size >= 2;
|
|
120
|
+
if (!builder && taskAuthors(task).size === 0) return reviewEventActors(task).size >= 2;
|
|
76
121
|
return independentReviewActors(task).size >= 1;
|
|
77
122
|
}
|
|
78
123
|
|
|
@@ -138,6 +183,8 @@ module.exports = {
|
|
|
138
183
|
normalizeActor,
|
|
139
184
|
isFleetEngineActor,
|
|
140
185
|
taskBuilder,
|
|
186
|
+
taskAuthors,
|
|
187
|
+
isTaskAuthor,
|
|
141
188
|
reviewEventActors,
|
|
142
189
|
independentReviewActors,
|
|
143
190
|
hasIndependentReview,
|