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
@@ -1,4 +1,7 @@
<script lang="ts">
import { manifestVersion, scriptVersionError } from '$lib/script-version.js';
import { artifactVersionLabel } from '$lib/artifact-version.js';
import ScriptVersionManifest from './ScriptVersionManifest.svelte';
import Input from '$ui/components/primitives/Input.svelte';
import Select from '$ui/components/primitives/Select.svelte';
import Field from '$ui/components/primitives/Field.svelte';
@@ -20,23 +23,34 @@
scriptId: number;
versionId: number;
version: number;
release_version: string | null;
}
interface Upload {
key: number;
file: File;
target: number | 'new' | '';
name: string;
releaseVersion: string;
duplicates: Duplicate[];
inspecting: boolean;
inspected: boolean;
error: string;
result: (BetaSelection & { version: number; reused: boolean }) | null;
result:
(BetaSelection & { version: number; release_version: string; reused: boolean }) | null;
}
let dialog: HTMLDialogElement;
let input: HTMLInputElement;
let queue = $state<Upload[]>([]);
let busy = $state(false);
let notes = $state('');
let manifest = $state<Record<string, string>>({});
function applyManifest(versions: Record<string, string>) {
manifest = versions;
for (const item of queue) {
if (!item.result && !item.releaseVersion.trim())
item.releaseVersion = manifestVersion(manifest, item.file.name);
}
}
let scopeType = $state<'none' | 'game' | 'map'>('none');
let scopeGame = $state('');
let scopeMap = $state('');
@@ -80,6 +94,7 @@
// only when there is no pending file to lose.
if (!pending.length) {
queue = [];
manifest = {};
notes = '';
message = '';
preferredTarget = scriptId ?? null;
@@ -121,6 +136,7 @@
preferredTarget ??
(matches.length === 1 ? matches[0].id : matches.length > 1 ? '' : 'new'),
name: file.name.replace(/\.[^.]+$/, ''),
releaseVersion: manifestVersion(manifest, file.name),
duplicates: [],
inspecting: true,
inspected: false,
@@ -153,9 +169,13 @@
}
}
function duplicate(item: Upload) {
return item.duplicates.find((d) => d.scriptId === item.target);
return item.duplicates.find(
(d) => d.scriptId === item.target && d.release_version === item.releaseVersion.trim()
);
}
function validation(item: Upload): string {
const versionError = scriptVersionError(item.releaseVersion);
if (versionError) return versionError;
if (item.target === '')
return 'Several scripts use this filename. Choose which script to update.';
if (item.target === 'new') {
@@ -194,6 +214,7 @@
const form = new FormData();
form.append('content', item.file);
form.append('notes', notes.trim());
form.append('releaseVersion', item.releaseVersion.trim());
const creating = item.target === 'new';
if (creating) {
form.append('name', item.name.trim());
@@ -211,6 +232,7 @@
scriptId: creating ? body.script.id : (item.target as number),
versionId: creating ? body.versionId : body.version.id,
version: creating ? 1 : body.version.version,
release_version: item.releaseVersion.trim(),
reused: !!body.reused
};
} catch (e) {
@@ -286,6 +308,7 @@
}}
/>
</div>
<ScriptVersionManifest disabled={busy || inspecting} onload={applyManifest} />
<div class="queue" aria-live="polite">
{#each queue as item (item.key)}
<section class="upload-item" aria-label={item.file.name}>
@@ -304,11 +327,26 @@
</div>
{#if item.result}
<p class="success">
{item.result.reused ? 'Already uploaded' : 'Uploaded'} as v{item.result
.version} ·
{item.result.reused ? 'Already uploaded' : 'Uploaded'} as {artifactVersionLabel(
item.result,
'script'
)} ·
<a href="/scripts/{item.result.scriptId}">View script →</a>
</p>
{:else}
<Field
label="Release version"
hint="Required. Use MAJOR.MINOR.PATCH, such as 1.2.0."
>
<Input
bind:value={item.releaseVersion}
required
maxlength={80}
placeholder="1.2.0"
disabled={busy}
oninput={() => (item.error = '')}
/>
</Field>
<Field label="Destination">
<Select
bind:value={item.target}
@@ -336,8 +374,10 @@
</p>{/if}
{#if item.inspecting}<p class="note">Checking file contents…</p>
{:else if duplicate(item)}<p class="success">
Already uploaded as v{duplicate(item)!.version}. We’ll reuse that
version.
Already uploaded as {artifactVersionLabel(
duplicate(item),
'script'
)}. We’ll reuse that version.
</p>
{:else if item.target !== 'new' && item.target !== ''}<p class="note">
New version for {scripts.find((s) => s.id === item.target)?.name}