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

johnandersen777.bsky.social/knotless

git that lives in your Atmosphere account

Forked from chadtmiller.com/knotless

0
0
86 lines · 3.9 KB Raw History
<script lang="ts">
	import type * as listRuns from '#lib/lexicons/dev/knotless/listRuns.ts';
	import { ago, capitalized, checkHref, overall, plural, shortRef, took } from '#lib/format.ts';
	import { checkIcon } from '#lib/icons.ts';
	import Icon from '#lib/atoms/Icon.svelte';
	import Time from '#lib/atoms/Time.svelte';
	/** Runs of a repository's workflows, newest first, one row for each commit they ran on (a pull's head, a merge queue's batch, a push): how they went and how long the longest took, what ran, and what failed or is still going, each linked to its steps and log. */
	let { runs }: { runs: listRuns.RunView[] } = $props();
	/** A check's key, less its workflow: the same for every run of one commit, as one push, pull head or batch ran it. */
	const key = (r: listRuns.RunView) => r.check.uri.slice(0, r.check.uri.lastIndexOf('.'));
	/** The runs, in order, each with those after it of the same commit. */
	const groups = $derived.by(() => {
		const by = new Map<string, listRuns.RunView[]>();
		for (const r of runs) by.set(key(r), [...(by.get(key(r)) ?? []), r]);
		return [...by.values()];
	});
	const words: Record<string, string> = {
		success: 'Passed',
		failure: 'Failed',
		running: 'Running',
		queued: 'Queued',
		cancelled: 'Cancelled'
	};
	/** Where a run ran: a pull, a merge queue's batch, or the ref pushed to. */
	const on = (r: listRuns.RunView) => (r.pull ? 'pull' : r.batch ? 'queue' : shortRef(r.ref));
	/** What it ran: the pull's title, the batch, or the commit's subject. */
	const what = (r: listRuns.RunView) =>
		r.pull?.title ??
		(r.batch
			? `a batch of ${plural(r.batch, 'pull', 'pulls')}`
			: (r.subject ?? r.check.commit.slice(0, 7)));
	const failed = (rs: listRuns.RunView[]) =>
		rs.filter((r) => r.check.status === 'failure' || r.check.status === 'error');
	const going = (rs: listRuns.RunView[]) =>
		rs.filter((r) => r.check.status === 'running' || r.check.status === 'queued');
	/** Under how it went: how many are still going, or how long the longest that ran took. */
	function under(rs: listRuns.RunView[]): string {
		if (going(rs).length) return `${going(rs).length} of ${rs.length} going`;
		const ms = rs
			.filter((r) => r.check.finishedAt && !r.check.skipped)
			.map((r) => Date.parse(r.check.finishedAt ?? '') - Date.parse(r.check.startedAt));
		return ms.length ? took(Math.max(...ms)) : '';
	}
	/** The run a row opens: the first that failed, else one still going, else the first. */
	const lead = (rs: listRuns.RunView[]) => failed(rs)[0] ?? going(rs)[0] ?? rs[0];
	/** Why runs could not run, said once: a conflict fails them all alike. */
	const reason = (rs: listRuns.RunView[]) => rs.find((r) => r.check.error)?.check.error;
</script>

<div class="box run-list">
	{#each groups as rs (key(rs[0]))}
		{@const r = rs[0]}
		{@const state = overall(rs.map((r) => r.check.status))}
		<div class="row">
			<div class="went">
				<span class="mark {state}"><Icon name={checkIcon(state)} /></span><b
					>{words[state] ?? capitalized(state)}</b
				><small>{under(rs)}</small>
			</div>
			<div class="what">
				<a class="title" href={checkHref(lead(rs).check.repo, lead(rs).check.uri)}>{what(r)}</a>
				<div class="sub">
					<span>{on(r)}</span><span class="sha">{r.check.commit.slice(0, 7)}</span><span
						>{plural(rs.length, 'workflow', 'workflows')}</span
					>
				</div>
				{#if reason(rs)}
					<p class="why" title={reason(rs)}>{reason(rs)}</p>
				{:else if failed(rs).length}
					<p class="why">
						{#each failed(rs) as f, i (f.check.uri)}{i ? ', ' : ''}<a
								href={checkHref(f.check.repo, f.check.uri)}>{f.check.workflow}</a
							>{/each} failed
					</p>
				{:else if going(rs).length}
					<p class="sub">
						{#each going(rs) as g, i (g.check.uri)}{i ? ', ' : ''}<a
								href={checkHref(g.check.repo, g.check.uri)}>{g.check.workflow}</a
							>{/each} still going
					</p>
				{/if}
			</div>
			<span class="when"><Time at={r.check.startedAt} show={ago} /></span>
		</div>
	{/each}
</div>