Skip to content

Commit 7739395

Browse files
authored
Add PR status passing/failing/unknown and last updated timestamp in migration table (#2668)
And as now we have a updated_at, also add the time column
1 parent fd398fe commit 7739395

File tree

2 files changed

+123
-2
lines changed

2 files changed

+123
-2
lines changed

src/pages/status/migration/index.jsx

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,20 @@ import styles from "./styles.module.css";
99
import { Tooltip } from "react-tooltip";
1010
import Tabs from '@theme/Tabs';
1111
import TabItem from '@theme/TabItem';
12+
import moment from 'moment';
13+
14+
// GitHub GraphQL MergeStateStatus documentation
15+
// Reference: https://docs.github.com/en/graphql/reference/enums#mergestatestatus
16+
const CI_STATUS_DESCRIPTIONS = {
17+
clean: "Mergeable and passing commit status.",
18+
unstable: "Mergeable with non-passing commit status.",
19+
behind: "The head ref is out of date.",
20+
blocked: "The merge is blocked.",
21+
dirty: "The merge commit cannot be cleanly created.",
22+
draft: "The merge is blocked due to the pull request being a draft.",
23+
has_hooks: "Mergeable with passing commit status and pre-receive hooks.",
24+
unknown: "The state cannot currently be determined."
25+
};
1226

1327
// { Done, In PR, Awaiting PR, Awaiting parents, Not solvable, Bot error }
1428
// The third value is a boolean representing the default display state on load
@@ -39,6 +53,44 @@ export function measureProgress(details) {
3953
return { done, percentage, total };
4054
}
4155

56+
// Mapping of GitHub PR status to UI display properties
57+
const STATUS_DISPLAY_MAP = {
58+
clean: { text: "Passing", badgeClass: "success" },
59+
unstable: { text: "Failing", badgeClass: "danger" },
60+
dirty: { text: "Conflicts", badgeClass: "warning" },
61+
blocked: { text: "Blocked", badgeClass: "danger" },
62+
behind: { text: "Passing*", badgeClass: "success" },
63+
draft: { text: "Draft", badgeClass: "secondary" },
64+
has_hooks: { text: "Unknown*", badgeClass: "secondary" },
65+
unknown: { text: "Unknown", badgeClass: "secondary" },
66+
};
67+
68+
function getStatusBadgeClass(prStatus) {
69+
if (!STATUS_DISPLAY_MAP[prStatus]) {
70+
console.warn(`Unknown PR status: "${prStatus}". Expected one of: ${Object.keys(STATUS_DISPLAY_MAP).join(", ")}`);
71+
return "secondary";
72+
}
73+
return STATUS_DISPLAY_MAP[prStatus].badgeClass;
74+
}
75+
76+
function getStatusDisplayText(prStatus) {
77+
if (!STATUS_DISPLAY_MAP[prStatus]) {
78+
console.warn(`Unknown PR status: "${prStatus}". Expected one of: ${Object.keys(STATUS_DISPLAY_MAP).join(", ")}`);
79+
return prStatus;
80+
}
81+
return STATUS_DISPLAY_MAP[prStatus].text;
82+
}
83+
84+
function formatRelativeTime(timestamp) {
85+
if (!timestamp) return null;
86+
return moment(timestamp).fromNow();
87+
}
88+
89+
function formatExactDateTime(timestamp) {
90+
if (!timestamp) return null;
91+
return moment(timestamp).format('LLLL');
92+
}
93+
4294
export default function MigrationDetails() {
4395
const location = useLocation();
4496
const { siteConfig } = useDocusaurusContext();
@@ -139,6 +191,16 @@ export default function MigrationDetails() {
139191
}
140192
</div>
141193
</div>
194+
{view === "table" && (
195+
<div className={`card margin-top--md`}>
196+
<div className="card__header">
197+
<h3>CI Status Legend</h3>
198+
</div>
199+
<div className="card__body">
200+
<CIStatusLegend />
201+
</div>
202+
</div>
203+
)}
142204
</main>
143205
</Layout>
144206
);
@@ -284,6 +346,7 @@ function Table({ details }) {
284346
|| ORDERED.findIndex(x => x[0] == a[1]) - ORDERED.findIndex(x => x[0] == b[1])
285347
|| a[0].localeCompare(b[0]))
286348
);
349+
287350
return (
288351
<>
289352
<Filters
@@ -295,7 +358,9 @@ function Table({ details }) {
295358
<thead>
296359
<tr>
297360
<th style={{ width: 200 }}>Name</th>
298-
<th style={{ width: 115 }}>Status</th>
361+
<th style={{ width: 115 }}>Migration Status</th>
362+
<th style={{ width: 115 }}>CI Status</th>
363+
<th style={{ width: 115 }}>Last Updated</th>
299364
<th style={{ width: 115 }}>Total number of children</th>
300365
<th style={{ flex: 1 }}>Immediate children</th>
301366
</tr>
@@ -317,6 +382,9 @@ function Row({ children }) {
317382
const total_children = feedstock["num_descendants"];
318383
const href = feedstock["pr_url"];
319384
const details = feedstock["pre_pr_migrator_status"];
385+
const pr_status = feedstock["pr_status"];
386+
387+
320388
return (<>
321389
<tr>
322390
<td>
@@ -332,6 +400,23 @@ function Row({ children }) {
332400
)}
333401
</td>
334402
<td style={{ textAlign: "center" }}>{TITLES[status]}</td>
403+
<td style={{ textAlign: "center" }}>
404+
{pr_status ? (
405+
<span
406+
className={`badge badge--${getStatusBadgeClass(pr_status)}`}
407+
title={CI_STATUS_DESCRIPTIONS[pr_status] || pr_status}
408+
>
409+
{getStatusDisplayText(pr_status)}
410+
</span>
411+
) : (
412+
<span></span>
413+
)}
414+
</td>
415+
<td style={{ textAlign: "center" }}>
416+
<span title={formatExactDateTime(feedstock["updated_at"])}>
417+
{formatRelativeTime(feedstock["updated_at"]) || "—"}
418+
</span>
419+
</td>
335420
<td style={{ textAlign: "center" }}>{total_children || null}</td>
336421
<td>
337422
{immediate_children.map((name, index) => (<React.Fragment key={index}>
@@ -343,11 +428,34 @@ function Row({ children }) {
343428
</td>
344429
</tr>
345430
{details && !collapsed && (<tr>
346-
<td colSpan={4}><pre dangerouslySetInnerHTML={{ __html: details}} /></td>
431+
<td colSpan={6}><pre dangerouslySetInnerHTML={{ __html: details}} /></td>
347432
</tr>)}
348433
</>);
349434
}
350435

436+
function CIStatusLegend() {
437+
const colorOrder = { danger: 0, success: 1, secondary: 2 };
438+
const sortedStatuses = Object.entries(CI_STATUS_DESCRIPTIONS).sort(
439+
([statusA], [statusB]) => {
440+
const classA = getStatusBadgeClass(statusA);
441+
const classB = getStatusBadgeClass(statusB);
442+
return (colorOrder[classA] ?? 3) - (colorOrder[classB] ?? 3);
443+
}
444+
);
445+
446+
return (
447+
<div className={styles.ci_status_legend}>
448+
{sortedStatuses.map(([status, description]) => (
449+
<div key={status} className={styles.ci_status_item}>
450+
<span className={`badge badge--${getStatusBadgeClass(status)}`}>{getStatusDisplayText(status)}</span>
451+
<span>{description}</span>
452+
</div>
453+
))}
454+
<a href="https://docs.github.com/en/graphql/reference/enums#mergestatestatus" target="_blank" rel="noopener noreferrer">See GitHub Docs</a>
455+
</div>
456+
);
457+
}
458+
351459
async function checkPausedOrClosed(name) {
352460
for (const status of ["paused", "closed"]) {
353461
try {

src/pages/status/migration/styles.module.css

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,16 @@
180180
position: relative;
181181
left: 14px;
182182
}
183+
184+
.ci_status_legend {
185+
display: flex;
186+
flex-wrap: wrap;
187+
gap: 8px;
188+
align-items: center;
189+
}
190+
191+
.ci_status_item {
192+
display: flex;
193+
align-items: center;
194+
gap: 8px;
195+
}

0 commit comments

Comments
 (0)