~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/knotless

git that lives in your Atmosphere account

Forked from chadtmiller.com/knotless

0
0
111 lines · 3.9 KB Raw History
<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}