Skip to content

Commit

A project that ships straight to its branch shows how often its workflow runs pass in Health, instead of "No checks have run recently"

syntaqxcommitted Parent0637405Browse files
4 files+37−40/4 viewed
+1−1
6363 | **Active branches** | Branches other than the default, newest first. See [active branches](#active-branches). |
6464 | **Recent changes**, **Latest on main**, **Activity**, **Previews** | What landed, the default branch's latest commits, what happened (pull requests opened, readied and merged, issues opened and closed, checks, reviews, comments, pushes to the default branch and production deploys), and the previews that are up. |
6565 | **About** | Its description, its [links](#links), what it is, and its latest release (its newest tag). People who can change its settings edit the description and links from here. |
66−| **Health**, **Clone** | How often checks pass, recent builds and open issues by age; the clone address. |
66+| **Health**, **Clone** | How often checks pass (on pull requests, or the project's workflow runs when it ships without them), recent builds and open issues by age; the clone address. |
6767
6868 ### The checklist
6969
+13−0
2828 nextSeen,
2929 pipelineStage,
3030 queuedNumbers,
31+ runHealth,
3132 rankNeeds,
3233 readCookie,
3334 sparkPoints,
179180 ]);
180181 assert.deepEqual(rate, { rate: 0.5, of: 2 });
181182 assert.deepEqual(firstPassRate([]), { rate: null, of: 0 });
183+ // Workflow runs: cancelled, skipped and unfinished ones don't count; a pass on a second attempt isn't a first-try pass.
184+ const runs = runHealth([
185+ { attempt: 1, status: "completed", conclusion: "success" },
186+ { attempt: 2, status: "completed", conclusion: "success" },
187+ { attempt: 1, status: "completed", conclusion: "failure" },
188+ { attempt: 1, status: "completed", conclusion: "cancelled" },
189+ { attempt: 1, status: "in_progress", conclusion: null },
190+ ]);
191+ assert.equal(runs.checkRuns, 3);
192+ assert.equal(runs.passRate, 2 / 3);
193+ assert.deepEqual(runs.firstPass, { rate: 1 / 3, of: 3 });
194+ assert.deepEqual(runHealth([]), { passRate: null, firstPass: { rate: null, of: 0 }, checkRuns: 0 });
182195 assert.equal(formatSpan(null), "—");
183196 assert.equal(formatSpan(30 * MIN), "30m");
184197 assert.equal(formatSpan(5 * HOUR), "5h");
+14−0
417417 return results.filter(Boolean).length / results.length;
418418 }
419419
420+/**
421+ * A project's health from its recent workflow runs, for one that ships
422+ * without pull requests (whose checks are what `checks.completed` counts).
423+ * Only runs that finished and passed or failed count: cancelled and skipped
424+ * say nothing about the code. A run passed on the first try when its first
425+ * attempt is the one that passed.
426+ */
427+export function runHealth(runs: { attempt: number; status: string; conclusion: string | null }[]): { passRate: number | null; firstPass: { rate: number | null; of: number }; checkRuns: number } {
428+ const decided = runs.filter((run) => run.status === "completed" && (run.conclusion === "success" || run.conclusion === "failure"));
429+ const passed = decided.map((run) => run.conclusion === "success");
430+ const first = decided.filter((run) => run.conclusion === "success" && run.attempt <= 1).length;
431+ return { passRate: passRate(passed), firstPass: { rate: decided.length ? first / decided.length : null, of: decided.length }, checkRuns: decided.length };
432+}
433+
420434 /** How long from an issue being opened to its change landing, for each that did, in ms. */
421435 export function issueToMerge(
422436 opened: { repo: string; number: number; at: number }[],
+9−3
7171 projectFeed,
7272 queuedNumbers,
7373 rankNeeds,
74+ runHealth,
7475 stuckMinutes,
7576 } from "../../lib/mission";
7677 import { agentWasAssigned, checklistPlan, hasInstructions, productionChecklist, releaseChecklist, startChecklist } from "../../lib/checklist";
160161 const libraryRepoP = Promise.all([planP, repoP]).then(([plan, repo]) => (plan === "release" && repo?.ok ? repo.value : null));
161162 const packagesP = libraryRepoP.then((repo) => (repo ? soft(packages.list(params.owner, viewer, { repoId: repo.id })) : null));
162163 const workflowsP = planP.then((plan) => (plan && plan !== "production" ? forMembers(() => actions.workflows(path, viewer)) : null));
164+ // Recent workflow runs, for Health when no pull request's checks have finished lately.
165+ const workflowRunsP = soft(actions.runs(path, viewer, { limit: 30 }));
163166 // Only the kinds the feed shows: the newest events are mostly session
164167 // steps and merge checks, which would otherwise crowd out everything.
165168 const eventsP = repoP.then((repo) =>
231234 // The latest release, for About.
232235 soft(repos.tags(path, viewer)),
233236 ]);
237+ const workflowRuns = await workflowRunsP;
234238 const ok = <T,>(result: { ok: true; value: T } | { ok: false } | null): T | null => (result?.ok ? result.value : null);
235239
236240 const openPulls = ok(open) ?? [];
458462 groups: groupActivity(items).slice(0, 30),
459463 eventsLoaded: recent != null,
460464 health: {
461− passRate: passRate(checkEvents.map((c) => c.passed)),
462− firstPass: firstPassRate(checkEvents),
463− checkRuns: checkEvents.length,
465+ // A pull request's checks when any finished lately; otherwise the
466+ // project's workflow runs, for one that ships straight to its branch.
467+ ...(checkEvents.length > 0 || !workflowRuns?.ok
468+ ? { passRate: passRate(checkEvents.map((c) => c.passed)), firstPass: firstPassRate(checkEvents), checkRuns: checkEvents.length }
469+ : runHealth(workflowRuns.value)),
464470 ages: openIssues ? ageBuckets(openIssues.map((issue) => issue.createdAt), now) : null,
465471 },
466472 knows: knows as Memory[],