@blogic-cz/agent-tools 1.2.2 → 1.3.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/README.md +34 -0
- package/dist/gh-tool/api.d.ts +16 -0
- package/dist/gh-tool/api.d.ts.map +1 -0
- package/dist/gh-tool/errors.d.ts +1 -0
- package/dist/gh-tool/errors.d.ts.map +1 -1
- package/dist/gh-tool/pr/commands.d.ts +2 -1
- package/dist/gh-tool/pr/commands.d.ts.map +1 -1
- package/dist/gh-tool/pr/core.d.ts +1 -0
- package/dist/gh-tool/pr/core.d.ts.map +1 -1
- package/dist/gh-tool/pr/index.d.ts +1 -1
- package/dist/gh-tool/pr/index.d.ts.map +1 -1
- package/dist/gh-tool/pr/stack-read.d.ts +7 -0
- package/dist/gh-tool/pr/stack-read.d.ts.map +1 -0
- package/dist/gh-tool/pr/stack.d.ts +42 -0
- package/dist/gh-tool/pr/stack.d.ts.map +1 -0
- package/dist/gh-tool/service.d.ts +2 -0
- package/dist/gh-tool/service.d.ts.map +1 -1
- package/dist/gh-tool/types.d.ts +38 -0
- package/dist/gh-tool/types.d.ts.map +1 -1
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/poll-until-resolved.d.ts +9 -0
- package/dist/shared/poll-until-resolved.d.ts.map +1 -0
- package/dist/shared/retry-transient.d.ts +7 -0
- package/dist/shared/retry-transient.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/gh-tool/api.ts +166 -0
- package/src/gh-tool/errors.ts +2 -0
- package/src/gh-tool/index.ts +2 -0
- package/src/gh-tool/pr/commands.ts +52 -0
- package/src/gh-tool/pr/core.ts +61 -21
- package/src/gh-tool/pr/index.ts +1 -0
- package/src/gh-tool/pr/stack-read.ts +73 -0
- package/src/gh-tool/pr/stack.ts +239 -0
- package/src/gh-tool/service.ts +22 -16
- package/src/gh-tool/types.ts +38 -0
- package/src/shared/index.ts +2 -0
- package/src/shared/poll-until-resolved.ts +39 -0
- package/src/shared/retry-transient.ts +24 -0
package/README.md
CHANGED
|
@@ -294,6 +294,40 @@ bun gh-tool gist delete --id <gist-id> --confirm
|
|
|
294
294
|
|
|
295
295
|
Gists are secret by default. `gist delete` prints a dry run unless `--confirm` is passed. `gist edit` requires a content or mutation flag and never opens an editor.
|
|
296
296
|
|
|
297
|
+
### Stacked pull requests
|
|
298
|
+
|
|
299
|
+
GitHub's native stacked PRs refuse the GraphQL merge and require the asynchronous REST
|
|
300
|
+
merge, which lands every unmerged PR in the stack up to and including the requested one.
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
bun gh-tool pr stack view --pr 123 # membership and order
|
|
304
|
+
bun gh-tool pr stack merge --pr 123 --strategy squash # ordered plan, merges nothing
|
|
305
|
+
bun gh-tool pr stack merge --pr 123 --strategy squash --confirm # one request, whole stack
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Ordinary `pr merge` refuses a stack member that has open PRs below it, and names them.
|
|
309
|
+
Merging such a member through the asynchronous endpoint would land those PRs too, which
|
|
310
|
+
its own dry-run never disclosed. The bottom member still merges normally.
|
|
311
|
+
|
|
312
|
+
`stack merge` targets the top open member, so one call lands the stack. It checks every open
|
|
313
|
+
member first and refuses the whole operation if any is a draft, conflicting, or has failing or
|
|
314
|
+
pending checks — a partial stack merge leaves a parent on the trunk and a broken child.
|
|
315
|
+
|
|
316
|
+
Membership comes from GitHub's stacks API, never from chaining `baseRefName` to `headRefName`.
|
|
317
|
+
That chain both over-reports (a promotion PR whose head is `main` adopts every ordinary PR as a
|
|
318
|
+
child) and under-reports (a PR based on a stack member's branch need not be in the stack).
|
|
319
|
+
|
|
320
|
+
A live end-to-end proof lives in `tests/e2e/stack-merge.e2e.ts`. It is excluded from
|
|
321
|
+
`bun run check` because it creates and mutates a throwaway GitHub repository:
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
AGENT_TOOLS_E2E=1 bun tests/e2e/stack-merge.e2e.ts
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
It builds a real three-deep stack and asserts the thing no mock can prove — that each
|
|
328
|
+
squash commit on the trunk adds only its own PR's changes, so a squashed parent is not
|
|
329
|
+
replayed inside its child.
|
|
330
|
+
|
|
297
331
|
### gh-tool machine contracts
|
|
298
332
|
|
|
299
333
|
`pr view` adds `headSha` and `baseSha`; failed-check evidence adds the same SHA pair. Review summaries, inline comments, and threads add `commitSha` plus `feedbackOrigin`: `current_head` only for an exact `commitSha === headSha`, `pre_existing` for a different known SHA (not an obsolescence verdict), and `unknown` when either SHA is absent. Issue comments always use `commitSha: null` and `feedbackOrigin: unknown`. `review-triage` preserves existing fields and adds `inlineComments` plus per-kind `feedbackOriginCounts`; batch triage returns the same object per PR. `pr request-review --reviewers alice,bob` emits sorted `submittedReviewers` (normalized input), `newlyRequested` and `alreadyPending` (the submitted logins split by whether a pending request already existed, so a fresh re-request is distinguishable from a no-op), and `requestedReviewers` (GitHub-confirmed result). `pr last-human-reviewer` derives `currentRequestedReviewers` from live `reviewRequests` only; timeline events are not replayed because GitHub clears a pending request on review submit without emitting `ReviewRequestRemovedEvent`.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { GitHubAuthError } from "./errors";
|
|
3
|
+
import type { GitHubApiError } from "./errors";
|
|
4
|
+
export type GitHubApiResponse<T> = {
|
|
5
|
+
status: number;
|
|
6
|
+
body: T;
|
|
7
|
+
};
|
|
8
|
+
export declare const resolveGitHubToken: () => Effect.Effect<string, GitHubAuthError, never>;
|
|
9
|
+
export type GitHubApiRequest = {
|
|
10
|
+
path: string;
|
|
11
|
+
method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
12
|
+
body?: unknown;
|
|
13
|
+
alsoAcceptStatus?: number[];
|
|
14
|
+
};
|
|
15
|
+
export declare const githubApi: <T>(opts: GitHubApiRequest) => Effect.Effect<GitHubApiResponse<T>, GitHubApiError>;
|
|
16
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/gh-tool/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIhC,OAAO,EAAE,eAAe,EAA2C,MAAM,UAAU,CAAC;AACpF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAS/C,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,CAAC,CAAC;CACT,CAAC;AAYF,eAAO,MAAM,kBAAkB,qDA4B7B,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;IACrD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B,CAAC;AAyEF,eAAO,MAAM,SAAS,GAAI,CAAC,EACzB,MAAM,gBAAgB,KACrB,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,cAAc,CAQjD,CAAC"}
|
package/dist/gh-tool/errors.d.ts
CHANGED
|
@@ -47,5 +47,6 @@ declare const GitHubTimeoutError_base: Schema.Class<GitHubTimeoutError, Schema.T
|
|
|
47
47
|
export declare class GitHubTimeoutError extends GitHubTimeoutError_base {
|
|
48
48
|
}
|
|
49
49
|
export type GitHubServiceError = GitHubCommandError | GitHubNotFoundError | GitHubAuthError | GitHubMergeError | GitHubTimeoutError;
|
|
50
|
+
export type GitHubApiError = GitHubCommandError | GitHubAuthError | GitHubNotFoundError;
|
|
50
51
|
export {};
|
|
51
52
|
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/gh-tool/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;;;;;;;;;;AAEhC,qBAAa,kBAAmB,SAAQ,uBAWvC;CAAG;;;;;;;;;AAEJ,qBAAa,mBAAoB,SAAQ,wBAUxC;CAAG;;;;;;;AAEJ,qBAAa,eAAgB,SAAQ,oBAKnC;CAAG;;;;;;;;AAEL,qBAAa,gBAAiB,SAAQ,qBAYpC;CAAG;;;;;;;;AAEL,qBAAa,kBAAmB,SAAQ,uBASvC;CAAG;AAEJ,MAAM,MAAM,kBAAkB,GAC1B,kBAAkB,GAClB,mBAAmB,GACnB,eAAe,GACf,gBAAgB,GAChB,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/gh-tool/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;;;;;;;;;;AAEhC,qBAAa,kBAAmB,SAAQ,uBAWvC;CAAG;;;;;;;;;AAEJ,qBAAa,mBAAoB,SAAQ,wBAUxC;CAAG;;;;;;;AAEJ,qBAAa,eAAgB,SAAQ,oBAKnC;CAAG;;;;;;;;AAEL,qBAAa,gBAAiB,SAAQ,qBAYpC;CAAG;;;;;;;;AAEL,qBAAa,kBAAmB,SAAQ,uBASvC;CAAG;AAEJ,MAAM,MAAM,kBAAkB,GAC1B,kBAAkB,GAClB,mBAAmB,GACnB,eAAe,GACf,gBAAgB,GAChB,kBAAkB,CAAC;AAEvB,MAAM,MAAM,cAAc,GAAG,kBAAkB,GAAG,eAAe,GAAG,mBAAmB,CAAC"}
|
|
@@ -172,7 +172,7 @@ export declare const prStatusCommand: Command.Command<"status", {
|
|
|
172
172
|
}, {}, any, import("effect/unstable/process/ChildProcessSpawner").ChildProcessSpawner | GitHubService>;
|
|
173
173
|
export declare const prListCommand: Command.Command<"list", {
|
|
174
174
|
readonly format: "json" | "toon";
|
|
175
|
-
readonly state: "open" | "closed" | "
|
|
175
|
+
readonly state: "open" | "closed" | "merged" | "all";
|
|
176
176
|
readonly author: Option.Option<string>;
|
|
177
177
|
readonly base: Option.Option<string>;
|
|
178
178
|
readonly head: Option.Option<string>;
|
|
@@ -391,5 +391,6 @@ export declare const prReplyAndResolveCommand: Command.Command<"reply-and-resolv
|
|
|
391
391
|
readonly repo: Option.Option<string>;
|
|
392
392
|
readonly threadId: Option.Option<string>;
|
|
393
393
|
}, {}, GitHubCommandError | import("#gh/errors").GitHubNotFoundError | import("#gh/errors").GitHubAuthError, GitHubService>;
|
|
394
|
+
export declare const prStackCommand: Command.Command<"stack", {} | {}, {}, import("#gh/errors").GitHubMergeError | (GitHubCommandError | import("#gh/errors").GitHubNotFoundError | import("#gh/errors").GitHubAuthError), GitHubService>;
|
|
394
395
|
export {};
|
|
395
396
|
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/pr/commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAe,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAW,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEjD,OAAO,KAAK,EAAE,WAAW,EAAkB,MAAM,WAAW,CAAC;AAG7D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/pr/commands.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,7 @@ import type { GitHubAuthError, GitHubNotFoundError } from "#gh/errors";
|
|
|
4
4
|
import { GitHubCommandError, GitHubMergeError } from "#gh/errors";
|
|
5
5
|
import { GitHubService } from "#gh/service";
|
|
6
6
|
export declare const NO_CHECKS_REPORTED_RE: RegExp;
|
|
7
|
+
export declare const fetchCheckResults: (pr: number | null) => Effect.Effect<CheckResult[], GitHubCommandError | GitHubNotFoundError | GitHubAuthError, GitHubService>;
|
|
7
8
|
export declare const fetchPRView: (prNumber: number | null) => Effect.Effect<{
|
|
8
9
|
headSha: string | null;
|
|
9
10
|
baseSha: string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/pr/core.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,MAAM,EAAkB,MAAM,QAAQ,CAAC;AAE1E,OAAO,KAAK,EACV,cAAc,EACd,WAAW,EACX,iBAAiB,EAEjB,WAAW,EACX,aAAa,EACb,MAAM,EAGN,iBAAiB,EAIlB,MAAM,WAAW,CAAC;AAEnB,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/pr/core.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { prChecksCommand, prChecksFailedCommand, prCloseCommand, prCommentCommand, prCommentsCommand, prCreateCommand, prDiscussionSummaryCommand, prEditCommand, prFeedbackCommand, prIssueCommentsCommand, prIssueCommentsLatestCommand, prLastHumanReviewerCommand, prListCommand, prMergeCommand, prReadyCommand, prRequestReviewCommand, prReplyCommand, prRerunChecksCommand, prReplyAndResolveCommand, prResolveCommand, prReviewCommand, prReviewsCommand, prStatusCommand, prSubmitReviewCommand, prThreadsCommand, prTriggerChecksCommand, prWaitMergeableCommand, prReviewTriageCommand, prReviewTriageBatchCommand, prViewCommand, prWatchCommand, } from "./commands";
|
|
1
|
+
export { prChecksCommand, prChecksFailedCommand, prCloseCommand, prCommentCommand, prCommentsCommand, prCreateCommand, prDiscussionSummaryCommand, prEditCommand, prFeedbackCommand, prIssueCommentsCommand, prIssueCommentsLatestCommand, prLastHumanReviewerCommand, prListCommand, prMergeCommand, prReadyCommand, prStackCommand, prRequestReviewCommand, prReplyCommand, prRerunChecksCommand, prReplyAndResolveCommand, prResolveCommand, prReviewCommand, prReviewsCommand, prStatusCommand, prSubmitReviewCommand, prThreadsCommand, prTriggerChecksCommand, prWaitMergeableCommand, prReviewTriageCommand, prReviewTriageBatchCommand, prViewCommand, prWatchCommand, } from "./commands";
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/pr/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,0BAA0B,EAC1B,aAAa,EACb,iBAAiB,EACjB,sBAAsB,EACtB,4BAA4B,EAC5B,0BAA0B,EAC1B,aAAa,EACb,cAAc,EACd,cAAc,EACd,sBAAsB,EACtB,cAAc,EACd,oBAAoB,EACpB,wBAAwB,EACxB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,gBAAgB,EAChB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,0BAA0B,EAC1B,aAAa,EACb,cAAc,GACf,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/pr/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,0BAA0B,EAC1B,aAAa,EACb,iBAAiB,EACjB,sBAAsB,EACtB,4BAA4B,EAC5B,0BAA0B,EAC1B,aAAa,EACb,cAAc,EACd,cAAc,EACd,cAAc,EACd,sBAAsB,EACtB,cAAc,EACd,oBAAoB,EACpB,wBAAwB,EACxB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,gBAAgB,EAChB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,0BAA0B,EAC1B,aAAa,EACb,cAAc,GACf,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { GitHubService } from "#gh/service";
|
|
3
|
+
import type { StackView } from "#gh/types";
|
|
4
|
+
export declare const readStack: (opts: {
|
|
5
|
+
pr: number;
|
|
6
|
+
}) => Effect.Effect<StackView, import("../errors").GitHubCommandError | import("../errors").GitHubNotFoundError | import("../errors").GitHubAuthError, GitHubService>;
|
|
7
|
+
//# sourceMappingURL=stack-read.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stack-read.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/pr/stack-read.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,KAAK,EAAe,SAAS,EAAE,MAAM,WAAW,CAAC;AAoBxD,eAAO,MAAM,SAAS;QAAoD,MAAM;qKAgD9E,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { GitHubService } from "#gh/service";
|
|
3
|
+
import { GitHubMergeError } from "#gh/errors";
|
|
4
|
+
import type { MergeStrategy, StackMergeBlocker } from "#gh/types";
|
|
5
|
+
import { readStack } from "./stack-read";
|
|
6
|
+
export { readStack };
|
|
7
|
+
export declare const mergeStack: (opts: {
|
|
8
|
+
pr: number;
|
|
9
|
+
strategy: MergeStrategy;
|
|
10
|
+
confirm: boolean;
|
|
11
|
+
}) => Effect.Effect<{
|
|
12
|
+
strategy: MergeStrategy;
|
|
13
|
+
merged: false;
|
|
14
|
+
dryRun: true;
|
|
15
|
+
sha: null;
|
|
16
|
+
adoptedExistingRequest: false;
|
|
17
|
+
stackNumber: number;
|
|
18
|
+
baseRef: string;
|
|
19
|
+
target: number;
|
|
20
|
+
plan: {
|
|
21
|
+
position: number;
|
|
22
|
+
number: number;
|
|
23
|
+
headRefName: string;
|
|
24
|
+
}[];
|
|
25
|
+
blockers: StackMergeBlocker[];
|
|
26
|
+
} | {
|
|
27
|
+
strategy: MergeStrategy;
|
|
28
|
+
merged: true;
|
|
29
|
+
dryRun: false;
|
|
30
|
+
sha: string | null;
|
|
31
|
+
adoptedExistingRequest: boolean;
|
|
32
|
+
stackNumber: number;
|
|
33
|
+
baseRef: string;
|
|
34
|
+
target: number;
|
|
35
|
+
plan: {
|
|
36
|
+
position: number;
|
|
37
|
+
number: number;
|
|
38
|
+
headRefName: string;
|
|
39
|
+
}[];
|
|
40
|
+
blockers: StackMergeBlocker[];
|
|
41
|
+
}, GitHubMergeError | (import("#gh/errors").GitHubCommandError | import("#gh/errors").GitHubNotFoundError | import("#gh/errors").GitHubAuthError), GitHubService>;
|
|
42
|
+
//# sourceMappingURL=stack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stack.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/pr/stack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,KAAK,EAAE,aAAa,EAAe,iBAAiB,EAAoB,MAAM,WAAW,CAAC;AAGjG,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,SAAS,EAAE,CAAC;AAqFrB,eAAO,MAAM,UAAU;QACjB,MAAM;cACA,aAAa;aACd,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iKA2IhB,CAAC"}
|
|
@@ -3,6 +3,7 @@ import { Context, Effect, Layer } from "effect";
|
|
|
3
3
|
import type { GitHubRepoConfig } from "#config";
|
|
4
4
|
import type { RepoInfo } from "./types";
|
|
5
5
|
import { GitHubAuthError, GitHubCommandError, GitHubNotFoundError } from "./errors";
|
|
6
|
+
import type { GitHubApiRequest, GitHubApiResponse } from "./api";
|
|
6
7
|
import { ConfigService } from "#config";
|
|
7
8
|
type GhResult = {
|
|
8
9
|
stdout: string;
|
|
@@ -14,6 +15,7 @@ declare const GitHubService_base: Context.ServiceClass<GitHubService, "@agent-to
|
|
|
14
15
|
readonly runGh: (args: string[]) => Effect.Effect<GhResult, GhError>;
|
|
15
16
|
readonly runGhJson: <T>(args: string[]) => Effect.Effect<T, GhError>;
|
|
16
17
|
readonly runGraphQL: (query: string, variables: Record<string, string | number | null>) => Effect.Effect<unknown, GhError>;
|
|
18
|
+
readonly apiRequest: <T>(opts: GitHubApiRequest) => Effect.Effect<GitHubApiResponse<T>, GhError>;
|
|
17
19
|
readonly getRepoConfig: () => Effect.Effect<GitHubRepoConfig | undefined, never>;
|
|
18
20
|
readonly getRepoInfo: () => Effect.Effect<RepoInfo, GhError>;
|
|
19
21
|
readonly withRepoTarget: <A, E, R>(target: string | null, effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E | GitHubCommandError, R>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/gh-tool/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/gh-tool/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAU,MAAM,QAAQ,CAAC;AAExD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGxC,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAGpF,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EAAE,aAAa,EAA4C,MAAM,SAAS,CAAC;AA2DlF,KAAK,QAAQ,GAAG;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,KAAK,OAAO,GAAG,kBAAkB,GAAG,eAAe,GAAG,mBAAmB,CAAC;;oBAKtD,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC;wBAChD,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC;yBAC/C,CACnB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,KAC9C,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC;yBACf,CAAC,CAAC,EACrB,IAAI,EAAE,gBAAgB,KACnB,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC;4BACzB,MAAM,MAAM,CAAC,MAAM,CAAC,gBAAgB,GAAG,SAAS,EAAE,KAAK,CAAC;0BAC1D,MAAM,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC;6BACnC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAC/B,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC3B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,kBAAkB,EAAE,CAAC,CAAC;;AAjBpD,qBAAa,aAAc,SAAQ,kBAmBF;IAC/B,MAAM,CAAC,QAAQ,CAAC,KAAK,6FAsRnB;CACH"}
|
package/dist/gh-tool/types.d.ts
CHANGED
|
@@ -278,4 +278,42 @@ export type JobAnnotations = {
|
|
|
278
278
|
jobName: string;
|
|
279
279
|
annotations: CheckRunAnnotation[];
|
|
280
280
|
};
|
|
281
|
+
export type StackMember = {
|
|
282
|
+
position: number;
|
|
283
|
+
number: number;
|
|
284
|
+
title: string;
|
|
285
|
+
headRefName: string;
|
|
286
|
+
baseRefName: string;
|
|
287
|
+
state: "open" | "closed" | "merged";
|
|
288
|
+
isDraft: boolean;
|
|
289
|
+
url: string;
|
|
290
|
+
};
|
|
291
|
+
export type StackView = {
|
|
292
|
+
pr: number;
|
|
293
|
+
isStacked: boolean;
|
|
294
|
+
stackNumber: number | null;
|
|
295
|
+
baseRef: string | null;
|
|
296
|
+
members: StackMember[];
|
|
297
|
+
};
|
|
298
|
+
export type StackMergeBlocker = {
|
|
299
|
+
number: number;
|
|
300
|
+
reason: "draft" | "not_mergeable" | "mergeability_unknown" | "checks_failing" | "checks_pending";
|
|
301
|
+
detail: string;
|
|
302
|
+
};
|
|
303
|
+
export type StackMergeResult = {
|
|
304
|
+
stackNumber: number;
|
|
305
|
+
baseRef: string;
|
|
306
|
+
target: number;
|
|
307
|
+
strategy: MergeStrategy;
|
|
308
|
+
plan: Array<{
|
|
309
|
+
position: number;
|
|
310
|
+
number: number;
|
|
311
|
+
headRefName: string;
|
|
312
|
+
}>;
|
|
313
|
+
merged: boolean;
|
|
314
|
+
dryRun: boolean;
|
|
315
|
+
blockers: StackMergeBlocker[];
|
|
316
|
+
sha: string | null;
|
|
317
|
+
adoptedExistingRequest: boolean;
|
|
318
|
+
};
|
|
281
319
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/gh-tool/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/gh-tool/types.ts"],"names":[],"mappings":"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"}
|
package/dist/shared/index.d.ts
CHANGED
|
@@ -9,4 +9,6 @@ export { execEffect, renderCommandLine, tokenizeCommandLine, type ExecError } fr
|
|
|
9
9
|
export { createThrottle, type ThrottleError } from "./throttle";
|
|
10
10
|
export { aggregateByField, deduplicateLines, formatCountSummary, parseTextTable, stripEmptyColumns, truncateRows, } from "./transform";
|
|
11
11
|
export { transformLogOutput } from "./log-transform";
|
|
12
|
+
export { retryTransient } from "./retry-transient";
|
|
13
|
+
export { pollUntilResolved } from "./poll-until-resolved";
|
|
12
14
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shared/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,UAAU,EACV,cAAc,EACd,aAAa,EACb,WAAW,EACX,YAAY,EACZ,UAAU,GACX,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAExF,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAE/C,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAE1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAIzF,eAAO,MAAM,OAAO,QAAc,CAAC;AAEnC,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,KAAK,SAAS,EAAE,MAAM,QAAQ,CAAC;AAE5F,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhE,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,YAAY,GACb,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shared/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,UAAU,EACV,cAAc,EACd,aAAa,EACb,WAAW,EACX,YAAY,EACZ,UAAU,GACX,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAExF,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAE/C,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAE1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAIzF,eAAO,MAAM,OAAO,QAAc,CAAC;AAEnC,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,KAAK,SAAS,EAAE,MAAM,QAAQ,CAAC;AAE5F,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhE,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,YAAY,GACb,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
export declare const pollUntilResolved: <A, E, R>(opts: {
|
|
3
|
+
initial: A;
|
|
4
|
+
isPending: (value: A) => boolean;
|
|
5
|
+
fetchLatest: () => Effect.Effect<A, E, R>;
|
|
6
|
+
intervalMs: number;
|
|
7
|
+
budgetSeconds: number;
|
|
8
|
+
}) => Effect.Effect<A, E, R>;
|
|
9
|
+
//# sourceMappingURL=poll-until-resolved.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poll-until-resolved.d.ts","sourceRoot":"","sources":["../../src/shared/poll-until-resolved.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEjD,eAAO,MAAM,iBAAiB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM;IAC/C,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC;IACjC,WAAW,EAAE,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACvB,KAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CA8BrB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
export declare const retryTransient: <A, E, R>(opts: {
|
|
3
|
+
attempt: () => Effect.Effect<A, E, R>;
|
|
4
|
+
isTransient: (error: E) => boolean;
|
|
5
|
+
maxRetries: number;
|
|
6
|
+
}) => Effect.Effect<A, E, R>;
|
|
7
|
+
//# sourceMappingURL=retry-transient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry-transient.d.ts","sourceRoot":"","sources":["../../src/shared/retry-transient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,MAAM,EAAE,MAAM,QAAQ,CAAC;AAI1C,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM;IAC5C,OAAO,EAAE,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACtC,WAAW,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;CACpB,KAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAexB,CAAC"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
|
|
3
|
+
import { retryTransient } from "#shared/retry-transient";
|
|
4
|
+
|
|
5
|
+
import { GitHubAuthError, GitHubCommandError, GitHubNotFoundError } from "./errors";
|
|
6
|
+
import type { GitHubApiError } from "./errors";
|
|
7
|
+
|
|
8
|
+
// Direct HTTP, not `gh api`: the CLI collapses every failure into a non-zero exit and
|
|
9
|
+
// loses the status code, but merge-async answers 202 (accepted), 200 (already merged or
|
|
10
|
+
// queued) and 409 (a request already exists, UUID returned) as three different outcomes.
|
|
11
|
+
const GITHUB_API_ROOT = "https://api.github.com";
|
|
12
|
+
const GITHUB_ACCEPT = "application/vnd.github+json";
|
|
13
|
+
const GITHUB_API_VERSION = "2022-11-28";
|
|
14
|
+
|
|
15
|
+
export type GitHubApiResponse<T> = {
|
|
16
|
+
status: number;
|
|
17
|
+
body: T;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const authFailure = (message: string) =>
|
|
21
|
+
new GitHubAuthError({
|
|
22
|
+
message,
|
|
23
|
+
hint: "Set GITHUB_TOKEN or GH_TOKEN, or authenticate the GitHub CLI with 'gh auth login'.",
|
|
24
|
+
nextCommand: "gh auth login",
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Environment first, GH_TOKEN before GITHUB_TOKEN to match the gh CLI: the active gh
|
|
28
|
+
// account is directory-scoped global state and the shell exports the matching token.
|
|
29
|
+
// `gh auth token` only covers shells that export none.
|
|
30
|
+
export const resolveGitHubToken = Effect.fn("gh.resolveGitHubToken")(function* () {
|
|
31
|
+
const fromEnv = [process.env["GH_TOKEN"], process.env["GITHUB_TOKEN"]].find(
|
|
32
|
+
(candidate) => candidate !== undefined && candidate.length > 0,
|
|
33
|
+
);
|
|
34
|
+
if (fromEnv !== undefined && fromEnv.length > 0) {
|
|
35
|
+
return fromEnv;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Bun.spawn rather than the ChildProcessSpawner service on purpose: routing it through
|
|
39
|
+
// the service would put that requirement in the error channel of every command that
|
|
40
|
+
// reads the API, and token resolution is not what those commands are testing.
|
|
41
|
+
const token = yield* Effect.tryPromise({
|
|
42
|
+
try: async () => {
|
|
43
|
+
const proc = Bun.spawn(["gh", "auth", "token", "--hostname", "github.com"], {
|
|
44
|
+
stdout: "pipe",
|
|
45
|
+
stderr: "ignore",
|
|
46
|
+
});
|
|
47
|
+
const stdout = await new Response(proc.stdout).text();
|
|
48
|
+
const exitCode = await proc.exited;
|
|
49
|
+
return exitCode === 0 ? stdout.trim() : "";
|
|
50
|
+
},
|
|
51
|
+
catch: () => authFailure("No GitHub token available."),
|
|
52
|
+
}).pipe(Effect.orElseSucceed(() => ""));
|
|
53
|
+
if (token.length === 0) {
|
|
54
|
+
return yield* authFailure("No GitHub token available.");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return token;
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
export type GitHubApiRequest = {
|
|
61
|
+
path: string;
|
|
62
|
+
method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
63
|
+
body?: unknown;
|
|
64
|
+
alsoAcceptStatus?: number[];
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const MAX_API_RETRIES = 2;
|
|
68
|
+
|
|
69
|
+
const githubApiAttempt = Effect.fn("gh.githubApiAttempt")(function* <T>(opts: GitHubApiRequest) {
|
|
70
|
+
const token = yield* resolveGitHubToken();
|
|
71
|
+
const method = opts.method ?? "GET";
|
|
72
|
+
const url = `${GITHUB_API_ROOT}/${opts.path.replace(/^\//, "")}`;
|
|
73
|
+
|
|
74
|
+
const response = yield* Effect.tryPromise({
|
|
75
|
+
try: () =>
|
|
76
|
+
fetch(url, {
|
|
77
|
+
method,
|
|
78
|
+
headers: {
|
|
79
|
+
Accept: GITHUB_ACCEPT,
|
|
80
|
+
Authorization: `Bearer ${token}`,
|
|
81
|
+
"X-GitHub-Api-Version": GITHUB_API_VERSION,
|
|
82
|
+
...(opts.body === undefined ? {} : { "Content-Type": "application/json" }),
|
|
83
|
+
},
|
|
84
|
+
...(opts.body === undefined ? {} : { body: JSON.stringify(opts.body) }),
|
|
85
|
+
}),
|
|
86
|
+
catch: (cause) =>
|
|
87
|
+
new GitHubCommandError({
|
|
88
|
+
message: `GitHub API request failed: ${String(cause)}`,
|
|
89
|
+
command: `${method} ${opts.path}`,
|
|
90
|
+
exitCode: -1,
|
|
91
|
+
stderr: String(cause),
|
|
92
|
+
retryable: true,
|
|
93
|
+
hint: "Check network connectivity and VPN state, then retry.",
|
|
94
|
+
}),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const text = yield* Effect.tryPromise({
|
|
98
|
+
try: () => response.text(),
|
|
99
|
+
catch: (cause) =>
|
|
100
|
+
new GitHubCommandError({
|
|
101
|
+
message: `GitHub API response could not be read: ${String(cause)}`,
|
|
102
|
+
command: `${method} ${opts.path}`,
|
|
103
|
+
exitCode: -1,
|
|
104
|
+
stderr: String(cause),
|
|
105
|
+
hint: "The connection dropped mid-response. Re-read the resource before retrying a mutation.",
|
|
106
|
+
}),
|
|
107
|
+
});
|
|
108
|
+
const parsed: unknown = text.length === 0 ? null : safeJsonParse(text);
|
|
109
|
+
|
|
110
|
+
const accepted = new Set([200, ...(opts.alsoAcceptStatus ?? [])]);
|
|
111
|
+
if (accepted.has(response.status)) {
|
|
112
|
+
return { status: response.status, body: parsed as T };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (response.status === 401) {
|
|
116
|
+
return yield* authFailure("GitHub credentials rejected (HTTP 401).");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (response.status === 404) {
|
|
120
|
+
return yield* new GitHubNotFoundError({
|
|
121
|
+
message: `GitHub API returned 404 for ${opts.path}`,
|
|
122
|
+
identifier: opts.path,
|
|
123
|
+
resource: "github-api",
|
|
124
|
+
hint: "Verify the resource exists, that you have access, and that the feature is enabled for this repository.",
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return yield* new GitHubCommandError({
|
|
129
|
+
message: apiErrorMessage(parsed, response.status),
|
|
130
|
+
command: `${method} ${opts.path}`,
|
|
131
|
+
exitCode: response.status,
|
|
132
|
+
stderr: text,
|
|
133
|
+
...(response.status >= 500 ? { retryable: true } : {}),
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Mirrors GitHubService.runGh: replay a transient failure, and only for an idempotent read.
|
|
138
|
+
export const githubApi = <T>(
|
|
139
|
+
opts: GitHubApiRequest,
|
|
140
|
+
): Effect.Effect<GitHubApiResponse<T>, GitHubApiError> =>
|
|
141
|
+
retryTransient({
|
|
142
|
+
attempt: () => githubApiAttempt<T>(opts),
|
|
143
|
+
isTransient: (error) =>
|
|
144
|
+
error._tag === "GitHubCommandError" &&
|
|
145
|
+
error.retryable === true &&
|
|
146
|
+
(opts.method ?? "GET") === "GET",
|
|
147
|
+
maxRetries: MAX_API_RETRIES,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const safeJsonParse = (text: string): unknown => {
|
|
151
|
+
try {
|
|
152
|
+
return JSON.parse(text);
|
|
153
|
+
} catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const apiErrorMessage = (body: unknown, status: number): string => {
|
|
159
|
+
if (typeof body === "object" && body !== null && "message" in body) {
|
|
160
|
+
const message = (body as { message?: unknown }).message;
|
|
161
|
+
if (typeof message === "string") {
|
|
162
|
+
return `GitHub API error (HTTP ${status}): ${message}`;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return `GitHub API error (HTTP ${status})`;
|
|
166
|
+
};
|
package/src/gh-tool/errors.ts
CHANGED
package/src/gh-tool/index.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
prEditCommand,
|
|
28
28
|
prMergeCommand,
|
|
29
29
|
prReadyCommand,
|
|
30
|
+
prStackCommand,
|
|
30
31
|
prRequestReviewCommand,
|
|
31
32
|
prThreadsCommand,
|
|
32
33
|
prCommentsCommand,
|
|
@@ -93,6 +94,7 @@ const prCommand = Command.make("pr", {}).pipe(
|
|
|
93
94
|
prEditCommand,
|
|
94
95
|
prMergeCommand,
|
|
95
96
|
prReadyCommand,
|
|
97
|
+
prStackCommand,
|
|
96
98
|
prRequestReviewCommand,
|
|
97
99
|
prWaitMergeableCommand,
|
|
98
100
|
prThreadsCommand,
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
REVIEW_EVENTS,
|
|
30
30
|
} from "#gh/config";
|
|
31
31
|
|
|
32
|
+
import { mergeStack, readStack } from "./stack";
|
|
32
33
|
import {
|
|
33
34
|
closePR,
|
|
34
35
|
collectWithStableState,
|
|
@@ -1609,3 +1610,54 @@ export const prReplyAndResolveCommand = Command.make(
|
|
|
1609
1610
|
"Composite: reply to a review comment and resolve its thread (PR/thread inferred from comment)",
|
|
1610
1611
|
),
|
|
1611
1612
|
);
|
|
1613
|
+
|
|
1614
|
+
const prStackViewCommand = Command.make(
|
|
1615
|
+
"view",
|
|
1616
|
+
{
|
|
1617
|
+
format: formatOption,
|
|
1618
|
+
pr: Flag.integer("pr").pipe(Flag.withDescription("PR number to read the stack of")),
|
|
1619
|
+
repo: repoOption,
|
|
1620
|
+
},
|
|
1621
|
+
({ format, pr, repo }) =>
|
|
1622
|
+
withRepo(
|
|
1623
|
+
repo,
|
|
1624
|
+
Effect.gen(function* () {
|
|
1625
|
+
const result = yield* readStack({ pr });
|
|
1626
|
+
yield* logFormatted(result, format);
|
|
1627
|
+
}),
|
|
1628
|
+
),
|
|
1629
|
+
).pipe(Command.withDescription("Show every PR in the stack containing this PR, bottom-up"));
|
|
1630
|
+
|
|
1631
|
+
const prStackMergeCommand = Command.make(
|
|
1632
|
+
"merge",
|
|
1633
|
+
{
|
|
1634
|
+
confirm: Flag.boolean("confirm").pipe(
|
|
1635
|
+
Flag.withDescription("Actually merge (without this flag, only shows the ordered plan)"),
|
|
1636
|
+
Flag.withDefault(false),
|
|
1637
|
+
),
|
|
1638
|
+
format: formatOption,
|
|
1639
|
+
pr: Flag.integer("pr").pipe(Flag.withDescription("Any PR in the stack to merge")),
|
|
1640
|
+
repo: repoOption,
|
|
1641
|
+
strategy: Flag.choice("strategy", MERGE_STRATEGIES).pipe(
|
|
1642
|
+
Flag.withDescription("Merge strategy: squash, merge, or rebase"),
|
|
1643
|
+
Flag.withDefault(DEFAULT_MERGE_STRATEGY),
|
|
1644
|
+
),
|
|
1645
|
+
},
|
|
1646
|
+
({ confirm, format, pr, repo, strategy }) =>
|
|
1647
|
+
withRepo(
|
|
1648
|
+
repo,
|
|
1649
|
+
Effect.gen(function* () {
|
|
1650
|
+
const result = yield* mergeStack({ confirm, pr, strategy });
|
|
1651
|
+
yield* logFormatted(result, format);
|
|
1652
|
+
}),
|
|
1653
|
+
),
|
|
1654
|
+
).pipe(
|
|
1655
|
+
Command.withDescription(
|
|
1656
|
+
"Merge every open PR in the stack in one request (dry-run by default, use --confirm)",
|
|
1657
|
+
),
|
|
1658
|
+
);
|
|
1659
|
+
|
|
1660
|
+
export const prStackCommand = Command.make("stack", {}).pipe(
|
|
1661
|
+
Command.withSubcommands([prStackViewCommand, prStackMergeCommand]),
|
|
1662
|
+
Command.withDescription("Stacked pull request operations"),
|
|
1663
|
+
);
|
package/src/gh-tool/pr/core.ts
CHANGED
|
@@ -22,7 +22,9 @@ import { GitHubService } from "#gh/service";
|
|
|
22
22
|
import { logText } from "#shared";
|
|
23
23
|
|
|
24
24
|
import type { ButStatusJson, PRViewJsonResult } from "./helpers";
|
|
25
|
+
import { pollUntilResolved } from "#shared/poll-until-resolved";
|
|
25
26
|
import { runLocalCommand } from "./helpers";
|
|
27
|
+
import { readStack } from "./stack-read";
|
|
26
28
|
import {
|
|
27
29
|
diagnoseLogEntries,
|
|
28
30
|
discoverDispatchedRun,
|
|
@@ -200,7 +202,7 @@ const fetchWorkflowRunFailureContext = Effect.fn("pr.fetchWorkflowRunFailureCont
|
|
|
200
202
|
// is an ordinary state, so map it to [] and keep the zero-check paths downstream reachable.
|
|
201
203
|
export const NO_CHECKS_REPORTED_RE = /no checks reported/i;
|
|
202
204
|
|
|
203
|
-
const fetchCheckResults = Effect.fn("pr.fetchCheckResults")(function* (pr: number | null) {
|
|
205
|
+
export const fetchCheckResults = Effect.fn("pr.fetchCheckResults")(function* (pr: number | null) {
|
|
204
206
|
const gh = yield* GitHubService;
|
|
205
207
|
|
|
206
208
|
const args = ["pr", "checks"];
|
|
@@ -763,26 +765,13 @@ const mergeViaAsyncApi = Effect.fn("pr.mergeViaAsyncApi")(function* (opts: {
|
|
|
763
765
|
]);
|
|
764
766
|
|
|
765
767
|
const uuid = latest.details?.uuid;
|
|
766
|
-
if (
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
while: () => latest.status === "pending" && !timedOut,
|
|
774
|
-
body: () =>
|
|
775
|
-
Effect.gen(function* () {
|
|
776
|
-
const now = yield* Clock.currentTimeMillis;
|
|
777
|
-
if (Number(now) >= deadlineMs) {
|
|
778
|
-
timedOut = true;
|
|
779
|
-
return;
|
|
780
|
-
}
|
|
781
|
-
const remaining = deadlineMs - Number(now);
|
|
782
|
-
yield* Effect.sleep(Duration.millis(Math.min(ASYNC_MERGE_POLL_INTERVAL_MS, remaining)));
|
|
783
|
-
latest = yield* gh.runGhJson<AsyncMergeResult>(["api", `${asyncPath}/${uuid}`]);
|
|
784
|
-
}),
|
|
785
|
-
step: () => undefined,
|
|
768
|
+
if (uuid !== undefined) {
|
|
769
|
+
latest = yield* pollUntilResolved({
|
|
770
|
+
initial: latest,
|
|
771
|
+
isPending: (value) => value.status === "pending",
|
|
772
|
+
fetchLatest: () => gh.runGhJson<AsyncMergeResult>(["api", `${asyncPath}/${uuid}`]),
|
|
773
|
+
intervalMs: ASYNC_MERGE_POLL_INTERVAL_MS,
|
|
774
|
+
budgetSeconds: MAX_ASYNC_MERGE_WAIT_SECONDS,
|
|
786
775
|
});
|
|
787
776
|
}
|
|
788
777
|
|
|
@@ -832,6 +821,57 @@ export const mergePR = Effect.fn("pr.mergePR")(function* (opts: {
|
|
|
832
821
|
|
|
833
822
|
const repo = opts.deleteBranch ? yield* gh.getRepoInfo() : null;
|
|
834
823
|
|
|
824
|
+
// merge-async lands the requested PR AND every unmerged PR below it in its stack, and
|
|
825
|
+
// `gh pr merge` falls back to that endpoint for a stacked PR. Merging one member can
|
|
826
|
+
// therefore land members this command never named. Refuse instead of merging silently.
|
|
827
|
+
// Fail closed: readStack already reports a repository without a stacks surface as
|
|
828
|
+
// unstacked, so a failure here leaves membership genuinely unknown, and proceeding
|
|
829
|
+
// would land whatever sits below this PR without naming it.
|
|
830
|
+
const stackView = yield* readStack({ pr: opts.pr }).pipe(
|
|
831
|
+
Effect.catch((error) =>
|
|
832
|
+
Effect.fail(
|
|
833
|
+
new GitHubMergeError({
|
|
834
|
+
message: `Could not determine whether PR #${opts.pr} belongs to a stack: ${error.message}`,
|
|
835
|
+
reason: "unknown",
|
|
836
|
+
hint: "Merging a stack member lands every open PR below it, so the merge is refused while membership is unknown. Retry, or read the stack with 'pr stack view'.",
|
|
837
|
+
nextCommand: `agent-tools-gh pr stack view --pr ${opts.pr}`,
|
|
838
|
+
}),
|
|
839
|
+
),
|
|
840
|
+
),
|
|
841
|
+
);
|
|
842
|
+
const ownPosition = stackView.members.find((entry) => entry.number === opts.pr)?.position;
|
|
843
|
+
|
|
844
|
+
// The stack was fetched by querying this PR, so it must be among its own members. If it
|
|
845
|
+
// is not, the PR left the stack between the list call and the detail call and membership
|
|
846
|
+
// is unknown again — the same reason the lookup failure above refuses.
|
|
847
|
+
if (stackView.isStacked && ownPosition === undefined) {
|
|
848
|
+
return yield* new GitHubMergeError({
|
|
849
|
+
message: `PR #${opts.pr} is missing from stack #${stackView.stackNumber}, which was read for that PR`,
|
|
850
|
+
reason: "unknown",
|
|
851
|
+
hint: "The stack changed while it was being read. Re-read it with 'pr stack view' before merging.",
|
|
852
|
+
nextCommand: `agent-tools-gh pr stack view --pr ${opts.pr}`,
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
const openBelow =
|
|
857
|
+
!stackView.isStacked || ownPosition === undefined
|
|
858
|
+
? []
|
|
859
|
+
: stackView.members.filter(
|
|
860
|
+
(member) => member.state === "open" && member.position < ownPosition,
|
|
861
|
+
);
|
|
862
|
+
|
|
863
|
+
if (openBelow.length > 0) {
|
|
864
|
+
return yield* new GitHubMergeError({
|
|
865
|
+
message:
|
|
866
|
+
`PR #${opts.pr} sits above ${openBelow.length} open PR(s) in stack #${stackView.stackNumber}: ` +
|
|
867
|
+
openBelow.map((member) => `#${member.number}`).join(", ") +
|
|
868
|
+
". Merging it would land them too.",
|
|
869
|
+
reason: "unknown",
|
|
870
|
+
hint: "Use 'pr stack merge' to merge a stack: it checks every member first and reports the whole plan.",
|
|
871
|
+
nextCommand: `agent-tools-gh pr stack merge --pr ${opts.pr}`,
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
|
|
835
875
|
// A long-lived branch (default/env branch) as PR head means a promotion PR
|
|
836
876
|
// (e.g. main -> staging). PRs based on it are unrelated work, not a stack —
|
|
837
877
|
// retargeting them would mass-rewrite their base — and the branch itself
|
package/src/gh-tool/pr/index.ts
CHANGED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
|
|
3
|
+
import { GitHubService } from "#gh/service";
|
|
4
|
+
|
|
5
|
+
import type { StackMember, StackView } from "#gh/types";
|
|
6
|
+
|
|
7
|
+
type StacksListResponse = Array<{ number: number }>;
|
|
8
|
+
|
|
9
|
+
type StackResponse = {
|
|
10
|
+
number: number;
|
|
11
|
+
base: { ref: string };
|
|
12
|
+
open: boolean;
|
|
13
|
+
pull_requests: Array<{
|
|
14
|
+
number: number;
|
|
15
|
+
title: string;
|
|
16
|
+
state: "open" | "closed";
|
|
17
|
+
merged_at: string | null;
|
|
18
|
+
draft: boolean;
|
|
19
|
+
html_url: string;
|
|
20
|
+
head: { ref: string };
|
|
21
|
+
base: { ref: string };
|
|
22
|
+
}>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const readStack = Effect.fn("pr.readStack")(function* (opts: { pr: number }) {
|
|
26
|
+
const gh = yield* GitHubService;
|
|
27
|
+
const repo = yield* gh.getRepoInfo();
|
|
28
|
+
const base = `repos/${repo.owner}/${repo.name}`;
|
|
29
|
+
|
|
30
|
+
const unstacked: StackView = {
|
|
31
|
+
pr: opts.pr,
|
|
32
|
+
isStacked: false,
|
|
33
|
+
stackNumber: null,
|
|
34
|
+
baseRef: null,
|
|
35
|
+
members: [],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// A 404 is the repository having no stacks surface at all, which reads the same as a PR
|
|
39
|
+
// that belongs to no stack. Every caller gets that reading from here, not its own.
|
|
40
|
+
const stacks = yield* gh
|
|
41
|
+
.apiRequest<StacksListResponse>({ path: `${base}/stacks?pull_request=${opts.pr}` })
|
|
42
|
+
.pipe(Effect.catchTag("GitHubNotFoundError", () => Effect.succeed(null)));
|
|
43
|
+
|
|
44
|
+
if (stacks === null) {
|
|
45
|
+
return unstacked;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const stackNumber = stacks.body?.[0]?.number;
|
|
49
|
+
if (stackNumber === undefined) {
|
|
50
|
+
return unstacked;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const stack = yield* gh.apiRequest<StackResponse>({ path: `${base}/stacks/${stackNumber}` });
|
|
54
|
+
|
|
55
|
+
const members: StackMember[] = stack.body.pull_requests.map((member, index) => ({
|
|
56
|
+
position: index + 1,
|
|
57
|
+
number: member.number,
|
|
58
|
+
title: member.title,
|
|
59
|
+
headRefName: member.head.ref,
|
|
60
|
+
baseRefName: member.base.ref,
|
|
61
|
+
state: member.merged_at === null ? member.state : "merged",
|
|
62
|
+
isDraft: member.draft,
|
|
63
|
+
url: member.html_url,
|
|
64
|
+
}));
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
pr: opts.pr,
|
|
68
|
+
isStacked: true,
|
|
69
|
+
stackNumber: stack.body.number,
|
|
70
|
+
baseRef: stack.body.base.ref,
|
|
71
|
+
members,
|
|
72
|
+
} satisfies StackView;
|
|
73
|
+
});
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
|
|
3
|
+
import { pollUntilResolved } from "#shared/poll-until-resolved";
|
|
4
|
+
import { GitHubService } from "#gh/service";
|
|
5
|
+
import { GitHubMergeError } from "#gh/errors";
|
|
6
|
+
|
|
7
|
+
import type { MergeStrategy, StackMember, StackMergeBlocker, StackMergeResult } from "#gh/types";
|
|
8
|
+
|
|
9
|
+
import { fetchCheckResults, fetchPRView } from "./core";
|
|
10
|
+
import { readStack } from "./stack-read";
|
|
11
|
+
|
|
12
|
+
export { readStack };
|
|
13
|
+
|
|
14
|
+
type AsyncMergeDetails = {
|
|
15
|
+
message?: string;
|
|
16
|
+
uuid?: string;
|
|
17
|
+
sha?: string;
|
|
18
|
+
merge_method?: MergeStrategy;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type AsyncMergeResult = {
|
|
22
|
+
status: "pending" | "merged" | "enqueued" | "failed";
|
|
23
|
+
details?: AsyncMergeDetails;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// One request merges every open member, so the server-side work scales with the stack;
|
|
27
|
+
// the single-PR budget in core.ts is deliberately shorter.
|
|
28
|
+
const POLL_INTERVAL_MS = 2000;
|
|
29
|
+
const MAX_WAIT_SECONDS = 300;
|
|
30
|
+
|
|
31
|
+
const stackMergeFailure = (opts: {
|
|
32
|
+
stackNumber: number;
|
|
33
|
+
pr: number;
|
|
34
|
+
message: string;
|
|
35
|
+
hint: string;
|
|
36
|
+
reason?: GitHubMergeError["reason"];
|
|
37
|
+
}) =>
|
|
38
|
+
new GitHubMergeError({
|
|
39
|
+
message: `Failed to merge stack #${opts.stackNumber}: ${opts.message}`,
|
|
40
|
+
reason: opts.reason ?? "unknown",
|
|
41
|
+
hint: opts.hint,
|
|
42
|
+
nextCommand: `agent-tools-gh pr stack view --pr ${opts.pr}`,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const collectBlockers = Effect.fn("pr.collectStackBlockers")(function* (members: StackMember[]) {
|
|
46
|
+
const blockers: StackMergeBlocker[] = [];
|
|
47
|
+
|
|
48
|
+
for (const member of members) {
|
|
49
|
+
if (member.isDraft) {
|
|
50
|
+
blockers.push({ number: member.number, reason: "draft", detail: "PR is a draft" });
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const info = yield* fetchPRView(member.number);
|
|
55
|
+
if (info.mergeable === "CONFLICTING") {
|
|
56
|
+
blockers.push({
|
|
57
|
+
number: member.number,
|
|
58
|
+
reason: "not_mergeable",
|
|
59
|
+
detail: "PR has merge conflicts",
|
|
60
|
+
});
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (info.mergeable !== "MERGEABLE") {
|
|
65
|
+
blockers.push({
|
|
66
|
+
number: member.number,
|
|
67
|
+
reason: "mergeability_unknown",
|
|
68
|
+
detail: `GitHub has not settled mergeability yet (${info.mergeable})`,
|
|
69
|
+
});
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const checks = yield* fetchCheckResults(member.number);
|
|
74
|
+
const failing = checks.filter((check) => check.bucket === "fail");
|
|
75
|
+
if (failing.length > 0) {
|
|
76
|
+
blockers.push({
|
|
77
|
+
number: member.number,
|
|
78
|
+
reason: "checks_failing",
|
|
79
|
+
detail: `${failing.length} failing check(s): ${failing.map((c) => c.name).join(", ")}`,
|
|
80
|
+
});
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const pending = checks.filter((check) => check.bucket === "pending");
|
|
85
|
+
if (pending.length > 0) {
|
|
86
|
+
blockers.push({
|
|
87
|
+
number: member.number,
|
|
88
|
+
reason: "checks_pending",
|
|
89
|
+
detail: `${pending.length} check(s) still running`,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return blockers;
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
export const mergeStack = Effect.fn("pr.mergeStack")(function* (opts: {
|
|
98
|
+
pr: number;
|
|
99
|
+
strategy: MergeStrategy;
|
|
100
|
+
confirm: boolean;
|
|
101
|
+
}) {
|
|
102
|
+
const gh = yield* GitHubService;
|
|
103
|
+
const repo = yield* gh.getRepoInfo();
|
|
104
|
+
const view = yield* readStack({ pr: opts.pr });
|
|
105
|
+
|
|
106
|
+
if (!view.isStacked || view.stackNumber === null || view.baseRef === null) {
|
|
107
|
+
return yield* new GitHubMergeError({
|
|
108
|
+
message: `PR #${opts.pr} is not part of a GitHub stack`,
|
|
109
|
+
reason: "unknown",
|
|
110
|
+
hint: "Use 'pr merge' for an unstacked PR. A chain of PRs based on each other is only a stack when GitHub has registered it as one.",
|
|
111
|
+
nextCommand: `agent-tools-gh pr merge --pr ${opts.pr}`,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const unmerged = view.members.filter((member) => member.state === "open");
|
|
116
|
+
if (unmerged.length === 0) {
|
|
117
|
+
return yield* new GitHubMergeError({
|
|
118
|
+
message: `Stack #${view.stackNumber} has no open pull requests left`,
|
|
119
|
+
reason: "unknown",
|
|
120
|
+
hint: "Every member is already merged or closed.",
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// merge-async merges every unmerged PR up to and including the requested one, so the
|
|
125
|
+
// top open member is the request that lands the whole stack.
|
|
126
|
+
const target = unmerged[unmerged.length - 1] as StackMember;
|
|
127
|
+
|
|
128
|
+
const blockers = yield* collectBlockers(unmerged);
|
|
129
|
+
|
|
130
|
+
const plan = unmerged.map((member) => ({
|
|
131
|
+
position: member.position,
|
|
132
|
+
number: member.number,
|
|
133
|
+
headRefName: member.headRefName,
|
|
134
|
+
}));
|
|
135
|
+
|
|
136
|
+
const base = {
|
|
137
|
+
stackNumber: view.stackNumber,
|
|
138
|
+
baseRef: view.baseRef,
|
|
139
|
+
target: target.number,
|
|
140
|
+
plan,
|
|
141
|
+
blockers,
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
if (!opts.confirm) {
|
|
145
|
+
return {
|
|
146
|
+
...base,
|
|
147
|
+
strategy: opts.strategy,
|
|
148
|
+
merged: false,
|
|
149
|
+
dryRun: true,
|
|
150
|
+
sha: null,
|
|
151
|
+
adoptedExistingRequest: false,
|
|
152
|
+
} satisfies StackMergeResult;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (blockers.length > 0) {
|
|
156
|
+
return yield* new GitHubMergeError({
|
|
157
|
+
message:
|
|
158
|
+
`Stack #${view.stackNumber} is not ready: ` +
|
|
159
|
+
blockers.map((b) => `#${b.number} ${b.detail}`).join("; "),
|
|
160
|
+
reason: "unknown",
|
|
161
|
+
hint: "A partial stack merge leaves a parent on the trunk and a broken child, so nothing was attempted.",
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const asyncPath = `repos/${repo.owner}/${repo.name}/pulls/${target.number}/merge-async`;
|
|
166
|
+
|
|
167
|
+
const requested = yield* gh.apiRequest<AsyncMergeResult>({
|
|
168
|
+
path: asyncPath,
|
|
169
|
+
method: "PUT",
|
|
170
|
+
body: { merge_method: opts.strategy, merge_action: "direct_merge" },
|
|
171
|
+
alsoAcceptStatus: [202, 409],
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// A 409 hands back an existing request whose options may differ from the ones asked
|
|
175
|
+
// for, so the result reports the strategy GitHub is actually applying.
|
|
176
|
+
const adoptedExistingRequest = requested.status === 409;
|
|
177
|
+
let latest = requested.body;
|
|
178
|
+
const effectiveStrategy = latest.details?.merge_method ?? opts.strategy;
|
|
179
|
+
|
|
180
|
+
const uuid = latest.details?.uuid;
|
|
181
|
+
if (latest.status === "pending" && (uuid === undefined || uuid.length === 0)) {
|
|
182
|
+
return yield* stackMergeFailure({
|
|
183
|
+
stackNumber: view.stackNumber,
|
|
184
|
+
pr: target.number,
|
|
185
|
+
message: "GitHub reported a pending merge without a request id",
|
|
186
|
+
hint: "The merge may or may not be running. Re-read the stack before retrying so the merge is not requested twice.",
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (uuid !== undefined) {
|
|
191
|
+
latest = yield* pollUntilResolved({
|
|
192
|
+
initial: latest,
|
|
193
|
+
isPending: (value) => value.status === "pending",
|
|
194
|
+
fetchLatest: () =>
|
|
195
|
+
gh
|
|
196
|
+
.apiRequest<AsyncMergeResult>({ path: `${asyncPath}/${uuid}` })
|
|
197
|
+
.pipe(Effect.map((response) => response.body)),
|
|
198
|
+
intervalMs: POLL_INTERVAL_MS,
|
|
199
|
+
budgetSeconds: MAX_WAIT_SECONDS,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (latest.status === "merged") {
|
|
204
|
+
return {
|
|
205
|
+
...base,
|
|
206
|
+
strategy: effectiveStrategy,
|
|
207
|
+
merged: true,
|
|
208
|
+
dryRun: false,
|
|
209
|
+
sha: latest.details?.sha ?? null,
|
|
210
|
+
adoptedExistingRequest,
|
|
211
|
+
} satisfies StackMergeResult;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (latest.status === "enqueued") {
|
|
215
|
+
return yield* stackMergeFailure({
|
|
216
|
+
stackNumber: view.stackNumber,
|
|
217
|
+
pr: target.number,
|
|
218
|
+
reason: "merge_queue",
|
|
219
|
+
message: latest.details?.message ?? "the stack entered a merge queue",
|
|
220
|
+
hint: "The merge queue owns the merge from here; it is not merged yet. Watch the PRs until the queue drains.",
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (latest.status === "pending") {
|
|
225
|
+
return yield* stackMergeFailure({
|
|
226
|
+
stackNumber: view.stackNumber,
|
|
227
|
+
pr: target.number,
|
|
228
|
+
message: `still pending after ${MAX_WAIT_SECONDS}s`,
|
|
229
|
+
hint: "The asynchronous merge is still running. Re-check the stack before retrying so the merge is not requested twice.",
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return yield* stackMergeFailure({
|
|
234
|
+
stackNumber: view.stackNumber,
|
|
235
|
+
pr: target.number,
|
|
236
|
+
message: latest.details?.message ?? "the merge request failed",
|
|
237
|
+
hint: "Inspect the stack state and branch protections, then retry.",
|
|
238
|
+
});
|
|
239
|
+
});
|
package/src/gh-tool/service.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
|
|
2
|
-
import { Context,
|
|
2
|
+
import { Context, Effect, Layer, Stream } from "effect";
|
|
3
3
|
|
|
4
4
|
import type { GitHubRepoConfig } from "#config";
|
|
5
5
|
import type { RepoInfo } from "./types";
|
|
6
6
|
|
|
7
7
|
import { GH_BINARY } from "./config";
|
|
8
8
|
import { GitHubAuthError, GitHubCommandError, GitHubNotFoundError } from "./errors";
|
|
9
|
+
import { retryTransient } from "#shared/retry-transient";
|
|
10
|
+
import { githubApi } from "./api";
|
|
11
|
+
import type { GitHubApiRequest, GitHubApiResponse } from "./api";
|
|
9
12
|
import { ConfigService, getGitHubConfig, resolveGitHubRepoTarget } from "#config";
|
|
10
13
|
|
|
11
14
|
// Transient GitHub-side failures worth a silent retry (vs. a hard error the agent must act on).
|
|
@@ -82,6 +85,9 @@ export class GitHubService extends Context.Service<
|
|
|
82
85
|
query: string,
|
|
83
86
|
variables: Record<string, string | number | null>,
|
|
84
87
|
) => Effect.Effect<unknown, GhError>;
|
|
88
|
+
readonly apiRequest: <T>(
|
|
89
|
+
opts: GitHubApiRequest,
|
|
90
|
+
) => Effect.Effect<GitHubApiResponse<T>, GhError>;
|
|
85
91
|
readonly getRepoConfig: () => Effect.Effect<GitHubRepoConfig | undefined, never>;
|
|
86
92
|
readonly getRepoInfo: () => Effect.Effect<RepoInfo, GhError>;
|
|
87
93
|
readonly withRepoTarget: <A, E, R>(
|
|
@@ -261,20 +267,12 @@ export class GitHubService extends Context.Service<
|
|
|
261
267
|
// Auto-retry transient failures, but only for idempotent reads (never replay a mutation).
|
|
262
268
|
const runGh = (args: string[]): Effect.Effect<GhResult, GhError> => {
|
|
263
269
|
const canRetry = isSafeRetryRead(args);
|
|
264
|
-
|
|
265
|
-
runGhAttempt(args)
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
return Effect.sleep(Duration.millis(500 * 2 ** attempt)).pipe(
|
|
271
|
-
Effect.flatMap(() => loop(attempt + 1)),
|
|
272
|
-
);
|
|
273
|
-
}
|
|
274
|
-
return Effect.fail(err);
|
|
275
|
-
}),
|
|
276
|
-
);
|
|
277
|
-
return loop(0);
|
|
270
|
+
return retryTransient({
|
|
271
|
+
attempt: () => runGhAttempt(args),
|
|
272
|
+
isTransient: (err) =>
|
|
273
|
+
err instanceof GitHubCommandError && err.retryable === true && canRetry,
|
|
274
|
+
maxRetries: MAX_GH_RETRIES,
|
|
275
|
+
});
|
|
278
276
|
};
|
|
279
277
|
|
|
280
278
|
const runGhJson = <T>(args: string[]) =>
|
|
@@ -365,7 +363,15 @@ export class GitHubService extends Context.Service<
|
|
|
365
363
|
return repoInfo;
|
|
366
364
|
});
|
|
367
365
|
|
|
368
|
-
return {
|
|
366
|
+
return {
|
|
367
|
+
runGh,
|
|
368
|
+
runGhJson,
|
|
369
|
+
runGraphQL,
|
|
370
|
+
apiRequest: githubApi,
|
|
371
|
+
getRepoConfig,
|
|
372
|
+
getRepoInfo,
|
|
373
|
+
withRepoTarget,
|
|
374
|
+
};
|
|
369
375
|
}),
|
|
370
376
|
),
|
|
371
377
|
);
|
package/src/gh-tool/types.ts
CHANGED
|
@@ -314,3 +314,41 @@ export type JobAnnotations = {
|
|
|
314
314
|
jobName: string;
|
|
315
315
|
annotations: CheckRunAnnotation[];
|
|
316
316
|
};
|
|
317
|
+
|
|
318
|
+
export type StackMember = {
|
|
319
|
+
position: number;
|
|
320
|
+
number: number;
|
|
321
|
+
title: string;
|
|
322
|
+
headRefName: string;
|
|
323
|
+
baseRefName: string;
|
|
324
|
+
state: "open" | "closed" | "merged";
|
|
325
|
+
isDraft: boolean;
|
|
326
|
+
url: string;
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
export type StackView = {
|
|
330
|
+
pr: number;
|
|
331
|
+
isStacked: boolean;
|
|
332
|
+
stackNumber: number | null;
|
|
333
|
+
baseRef: string | null;
|
|
334
|
+
members: StackMember[];
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
export type StackMergeBlocker = {
|
|
338
|
+
number: number;
|
|
339
|
+
reason: "draft" | "not_mergeable" | "mergeability_unknown" | "checks_failing" | "checks_pending";
|
|
340
|
+
detail: string;
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
export type StackMergeResult = {
|
|
344
|
+
stackNumber: number;
|
|
345
|
+
baseRef: string;
|
|
346
|
+
target: number;
|
|
347
|
+
strategy: MergeStrategy;
|
|
348
|
+
plan: Array<{ position: number; number: number; headRefName: string }>;
|
|
349
|
+
merged: boolean;
|
|
350
|
+
dryRun: boolean;
|
|
351
|
+
blockers: StackMergeBlocker[];
|
|
352
|
+
sha: string | null;
|
|
353
|
+
adoptedExistingRequest: boolean;
|
|
354
|
+
};
|
package/src/shared/index.ts
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Clock, Duration, Effect } from "effect";
|
|
2
|
+
|
|
3
|
+
export const pollUntilResolved = <A, E, R>(opts: {
|
|
4
|
+
initial: A;
|
|
5
|
+
isPending: (value: A) => boolean;
|
|
6
|
+
fetchLatest: () => Effect.Effect<A, E, R>;
|
|
7
|
+
intervalMs: number;
|
|
8
|
+
budgetSeconds: number;
|
|
9
|
+
}): Effect.Effect<A, E, R> =>
|
|
10
|
+
Effect.gen(function* () {
|
|
11
|
+
let latest = opts.initial;
|
|
12
|
+
|
|
13
|
+
if (!opts.isPending(latest)) {
|
|
14
|
+
return latest;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const start = yield* Clock.currentTimeMillis;
|
|
18
|
+
const deadlineMs = Number(start) + opts.budgetSeconds * 1000;
|
|
19
|
+
let timedOut = false;
|
|
20
|
+
|
|
21
|
+
// Effect.whileLoop (not recursion) so TestClock.adjust can advance Effect.sleep without real waits.
|
|
22
|
+
yield* Effect.whileLoop({
|
|
23
|
+
while: () => opts.isPending(latest) && !timedOut,
|
|
24
|
+
body: () =>
|
|
25
|
+
Effect.gen(function* () {
|
|
26
|
+
const now = yield* Clock.currentTimeMillis;
|
|
27
|
+
if (Number(now) >= deadlineMs) {
|
|
28
|
+
timedOut = true;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const remaining = deadlineMs - Number(now);
|
|
32
|
+
yield* Effect.sleep(Duration.millis(Math.min(opts.intervalMs, remaining)));
|
|
33
|
+
latest = yield* opts.fetchLatest();
|
|
34
|
+
}),
|
|
35
|
+
step: () => undefined,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return latest;
|
|
39
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Duration, Effect } from "effect";
|
|
2
|
+
|
|
3
|
+
const BASE_DELAY_MS = 500;
|
|
4
|
+
|
|
5
|
+
export const retryTransient = <A, E, R>(opts: {
|
|
6
|
+
attempt: () => Effect.Effect<A, E, R>;
|
|
7
|
+
isTransient: (error: E) => boolean;
|
|
8
|
+
maxRetries: number;
|
|
9
|
+
}): Effect.Effect<A, E, R> => {
|
|
10
|
+
const loop = (attempt: number): Effect.Effect<A, E, R> =>
|
|
11
|
+
opts
|
|
12
|
+
.attempt()
|
|
13
|
+
.pipe(
|
|
14
|
+
Effect.catch((error: E) =>
|
|
15
|
+
opts.isTransient(error) && attempt < opts.maxRetries
|
|
16
|
+
? Effect.sleep(Duration.millis(BASE_DELAY_MS * 2 ** attempt)).pipe(
|
|
17
|
+
Effect.flatMap(() => loop(attempt + 1)),
|
|
18
|
+
)
|
|
19
|
+
: Effect.fail(error),
|
|
20
|
+
),
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
return loop(0);
|
|
24
|
+
};
|