johnandersen777.bsky.social/knotless
git that lives in your Atmosphere account
Forked from chadtmiller.com/knotless
0
0
<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>