johnandersen777.bsky.social/knotless
git that lives in your Atmosphere account
Forked from chadtmiller.com/knotless
0
0
<script lang="ts">
import type { CheckView, MinutesView } from '#lib/lexicons/dev/knotless/defs.ts';
import type { Snippet } from 'svelte';
import { checkHref, checkSaid, overall, plural } from '#lib/format.ts';
import { checkIcon } from '#lib/icons.ts';
import Icon from '#lib/atoms/Icon.svelte';
import MinutesNote from '#lib/molecules/MinutesNote.svelte';
/**
* A merge box's checks, as GitHub's shows them: where they stand and how
* many are in each state, then a row per workflow, grouped by state, those
* that failed or are running open and the rest folded, each linked to its
* run, whose page has its log and pictures. `standing` is what the server
* says of them at the pull's head merged with `base` as it is now, once
* asked; `end`, at the end of its head.
*/
let {
checks,
base,
standing,
minutes = [],
end
}: {
checks: CheckView[];
base: string;
standing?: string;
minutes?: MinutesView[];
end?: Snippet;
} = $props();
/** The runners whose minutes for the owner are at 80% of their allowance or more, beneath the rows. */
const low = $derived(
minutes.flatMap((m) =>
m.allowance && m.minutes * 5 >= m.allowance * 4 ? [{ m, allowance: m.allowance }] : []
)
);
/** A check's state here: a success that ran nothing is skipped. */
const kind = (c: CheckView) =>
c.status === 'error' ? 'failure' : c.status === 'success' && c.skipped ? 'skipped' : c.status;
const groups = $derived(
[
{ kind: 'failure', said: 'failed', open: true },
{ kind: 'running', said: 'running', open: true },
{ kind: 'queued', said: 'queued', open: true },
{ kind: 'cancelled', said: 'cancelled', open: false },
{ kind: 'success', said: 'passed', open: false },
{ kind: 'skipped', said: 'skipped', open: false }
]
.map((g) => ({ ...g, checks: checks.filter((c) => kind(c) === g.kind) }))
.filter((g) => g.checks.length)
);
/** Not yet run at its head merged with its base as it is now, though what ran before may have passed. */
const pending = $derived(
standing === 'pending' ||
(standing !== undefined &&
standing !== 'passed' &&
standing !== 'none' &&
overall(checks.map((c) => c.status)) === 'success')
);
const state = $derived(pending ? 'pending' : overall(checks.map((c) => c.status)));
const heading = $derived(
{
success: 'All checks have passed',
running: 'Some checks are still running',
queued: 'Some checks are waiting for a runner',
failure: 'Some checks were not successful',
cancelled: 'Some checks were cancelled',
pending: `Checks have not run against ${base} as it is now`
}[state]
);
const row = $derived(
{
success: 'clean',
running: 'running',
queued: 'running',
failure: 'failed',
cancelled: '',
pending: 'pending'
}[state]
);
const counts = $derived(groups.map((g) => `${g.checks.length} ${g.said}`).join(', '));
/** A row's words, and the pictures its run made, which its page shows. */
const said = (c: CheckView) =>
`${checkSaid(c)}${c.images.length ? ` · ${plural(c.images.length, 'picture', 'pictures')}` : ''}`;
</script>
<div class="row {row}">
<Icon name={pending ? 'running' : checkIcon(state)} />
<div class="grow">
<b>{heading}</b>
<p class="mute">{counts}</p>
</div>
{#if end}<span class="end">{@render end()}</span>{/if}
</div>
{#if groups.length}
<div class="check-groups">
{#each groups as g (g.kind)}
<details class="group" open={g.open}>
<summary>{plural(g.checks.length, `${g.said} check`, `${g.said} checks`)}</summary>
{#each g.checks as c (c.workflow)}
<a class="check" href={checkHref(c.repo, c.uri)}
><span class="mark {kind(c)}"><Icon name={checkIcon(kind(c))} /></span><b
>{c.workflow}</b
><span class="said" title={c.skipped ?? c.error ?? undefined}>{said(c)}</span><Icon
name="chevron"
/></a
>
{/each}
</details>
{/each}
</div>
{/if}
{#each low as { m, allowance } (m.runner.did)}<MinutesNote {m} {allowance} />{/each}