@kud/gh-ink 0.29.1 → 0.30.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/index.d.ts +1 -0
- package/dist/index.js +61 -25
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React2, { createContext, useState, useRef, useEffect, useMemo, useContext } from 'react';
|
|
2
2
|
import { useWindowSize, useInput, Text, Box } from 'ink';
|
|
3
3
|
import { colors, ScrollView, TextInput, LoadingScreen, StatusMessage, FooterHints, useListCursor, Tabs } from '@kud/ink-ui';
|
|
4
|
-
import { isPassCheck, isFailCheck, resolveThread, unresolveThread, replyToThread, rerunFailedRun, mergePr, reRequestReviewer } from '@kud/gh';
|
|
4
|
+
import { isPassCheck, isFailCheck, isInconclusiveCheck, resolveThread, unresolveThread, replyToThread, rerunFailedRun, mergePr, reRequestReviewer } from '@kud/gh';
|
|
5
5
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
6
6
|
import { $ } from 'zx';
|
|
7
7
|
import { spawn } from 'child_process';
|
|
@@ -417,7 +417,7 @@ var CommentsPanel = ({
|
|
|
417
417
|
) : null
|
|
418
418
|
] });
|
|
419
419
|
};
|
|
420
|
-
var checkIcon = (c) => isPassCheck(c) ? ["\u2713", colors.success] : isFailCheck(c) ? ["\u2717", colors.error] : ["\xB7", colors.warning];
|
|
420
|
+
var checkIcon = (c) => isPassCheck(c) ? ["\u2713", colors.success] : isFailCheck(c) ? ["\u2717", colors.error] : isInconclusiveCheck(c) ? ["-", colors.warning] : ["\xB7", colors.warning];
|
|
421
421
|
var checkLabel = (c) => {
|
|
422
422
|
const name = c.context ?? c.name ?? "";
|
|
423
423
|
return c.workflowName ? `${c.workflowName} / ${name}` : name;
|
|
@@ -491,9 +491,11 @@ var HealthPanel = ({
|
|
|
491
491
|
}
|
|
492
492
|
};
|
|
493
493
|
const retrigger = () => {
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
|
|
494
|
+
const stuck = checks.find(
|
|
495
|
+
(c) => (isFailCheck(c) || isInconclusiveCheck(c)) && runIdOf(checkUrl(c))
|
|
496
|
+
);
|
|
497
|
+
const runId = stuck && runIdOf(checkUrl(stuck));
|
|
498
|
+
if (!runId) return flash("No Actions run to retrigger");
|
|
497
499
|
void act("retrigger CI", () => rerunFailedRun(repo, runId));
|
|
498
500
|
};
|
|
499
501
|
useInput((input, key) => {
|
|
@@ -528,7 +530,8 @@ var HealthPanel = ({
|
|
|
528
530
|
const reviewerMap = Object.fromEntries(reviewers);
|
|
529
531
|
const passed = checks.filter(isPassCheck).length;
|
|
530
532
|
const failed = checks.filter(isFailCheck).length;
|
|
531
|
-
const
|
|
533
|
+
const stale = checks.filter(isInconclusiveCheck).length;
|
|
534
|
+
const pending = checks.length - passed - failed - stale;
|
|
532
535
|
const approved = Object.values(reviewerMap).filter(
|
|
533
536
|
(s) => s === "APPROVED"
|
|
534
537
|
).length;
|
|
@@ -537,6 +540,7 @@ var HealthPanel = ({
|
|
|
537
540
|
).length;
|
|
538
541
|
const checkParts = [[`${passed} passed`, colors.success]];
|
|
539
542
|
if (failed > 0) checkParts.push([`${failed} failed`, colors.error]);
|
|
543
|
+
if (stale > 0) checkParts.push([`${stale} cancelled`, colors.warning]);
|
|
540
544
|
if (pending > 0) checkParts.push([`${pending} pending`, colors.warning]);
|
|
541
545
|
const reviewParts = [
|
|
542
546
|
approved > 0 ? [`${approved} approved`, colors.success] : ["0 approved", "white"]
|
|
@@ -823,11 +827,13 @@ var healthSentence = (item) => {
|
|
|
823
827
|
};
|
|
824
828
|
var checksSentence = (d) => {
|
|
825
829
|
if (!d) return null;
|
|
826
|
-
const
|
|
830
|
+
const stale = d.checksStale ?? 0;
|
|
831
|
+
const total = d.checksPass + d.checksFail + d.checksPending + stale;
|
|
827
832
|
if (total === 0) return "No CI checks run on it.";
|
|
828
833
|
const parts = [];
|
|
829
834
|
if (d.checksPass) parts.push(`${d.checksPass} passing`);
|
|
830
835
|
if (d.checksFail) parts.push(`${d.checksFail} failing`);
|
|
836
|
+
if (stale) parts.push(`${stale} cancelled`);
|
|
831
837
|
if (d.checksPending) parts.push(`${d.checksPending} running`);
|
|
832
838
|
return `Checks: ${parts.join(", ")}.`;
|
|
833
839
|
};
|
|
@@ -920,7 +926,14 @@ var STANDING = {
|
|
|
920
926
|
reviewed: "spoken"
|
|
921
927
|
};
|
|
922
928
|
var YOURS = {
|
|
923
|
-
authored: [
|
|
929
|
+
authored: [
|
|
930
|
+
"ci-fail",
|
|
931
|
+
"conflict",
|
|
932
|
+
"changes-req",
|
|
933
|
+
"threads",
|
|
934
|
+
"approved",
|
|
935
|
+
"draft"
|
|
936
|
+
],
|
|
924
937
|
queued: ["waiting", "pending", "threads", "approved"],
|
|
925
938
|
spoken: ["threads"]
|
|
926
939
|
};
|
|
@@ -1052,7 +1065,10 @@ var budgetNotice = (budget, now = Date.now()) => {
|
|
|
1052
1065
|
1,
|
|
1053
1066
|
Math.round((new Date(budget.resetAt).getTime() - now) / 6e4)
|
|
1054
1067
|
);
|
|
1055
|
-
return fetches <= 0 ? { label: `\u26A1 API budget spent \xB7 ${mins}m`, critical: true } : {
|
|
1068
|
+
return fetches <= 0 ? { label: `\u26A1 API budget spent \xB7 ${mins}m`, critical: true } : {
|
|
1069
|
+
label: `\u26A1 ${fetches} fetch${fetches === 1 ? "" : "es"} left`,
|
|
1070
|
+
critical: false
|
|
1071
|
+
};
|
|
1056
1072
|
};
|
|
1057
1073
|
var isChildRow = (item) => !!item && (item.kind === "show-more" || item.kind === "show-less" || "indent" in item && item.indent === true);
|
|
1058
1074
|
var treeUrls = (items, i) => {
|
|
@@ -1074,7 +1090,8 @@ var treeUrls = (items, i) => {
|
|
|
1074
1090
|
var gapsAbove = (items, i) => {
|
|
1075
1091
|
const item = items[i];
|
|
1076
1092
|
if (!item || i === 0) return false;
|
|
1077
|
-
if (item.kind === "repo-header" || item.kind === "subgroup-header")
|
|
1093
|
+
if (item.kind === "repo-header" || item.kind === "subgroup-header")
|
|
1094
|
+
return true;
|
|
1078
1095
|
if (item.kind !== "task") return false;
|
|
1079
1096
|
return isChildRow(items[i + 1]) || isChildRow(items[i - 1]);
|
|
1080
1097
|
};
|
|
@@ -1342,9 +1359,15 @@ var UNSUBSCRIBE_MUTATION = `
|
|
|
1342
1359
|
}
|
|
1343
1360
|
`;
|
|
1344
1361
|
var GH_ACTION_FAILURES = [
|
|
1345
|
-
[
|
|
1362
|
+
[
|
|
1363
|
+
/rate limit|RATE_LIMIT/i,
|
|
1364
|
+
"GitHub rate limit spent \u2014 it resets within the hour"
|
|
1365
|
+
],
|
|
1346
1366
|
[/\b401\b|not logged in|authentication/i, "gh is not authenticated"],
|
|
1347
|
-
[
|
|
1367
|
+
[
|
|
1368
|
+
/\b403\b|must have admin|not authorized|permission/i,
|
|
1369
|
+
"not permitted on this repo"
|
|
1370
|
+
],
|
|
1348
1371
|
[/\b50[0234]\b/, "GitHub's API is failing (5xx)"],
|
|
1349
1372
|
[/ENOTFOUND|EAI_AGAIN|ETIMEDOUT|ECONNREFUSED/i, "no route to GitHub"]
|
|
1350
1373
|
];
|
|
@@ -1468,9 +1491,7 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
1468
1491
|
hint: "u",
|
|
1469
1492
|
run: () => {
|
|
1470
1493
|
showFlash(`\u22EF Unsubscribing from #${item.number}\u2026`);
|
|
1471
|
-
void unsubscribeFrom(item).then(() => showFlash(`\u2713 Unsubscribed from #${item.number}`)).catch(
|
|
1472
|
-
(e) => showFlash(`\u2717 #${item.number}: ${explainGhAction(e)}`)
|
|
1473
|
-
);
|
|
1494
|
+
void unsubscribeFrom(item).then(() => showFlash(`\u2713 Unsubscribed from #${item.number}`)).catch((e) => showFlash(`\u2717 #${item.number}: ${explainGhAction(e)}`));
|
|
1474
1495
|
}
|
|
1475
1496
|
});
|
|
1476
1497
|
if (item.kind === "pr" && item.branch) {
|
|
@@ -1744,6 +1765,7 @@ var MERGED_FRAMES = ["\u2726", "\u2727", "\u2736", "\u2727"];
|
|
|
1744
1765
|
var MERGED_COLOUR = "#A371F7";
|
|
1745
1766
|
var TRANSIT_HOLD_MS = 7e3;
|
|
1746
1767
|
var NO_TRANSIENTS = /* @__PURE__ */ new Map();
|
|
1768
|
+
var NO_HOLDS = /* @__PURE__ */ new Map();
|
|
1747
1769
|
var TRANSIT_OUT_FRAMES = ["\u25C9", "\u25CE", "\u25CB", "\xB7"];
|
|
1748
1770
|
var TRANSIT_IN_FRAMES = ["\xB7", "\u25CB", "\u25CE", "\u25C9"];
|
|
1749
1771
|
var TRANSIT_COLOUR = {
|
|
@@ -2807,6 +2829,7 @@ var App = ({
|
|
|
2807
2829
|
const displayedSections = useRef([]);
|
|
2808
2830
|
const [transients, setTransients] = useState(NO_TRANSIENTS);
|
|
2809
2831
|
const transitTabs = useRef(/* @__PURE__ */ new Map());
|
|
2832
|
+
const [heldSince, setHeldSince] = useState(NO_HOLDS);
|
|
2810
2833
|
const [visibleTab, setVisibleTab] = useState("");
|
|
2811
2834
|
const showData = (sections, login) => {
|
|
2812
2835
|
const before = displayedSections.current;
|
|
@@ -2861,15 +2884,31 @@ var App = ({
|
|
|
2861
2884
|
);
|
|
2862
2885
|
};
|
|
2863
2886
|
useEffect(() => {
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2887
|
+
const held = new Set(transitTabs.current.values());
|
|
2888
|
+
const arrived = state.phase === "browse" && !!visibleTab && held.has(visibleTab);
|
|
2889
|
+
setHeldSince((prev) => {
|
|
2890
|
+
const next = new Map([...prev].filter(([tab]) => held.has(tab)));
|
|
2891
|
+
if (arrived && !next.has(visibleTab)) next.set(visibleTab, Date.now());
|
|
2892
|
+
const same = next.size === prev.size && [...next.keys()].every((t) => prev.has(t));
|
|
2893
|
+
return same ? prev : next.size === 0 ? NO_HOLDS : next;
|
|
2894
|
+
});
|
|
2895
|
+
}, [transients, visibleTab, state.phase]);
|
|
2896
|
+
useEffect(() => {
|
|
2897
|
+
if (transients.size === 0 || heldSince.size === 0) return;
|
|
2898
|
+
if (state.phase !== "browse") return;
|
|
2899
|
+
const held = new Set(transitTabs.current.values());
|
|
2900
|
+
const running = [...heldSince].filter(([tab]) => held.has(tab));
|
|
2901
|
+
if (running.length === 0) return;
|
|
2902
|
+
const remaining = (at) => at + TRANSIT_HOLD_MS - Date.now();
|
|
2867
2903
|
const timer = setTimeout(
|
|
2868
|
-
() =>
|
|
2869
|
-
|
|
2904
|
+
() => {
|
|
2905
|
+
for (const [tab, at] of running)
|
|
2906
|
+
if (remaining(at) <= 0) settleTab(tab, transients);
|
|
2907
|
+
},
|
|
2908
|
+
Math.max(0, Math.min(...running.map(([, at]) => remaining(at))))
|
|
2870
2909
|
);
|
|
2871
2910
|
return () => clearTimeout(timer);
|
|
2872
|
-
}, [transients,
|
|
2911
|
+
}, [transients, heldSince, state.phase]);
|
|
2873
2912
|
const pendingSummary = useMemo(
|
|
2874
2913
|
() => pending ? summariseDiff(
|
|
2875
2914
|
diffSections(displayedSections.current, pending.sections).counts
|
|
@@ -2917,10 +2956,7 @@ var App = ({
|
|
|
2917
2956
|
}
|
|
2918
2957
|
showData(fresh.sections, fresh.login);
|
|
2919
2958
|
} else if (freshKey !== displayedKey.current) {
|
|
2920
|
-
const { counts } = diffSections(
|
|
2921
|
-
displayedSections.current,
|
|
2922
|
-
fresh.sections
|
|
2923
|
-
);
|
|
2959
|
+
const { counts } = diffSections(displayedSections.current, fresh.sections);
|
|
2924
2960
|
if (counts.added + counts.removed + counts.changed === 0)
|
|
2925
2961
|
showData(fresh.sections, fresh.login);
|
|
2926
2962
|
else setPending(fresh);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kud/gh-ink",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"description": "Ink components for rendering GitHub PR review comments and health — controlled, presentation-only, built on @kud/ink-ui and fed by @kud/gh.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"react": ">=19"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@kud/gh": "0.
|
|
51
|
+
"@kud/gh": "0.8.0",
|
|
52
52
|
"@kud/ink-ui": "0.14.0",
|
|
53
53
|
"zx": "8.8.5"
|
|
54
54
|
},
|