@clipboard-health/groundcrew 4.18.1 → 4.18.2
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/commands/reviewer.d.ts +18 -15
- package/dist/commands/reviewer.d.ts.map +1 -1
- package/dist/commands/reviewer.js +76 -42
- package/dist/lib/adapters/shell/factory.d.ts +1 -0
- package/dist/lib/adapters/shell/factory.d.ts.map +1 -1
- package/dist/lib/adapters/shell/factory.js +13 -0
- package/dist/lib/adapters/shell/schema.d.ts +2 -0
- package/dist/lib/adapters/shell/schema.d.ts.map +1 -1
- package/dist/lib/adapters/shell/schema.js +2 -0
- package/dist/lib/board.d.ts +7 -1
- package/dist/lib/board.d.ts.map +1 -1
- package/dist/lib/board.js +10 -0
- package/dist/lib/pullRequests.d.ts +2 -0
- package/dist/lib/pullRequests.d.ts.map +1 -1
- package/dist/lib/pullRequests.js +21 -15
- package/dist/lib/ticketSource.d.ts +16 -0
- package/dist/lib/ticketSource.d.ts.map +1 -1
- package/docs/ticket-sources.md +11 -7
- package/package.json +1 -1
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Per-iteration scanner that advances
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* transition frees a dispatch slot (the slot math counts only in-progress)
|
|
6
|
-
* while leaving the worktree intact for review, since the cleaner only tears
|
|
7
|
-
* down `done` tickets.
|
|
2
|
+
* Per-iteration scanner that advances a ticket based on its worktree's pull
|
|
3
|
+
* request state. Sits between the cleaner and the dispatcher in each
|
|
4
|
+
* `orchestrate()` tick.
|
|
8
5
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
6
|
+
* - An **open** PR on an **in-progress** ticket → `markInReview`: frees a
|
|
7
|
+
* dispatch slot (slot math counts only in-progress) while leaving the
|
|
8
|
+
* worktree intact for review, since the cleaner only tears down `done`.
|
|
9
|
+
* - A **merged** PR (on an in-progress or in-review ticket) → `markDone`:
|
|
10
|
+
* the work has landed, so the ticket is terminal and the cleaner tears the
|
|
11
|
+
* worktree down on a later tick. `merged` never routes to `in-review`.
|
|
14
12
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
13
|
+
* Sources that don't implement `markDone` (e.g. Linear) return `unsupported`;
|
|
14
|
+
* the reviewer logs the skip and does nothing — there is no in-review
|
|
15
|
+
* fallback. (Linear's own GitHub integration moves merged issues to Done,
|
|
16
|
+
* which groundcrew observes via `fetch()`.)
|
|
17
|
+
*
|
|
18
|
+
* The write-back lands in the ticket source, not the in-memory `BoardState`,
|
|
19
|
+
* so the dispatcher in the SAME tick still sees prior state; the slot frees on
|
|
20
|
+
* the NEXT tick's `board.fetch()`. That one-tick latency is deliberate. One
|
|
21
|
+
* per `orchestrate()`; stateless across iterations. Mirrors `Cleaner`.
|
|
19
22
|
*/
|
|
20
23
|
import type { Board } from "../lib/board.ts";
|
|
21
24
|
import type { PullRequestSummary } from "../lib/pullRequests.ts";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reviewer.d.ts","sourceRoot":"","sources":["../../src/commands/reviewer.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"reviewer.d.ts","sourceRoot":"","sources":["../../src/commands/reviewer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EACL,KAAK,UAAU,EAIhB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,UAAU,EAAE;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,KAAK,OAAO,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAAC;AAE7C,UAAU,YAAY;IACpB,KAAK,EAAE,KAAK,CAAC;IACb,gBAAgB,EAAE,gBAAgB,CAAC;CACpC;AAED,sEAAsE;AACtE,UAAU,eAAe;IACvB,KAAK,EAAE,UAAU,CAAC;IAClB,eAAe,EAAE,SAAS,aAAa,EAAE,CAAC;IAC1C,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,CAAC,UAAU,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD;AA+CD,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,QAAQ,CAwH3D"}
|
|
@@ -1,28 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Per-iteration scanner that advances
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* transition frees a dispatch slot (the slot math counts only in-progress)
|
|
6
|
-
* while leaving the worktree intact for review, since the cleaner only tears
|
|
7
|
-
* down `done` tickets.
|
|
2
|
+
* Per-iteration scanner that advances a ticket based on its worktree's pull
|
|
3
|
+
* request state. Sits between the cleaner and the dispatcher in each
|
|
4
|
+
* `orchestrate()` tick.
|
|
8
5
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
6
|
+
* - An **open** PR on an **in-progress** ticket → `markInReview`: frees a
|
|
7
|
+
* dispatch slot (slot math counts only in-progress) while leaving the
|
|
8
|
+
* worktree intact for review, since the cleaner only tears down `done`.
|
|
9
|
+
* - A **merged** PR (on an in-progress or in-review ticket) → `markDone`:
|
|
10
|
+
* the work has landed, so the ticket is terminal and the cleaner tears the
|
|
11
|
+
* worktree down on a later tick. `merged` never routes to `in-review`.
|
|
14
12
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
13
|
+
* Sources that don't implement `markDone` (e.g. Linear) return `unsupported`;
|
|
14
|
+
* the reviewer logs the skip and does nothing — there is no in-review
|
|
15
|
+
* fallback. (Linear's own GitHub integration moves merged issues to Done,
|
|
16
|
+
* which groundcrew observes via `fetch()`.)
|
|
17
|
+
*
|
|
18
|
+
* The write-back lands in the ticket source, not the in-memory `BoardState`,
|
|
19
|
+
* so the dispatcher in the SAME tick still sees prior state; the slot frees on
|
|
20
|
+
* the NEXT tick's `board.fetch()`. That one-tick latency is deliberate. One
|
|
21
|
+
* per `orchestrate()`; stateless across iterations. Mirrors `Cleaner`.
|
|
19
22
|
*/
|
|
20
|
-
import { naturalIdFromCanonical } from "../lib/ticketSource.js";
|
|
23
|
+
import { naturalIdFromCanonical, } from "../lib/ticketSource.js";
|
|
21
24
|
import { debug, errorMessage, log, logEvent } from "../lib/util.js";
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
// Maps a worktree's PRs to the transition its ticket should make. A merged PR
|
|
26
|
+
// means the work landed → done; an open PR on an in-progress ticket means it's
|
|
27
|
+
// up for review. `merged` wins over `open`. An open PR on an already in-review
|
|
28
|
+
// ticket is a no-op (returns undefined). Closed-only PRs are ignored.
|
|
29
|
+
function intendedTransition(pullRequests, status) {
|
|
30
|
+
if (pullRequests.some((pr) => pr.state === "merged")) {
|
|
31
|
+
return "done";
|
|
32
|
+
}
|
|
33
|
+
if (status === "in-progress" && pullRequests.some((pr) => pr.state === "open")) {
|
|
34
|
+
return "in-review";
|
|
35
|
+
}
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
// The PR to name in logs for a transition: the merged one for `done`, the open
|
|
39
|
+
// one for `in-review`. Guaranteed to exist because intendedTransition only
|
|
40
|
+
// returns a transition when a PR of the matching state is present.
|
|
41
|
+
function pullRequestForTransition(pullRequests, transition) {
|
|
42
|
+
const state = transition === "done" ? "merged" : "open";
|
|
43
|
+
// oxlint-disable-next-line typescript/no-non-null-assertion -- intendedTransition guarantees a PR of this state exists
|
|
44
|
+
return pullRequests.find((pr) => pr.state === state);
|
|
26
45
|
}
|
|
27
46
|
function matchingWorktreeEntries(arguments_) {
|
|
28
47
|
const { issue, worktreeEntries, ticket } = arguments_;
|
|
@@ -35,12 +54,12 @@ export function createReviewer(deps) {
|
|
|
35
54
|
const { board, findPullRequests } = deps;
|
|
36
55
|
async function runOnce(arguments_) {
|
|
37
56
|
const { state, worktreeEntries, dryRun, signal } = arguments_;
|
|
38
|
-
const
|
|
39
|
-
if (
|
|
57
|
+
const candidates = state.issues.filter((issue) => issue.status === "in-progress" || issue.status === "in-review");
|
|
58
|
+
if (candidates.length === 0) {
|
|
40
59
|
return;
|
|
41
60
|
}
|
|
42
|
-
for (const issue of
|
|
43
|
-
// oxlint-disable-next-line no-await-in-loop --
|
|
61
|
+
for (const issue of candidates) {
|
|
62
|
+
// oxlint-disable-next-line no-await-in-loop -- few candidates per tick; sequential keeps gh load low.
|
|
44
63
|
await advanceIfReviewable({
|
|
45
64
|
issue,
|
|
46
65
|
worktreeEntries,
|
|
@@ -49,9 +68,10 @@ export function createReviewer(deps) {
|
|
|
49
68
|
});
|
|
50
69
|
}
|
|
51
70
|
}
|
|
52
|
-
// Idempotent after an applied transition: once advanced, the issue leaves
|
|
53
|
-
//
|
|
54
|
-
// are skipped without claiming success and may retry
|
|
71
|
+
// Idempotent after an applied transition: once advanced, the issue leaves the
|
|
72
|
+
// in-progress/in-review candidate set, so it never reaches this scan again.
|
|
73
|
+
// Unsupported writebacks are skipped without claiming success and may retry
|
|
74
|
+
// on later ticks.
|
|
55
75
|
async function advanceIfReviewable(arguments_) {
|
|
56
76
|
const { issue, worktreeEntries, dryRun, signal } = arguments_;
|
|
57
77
|
const ticket = naturalIdFromCanonical(issue.id);
|
|
@@ -59,8 +79,8 @@ export function createReviewer(deps) {
|
|
|
59
79
|
for (const entry of entries) {
|
|
60
80
|
// The injected lookup is contracted never to reject (failures resolve to
|
|
61
81
|
// []), but we still guard it so one bad lookup can never abort the tick
|
|
62
|
-
// and starve the other
|
|
63
|
-
//
|
|
82
|
+
// and starve the other candidates. A failure means "can't tell yet" →
|
|
83
|
+
// skip this worktree and retry next tick.
|
|
64
84
|
let pullRequests;
|
|
65
85
|
try {
|
|
66
86
|
// oxlint-disable-next-line no-await-in-loop -- a ticket almost always has one worktree; sequential lookups are fine.
|
|
@@ -74,47 +94,61 @@ export function createReviewer(deps) {
|
|
|
74
94
|
debug(`PR lookup failed for ${ticket} (${entry.branchName}): ${errorMessage(error)}`);
|
|
75
95
|
continue;
|
|
76
96
|
}
|
|
77
|
-
const
|
|
78
|
-
if (
|
|
97
|
+
const transition = intendedTransition(pullRequests, issue.status);
|
|
98
|
+
if (transition === undefined) {
|
|
79
99
|
continue;
|
|
80
100
|
}
|
|
101
|
+
const pullRequest = pullRequestForTransition(pullRequests, transition);
|
|
81
102
|
if (dryRun) {
|
|
82
|
-
log(`[dry-run] Would advance ${ticket} to
|
|
83
|
-
logEvent("review", {
|
|
103
|
+
log(`[dry-run] Would advance ${ticket} to ${transition} (PR ${pullRequest.url})`);
|
|
104
|
+
logEvent("review", {
|
|
105
|
+
outcome: "skipped",
|
|
106
|
+
reason: "dry_run",
|
|
107
|
+
ticket,
|
|
108
|
+
pr: pullRequest.url,
|
|
109
|
+
to: transition,
|
|
110
|
+
});
|
|
84
111
|
return;
|
|
85
112
|
}
|
|
86
|
-
// oxlint-disable-next-line no-await-in-loop -- single write-back then return; never iterates past the first
|
|
87
|
-
await advance({ issue, ticket, pullRequest
|
|
113
|
+
// oxlint-disable-next-line no-await-in-loop -- single write-back then return; never iterates past the first actionable worktree.
|
|
114
|
+
await advance({ issue, ticket, pullRequest, transition });
|
|
88
115
|
return;
|
|
89
116
|
}
|
|
90
117
|
}
|
|
91
118
|
// A writeback failure (shell/Linear error) is logged and swallowed: the
|
|
92
|
-
// ticket
|
|
119
|
+
// ticket keeps its status and is retried next tick, exactly like a failed
|
|
93
120
|
// lookup. We never let one ticket's writeback abort the others' reviews.
|
|
94
121
|
async function advance(arguments_) {
|
|
95
|
-
const { issue, ticket, pullRequest } = arguments_;
|
|
122
|
+
const { issue, ticket, pullRequest, transition } = arguments_;
|
|
96
123
|
try {
|
|
97
|
-
const result = await board.markInReview(issue);
|
|
124
|
+
const result = transition === "done" ? await board.markDone(issue) : await board.markInReview(issue);
|
|
98
125
|
if (result.outcome === "unsupported") {
|
|
99
|
-
log(`Skipped advancing ${ticket} to
|
|
126
|
+
log(`Skipped advancing ${ticket} to ${transition}: ${result.reason}`);
|
|
100
127
|
logEvent("review", {
|
|
101
128
|
outcome: "skipped",
|
|
102
129
|
reason: "unsupported",
|
|
103
130
|
ticket,
|
|
131
|
+
to: transition,
|
|
104
132
|
});
|
|
105
133
|
return;
|
|
106
134
|
}
|
|
107
|
-
log(`Advanced ${ticket} to
|
|
135
|
+
log(`Advanced ${ticket} to ${transition} (PR ${pullRequest.url})`);
|
|
108
136
|
logEvent("review", {
|
|
109
137
|
outcome: "advanced",
|
|
110
138
|
ticket,
|
|
111
139
|
pr: pullRequest.url,
|
|
112
140
|
state: pullRequest.state,
|
|
141
|
+
to: transition,
|
|
113
142
|
});
|
|
114
143
|
}
|
|
115
144
|
catch (error) {
|
|
116
|
-
log(`Failed to advance ${ticket} to
|
|
117
|
-
logEvent("review", {
|
|
145
|
+
log(`Failed to advance ${ticket} to ${transition}: ${errorMessage(error)}`);
|
|
146
|
+
logEvent("review", {
|
|
147
|
+
outcome: "failed",
|
|
148
|
+
reason: "writeback_failed",
|
|
149
|
+
ticket,
|
|
150
|
+
to: transition,
|
|
151
|
+
});
|
|
118
152
|
}
|
|
119
153
|
}
|
|
120
154
|
return { runOnce };
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* in MVP-2.
|
|
13
13
|
* - `markInProgress` absent → silent no-op.
|
|
14
14
|
* - `markInReview` absent → reports unsupported.
|
|
15
|
+
* - `markDone` absent → reports unsupported.
|
|
15
16
|
* - `fetch` is required by the Zod schema.
|
|
16
17
|
*/
|
|
17
18
|
import type { AdapterContext } from "../../adapterDefinition.ts";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/shell/factory.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/shell/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAGL,KAAK,KAAK,IAAI,cAAc,EAG5B,KAAK,YAAY,EAClB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,KAAK,kBAAkB,EAEvB,KAAK,UAAU,EAEhB,MAAM,aAAa,CAAC;AA+BrB,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,cAAc,CAuB3F;AAED,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE,cAAc,GACvB,YAAY,CA8Gd"}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* in MVP-2.
|
|
13
13
|
* - `markInProgress` absent → silent no-op.
|
|
14
14
|
* - `markInReview` absent → reports unsupported.
|
|
15
|
+
* - `markDone` absent → reports unsupported.
|
|
15
16
|
* - `fetch` is required by the Zod schema.
|
|
16
17
|
*/
|
|
17
18
|
import { toCanonicalId, } from "../../ticketSource.js";
|
|
@@ -23,6 +24,7 @@ const DEFAULT_TIMEOUTS = {
|
|
|
23
24
|
resolveOne: 10_000,
|
|
24
25
|
markInProgress: 10_000,
|
|
25
26
|
markInReview: 10_000,
|
|
27
|
+
markDone: 10_000,
|
|
26
28
|
};
|
|
27
29
|
function mergeTimeouts(overrides) {
|
|
28
30
|
return {
|
|
@@ -31,6 +33,7 @@ function mergeTimeouts(overrides) {
|
|
|
31
33
|
resolveOne: overrides?.resolveOne ?? DEFAULT_TIMEOUTS.resolveOne,
|
|
32
34
|
markInProgress: overrides?.markInProgress ?? DEFAULT_TIMEOUTS.markInProgress,
|
|
33
35
|
markInReview: overrides?.markInReview ?? DEFAULT_TIMEOUTS.markInReview,
|
|
36
|
+
markDone: overrides?.markDone ?? DEFAULT_TIMEOUTS.markDone,
|
|
34
37
|
};
|
|
35
38
|
}
|
|
36
39
|
export function toCanonicalIssue(shellIssue, sourceName) {
|
|
@@ -149,5 +152,15 @@ export function createShellTicketSource(config, _context) {
|
|
|
149
152
|
await invokeWriteback(config.commands.markInReview, timeouts.markInReview, issue);
|
|
150
153
|
return { outcome: "applied" };
|
|
151
154
|
},
|
|
155
|
+
async markDone(issue) {
|
|
156
|
+
if (config.commands.markDone === undefined) {
|
|
157
|
+
return {
|
|
158
|
+
outcome: "unsupported",
|
|
159
|
+
reason: `shell source "${sourceName}" has no commands.markDone configured`,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
await invokeWriteback(config.commands.markDone, timeouts.markDone, issue);
|
|
163
|
+
return { outcome: "applied" };
|
|
164
|
+
},
|
|
152
165
|
};
|
|
153
166
|
}
|
|
@@ -89,6 +89,7 @@ export declare const shellAdapterConfigSchema: z.ZodObject<{
|
|
|
89
89
|
resolveOne: z.ZodOptional<z.ZodString>;
|
|
90
90
|
markInProgress: z.ZodOptional<z.ZodString>;
|
|
91
91
|
markInReview: z.ZodOptional<z.ZodString>;
|
|
92
|
+
markDone: z.ZodOptional<z.ZodString>;
|
|
92
93
|
}, z.core.$strip>;
|
|
93
94
|
cwd: z.ZodOptional<z.ZodString>;
|
|
94
95
|
timeouts: z.ZodOptional<z.ZodObject<{
|
|
@@ -97,6 +98,7 @@ export declare const shellAdapterConfigSchema: z.ZodObject<{
|
|
|
97
98
|
resolveOne: z.ZodOptional<z.ZodNumber>;
|
|
98
99
|
markInProgress: z.ZodOptional<z.ZodNumber>;
|
|
99
100
|
markInReview: z.ZodOptional<z.ZodNumber>;
|
|
101
|
+
markDone: z.ZodOptional<z.ZodNumber>;
|
|
100
102
|
}, z.core.$strip>>;
|
|
101
103
|
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
102
104
|
}, z.core.$strip>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/shell/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiB3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAA4B,CAAC;AAEhE,eAAO,MAAM,wBAAwB
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/shell/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiB3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAA4B,CAAC;AAEhE,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;iBA4BnC,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC"}
|
|
@@ -47,6 +47,7 @@ export const shellAdapterConfigSchema = z.object({
|
|
|
47
47
|
resolveOne: z.string().optional(),
|
|
48
48
|
markInProgress: z.string().optional(),
|
|
49
49
|
markInReview: z.string().optional(),
|
|
50
|
+
markDone: z.string().optional(),
|
|
50
51
|
}),
|
|
51
52
|
cwd: z.string().optional(),
|
|
52
53
|
timeouts: z
|
|
@@ -59,6 +60,7 @@ export const shellAdapterConfigSchema = z.object({
|
|
|
59
60
|
resolveOne: z.number().int().positive().optional(),
|
|
60
61
|
markInProgress: z.number().int().positive().optional(),
|
|
61
62
|
markInReview: z.number().int().positive().optional(),
|
|
63
|
+
markDone: z.number().int().positive().optional(),
|
|
62
64
|
})
|
|
63
65
|
.optional(),
|
|
64
66
|
env: z.record(z.string(), z.string()).optional(),
|
package/dist/lib/board.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* goes through this; the moment we skip the wrapper we grow Linear assumptions
|
|
5
5
|
* back into consumers.
|
|
6
6
|
*/
|
|
7
|
-
import { type BoardState, type Issue, type MarkInReviewResult, type TicketSource } from "./ticketSource.ts";
|
|
7
|
+
import { type BoardState, type Issue, type MarkDoneResult, type MarkInReviewResult, type TicketSource } from "./ticketSource.ts";
|
|
8
8
|
export interface Board {
|
|
9
9
|
verify(): Promise<void>;
|
|
10
10
|
fetch(): Promise<BoardState>;
|
|
@@ -21,6 +21,12 @@ export interface Board {
|
|
|
21
21
|
* return `unsupported` (see `TicketSource.markInReview`).
|
|
22
22
|
*/
|
|
23
23
|
markInReview(issue: Issue): Promise<MarkInReviewResult>;
|
|
24
|
+
/**
|
|
25
|
+
* Advances a ticket to done on the adapter whose `name` matches
|
|
26
|
+
* `issue.source`. Unknown source throws. Sources that don't implement the
|
|
27
|
+
* optional `markDone` return `unsupported` (see `TicketSource.markDone`).
|
|
28
|
+
*/
|
|
29
|
+
markDone(issue: Issue): Promise<MarkDoneResult>;
|
|
24
30
|
}
|
|
25
31
|
export declare function createBoard(sources: readonly TicketSource[]): Board;
|
|
26
32
|
//# sourceMappingURL=board.d.ts.map
|
package/dist/lib/board.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"board.d.ts","sourceRoot":"","sources":["../../src/lib/board.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,KAAK,EACV,KAAK,kBAAkB,EAEvB,KAAK,YAAY,EAClB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,KAAK;IACpB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;IAC7B;;;OAGG;IACH,UAAU,CAAC,oBAAoB,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;IACrE,wFAAwF;IACxF,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C;;;;OAIG;IACH,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"board.d.ts","sourceRoot":"","sources":["../../src/lib/board.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,KAAK,EACV,KAAK,cAAc,EACnB,KAAK,kBAAkB,EAEvB,KAAK,YAAY,EAClB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,KAAK;IACpB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;IAC7B;;;OAGG;IACH,UAAU,CAAC,oBAAoB,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;IACrE,wFAAwF;IACxF,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C;;;;OAIG;IACH,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACxD;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CACjD;AAqBD,wBAAgB,WAAW,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,KAAK,CAmHnE"}
|
package/dist/lib/board.js
CHANGED
|
@@ -111,6 +111,16 @@ export function createBoard(sources) {
|
|
|
111
111
|
async markInReview(issue) {
|
|
112
112
|
return await routeWriteback(byName, issue).markInReview(issue);
|
|
113
113
|
},
|
|
114
|
+
async markDone(issue) {
|
|
115
|
+
const source = routeWriteback(byName, issue);
|
|
116
|
+
if (source.markDone === undefined) {
|
|
117
|
+
return {
|
|
118
|
+
outcome: "unsupported",
|
|
119
|
+
reason: `source "${source.name}" does not support markDone`,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return await source.markDone(issue);
|
|
123
|
+
},
|
|
114
124
|
};
|
|
115
125
|
}
|
|
116
126
|
/**
|
|
@@ -15,6 +15,8 @@ export interface PullRequestSummary {
|
|
|
15
15
|
/** Lowercased lifecycle: "open" | "merged" | "closed". */
|
|
16
16
|
state: string;
|
|
17
17
|
title: string;
|
|
18
|
+
/** PR head commit SHA used to ignore historical PRs from reused branch names. */
|
|
19
|
+
headRefOid: string;
|
|
18
20
|
}
|
|
19
21
|
interface LookupArgs {
|
|
20
22
|
/** Worktree directory; `gh` resolves the GitHub repo from its git remote. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pullRequests.d.ts","sourceRoot":"","sources":["../../src/lib/pullRequests.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"pullRequests.d.ts","sourceRoot":"","sources":["../../src/lib/pullRequests.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,UAAU,EAAE,MAAM,CAAC;CACpB;AASD,UAAU,UAAU;IAClB,6EAA6E;IAC7E,GAAG,EAAE,MAAM,CAAC;IACZ,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAmDD,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,SAAS,kBAAkB,EAAE,CAAC,CA6BxC"}
|
package/dist/lib/pullRequests.js
CHANGED
|
@@ -37,6 +37,7 @@ function parsePullRequests(output) {
|
|
|
37
37
|
number: entry.number,
|
|
38
38
|
state: STATE_MAP[entry.state] ?? entry.state.toLowerCase(),
|
|
39
39
|
title: entry.title,
|
|
40
|
+
headRefOid: entry.headRefOid,
|
|
40
41
|
});
|
|
41
42
|
}
|
|
42
43
|
return summaries;
|
|
@@ -50,27 +51,32 @@ function isRawPullRequest(value) {
|
|
|
50
51
|
return (typeof record["url"] === "string" &&
|
|
51
52
|
typeof record["number"] === "number" &&
|
|
52
53
|
typeof record["state"] === "string" &&
|
|
53
|
-
typeof record["title"] === "string"
|
|
54
|
+
typeof record["title"] === "string" &&
|
|
55
|
+
typeof record["headRefOid"] === "string");
|
|
54
56
|
}
|
|
55
57
|
export async function findPullRequestsForBranch(arguments_) {
|
|
56
58
|
const { cwd, branchName, signal } = arguments_;
|
|
59
|
+
const options = signal === undefined ? { cwd } : { cwd, signal };
|
|
57
60
|
try {
|
|
58
|
-
const output = await
|
|
59
|
-
"
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
61
|
+
const [output, currentHeadOid] = await Promise.all([
|
|
62
|
+
runCommandAsync("gh", [
|
|
63
|
+
"pr",
|
|
64
|
+
"list",
|
|
65
|
+
"--head",
|
|
66
|
+
branchName,
|
|
67
|
+
"--state",
|
|
68
|
+
"all",
|
|
69
|
+
"--limit",
|
|
70
|
+
String(GH_PR_LIST_LIMIT),
|
|
71
|
+
"--json",
|
|
72
|
+
"url,number,state,title,headRefOid",
|
|
73
|
+
], options),
|
|
74
|
+
runCommandAsync("git", ["rev-parse", "HEAD"], options),
|
|
75
|
+
]);
|
|
76
|
+
return parsePullRequests(output).filter((pr) => pr.headRefOid === currentHeadOid);
|
|
71
77
|
}
|
|
72
78
|
catch {
|
|
73
|
-
// gh not installed / not authenticated / non-GitHub remote / network
|
|
79
|
+
// gh/git not installed / not authenticated / non-GitHub remote / network
|
|
74
80
|
// error / etc. All resolve to "no PR info available" for display.
|
|
75
81
|
return [];
|
|
76
82
|
}
|
|
@@ -116,6 +116,12 @@ export type MarkInReviewResult = {
|
|
|
116
116
|
outcome: "unsupported";
|
|
117
117
|
reason: string;
|
|
118
118
|
};
|
|
119
|
+
export type MarkDoneResult = {
|
|
120
|
+
outcome: "applied";
|
|
121
|
+
} | {
|
|
122
|
+
outcome: "unsupported";
|
|
123
|
+
reason: string;
|
|
124
|
+
};
|
|
119
125
|
export interface TicketSource {
|
|
120
126
|
/** Stable identifier used as the id prefix and in log lines. Equal to the source's config `name`. */
|
|
121
127
|
readonly name: string;
|
|
@@ -136,6 +142,16 @@ export interface TicketSource {
|
|
|
136
142
|
* rather than pretending the transition happened.
|
|
137
143
|
*/
|
|
138
144
|
markInReview(issue: Issue): Promise<MarkInReviewResult>;
|
|
145
|
+
/**
|
|
146
|
+
* Optional writeback: advance a ticket to done once its PR has merged.
|
|
147
|
+
* Sources without a native/configured done transition omit this method; the
|
|
148
|
+
* Board treats an absent method as `{ outcome: "unsupported" }` so the
|
|
149
|
+
* reviewer can log the skip without claiming a transition that never
|
|
150
|
+
* happened. Linear omits it on purpose: on merge, Linear's own GitHub
|
|
151
|
+
* integration moves the issue to Done, which groundcrew then observes via
|
|
152
|
+
* `fetch()` and the cleaner tears down.
|
|
153
|
+
*/
|
|
154
|
+
markDone?(issue: Issue): Promise<MarkDoneResult>;
|
|
139
155
|
/**
|
|
140
156
|
* Optional: return parent tickets that were excluded from `fetch()` because
|
|
141
157
|
* they have sub-issues. Board surfaces these so the dispatcher can log WHY
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ticketSource.d.ts","sourceRoot":"","sources":["../../src/lib/ticketSource.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ticketSource.d.ts","sourceRoot":"","sources":["../../src/lib/ticketSource.ts"],"names":[],"mappings":"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"}
|
package/docs/ticket-sources.md
CHANGED
|
@@ -16,8 +16,9 @@ export default {
|
|
|
16
16
|
resolveOne: "~/.config/groundcrew/jira-resolve.sh ${id}",
|
|
17
17
|
markInProgress: "jira issue move ${id} 'In Progress'",
|
|
18
18
|
markInReview: "jira issue move ${id} 'In Review'",
|
|
19
|
+
markDone: "jira issue move ${id} 'Done'",
|
|
19
20
|
},
|
|
20
|
-
timeouts: { fetch: 60_000, markInReview: 15_000 },
|
|
21
|
+
timeouts: { fetch: 60_000, markInReview: 15_000, markDone: 15_000 },
|
|
21
22
|
},
|
|
22
23
|
],
|
|
23
24
|
};
|
|
@@ -26,12 +27,15 @@ export default {
|
|
|
26
27
|
`commands.fetch` must print a JSON array of issues. `commands.resolveOne`, when
|
|
27
28
|
set, must print one issue, print nothing for "not found", or exit `3` for "not
|
|
28
29
|
found". `commands.markInProgress`, when set, receives the issue's `sourceRef` as
|
|
29
|
-
JSON on stdin. `commands.markInReview`, when set, receives the same `sourceRef`
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
JSON on stdin. `commands.markInReview`, when set, receives the same `sourceRef` and is run
|
|
31
|
+
after groundcrew sees an **open** PR on the ticket's worktree branch (in-progress
|
|
32
|
+
tickets only). If omitted, groundcrew treats in-review advancement as unsupported
|
|
33
|
+
for that source and does not claim the transition succeeded. `commands.markDone`,
|
|
34
|
+
when set, receives the same `sourceRef` and is run after groundcrew sees a
|
|
35
|
+
**merged** PR on the ticket's worktree branch (a merged PR never advances to
|
|
36
|
+
in-review). If omitted, groundcrew treats done advancement as unsupported and
|
|
37
|
+
leaves the ticket for the source's own integration to close out. `${id}`,
|
|
38
|
+
`${canonicalId}`, and `${name}` placeholders are shell-quoted before substitution.
|
|
35
39
|
|
|
36
40
|
```json
|
|
37
41
|
[
|
package/package.json
CHANGED