@renaiss-shipflow/cli 0.2.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +41 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +57 -8
- package/dist/client.js.map +1 -1
- package/dist/commands/claims.js +2 -2
- package/dist/commands/claims.js.map +1 -1
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +86 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/features.d.ts +3 -0
- package/dist/commands/features.d.ts.map +1 -0
- package/dist/commands/features.js +50 -0
- package/dist/commands/features.js.map +1 -0
- package/dist/commands/inbox.d.ts +4 -0
- package/dist/commands/inbox.d.ts.map +1 -0
- package/dist/commands/inbox.js +72 -0
- package/dist/commands/inbox.js.map +1 -0
- package/dist/commands/init.js +2 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/issue.d.ts.map +1 -1
- package/dist/commands/issue.js +140 -32
- package/dist/commands/issue.js.map +1 -1
- package/dist/commands/issues.js +2 -2
- package/dist/commands/issues.js.map +1 -1
- package/dist/commands/pr.d.ts +4 -0
- package/dist/commands/pr.d.ts.map +1 -1
- package/dist/commands/pr.js +234 -12
- package/dist/commands/pr.js.map +1 -1
- package/dist/commands/regression.js +2 -2
- package/dist/commands/regression.js.map +1 -1
- package/dist/commands/release.js +2 -2
- package/dist/commands/release.js.map +1 -1
- package/dist/commands/status.js +2 -2
- package/dist/commands/status.js.map +1 -1
- package/dist/config.d.ts +49 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +99 -0
- package/dist/config.js.map +1 -1
- package/dist/gh.d.ts +70 -1
- package/dist/gh.d.ts.map +1 -1
- package/dist/gh.js +88 -4
- package/dist/gh.js.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/issue-order.d.ts +8 -0
- package/dist/issue-order.d.ts.map +1 -0
- package/dist/issue-order.js +33 -0
- package/dist/issue-order.js.map +1 -0
- package/dist/pr-state.d.ts +57 -0
- package/dist/pr-state.d.ts.map +1 -0
- package/dist/pr-state.js +140 -0
- package/dist/pr-state.js.map +1 -0
- package/package.json +1 -1
- package/dist/__tests__/gh.test.d.ts +0 -2
- package/dist/__tests__/gh.test.d.ts.map +0 -1
- package/dist/__tests__/gh.test.js +0 -46
- package/dist/__tests__/gh.test.js.map +0 -1
- package/dist/__tests__/login.test.d.ts +0 -2
- package/dist/__tests__/login.test.d.ts.map +0 -1
- package/dist/__tests__/login.test.js +0 -25
- package/dist/__tests__/login.test.js.map +0 -1
- package/dist/__tests__/project.test.d.ts +0 -2
- package/dist/__tests__/project.test.d.ts.map +0 -1
- package/dist/__tests__/project.test.js +0 -17
- package/dist/__tests__/project.test.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
2
3
|
import { Command } from "commander";
|
|
3
4
|
import { registerAuthCommands } from "./commands/auth.js";
|
|
4
5
|
import { registerRepoCommands } from "./commands/repos.js";
|
|
@@ -12,16 +13,21 @@ import { registerInitCommand } from "./commands/init.js";
|
|
|
12
13
|
import { registerStatusCommand } from "./commands/status.js";
|
|
13
14
|
import { registerIssuesCommand } from "./commands/issues.js";
|
|
14
15
|
import { registerIssueCommand } from "./commands/issue.js";
|
|
16
|
+
import { registerInboxCommand } from "./commands/inbox.js";
|
|
17
|
+
import { registerFeaturesCommand } from "./commands/features.js";
|
|
18
|
+
import { registerConfigCommand } from "./commands/config.js";
|
|
15
19
|
import { registerClaimsCommand } from "./commands/claims.js";
|
|
16
20
|
import { registerPRCommand } from "./commands/pr.js";
|
|
17
21
|
import { registerTestCommand } from "./commands/test.js";
|
|
18
22
|
import { registerRegressionCommand } from "./commands/regression.js";
|
|
19
23
|
import { registerReleaseCommand } from "./commands/release.js";
|
|
24
|
+
// Read the version from package.json so it never drifts from the published tag.
|
|
25
|
+
const pkg = createRequire(import.meta.url)("../package.json");
|
|
20
26
|
const program = new Command();
|
|
21
27
|
program
|
|
22
28
|
.name("renaiss-shipflow")
|
|
23
29
|
.description("CLI for RenaissShipFlow - AI-powered project management automation")
|
|
24
|
-
.version(
|
|
30
|
+
.version(pkg.version)
|
|
25
31
|
.option("--api-url <url>", "RenaissShipFlow API base URL")
|
|
26
32
|
.option("--org <org>", "Organization slug (default: \"default\")", "default");
|
|
27
33
|
registerAuthCommands(program);
|
|
@@ -36,6 +42,9 @@ registerInitCommand(program);
|
|
|
36
42
|
registerStatusCommand(program);
|
|
37
43
|
registerIssuesCommand(program);
|
|
38
44
|
registerIssueCommand(program);
|
|
45
|
+
registerInboxCommand(program);
|
|
46
|
+
registerFeaturesCommand(program);
|
|
47
|
+
registerConfigCommand(program);
|
|
39
48
|
registerClaimsCommand(program);
|
|
40
49
|
registerPRCommand(program);
|
|
41
50
|
registerTestCommand(program);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAE/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAE/D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,kBAAkB,CAAC;KACxB,WAAW,CAAC,oEAAoE,CAAC;KACjF,OAAO,CAAC,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { GhIssue } from "./gh.js";
|
|
2
|
+
/**
|
|
3
|
+
* Orders issues for the work loop: highest `priority:*` first, then highest
|
|
4
|
+
* `severity:*`, then most recently created (latest wins on an otherwise-equal
|
|
5
|
+
* tie). Returns a new array; the input is not mutated.
|
|
6
|
+
*/
|
|
7
|
+
export declare function sortIssuesForPickup<T extends GhIssue>(issues: T[]): T[];
|
|
8
|
+
//# sourceMappingURL=issue-order.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issue-order.d.ts","sourceRoot":"","sources":["../src/issue-order.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAiBvC;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAYvE"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Higher number = picked first. Issues without a priority/severity label rank 0,
|
|
2
|
+
// so they sort after any labeled issue.
|
|
3
|
+
const PRIORITY_RANK = { critical: 4, high: 3, medium: 2, low: 1 };
|
|
4
|
+
const SEVERITY_RANK = { blocking: 4, major: 3, minor: 2, cosmetic: 1 };
|
|
5
|
+
/** Best rank among an issue's labels matching `prefix` (e.g. "priority:"). */
|
|
6
|
+
function labelRank(labels, prefix, ranks) {
|
|
7
|
+
let best = 0;
|
|
8
|
+
for (const l of labels) {
|
|
9
|
+
const name = l.name.toLowerCase();
|
|
10
|
+
if (name.startsWith(prefix))
|
|
11
|
+
best = Math.max(best, ranks[name.slice(prefix.length)] ?? 0);
|
|
12
|
+
}
|
|
13
|
+
return best;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Orders issues for the work loop: highest `priority:*` first, then highest
|
|
17
|
+
* `severity:*`, then most recently created (latest wins on an otherwise-equal
|
|
18
|
+
* tie). Returns a new array; the input is not mutated.
|
|
19
|
+
*/
|
|
20
|
+
export function sortIssuesForPickup(issues) {
|
|
21
|
+
return [...issues].sort((a, b) => {
|
|
22
|
+
const byPriority = labelRank(b.labels, "priority:", PRIORITY_RANK) - labelRank(a.labels, "priority:", PRIORITY_RANK);
|
|
23
|
+
if (byPriority !== 0)
|
|
24
|
+
return byPriority;
|
|
25
|
+
const bySeverity = labelRank(b.labels, "severity:", SEVERITY_RANK) - labelRank(a.labels, "severity:", SEVERITY_RANK);
|
|
26
|
+
if (bySeverity !== 0)
|
|
27
|
+
return bySeverity;
|
|
28
|
+
// ISO 8601 strings compare lexicographically in chronological order, so
|
|
29
|
+
// this puts the newest issue first.
|
|
30
|
+
return (b.createdAt ?? "").localeCompare(a.createdAt ?? "");
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=issue-order.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issue-order.js","sourceRoot":"","sources":["../src/issue-order.ts"],"names":[],"mappings":"AAEA,iFAAiF;AACjF,wCAAwC;AACxC,MAAM,aAAa,GAA2B,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;AAC1F,MAAM,aAAa,GAA2B,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;AAE/F,8EAA8E;AAC9E,SAAS,SAAS,CAAC,MAA0B,EAAE,MAAc,EAAE,KAA6B;IAC1F,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QAClC,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5F,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAoB,MAAW;IAChE,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC/B,MAAM,UAAU,GACd,SAAS,CAAC,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,aAAa,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC;QACpG,IAAI,UAAU,KAAK,CAAC;YAAE,OAAO,UAAU,CAAC;QACxC,MAAM,UAAU,GACd,SAAS,CAAC,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,aAAa,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC;QACpG,IAAI,UAAU,KAAK,CAAC;YAAE,OAAO,UAAU,CAAC;QACxC,wEAAwE;QACxE,oCAAoC;QACpC,OAAO,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { GhPR, GhCheck, GhComment } from "./gh.js";
|
|
2
|
+
import type { MergePolicy } from "./config.js";
|
|
3
|
+
export type CiState = "passing" | "failing" | "pending" | "none";
|
|
4
|
+
export type PRState = "ci_failing" | "changes_requested" | "review_comments" | "ci_pending" | "approved_ready" | "stale" | "awaiting_review";
|
|
5
|
+
/** Collapse the status-check rollup into one CI verdict. */
|
|
6
|
+
export declare function ciStateOf(checks: GhCheck[] | undefined): CiState;
|
|
7
|
+
/**
|
|
8
|
+
* Why an open PR may need follow-up. Empty = nothing to do. "review_comments" is
|
|
9
|
+
* advisory (the comment may already be addressed); "changes_requested" and
|
|
10
|
+
* "ci_failing" are definite.
|
|
11
|
+
*/
|
|
12
|
+
export declare function prAttentionReasons(pr: GhPR, me: string): string[];
|
|
13
|
+
/** The newest comment on an issue if it's from someone other than `me` (i.e. you
|
|
14
|
+
* may owe a reply); otherwise null. */
|
|
15
|
+
export declare function issueNeedsReply(comments: GhComment[], me: string): GhComment | null;
|
|
16
|
+
/** Approved via a native GitHub review or a `shipflow-approved`-style label. */
|
|
17
|
+
export declare function isApproved(pr: GhPR): boolean;
|
|
18
|
+
/** Whole hours since an ISO timestamp. */
|
|
19
|
+
export declare function hoursSince(iso: string | undefined, nowMs?: number): number;
|
|
20
|
+
export interface PRClassification {
|
|
21
|
+
number: number;
|
|
22
|
+
state: PRState;
|
|
23
|
+
ciState: CiState;
|
|
24
|
+
approved: boolean;
|
|
25
|
+
ageHours: number;
|
|
26
|
+
reasons: string[];
|
|
27
|
+
needsAction: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Reduce a PR to the single next action the loop should take. Priority: a red
|
|
31
|
+
* build and explicit change requests come before advisory comments; pending CI
|
|
32
|
+
* means wait; an approved+green PR is a merge candidate; otherwise it's parked
|
|
33
|
+
* (or stale once it ages past the threshold).
|
|
34
|
+
*/
|
|
35
|
+
export declare function classifyPR(pr: GhPR, me: string, opts?: {
|
|
36
|
+
staleHours?: number;
|
|
37
|
+
nowMs?: number;
|
|
38
|
+
}): PRClassification;
|
|
39
|
+
export interface MergeDecision {
|
|
40
|
+
policy: MergePolicy;
|
|
41
|
+
wouldMerge: boolean;
|
|
42
|
+
blockers: string[];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Decide whether the loop may merge this PR under the active policy. A red or
|
|
46
|
+
* pending build, a merge conflict, or requested changes always block. Approval
|
|
47
|
+
* is required for `auto-on-green`; `auto-timeout` also accepts a green PR that
|
|
48
|
+
* has aged past the stale threshold with no objection. `manual` never merges.
|
|
49
|
+
*/
|
|
50
|
+
export declare function mergeDecision(pr: GhPR, me: string, opts: {
|
|
51
|
+
policy: MergePolicy;
|
|
52
|
+
requireCi: boolean;
|
|
53
|
+
staleHours: number;
|
|
54
|
+
nowMs?: number;
|
|
55
|
+
unresolvedThreads?: number;
|
|
56
|
+
}): MergeDecision;
|
|
57
|
+
//# sourceMappingURL=pr-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pr-state.d.ts","sourceRoot":"","sources":["../src/pr-state.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;AAEjE,MAAM,MAAM,OAAO,GACf,YAAY,GACZ,mBAAmB,GACnB,iBAAiB,GACjB,YAAY,GACZ,gBAAgB,GAChB,OAAO,GACP,iBAAiB,CAAC;AAUtB,4DAA4D;AAC5D,wBAAgB,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,SAAS,GAAG,OAAO,CAqBhE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAcjE;AAED;uCACuC;AACvC,wBAAgB,eAAe,CAAC,QAAQ,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAInF;AAED,gFAAgF;AAChF,wBAAgB,UAAU,CAAC,EAAE,EAAE,IAAI,GAAG,OAAO,CAG5C;AAED,0CAA0C;AAC1C,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,GAAE,MAAmB,GAAG,MAAM,CAKtF;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CACxB,EAAE,EAAE,IAAI,EACR,EAAE,EAAE,MAAM,EACV,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GACjD,gBAAgB,CAkBlB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,WAAW,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,EAAE,EAAE,IAAI,EACR,EAAE,EAAE,MAAM,EACV,IAAI,EAAE;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAE,GAChH,aAAa,CAoBf"}
|
package/dist/pr-state.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// Check conclusions/states that mean "broken — go look".
|
|
2
|
+
const FAILING = new Set(["FAILURE", "TIMED_OUT", "CANCELLED", "ACTION_REQUIRED", "ERROR", "STARTUP_FAILURE"]);
|
|
3
|
+
// States that mean "still running — not a verdict yet".
|
|
4
|
+
const PENDING = new Set(["PENDING", "EXPECTED", "QUEUED", "IN_PROGRESS", "WAITING", "REQUESTED"]);
|
|
5
|
+
// A human (or the dashboard) can approve a PR with one of these labels, in
|
|
6
|
+
// addition to a native GitHub review approval.
|
|
7
|
+
const APPROVAL_LABELS = new Set(["shipflow-approved", "approved", "✅ approved"]);
|
|
8
|
+
/** Collapse the status-check rollup into one CI verdict. */
|
|
9
|
+
export function ciStateOf(checks) {
|
|
10
|
+
if (!checks || checks.length === 0)
|
|
11
|
+
return "none";
|
|
12
|
+
let failing = false;
|
|
13
|
+
let pending = false;
|
|
14
|
+
let passing = false;
|
|
15
|
+
for (const c of checks) {
|
|
16
|
+
const concl = (c.conclusion ?? "").toUpperCase();
|
|
17
|
+
const status = (c.status ?? "").toUpperCase();
|
|
18
|
+
const state = (c.state ?? "").toUpperCase();
|
|
19
|
+
if (FAILING.has(concl) || FAILING.has(state)) {
|
|
20
|
+
failing = true;
|
|
21
|
+
}
|
|
22
|
+
else if ((status && status !== "COMPLETED") || PENDING.has(state)) {
|
|
23
|
+
pending = true;
|
|
24
|
+
}
|
|
25
|
+
else if (concl === "SUCCESS" || concl === "NEUTRAL" || concl === "SKIPPED" || state === "SUCCESS") {
|
|
26
|
+
passing = true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (failing)
|
|
30
|
+
return "failing";
|
|
31
|
+
if (pending)
|
|
32
|
+
return "pending";
|
|
33
|
+
if (passing)
|
|
34
|
+
return "passing";
|
|
35
|
+
return "none";
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Why an open PR may need follow-up. Empty = nothing to do. "review_comments" is
|
|
39
|
+
* advisory (the comment may already be addressed); "changes_requested" and
|
|
40
|
+
* "ci_failing" are definite.
|
|
41
|
+
*/
|
|
42
|
+
export function prAttentionReasons(pr, me) {
|
|
43
|
+
const reasons = [];
|
|
44
|
+
if (pr.reviewDecision === "CHANGES_REQUESTED")
|
|
45
|
+
reasons.push("changes_requested");
|
|
46
|
+
const failing = (pr.statusCheckRollup ?? []).some((c) => FAILING.has((c.conclusion ?? "").toUpperCase()) || FAILING.has((c.state ?? "").toUpperCase()));
|
|
47
|
+
if (failing)
|
|
48
|
+
reasons.push("ci_failing");
|
|
49
|
+
const fromOthers = (a) => !!a.author && a.author.login !== me;
|
|
50
|
+
const reviewFeedback = (pr.reviews ?? []).filter((r) => fromOthers(r) && (r.state === "CHANGES_REQUESTED" || r.state === "COMMENTED"));
|
|
51
|
+
const otherComments = (pr.comments ?? []).filter(fromOthers);
|
|
52
|
+
if (reviewFeedback.length || otherComments.length)
|
|
53
|
+
reasons.push("review_comments");
|
|
54
|
+
return reasons;
|
|
55
|
+
}
|
|
56
|
+
/** The newest comment on an issue if it's from someone other than `me` (i.e. you
|
|
57
|
+
* may owe a reply); otherwise null. */
|
|
58
|
+
export function issueNeedsReply(comments, me) {
|
|
59
|
+
if (!comments?.length)
|
|
60
|
+
return null;
|
|
61
|
+
const last = comments[comments.length - 1];
|
|
62
|
+
return last.author && last.author.login !== me ? last : null;
|
|
63
|
+
}
|
|
64
|
+
/** Approved via a native GitHub review or a `shipflow-approved`-style label. */
|
|
65
|
+
export function isApproved(pr) {
|
|
66
|
+
if (pr.reviewDecision === "APPROVED")
|
|
67
|
+
return true;
|
|
68
|
+
return (pr.labels ?? []).some((l) => APPROVAL_LABELS.has(l.name.trim().toLowerCase()));
|
|
69
|
+
}
|
|
70
|
+
/** Whole hours since an ISO timestamp. */
|
|
71
|
+
export function hoursSince(iso, nowMs = Date.now()) {
|
|
72
|
+
if (!iso)
|
|
73
|
+
return 0;
|
|
74
|
+
const t = Date.parse(iso);
|
|
75
|
+
if (Number.isNaN(t))
|
|
76
|
+
return 0;
|
|
77
|
+
return Math.max(0, (nowMs - t) / 3_600_000);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Reduce a PR to the single next action the loop should take. Priority: a red
|
|
81
|
+
* build and explicit change requests come before advisory comments; pending CI
|
|
82
|
+
* means wait; an approved+green PR is a merge candidate; otherwise it's parked
|
|
83
|
+
* (or stale once it ages past the threshold).
|
|
84
|
+
*/
|
|
85
|
+
export function classifyPR(pr, me, opts = {}) {
|
|
86
|
+
const reasons = prAttentionReasons(pr, me);
|
|
87
|
+
const ciState = ciStateOf(pr.statusCheckRollup);
|
|
88
|
+
const approved = isApproved(pr);
|
|
89
|
+
const ageHours = hoursSince(pr.updatedAt, opts.nowMs);
|
|
90
|
+
const staleHours = opts.staleHours ?? 48;
|
|
91
|
+
let state;
|
|
92
|
+
if (ciState === "failing")
|
|
93
|
+
state = "ci_failing";
|
|
94
|
+
else if (pr.reviewDecision === "CHANGES_REQUESTED")
|
|
95
|
+
state = "changes_requested";
|
|
96
|
+
else if (reasons.includes("review_comments"))
|
|
97
|
+
state = "review_comments";
|
|
98
|
+
else if (ciState === "pending")
|
|
99
|
+
state = "ci_pending";
|
|
100
|
+
else if (approved)
|
|
101
|
+
state = "approved_ready"; // CI passing or none
|
|
102
|
+
else if (ageHours >= staleHours)
|
|
103
|
+
state = "stale";
|
|
104
|
+
else
|
|
105
|
+
state = "awaiting_review";
|
|
106
|
+
const needsAction = state !== "ci_pending" && state !== "awaiting_review";
|
|
107
|
+
return { number: pr.number, state, ciState, approved, ageHours, reasons, needsAction };
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Decide whether the loop may merge this PR under the active policy. A red or
|
|
111
|
+
* pending build, a merge conflict, or requested changes always block. Approval
|
|
112
|
+
* is required for `auto-on-green`; `auto-timeout` also accepts a green PR that
|
|
113
|
+
* has aged past the stale threshold with no objection. `manual` never merges.
|
|
114
|
+
*/
|
|
115
|
+
export function mergeDecision(pr, me, opts) {
|
|
116
|
+
const cl = classifyPR(pr, me, { staleHours: opts.staleHours, nowMs: opts.nowMs });
|
|
117
|
+
const blockers = [];
|
|
118
|
+
if (opts.policy === "manual")
|
|
119
|
+
blockers.push("merge-policy is manual (human merge required)");
|
|
120
|
+
if (cl.ciState === "failing")
|
|
121
|
+
blockers.push("CI is failing");
|
|
122
|
+
if (cl.ciState === "pending")
|
|
123
|
+
blockers.push("CI still running");
|
|
124
|
+
if (opts.requireCi && cl.ciState === "none")
|
|
125
|
+
blockers.push("no CI checks to confirm (set require-ci=false to allow)");
|
|
126
|
+
if (pr.reviewDecision === "CHANGES_REQUESTED")
|
|
127
|
+
blockers.push("changes requested");
|
|
128
|
+
if ((opts.unresolvedThreads ?? 0) > 0)
|
|
129
|
+
blockers.push(`${opts.unresolvedThreads} unresolved review thread(s) — address + resolve them first`);
|
|
130
|
+
if ((pr.mergeable ?? "").toUpperCase() === "CONFLICTING")
|
|
131
|
+
blockers.push("merge conflict with base (run: pr sync)");
|
|
132
|
+
if (opts.policy === "auto-on-green" && !cl.approved) {
|
|
133
|
+
blockers.push("not approved (needs a GitHub review approval or a shipflow-approved label)");
|
|
134
|
+
}
|
|
135
|
+
if (opts.policy === "auto-timeout" && !cl.approved && cl.ageHours < opts.staleHours) {
|
|
136
|
+
blockers.push(`awaiting approval or the ${opts.staleHours}h timeout (age ${Math.round(cl.ageHours)}h)`);
|
|
137
|
+
}
|
|
138
|
+
return { policy: opts.policy, wouldMerge: blockers.length === 0, blockers };
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=pr-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pr-state.js","sourceRoot":"","sources":["../src/pr-state.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gh.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/gh.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, mock, beforeEach } from "bun:test";
|
|
2
|
-
// Mock child_process before importing gh.ts
|
|
3
|
-
let lastCmd = "";
|
|
4
|
-
let mockStdout = "";
|
|
5
|
-
let mockExit = 0;
|
|
6
|
-
mock.module("node:child_process", () => ({
|
|
7
|
-
execSync: (cmd) => {
|
|
8
|
-
lastCmd = cmd;
|
|
9
|
-
if (mockExit !== 0) {
|
|
10
|
-
const e = new Error("non-zero exit");
|
|
11
|
-
e.status = mockExit;
|
|
12
|
-
throw e;
|
|
13
|
-
}
|
|
14
|
-
return mockStdout;
|
|
15
|
-
},
|
|
16
|
-
spawnSync: (cmd, args) => {
|
|
17
|
-
lastCmd = `${cmd} ${args.join(" ")}`;
|
|
18
|
-
return { status: mockExit, stdout: Buffer.from(mockStdout), stderr: Buffer.from("") };
|
|
19
|
-
},
|
|
20
|
-
}));
|
|
21
|
-
const { ghAuthStatus, ghAuthToken, ghAuthLogin, ghIssueView } = await import("../gh.js");
|
|
22
|
-
beforeEach(() => { lastCmd = ""; mockStdout = ""; mockExit = 0; });
|
|
23
|
-
describe("ghAuthStatus", () => {
|
|
24
|
-
it("returns 'logged-in' when gh exits 0", () => {
|
|
25
|
-
mockExit = 0;
|
|
26
|
-
expect(ghAuthStatus()).toBe("logged-in");
|
|
27
|
-
});
|
|
28
|
-
it("returns 'logged-out' when gh exits non-zero", () => {
|
|
29
|
-
mockExit = 1;
|
|
30
|
-
expect(ghAuthStatus()).toBe("logged-out");
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
describe("ghAuthToken", () => {
|
|
34
|
-
it("returns the trimmed token", () => {
|
|
35
|
-
mockStdout = "gho_abc123\n";
|
|
36
|
-
expect(ghAuthToken()).toBe("gho_abc123");
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
describe("ghIssueView", () => {
|
|
40
|
-
it("parses JSON output", () => {
|
|
41
|
-
mockStdout = JSON.stringify({ number: 42, title: "x", body: "y", state: "open", labels: [], assignees: [], url: "https://x" });
|
|
42
|
-
const issue = ghIssueView("owner/repo", 42);
|
|
43
|
-
expect(issue.number).toBe(42);
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
//# sourceMappingURL=gh.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gh.test.js","sourceRoot":"","sources":["../../src/__tests__/gh.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAElE,4CAA4C;AAC5C,IAAI,OAAO,GAAG,EAAE,CAAC;AACjB,IAAI,UAAU,GAAG,EAAE,CAAC;AACpB,IAAI,QAAQ,GAAG,CAAC,CAAC;AAEjB,IAAI,CAAC,MAAM,CAAC,oBAAoB,EAAE,GAAG,EAAE,CAAC,CAAC;IACvC,QAAQ,EAAE,CAAC,GAAW,EAAE,EAAE;QACxB,OAAO,GAAG,GAAG,CAAC;QACd,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;YACnB,MAAM,CAAC,GAAQ,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;YAC1C,CAAC,CAAC,MAAM,GAAG,QAAQ,CAAC;YACpB,MAAM,CAAC,CAAC;QACV,CAAC;QACD,OAAO,UAAU,CAAC;IACpB,CAAC;IACD,SAAS,EAAE,CAAC,GAAW,EAAE,IAAc,EAAE,EAAE;QACzC,OAAO,GAAG,GAAG,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;IACxF,CAAC;CACF,CAAC,CAAC,CAAC;AAEJ,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;AAEzF,UAAU,CAAC,GAAG,EAAE,GAAG,OAAO,GAAG,EAAE,CAAC,CAAC,UAAU,GAAG,EAAE,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAEnE,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,QAAQ,GAAG,CAAC,CAAC;QACb,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,QAAQ,GAAG,CAAC,CAAC;QACb,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,UAAU,GAAG,cAAc,CAAC;QAC5B,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAC5B,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC,CAAC;QAC/H,MAAM,KAAK,GAAG,WAAW,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"login.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/login.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, mock } from "bun:test";
|
|
2
|
-
mock.module("../gh.js", () => ({
|
|
3
|
-
ghInstalled: () => true,
|
|
4
|
-
ghAuthStatus: () => "logged-in",
|
|
5
|
-
ghAuthLogin: () => true,
|
|
6
|
-
ghAuthToken: () => "gho_test",
|
|
7
|
-
}));
|
|
8
|
-
mock.module("../client.js", () => ({
|
|
9
|
-
ShipFlowClient: class {
|
|
10
|
-
async exchangeGhToken() {
|
|
11
|
-
return {
|
|
12
|
-
token: "jwt-1", refreshToken: "rt-1",
|
|
13
|
-
tenants: [{ token: "jwt-1", refreshToken: "rt-1", tenant: { id: "t1", githubOrg: "acme", displayName: "Acme" } }],
|
|
14
|
-
tenant: { id: "t1", githubOrg: "acme", displayName: "Acme" },
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
},
|
|
18
|
-
}));
|
|
19
|
-
describe("login command", () => {
|
|
20
|
-
it("imports without throwing", async () => {
|
|
21
|
-
const m = await import("../commands/login.js");
|
|
22
|
-
expect(typeof m.registerLoginCommand).toBe("function");
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
//# sourceMappingURL=login.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"login.test.js","sourceRoot":"","sources":["../../src/__tests__/login.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAEtD,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;IAC7B,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI;IACvB,YAAY,EAAE,GAAG,EAAE,CAAC,WAAW;IAC/B,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI;IACvB,WAAW,EAAE,GAAG,EAAE,CAAC,UAAU;CAC9B,CAAC,CAAC,CAAC;AACJ,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,CAAC;IACjC,cAAc,EAAE;QACd,KAAK,CAAC,eAAe;YACnB,OAAO;gBACL,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM;gBACpC,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,CAAC;gBACjH,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE;aAC7D,CAAC;QACJ,CAAC;KACF;CACF,CAAC,CAAC,CAAC;AAEJ,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;QACxC,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;QAC/C,MAAM,CAAC,OAAO,CAAC,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"project.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/project.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "bun:test";
|
|
2
|
-
import { parseGitRemote } from "../project.js";
|
|
3
|
-
describe("parseGitRemote", () => {
|
|
4
|
-
it("parses https URL", () => {
|
|
5
|
-
expect(parseGitRemote("https://github.com/owner/repo.git")).toEqual({ owner: "owner", repo: "repo" });
|
|
6
|
-
});
|
|
7
|
-
it("parses git@github.com SSH URL", () => {
|
|
8
|
-
expect(parseGitRemote("git@github.com:owner/repo.git")).toEqual({ owner: "owner", repo: "repo" });
|
|
9
|
-
});
|
|
10
|
-
it("parses URL without .git suffix", () => {
|
|
11
|
-
expect(parseGitRemote("https://github.com/owner/repo")).toEqual({ owner: "owner", repo: "repo" });
|
|
12
|
-
});
|
|
13
|
-
it("returns null for non-github URL", () => {
|
|
14
|
-
expect(parseGitRemote("https://gitlab.com/x/y")).toBeNull();
|
|
15
|
-
});
|
|
16
|
-
});
|
|
17
|
-
//# sourceMappingURL=project.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"project.test.js","sourceRoot":"","sources":["../../src/__tests__/project.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAC1B,MAAM,CAAC,cAAc,CAAC,mCAAmC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IACxG,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,CAAC,cAAc,CAAC,+BAA+B,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IACpG,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,CAAC,cAAc,CAAC,+BAA+B,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IACpG,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,CAAC,cAAc,CAAC,wBAAwB,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC9D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|