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

johnandersen777.bsky.social/knotless

git that lives in your Atmosphere account

Forked from chadtmiller.com/knotless

0
0
123 lines · 4.0 KB Raw History
<script lang="ts">
	import type { RepoViewDetailed } from '#lib/lexicons/dev/knotless/defs.ts';
	import type * as listWorkflows from '#lib/lexicons/dev/knotless/listWorkflows.ts';
	import type * as requestRerun from '#lib/lexicons/dev/knotless/requestRerun.ts';
	import { ask, call, type Answer } from '#lib/data.ts';
	import Icon from '#lib/atoms/Icon.svelte';
	import Dialog from '#lib/molecules/Dialog.svelte';
	/**
	 * For the owner of `repo`: asking its runners to run one workflow at a
	 * ref, by name, its steps seeing the variables given, as `knotless run rerun
	 * --ref --workflow -e` does: one that runs on a push there, or one that
	 * runs only when asked.
	 */
	let { repo }: { repo: RepoViewDetailed } = $props();
	const id = 'run-workflow';
	let ref = $state('');
	let listed = $state<Answer<typeof listWorkflows.main> | null>(null);
	let workflow = $state('');
	let vars = $state<{ name: string; value: string }[]>([]);
	let problem = $state('');
	let said = $state('');
	let busy = $state(false);
	const asked = $derived(listed?.workflows.filter((w) => w.asked) ?? []);

	/** How many times the workflows were asked for: an answer to any but the last is let go. */
	let asking = 0;
	/** The workflows at the ref named, or the default branch. */
	async function load() {
		const mine = ++asking;
		problem = '';
		try {
			const answer = await ask<typeof listWorkflows.main>('dev.knotless.listWorkflows', {
				repo: repo.uri,
				ref: ref.trim() || undefined
			});
			// The default branch's, asked as the dialog opened, can come after a ref typed since.
			if (mine !== asking) return;
			listed = answer;
			if (!asked.some((w) => w.name === workflow)) workflow = asked[0]?.name ?? '';
		} catch (e) {
			if (mine !== asking) return;
			listed = null;
			problem = (e as Error).message;
		}
	}
	async function submit(e: SubmitEvent) {
		e.preventDefault();
		busy = true;
		problem = '';
		said = '';
		const env = vars
			.filter((v) => v.name.trim())
			.map((v) => ({ name: v.name.trim(), value: v.value }));
		try {
			await call<typeof requestRerun.main>('dev.knotless.requestRerun', {
				repo: repo.uri,
				ref: listed?.ref ?? ref,
				workflow,
				env
			});
			said = `Asked for ${workflow}: its run shows here once a runner takes it.`;
		} catch (e) {
			problem = (e as Error).message;
		}
		busy = false;
	}
</script>

<button class="btn" type="button" popovertarget={id}><Icon name="play" />Run workflow</button>
<Dialog
	{id}
	class="run-workflow"
	title="Run a workflow"
	ontoggle={(e) => e.newState === 'open' && !listed && load()}
>
	<form onsubmit={submit}>
		<label for="{id}-ref">Branch or tag</label>
		<input
			id="{id}-ref"
			placeholder={repo.defaultBranch}
			autocapitalize="none"
			spellcheck="false"
			bind:value={ref}
			onchange={load}
		/>
		<label for="{id}-workflow">Workflow</label>
		{#if asked.length}
			<select id="{id}-workflow" bind:value={workflow}>
				{#each asked as w (w.name)}<option value={w.name}
						>{w.push ? w.name : `${w.name} (only when asked)`}</option
					>{/each}
			</select>
		{:else}
			<p class="mute">
				{listed ? `No workflow at ${listed.ref} may be asked for there.` : 'Reading its workflows…'}
			</p>
		{/if}
		<span class="label-like">Variables</span>
		{#each vars as v, i (i)}
			<div class="var">
				<input
					aria-label="Name"
					placeholder="NAME"
					autocapitalize="characters"
					spellcheck="false"
					bind:value={v.name}
				/>
				<input aria-label="Value" placeholder="value" spellcheck="false" bind:value={v.value} />
				<button class="btn" type="button" aria-label="Remove" onclick={() => vars.splice(i, 1)}
					>×</button
				>
			</div>
		{/each}
		<button class="btn" type="button" onclick={() => vars.push({ name: '', value: '' })}
			>Add a variable</button
		>
		{#if problem}<p class="problem" role="alert">{problem}</p>{/if}
		{#if said}<p class="mute" role="status">{said}</p>{/if}
		<button class="btn primary" type="submit" disabled={busy || !workflow} aria-busy={busy}
			>{busy ? 'Starting…' : 'Run workflow'}</button
		>
	</form>
</Dialog>