Require release versions for script uploads
CI / checks (push) Successful in 5m26s

This commit is contained in:
2026-10-12 18:46:57 -04:00
parent ffda04f276
commit 2f52dcb1ef
50 changed files with 871 additions and 113 deletions
+42 -9
View File
@@ -1,4 +1,6 @@
<script lang="ts">
import { manifestVersion, scriptVersionError } from '$lib/script-version.js';
import ScriptVersionManifest from './ScriptVersionManifest.svelte';
import { serverTitle } from '$ui/server-labels.js';
import RelativeTime from '$ui/components/RelativeTime.svelte';
import { page } from '$app/stores';
@@ -69,7 +71,13 @@
let versions = $state<Version[]>([]);
let matchedServers = $state<string[]>([]);
let installedVersions = $state<
Array<{ server: string; version: number; versionId: number; recordedAt: string }>
Array<{
server: string;
version: number;
versionId: number;
release_version: string | null;
recordedAt: string;
}>
>([]);
interface ScopeImpact {
before: string[];
@@ -103,6 +111,7 @@
let versionFile = $state<File | null>(null);
let versionNotes = $state('');
let releaseVersion = $state('');
let versionManifest = $state<Record<string, string>>({});
let uploadingVersion = $state(false);
let scopeDialog = $state<HTMLDialogElement | null>(null);
@@ -244,6 +253,7 @@
function openUploadModal(): void {
if (!editable || runningBetaId !== null) return;
releaseVersion = '';
versionManifest = {};
versionFile = null;
versionNotes = '';
flash = null;
@@ -271,16 +281,23 @@
function pickVersionFile(e: Event): void {
const t = e.target as HTMLInputElement;
versionFile = t.files?.[0] ?? null;
if (!releaseVersion.trim() && versionFile)
releaseVersion = manifestVersion(versionManifest, versionFile.name);
}
async function uploadVersion(e: Event): Promise<void> {
e.preventDefault();
if (!editable || !versionFile || uploadingVersion) return;
if (family === 'script' && scriptVersionError(releaseVersion)) {
flash = scriptVersionError(releaseVersion);
return;
}
uploadingVersion = true;
flash = null;
const form = new FormData();
form.append('content', versionFile);
if (family === 'plugin') form.append('releaseVersion', releaseVersion.trim());
if (family === 'plugin' || family === 'script')
form.append('releaseVersion', releaseVersion.trim());
if (versionNotes.trim()) form.append('notes', versionNotes.trim());
try {
const res = await fetch(`/api/scripts/${id}/versions`, { method: 'POST', body: form });
@@ -419,7 +436,7 @@
{#if latestVersion}<Card padding="lg"
><div class="release-summary">
<div>
<h2>Latest {artifactVersionLabel(latestVersion)}</h2>
<h2>Latest {artifactVersionLabel(latestVersion, family)}</h2>
<StatusBadge
state={latestVersion.id === a.current_version_id
? 'promoted'
@@ -517,7 +534,7 @@
<span class="k">Promoted version</span>
<span class="v mono">
{#if currentVersion}
{artifactVersionLabel(currentVersion)} · {currentVersion.sha256.slice(
{artifactVersionLabel(currentVersion, family)} · {currentVersion.sha256.slice(
0,
8
)}
@@ -541,7 +558,9 @@
<a href={'/servers/' + encodeURIComponent(installed.server)}
>{serverTitle(installed.server)}</a
>
· v{installed.version} · <RelativeTime value={installed.recordedAt} />
· {artifactVersionLabel(installed, family)} · <RelativeTime
value={installed.recordedAt}
/>
</li>{/each}
</ul>
</details>{:else if family !== 'config'}<p class="dim">
@@ -575,7 +594,7 @@
<div class="version-list">
{#each versions.slice(0, limit) as v (v.id)}
<div class="version-row" class:current={a.current_version_id === v.id}>
<span class="v-num">{artifactVersionLabel(v)}</span>
<span class="v-num">{artifactVersionLabel(v, family)}</span>
<span class="v-notes"
>{v.notes ?? ''}
{#if v.source}<a
@@ -750,14 +769,28 @@
disabled={uploadingVersion}
/>
</Field>
{#if family === 'plugin'}
{#if family === 'script'}
<ScriptVersionManifest
disabled={uploadingVersion}
onload={(versions) => {
versionManifest = versions;
if (versionFile && !releaseVersion.trim())
releaseVersion = manifestVersion(versions, versionFile.name);
}}
/>
{/if}
{#if family === 'plugin' || family === 'script'}
<Field
label="Release version"
hint="Optional. Use the publisher’s version or release tag, such as v3.1.0."
hint={family === 'script'
? 'Required. Use MAJOR.MINOR.PATCH, such as 1.2.0.'
: 'Optional. Use the publisher’s version or release tag, such as v3.1.0.'}
>
<Input
bind:value={releaseVersion}
placeholder="v3.1.0"
placeholder={family === 'script' ? '1.2.0' : 'v3.1.0'}
required={family === 'script'}
maxlength={80}
disabled={uploadingVersion}
/>
</Field>