@detergent-software/conductor 1.0.0-dev.3 → 1.0.0-dev.31
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/CHANGELOG.md +240 -0
- package/README.md +391 -10
- package/build/brief-cli.js +68 -0
- package/build/brief-cli.js.map +1 -0
- package/build/brief.js +211 -0
- package/build/brief.js.map +1 -0
- package/build/broker-cli.js +126 -0
- package/build/broker-cli.js.map +1 -0
- package/build/broker.js +154 -0
- package/build/broker.js.map +1 -0
- package/build/cli.js +621 -99
- package/build/cli.js.map +1 -1
- package/build/clock.js +17 -0
- package/build/clock.js.map +1 -0
- package/build/conditions.js +20 -14
- package/build/conditions.js.map +1 -1
- package/build/config.js +95 -1
- package/build/config.js.map +1 -1
- package/build/console.js +176 -0
- package/build/console.js.map +1 -0
- package/build/credential.js +52 -0
- package/build/credential.js.map +1 -0
- package/build/daemon.js +168 -3
- package/build/daemon.js.map +1 -1
- package/build/events.js +107 -0
- package/build/events.js.map +1 -0
- package/build/help.js +447 -0
- package/build/help.js.map +1 -0
- package/build/history.js +634 -0
- package/build/history.js.map +1 -0
- package/build/inbox.js +408 -0
- package/build/inbox.js.map +1 -0
- package/build/manager.js +132 -34
- package/build/manager.js.map +1 -1
- package/build/mod.js +51 -0
- package/build/mod.js.map +1 -0
- package/build/note.js +71 -0
- package/build/note.js.map +1 -0
- package/build/notes.js +172 -0
- package/build/notes.js.map +1 -0
- package/build/notify.js +422 -0
- package/build/notify.js.map +1 -0
- package/build/overrides.js +83 -0
- package/build/overrides.js.map +1 -0
- package/build/paths.js +10 -1
- package/build/paths.js.map +1 -1
- package/build/profile.js +68 -2
- package/build/profile.js.map +1 -1
- package/build/pushover.js +67 -0
- package/build/pushover.js.map +1 -0
- package/build/queue.js +183 -0
- package/build/queue.js.map +1 -0
- package/build/registry.js +17 -10
- package/build/registry.js.map +1 -1
- package/build/rule-actions.js +444 -0
- package/build/rule-actions.js.map +1 -0
- package/build/rule-input.js +17 -0
- package/build/rule-input.js.map +1 -0
- package/build/rules-service.js +402 -0
- package/build/rules-service.js.map +1 -0
- package/build/rules.js +850 -0
- package/build/rules.js.map +1 -0
- package/build/scheduler.js +293 -0
- package/build/scheduler.js.map +1 -0
- package/build/serve.js +51 -0
- package/build/serve.js.map +1 -0
- package/build/server.js +329 -0
- package/build/server.js.map +1 -1
- package/build/spend.js +50 -0
- package/build/spend.js.map +1 -1
- package/build/store.js +452 -4
- package/build/store.js.map +1 -1
- package/build/trackers.js +15 -0
- package/build/trackers.js.map +1 -0
- package/build/upgrade.js +18 -14
- package/build/upgrade.js.map +1 -1
- package/build/wait.js +35 -41
- package/build/wait.js.map +1 -1
- package/build/watchers/ado-api.js +55 -0
- package/build/watchers/ado-api.js.map +1 -0
- package/build/watchers/ado.js +37 -49
- package/build/watchers/ado.js.map +1 -1
- package/build/watchers/boards-brief.js +63 -0
- package/build/watchers/boards-brief.js.map +1 -0
- package/build/watchers/boards-queue.js +120 -0
- package/build/watchers/boards-queue.js.map +1 -0
- package/build/watchers/boards-writes.js +83 -0
- package/build/watchers/boards-writes.js.map +1 -0
- package/build/watchers/boards.js +271 -0
- package/build/watchers/boards.js.map +1 -0
- package/build/watchers/github.js +5 -2
- package/build/watchers/github.js.map +1 -1
- package/build/watchers/index.js +1 -0
- package/build/watchers/index.js.map +1 -1
- package/build/watchers/linear-api.js +55 -0
- package/build/watchers/linear-api.js.map +1 -0
- package/build/watchers/linear-key.js +6 -45
- package/build/watchers/linear-key.js.map +1 -1
- package/build/watchers/linear-labels.js +43 -0
- package/build/watchers/linear-labels.js.map +1 -0
- package/build/watchers/linear-queue.js +96 -0
- package/build/watchers/linear-queue.js.map +1 -0
- package/build/watchers/linear.js +29 -18
- package/build/watchers/linear.js.map +1 -1
- package/build/why.js +78 -0
- package/build/why.js.map +1 -0
- package/mod/.claude-plugin/marketplace.json +6 -0
- package/mod/.claude-plugin/plugin.json +7 -0
- package/mod/README.md +17 -0
- package/mod/hooks/hooks.json +1 -0
- package/mod/hooks/model.ts +457 -0
- package/mod/hooks/register.tsx +542 -0
- package/mod/types/index.d.ts +57 -0
- package/package.json +4 -1
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { BriefError } from "../brief.js";
|
|
2
|
+
import { areaOfPath } from "./boards-queue.js";
|
|
3
|
+
import { boardsAsk, htmlText, readComments, readViewer, readWorkItems, requireBoard, stateType, tagsOf, workItemId } from "./boards.js";
|
|
4
|
+
const LINKS = new Map([
|
|
5
|
+
['System.LinkTypes.Dependency-Forward', 'blocks'],
|
|
6
|
+
['System.LinkTypes.Dependency-Reverse', 'blockedBy'],
|
|
7
|
+
['System.LinkTypes.Related', 'related'],
|
|
8
|
+
]);
|
|
9
|
+
const LINKED_FIELDS = ['System.Id', 'System.Title', 'System.State'];
|
|
10
|
+
export function boardsBriefs(options = {}) {
|
|
11
|
+
// The az login's identity by organization: it doesn't change while the daemon runs.
|
|
12
|
+
const viewers = new Map();
|
|
13
|
+
return {
|
|
14
|
+
read: async (item, profile) => {
|
|
15
|
+
const board = requireBoard(profile);
|
|
16
|
+
const id = workItemId(item, board);
|
|
17
|
+
if (id === null)
|
|
18
|
+
throw new BriefError(`${item} isn't an item of the board keyed ${board.key}`, 'missing');
|
|
19
|
+
const ask = boardsAsk(options, board, null);
|
|
20
|
+
const [found] = await readWorkItems(ask, [id], { relations: true });
|
|
21
|
+
if (!found)
|
|
22
|
+
throw new BriefError(`Azure DevOps Boards has no work item ${id} (${item})`, 'missing');
|
|
23
|
+
const links = linksOf(found);
|
|
24
|
+
const [read, comments, viewer] = await Promise.all([
|
|
25
|
+
readWorkItems(ask, [...new Set(links.map((l) => l.id))], { fields: LINKED_FIELDS }),
|
|
26
|
+
readComments(ask, id),
|
|
27
|
+
viewers.get(board.organization) ?? readViewer(ask),
|
|
28
|
+
]);
|
|
29
|
+
viewers.set(board.organization, viewer);
|
|
30
|
+
const linked = new Map(read.map((w) => [w.id, w]));
|
|
31
|
+
const linksAs = (kind) => links.filter((l) => l.kind === kind).map(({ id: to }) => {
|
|
32
|
+
const w = linked.get(to);
|
|
33
|
+
return { id: `${board.key}-${to}`, status: w ? text(w.fields['System.State']) : null, title: w ? text(w.fields['System.Title']) : null };
|
|
34
|
+
});
|
|
35
|
+
const state = String(found.fields['System.State'] ?? '');
|
|
36
|
+
return {
|
|
37
|
+
area: areaOfPath(String(found.fields['System.AreaPath'] ?? ''), board.areaRoot),
|
|
38
|
+
blockedBy: linksAs('blockedBy'),
|
|
39
|
+
blocks: linksAs('blocks'),
|
|
40
|
+
comments,
|
|
41
|
+
description: htmlText(String(found.fields['System.Description'] ?? '')).trim(),
|
|
42
|
+
labels: tagsOf(found.fields['System.Tags']),
|
|
43
|
+
related: linksAs('related'),
|
|
44
|
+
status: { name: state, type: stateType(state, board) },
|
|
45
|
+
title: String(found.fields['System.Title'] ?? ''),
|
|
46
|
+
trackerProject: null,
|
|
47
|
+
url: `${board.base}/_workitems/edit/${id}`,
|
|
48
|
+
viewer,
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function linksOf(w) {
|
|
54
|
+
return (w.relations ?? []).flatMap((r) => {
|
|
55
|
+
const kind = LINKS.get(r.rel);
|
|
56
|
+
const id = /\/workItems\/(\d+)$/i.exec(r.url)?.[1];
|
|
57
|
+
return kind && id ? [{ id: Number(id), kind }] : [];
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function text(value) {
|
|
61
|
+
return typeof value === 'string' ? value : null;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=boards-brief.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boards-brief.js","sourceRoot":"","sources":["../../src/watchers/boards-brief.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ConductorError } from "../errors.js";
|
|
3
|
+
import { boardsAsk, CLOSED_TYPES, expectOk, OPEN_TYPES, readWorkItems, requireBoard, stateType, tagsOf, workItemId } from "./boards.js";
|
|
4
|
+
// The scheduler's read of an ADO Boards queue (design note, section 12): the saved query the
|
|
5
|
+
// profile names is the conducted queue, in its own order (by default Priority, then Backlog
|
|
6
|
+
// Priority). Its items, the in-flight ones and their open Predecessors are read in batches.
|
|
7
|
+
// "B has Predecessor A" is stored on B as a Dependency-Reverse link to A.
|
|
8
|
+
const PREDECESSOR = 'System.LinkTypes.Dependency-Reverse';
|
|
9
|
+
const savedQuery = z.looseObject({ id: z.string(), isFolder: z.boolean().optional(), name: z.string() });
|
|
10
|
+
const queryRun = z.looseObject({ queryType: z.string(), workItems: z.array(z.looseObject({ id: z.number() })).nullish() });
|
|
11
|
+
// The Area of an Area Path: its path below the root, with `/` between levels (as an Area label
|
|
12
|
+
// group's sub-Areas are written); null at the root. A path outside the root keeps all of it.
|
|
13
|
+
export function areaOfPath(path, root) {
|
|
14
|
+
const levels = (p) => p.split('\\').filter((l) => l !== '');
|
|
15
|
+
const mine = levels(path);
|
|
16
|
+
const top = levels(root);
|
|
17
|
+
const below = top.every((l, i) => mine[i]?.toLowerCase() === l.toLowerCase()) ? mine.slice(top.length) : mine;
|
|
18
|
+
return below.length > 0 ? below.join('/') : null;
|
|
19
|
+
}
|
|
20
|
+
export function boardsQueue(options = {}) {
|
|
21
|
+
// A saved query's id and name, by board and the profile's path or id.
|
|
22
|
+
const queries = new Map();
|
|
23
|
+
const findQuery = async (ask, board) => {
|
|
24
|
+
const key = `${board.base} ${board.query}`;
|
|
25
|
+
const known = queries.get(key);
|
|
26
|
+
if (known)
|
|
27
|
+
return known;
|
|
28
|
+
const answer = await ask(`wit/queries/${encodeURIComponent(board.query)}`);
|
|
29
|
+
if (answer.status === 404)
|
|
30
|
+
throw new ConductorError(`Azure DevOps Boards has no saved query "${board.query}" in ${board.project}`);
|
|
31
|
+
const query = savedQuery.parse(expectOk(answer, `the saved query "${board.query}"`));
|
|
32
|
+
if (query.isFolder)
|
|
33
|
+
throw new ConductorError(`"${board.query}" is a query folder: tracker.query names a saved query`);
|
|
34
|
+
queries.set(key, query);
|
|
35
|
+
return query;
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
read: async (profile, inFlight, budget) => {
|
|
39
|
+
const board = requireBoard(profile);
|
|
40
|
+
const ask = boardsAsk(options, board, budget);
|
|
41
|
+
const query = await findQuery(ask, board);
|
|
42
|
+
const answer = await ask(`wit/wiql/${query.id}`);
|
|
43
|
+
// A query deleted and saved again has a new id: look it up again next time.
|
|
44
|
+
if (answer.status === 404)
|
|
45
|
+
queries.delete(`${board.base} ${board.query}`);
|
|
46
|
+
const ran = queryRun.parse(expectOk(answer, `the saved query "${query.name}"`));
|
|
47
|
+
if (ran.queryType !== 'flat') {
|
|
48
|
+
throw new ConductorError(`the saved query "${query.name}" is a ${ran.queryType} query: the queue is a flat list of work items`);
|
|
49
|
+
}
|
|
50
|
+
const queued = (ran.workItems ?? []).map((w) => w.id);
|
|
51
|
+
const flying = inFlight.flatMap((item) => workItemId(item, board) ?? []);
|
|
52
|
+
const items = new Map();
|
|
53
|
+
const load = async (ids) => {
|
|
54
|
+
const missing = [...new Set(ids)].filter((id) => !items.has(id));
|
|
55
|
+
for (const w of await readWorkItems(ask, missing, { relations: true }))
|
|
56
|
+
items.set(w.id, w);
|
|
57
|
+
};
|
|
58
|
+
await load([...queued, ...flying]);
|
|
59
|
+
// A Predecessor outside the query still blocks an open item, so it is read too.
|
|
60
|
+
const open = [...items.values()].filter((w) => OPEN_TYPES.has(stateType(String(w.fields['System.State'] ?? ''), board)));
|
|
61
|
+
await load(open.flatMap(predecessors));
|
|
62
|
+
return boardsSnapshot(board, query.name, queued, flying, items);
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// The snapshot from the query's ids (in its order), the in-flight ids and every item read. Open
|
|
67
|
+
// query items are the queue, in one tracker project named after the query, as are in-flight items
|
|
68
|
+
// a query on state has dropped (so the project isn't read as ready to release while they're
|
|
69
|
+
// worked); an open Predecessor outside both is listed without a project, so it holds its Area and
|
|
70
|
+
// blocks.
|
|
71
|
+
export function boardsSnapshot(board, queryName, queued, flying, items) {
|
|
72
|
+
const type = (w) => stateType(String(w.fields['System.State'] ?? ''), board);
|
|
73
|
+
const open = new Set([...items.values()].filter((w) => OPEN_TYPES.has(type(w))).map((w) => w.id));
|
|
74
|
+
const rank = new Map(queued.map((id, i) => [id, i]));
|
|
75
|
+
const listed = [...new Set([...queued, ...items.keys()])].filter((id) => open.has(id));
|
|
76
|
+
const issues = listed.flatMap((id) => {
|
|
77
|
+
const w = items.get(id);
|
|
78
|
+
if (!w)
|
|
79
|
+
return [];
|
|
80
|
+
const priority = Number(w.fields['Microsoft.VSTS.Common.Priority']);
|
|
81
|
+
const inQuery = rank.has(id);
|
|
82
|
+
return [
|
|
83
|
+
{
|
|
84
|
+
area: areaOfPath(String(w.fields['System.AreaPath'] ?? ''), board.areaRoot),
|
|
85
|
+
blockedBy: predecessors(w)
|
|
86
|
+
.filter((p) => open.has(p))
|
|
87
|
+
.map((p) => keyOf(board, p)),
|
|
88
|
+
createdAt: typeof w.fields['System.CreatedDate'] === 'string' ? w.fields['System.CreatedDate'] : null,
|
|
89
|
+
id: keyOf(board, id),
|
|
90
|
+
// ADO tags ignore case and keep the org's spelling, so the human tag is spelt as the scheduler looks for it.
|
|
91
|
+
labels: tagsOf(w.fields['System.Tags']).map((t) => (t.toLowerCase() === board.humanLabel.toLowerCase() ? board.humanLabel : t)),
|
|
92
|
+
priority: Number.isInteger(priority) ? priority : null,
|
|
93
|
+
...(inQuery ? { rank: rank.get(id) } : {}),
|
|
94
|
+
stateType: type(w),
|
|
95
|
+
title: typeof w.fields['System.Title'] === 'string' ? w.fields['System.Title'] : null,
|
|
96
|
+
trackerProject: inQuery || flying.includes(id) ? queryName : null,
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
});
|
|
100
|
+
return {
|
|
101
|
+
areaLabels: [...new Set(issues.flatMap((i) => i.area ?? []))].sort(),
|
|
102
|
+
closed: flying.filter((id) => {
|
|
103
|
+
const w = items.get(id);
|
|
104
|
+
return w !== undefined && CLOSED_TYPES.has(type(w));
|
|
105
|
+
}).map((id) => keyOf(board, id)),
|
|
106
|
+
issues,
|
|
107
|
+
projects: [{ name: queryName, priority: board.priority, sortOrder: 0 }],
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function keyOf(board, id) {
|
|
111
|
+
return `${board.key}-${id}`;
|
|
112
|
+
}
|
|
113
|
+
// The ids of a work item's Predecessors.
|
|
114
|
+
function predecessors(w) {
|
|
115
|
+
return (w.relations ?? []).flatMap((r) => {
|
|
116
|
+
const id = r.rel === PREDECESSOR ? /\/workItems\/(\d+)$/i.exec(r.url)?.[1] : undefined;
|
|
117
|
+
return id ? [Number(id)] : [];
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=boards-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boards-queue.js","sourceRoot":"","sources":["../../src/watchers/boards-queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ConductorError } from "../errors.js";
|
|
3
|
+
import { NotePostError } from "../notes.js";
|
|
4
|
+
import { adoMessage, boardsAsk, expectOk, requireBoard, tagsOf, workItemId } from "./boards.js";
|
|
5
|
+
// The conductor's writes to ADO Boards: phase notes as discussion comments, and a rule's tag edit.
|
|
6
|
+
// Neither takes from the watchers' budget, as Linear's writes don't.
|
|
7
|
+
const created = z.looseObject({ id: z.number() });
|
|
8
|
+
// A note as a markdown discussion comment. A refused request (4xx, or 203, ADO's sign-in page for
|
|
9
|
+
// a token it doesn't take) posted nothing; a lost answer or a 5xx may have posted it.
|
|
10
|
+
export function boardsNotes(options = {}) {
|
|
11
|
+
return {
|
|
12
|
+
comment: async (item, body, profile) => {
|
|
13
|
+
const board = requireBoard(profile);
|
|
14
|
+
const id = itemId(item, board, (message) => new NotePostError(message, 'no'));
|
|
15
|
+
const ask = boardsAsk(options, board, null);
|
|
16
|
+
let answer;
|
|
17
|
+
try {
|
|
18
|
+
answer = await ask(`wit/workItems/${id}/comments?format=markdown`, { body: { text: body }, method: 'POST', version: '7.1-preview.4' });
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
22
|
+
// No az token: nothing was sent.
|
|
23
|
+
if (error instanceof ConductorError)
|
|
24
|
+
throw new NotePostError(message, 'no', error.details);
|
|
25
|
+
throw new NotePostError(`Azure DevOps unreachable: ${message}`, 'unknown');
|
|
26
|
+
}
|
|
27
|
+
if (answer.status === 203 || (answer.status >= 400 && answer.status < 500)) {
|
|
28
|
+
throw new NotePostError(`Azure DevOps refused the comment on ${item} (${answer.status}${adoMessage(answer.body)})`, 'no');
|
|
29
|
+
}
|
|
30
|
+
const comment = answer.status >= 200 && answer.status < 300 ? created.safeParse(answer.body).data : undefined;
|
|
31
|
+
if (!comment)
|
|
32
|
+
throw new NotePostError(`Azure DevOps' answer to the comment on ${item} didn't read (${answer.status})`, 'unknown');
|
|
33
|
+
return { id: String(comment.id), url: `${board.base}/_workitems/edit/${id}` };
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const tagged = z.looseObject({ fields: z.record(z.string(), z.unknown()), rev: z.number() });
|
|
38
|
+
// A tag added to or removed from a work item: a patch of `System.Tags` alone, refused by ADO when
|
|
39
|
+
// the item changed since it was read (`test /rev`), so a concurrent edit isn't overwritten.
|
|
40
|
+
export function boardsLabels(options = {}) {
|
|
41
|
+
return {
|
|
42
|
+
label: async (item, label, op, profile) => {
|
|
43
|
+
const board = requireBoard(profile);
|
|
44
|
+
const id = itemId(item, board, (message) => new ConductorError(message));
|
|
45
|
+
const ask = boardsAsk(options, board, null);
|
|
46
|
+
const read = await readTags(ask, id, item);
|
|
47
|
+
const has = read.tags.some((t) => t.toLowerCase() === label.toLowerCase());
|
|
48
|
+
if (has === (op === 'add'))
|
|
49
|
+
return 'already';
|
|
50
|
+
const tags = op === 'add' ? [...read.tags, label] : read.tags.filter((t) => t.toLowerCase() !== label.toLowerCase());
|
|
51
|
+
// `add` on System.Tags merges into the tags there (removing nothing), so a list that replaces
|
|
52
|
+
// them is a `replace`, which needs the field to exist.
|
|
53
|
+
const patch = [
|
|
54
|
+
{ op: 'test', path: '/rev', value: read.rev },
|
|
55
|
+
{ op: read.tags.length > 0 ? 'replace' : 'add', path: '/fields/System.Tags', value: tags.join('; ') },
|
|
56
|
+
];
|
|
57
|
+
const answer = await ask(`wit/workitems/${id}`, { body: patch, contentType: 'application/json-patch+json', method: 'PATCH' });
|
|
58
|
+
const failed = `Azure DevOps didn't ${op} the tag "${label}" on ${item}`;
|
|
59
|
+
if (answer.status < 200 || answer.status > 299)
|
|
60
|
+
throw new ConductorError(`${failed} (${answer.status}${adoMessage(answer.body)})`);
|
|
61
|
+
// A patch ADO takes without changing anything still answers 200, so the answer's tags decide.
|
|
62
|
+
const after = tagged.safeParse(answer.body).data;
|
|
63
|
+
const now = tagsOf(after?.fields['System.Tags']).some((t) => t.toLowerCase() === label.toLowerCase());
|
|
64
|
+
if (!after || now !== (op === 'add'))
|
|
65
|
+
throw new ConductorError(`${failed}: its tags are "${String(after?.fields['System.Tags'] ?? '')}"`);
|
|
66
|
+
return op === 'add' ? 'added' : 'removed';
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function itemId(item, board, error) {
|
|
71
|
+
const id = workItemId(item, board);
|
|
72
|
+
if (id === null)
|
|
73
|
+
throw error(`${item} isn't an item of the board keyed ${board.key}, such as ${board.key}-1234`);
|
|
74
|
+
return id;
|
|
75
|
+
}
|
|
76
|
+
async function readTags(ask, id, item) {
|
|
77
|
+
const answer = await ask(`wit/workitems/${id}?fields=System.Tags`);
|
|
78
|
+
if (answer.status === 404)
|
|
79
|
+
throw new ConductorError(`Azure DevOps Boards has no work item ${id} (${item})`);
|
|
80
|
+
const w = tagged.parse(expectOk(answer, `work item ${id}`));
|
|
81
|
+
return { rev: w.rev, tags: tagsOf(w.fields['System.Tags']) };
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=boards-writes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boards-writes.js","sourceRoot":"","sources":["../../src/watchers/boards-writes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ConductorError } from "../errors.js";
|
|
3
|
+
import { notePrefixes } from "../profile.js";
|
|
4
|
+
import { adoRequest, azToken } from "./ado-api.js";
|
|
5
|
+
import { approvalAfterPlan, phaseOf } from "./linear.js";
|
|
6
|
+
import { perWait, spend, WaitProblem } from "./source.js";
|
|
7
|
+
// State names to types for the Scrum, Agile, Basic and CMMI processes; a profile adds its own.
|
|
8
|
+
const BUILT_IN_STATES = {
|
|
9
|
+
active: 'started',
|
|
10
|
+
approved: 'unstarted',
|
|
11
|
+
closed: 'completed',
|
|
12
|
+
committed: 'started',
|
|
13
|
+
doing: 'started',
|
|
14
|
+
done: 'completed',
|
|
15
|
+
'in progress': 'started',
|
|
16
|
+
new: 'unstarted',
|
|
17
|
+
proposed: 'unstarted',
|
|
18
|
+
removed: 'canceled',
|
|
19
|
+
resolved: 'started',
|
|
20
|
+
'to do': 'unstarted',
|
|
21
|
+
};
|
|
22
|
+
export const OPEN_TYPES = new Set(['backlog', 'started', 'unstarted']);
|
|
23
|
+
export const CLOSED_TYPES = new Set(['canceled', 'completed']);
|
|
24
|
+
// ADO's own message in an error answer, as ": <message>", or nothing.
|
|
25
|
+
export function adoMessage(body) {
|
|
26
|
+
const message = z.looseObject({ message: z.string() }).safeParse(body).data?.message;
|
|
27
|
+
return message ? `: ${message}` : '';
|
|
28
|
+
}
|
|
29
|
+
// The profile's board, or null when its tracker isn't ADO Boards.
|
|
30
|
+
export function boardOf(profile) {
|
|
31
|
+
if (profile?.tracker.kind !== 'ado-boards')
|
|
32
|
+
return null;
|
|
33
|
+
const { tracker } = profile;
|
|
34
|
+
const organization = (tracker.organization ?? profile.codeHost.organization).replace(/\/+$/, '');
|
|
35
|
+
const project = tracker.project ?? profile.codeHost.project;
|
|
36
|
+
return {
|
|
37
|
+
areaRoot: tracker.areaRoot ?? project,
|
|
38
|
+
base: `${organization}/${encodeURIComponent(project)}`,
|
|
39
|
+
humanLabel: tracker.humanLabel,
|
|
40
|
+
key: tracker.key,
|
|
41
|
+
organization,
|
|
42
|
+
priority: tracker.priority,
|
|
43
|
+
project,
|
|
44
|
+
query: tracker.query,
|
|
45
|
+
states: Object.fromEntries(Object.entries(tracker.states).map(([name, type]) => [name.toLowerCase(), type])),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export function boardsAsk({ fetch: fetcher = globalThis.fetch, token = azToken() }, board, budget) {
|
|
49
|
+
return async (path, { organization = false, version = '7.1', ...init } = {}) => {
|
|
50
|
+
if (budget)
|
|
51
|
+
spend(budget);
|
|
52
|
+
const url = `${organization ? board.organization : board.base}/_apis/${path}${path.includes('?') ? '&' : '?'}api-version=${version}`;
|
|
53
|
+
return adoRequest(fetcher, token, url, { ...init, ...(budget ? { onRetry: () => spend(budget) } : {}) });
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
// A comment's text: markdown as it is, the web UI's HTML as plain lines.
|
|
57
|
+
export function commentText(text, format) {
|
|
58
|
+
if (!text)
|
|
59
|
+
return '';
|
|
60
|
+
return format?.toLowerCase() === 'html' ? htmlText(text) : text;
|
|
61
|
+
}
|
|
62
|
+
// An answer that has to be 2xx, else an Error naming what was asked: a WaitProblem for a 400 or
|
|
63
|
+
// 404, which asking again won't change (a project or item ADO doesn't have).
|
|
64
|
+
export function expectOk(answer, what) {
|
|
65
|
+
if (answer.status >= 200 && answer.status <= 299)
|
|
66
|
+
return answer.body;
|
|
67
|
+
const message = `Azure DevOps answered ${answer.status} for ${what}${adoMessage(answer.body)}`;
|
|
68
|
+
throw answer.status === 400 || answer.status === 404 ? new WaitProblem(message) : new Error(message);
|
|
69
|
+
}
|
|
70
|
+
// HTML to the text a reader sees: block ends and <br> as new lines, tags dropped, entities decoded.
|
|
71
|
+
export function htmlText(html) {
|
|
72
|
+
return html
|
|
73
|
+
.replace(/<br\s*\/?>/gi, '\n')
|
|
74
|
+
.replace(/<\/(?:blockquote|div|h[1-6]|li|p|pre|tr)>/gi, '\n')
|
|
75
|
+
.replace(/<[^>]*>/g, '')
|
|
76
|
+
.replace(/ /g, ' ')
|
|
77
|
+
.replace(/</g, '<')
|
|
78
|
+
.replace(/>/g, '>')
|
|
79
|
+
.replace(/"/g, '"')
|
|
80
|
+
.replace(/&#(\d+);/g, (_, code) => String.fromCodePoint(Number(code)))
|
|
81
|
+
.replace(/&/g, '&');
|
|
82
|
+
}
|
|
83
|
+
// The state's type, by the profile's map, then the built-in one; null for a state neither knows,
|
|
84
|
+
// which is neither open nor closed, so such an item is never launched or retired.
|
|
85
|
+
export function stateType(state, board) {
|
|
86
|
+
const name = state.toLowerCase();
|
|
87
|
+
return board.states[name] ?? BUILT_IN_STATES[name] ?? null;
|
|
88
|
+
}
|
|
89
|
+
// `System.Tags` ("human; docs") as a list.
|
|
90
|
+
export function tagsOf(tags) {
|
|
91
|
+
return typeof tags === 'string'
|
|
92
|
+
? tags
|
|
93
|
+
.split(';')
|
|
94
|
+
.map((t) => t.trim())
|
|
95
|
+
.filter((t) => t !== '')
|
|
96
|
+
: [];
|
|
97
|
+
}
|
|
98
|
+
// The work item id of `CAD-1234` on the board keyed CAD, or null for another key.
|
|
99
|
+
export function workItemId(item, board) {
|
|
100
|
+
const match = /^([A-Za-z][A-Za-z0-9]*)-(\d+)$/.exec(item);
|
|
101
|
+
return match?.[1]?.toUpperCase() === board.key && match[2] ? Number(match[2]) : null;
|
|
102
|
+
}
|
|
103
|
+
const BATCH = 200;
|
|
104
|
+
// The work items with these fields (or all of them and their relations, with `relations`), in
|
|
105
|
+
// batches of 200. An id ADO doesn't have is left out.
|
|
106
|
+
export async function readWorkItems(ask, ids, { fields, relations = false }) {
|
|
107
|
+
const found = [];
|
|
108
|
+
for (let at = 0; at < ids.length; at += BATCH) {
|
|
109
|
+
const batch = ids.slice(at, at + BATCH);
|
|
110
|
+
// ADO takes either fields or an expand, not both.
|
|
111
|
+
const body = { errorPolicy: 'Omit', ids: batch, ...(relations ? { $expand: 'Relations' } : { fields }) };
|
|
112
|
+
const answer = expectOk(await ask('wit/workitemsbatch', { body, method: 'POST' }), 'wit/workitemsbatch');
|
|
113
|
+
found.push(...z.object({ value: z.array(workItem.nullable()) }).parse(answer).value.flatMap((w) => (w ? [w] : [])));
|
|
114
|
+
}
|
|
115
|
+
return found;
|
|
116
|
+
}
|
|
117
|
+
export const workItem = z.looseObject({
|
|
118
|
+
fields: z.record(z.string(), z.unknown()),
|
|
119
|
+
id: z.number(),
|
|
120
|
+
relations: z.array(z.looseObject({ rel: z.string(), url: z.string() })).nullish(),
|
|
121
|
+
rev: z.number(),
|
|
122
|
+
});
|
|
123
|
+
const commentPage = z.looseObject({
|
|
124
|
+
comments: z.array(z.looseObject({
|
|
125
|
+
createdBy: z.looseObject({ id: z.string() }).nullish(),
|
|
126
|
+
createdDate: z.string(),
|
|
127
|
+
format: z.string().nullish(),
|
|
128
|
+
id: z.number(),
|
|
129
|
+
isDeleted: z.boolean().optional(),
|
|
130
|
+
text: z.string().nullish(),
|
|
131
|
+
})),
|
|
132
|
+
continuationToken: z.string().nullish(),
|
|
133
|
+
});
|
|
134
|
+
const connection = z.looseObject({ authenticatedUser: z.looseObject({ id: z.string() }) });
|
|
135
|
+
// A work item's discussion comments, oldest first, without deleted ones.
|
|
136
|
+
export async function readComments(ask, id) {
|
|
137
|
+
const comments = [];
|
|
138
|
+
for (let first = true, token = null; first || token; first = false) {
|
|
139
|
+
const path = `wit/workItems/${id}/comments?$top=200${token ? `&continuationToken=${encodeURIComponent(token)}` : ''}`;
|
|
140
|
+
const page = commentPage.parse(expectOk(await ask(path, { version: '7.1-preview.4' }), `the comments of work item ${id}`));
|
|
141
|
+
for (const c of page.comments) {
|
|
142
|
+
if (!c.isDeleted)
|
|
143
|
+
comments.push({ authorId: c.createdBy?.id ?? null, body: commentText(c.text, c.format), createdAt: c.createdDate });
|
|
144
|
+
}
|
|
145
|
+
token = page.continuationToken ?? null;
|
|
146
|
+
}
|
|
147
|
+
return comments;
|
|
148
|
+
}
|
|
149
|
+
// The az login's identity, whose `approve` / `revise:` counts.
|
|
150
|
+
export async function readViewer(ask) {
|
|
151
|
+
return connection.parse(expectOk(await ask('connectionData', { organization: true, version: '7.1-preview' }), 'connectionData')).authenticatedUser.id;
|
|
152
|
+
}
|
|
153
|
+
const WAIT_FIELDS = ['System.Id', 'System.Rev', 'System.State', 'System.Tags', 'System.CommentCount'];
|
|
154
|
+
// The approval and item waits on Boards items, for the ADO source. Each pass reads every waited
|
|
155
|
+
// item of a board in one batch (state, tags, rev, comment count), and reads an item's comments only
|
|
156
|
+
// when a wait needs them and they changed since the last read. State lives in memory.
|
|
157
|
+
export function boardsWaits(options = {}) {
|
|
158
|
+
const boards = new Map();
|
|
159
|
+
return {
|
|
160
|
+
check: async (waits, budget, outcomes) => {
|
|
161
|
+
const byBoard = new Map();
|
|
162
|
+
for (const wait of waits) {
|
|
163
|
+
const c = wait.condition;
|
|
164
|
+
if ((c.kind !== 'approval' && c.kind !== 'item') || c.tracker !== 'ado-boards')
|
|
165
|
+
continue;
|
|
166
|
+
const board = boardOf(wait.profile);
|
|
167
|
+
if (!board) {
|
|
168
|
+
outcomes.set(wait.id, { error: "the project's profile has no ADO Boards tracker" });
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
// By ADO project and key: two conducted projects can share an ADO project.
|
|
172
|
+
const at = `${board.base} ${board.key}`;
|
|
173
|
+
const group = byBoard.get(at) ?? { board, waits: [] };
|
|
174
|
+
group.waits.push(wait);
|
|
175
|
+
byBoard.set(at, group);
|
|
176
|
+
}
|
|
177
|
+
for (const [base, group] of byBoard) {
|
|
178
|
+
try {
|
|
179
|
+
await checkBoard(base, group, budget, outcomes);
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
// A board ADO refuses (a 400 or 404: a project or item it doesn't have) is a problem with
|
|
183
|
+
// its waits alone, so the PR and run waits of the same pass go on.
|
|
184
|
+
if (!(error instanceof WaitProblem))
|
|
185
|
+
throw error;
|
|
186
|
+
for (const wait of group.waits)
|
|
187
|
+
if (!outcomes.has(wait.id))
|
|
188
|
+
outcomes.set(wait.id, { error: error.message });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
async function checkBoard(base, group, budget, outcomes) {
|
|
194
|
+
const { board } = group;
|
|
195
|
+
const ask = boardsAsk(options, board, budget);
|
|
196
|
+
const state = boards.get(base) ?? { items: new Map(), viewer: null };
|
|
197
|
+
boards.set(base, state);
|
|
198
|
+
const idOf = new Map(group.waits.map((w) => [w.id, workItemId(itemOf(w), board)]));
|
|
199
|
+
const ids = [...new Set([...idOf.values()].flatMap((id) => (id === null ? [] : [id])))];
|
|
200
|
+
const read = await readWorkItems(ask, ids, { fields: WAIT_FIELDS });
|
|
201
|
+
const seen = new Map(read.map((w) => [w.id, w]));
|
|
202
|
+
// Only the items something waits on are kept, with their comments.
|
|
203
|
+
for (const id of state.items.keys())
|
|
204
|
+
if (!ids.includes(id))
|
|
205
|
+
state.items.delete(id);
|
|
206
|
+
for (const id of ids) {
|
|
207
|
+
const w = seen.get(id);
|
|
208
|
+
if (!w) {
|
|
209
|
+
state.items.delete(id);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
const was = state.items.get(id);
|
|
213
|
+
const version = `${w.rev}/${String(w.fields['System.CommentCount'] ?? 0)}`;
|
|
214
|
+
state.items.set(id, {
|
|
215
|
+
comments: was?.comments ?? null,
|
|
216
|
+
readAt: was?.readAt ?? null,
|
|
217
|
+
state: String(w.fields['System.State'] ?? ''),
|
|
218
|
+
tags: tagsOf(w.fields['System.Tags']),
|
|
219
|
+
version,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
const readsNotes = (wait) => wait.condition.kind === 'approval' || (wait.condition.kind === 'item' && wait.condition.field === 'note');
|
|
223
|
+
if (state.viewer === null && group.waits.some((w) => w.condition.kind === 'approval'))
|
|
224
|
+
state.viewer = await readViewer(ask);
|
|
225
|
+
for (const wait of group.waits) {
|
|
226
|
+
const c = wait.condition;
|
|
227
|
+
if (c.kind !== 'approval' && c.kind !== 'item')
|
|
228
|
+
continue;
|
|
229
|
+
await perWait(outcomes, wait.id, async () => {
|
|
230
|
+
const id = idOf.get(wait.id) ?? null;
|
|
231
|
+
if (id === null)
|
|
232
|
+
throw new WaitProblem(`${c.item} isn't an item of the board keyed ${board.key}`);
|
|
233
|
+
const item = state.items.get(id);
|
|
234
|
+
if (!item)
|
|
235
|
+
throw new WaitProblem(`Azure DevOps Boards has no work item ${id} (${c.item})`);
|
|
236
|
+
if (readsNotes(wait) && (item.comments === null || item.readAt !== item.version)) {
|
|
237
|
+
item.comments = await readComments(ask, id);
|
|
238
|
+
item.readAt = item.version;
|
|
239
|
+
}
|
|
240
|
+
const prefixes = notePrefixes(wait.profile);
|
|
241
|
+
if (c.kind === 'approval') {
|
|
242
|
+
const found = approvalAfterPlan(item.comments ?? [], state.viewer, prefixes);
|
|
243
|
+
return found ? `approval ${c.item}: ${found.kind}` : null;
|
|
244
|
+
}
|
|
245
|
+
const value = c.value.toLowerCase();
|
|
246
|
+
if (c.field === 'state') {
|
|
247
|
+
const type = stateType(item.state, board);
|
|
248
|
+
return item.state.toLowerCase() === value || type === value ? `item ${c.item} state ${item.state}` : null;
|
|
249
|
+
}
|
|
250
|
+
if (c.field === 'label') {
|
|
251
|
+
const tag = item.tags.find((t) => t.toLowerCase() === value);
|
|
252
|
+
return tag ? `item ${c.item} labelled ${tag}` : null;
|
|
253
|
+
}
|
|
254
|
+
const note = (item.comments ?? []).map((x) => phaseOf(x.body, prefixes)).find((p) => p?.toLowerCase() === value);
|
|
255
|
+
return note ? `item ${c.item} note ${note}` : null;
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// A ConductorError for a project whose tracker isn't ADO Boards.
|
|
261
|
+
export function requireBoard(profile) {
|
|
262
|
+
const board = boardOf(profile);
|
|
263
|
+
if (!board)
|
|
264
|
+
throw new ConductorError(`project ${profile?.name ?? '?'} has no ADO Boards tracker`);
|
|
265
|
+
return board;
|
|
266
|
+
}
|
|
267
|
+
function itemOf(wait) {
|
|
268
|
+
const c = wait.condition;
|
|
269
|
+
return c.kind === 'approval' || c.kind === 'item' ? c.item : '';
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=boards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boards.js","sourceRoot":"","sources":["../../src/watchers/boards.ts"],"names":[],"mappings":"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"}
|
package/build/watchers/github.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { notePrefixes } from "../profile.js";
|
|
2
3
|
import { outputOf, run } from "../run.js";
|
|
4
|
+
import { noteHeading } from "./linear.js";
|
|
3
5
|
import { perPass, perWait, untilSpent, WaitProblem } from "./source.js";
|
|
4
6
|
const pull = z.looseObject({
|
|
5
7
|
head: z.looseObject({ sha: z.string() }),
|
|
@@ -56,9 +58,10 @@ export function githubSource({ gh = ghApi() } = {}) {
|
|
|
56
58
|
const failed = runs.filter((r) => !PASSING.has(r.conclusion ?? '')).map((r) => r.name);
|
|
57
59
|
return failed.length > 0 ? `${at} checks failed: ${failed.join(', ')}` : `${at} checks passed`;
|
|
58
60
|
}
|
|
59
|
-
const
|
|
61
|
+
const notes = notePrefixes(wait.profile);
|
|
60
62
|
const since = Date.parse(wait.createdAt);
|
|
61
|
-
const
|
|
63
|
+
const isNote = (body) => noteHeading(body ?? '', notes) !== null;
|
|
64
|
+
const byPerson = (u, body, when) => u?.type !== 'Bot' && when != null && Date.parse(when) > since && !isNote(body);
|
|
62
65
|
// The comment lists come oldest first, so they're asked from the wait on (`since`), which
|
|
63
66
|
// keeps a new comment on the first page. Reviews can't be asked that way; a PR has few.
|
|
64
67
|
const after = `per_page=100&since=${encodeURIComponent(wait.createdAt)}`;
|