| |
};
|
| |
|
| |
let oid = $state("");
|
| + |
// Selected tag refname. "" means "no tag" — the release is keyed by the
|
| + |
// commit OID alone. Annotated tags also contribute their tag OID to the
|
| + |
// COB; lightweight tags only set the commit OID.
|
| + |
let selectedTagName = $state("");
|
| |
const files = $state<StagedFile[]>([]);
|
| |
let submitting = $state(false);
|
| |
let autoSeed = $state(true);
|
| |
let submitError: string | undefined = $state();
|
| |
// Recent commits offered as autocomplete suggestions for the OID input.
|
| |
let commits = $state<Commit[]>([]);
|
| + |
// Tags listed for the repo, surfaced as the primary picker.
|
| + |
let tags = $state<Tag[]>([]);
|
| + |
|
| + |
// The annotated-tag OID we'll record alongside the commit, if a tag is
|
| + |
// selected and it's annotated. Lightweight tags resolve to a commit OID
|
| + |
// only and don't contribute a separate tag OID.
|
| + |
const selectedTag = $derived(tags.find(t => t.name === selectedTagName));
|
| + |
const tagOid = $derived(
|
| + |
selectedTag?.annotated ? selectedTag.oid : undefined,
|
| + |
);
|
| |
|
| |
void invoke<boolean>("get_auto_seed_artifacts").then(v => {
|
| |
autoSeed = v;
|
| |
|
| |
onMount(async () => {
|
| |
try {
|
| - |
const result = await invoke<PaginatedQuery<Commit[]>>(
|
| - |
"list_repo_commits",
|
| - |
{ rid: repo.rid, skip: 0, take: 50 },
|
| - |
);
|
| - |
commits = result.content;
|
| + |
const [commitsResult, tagsResult] = await Promise.all([
|
| + |
invoke<PaginatedQuery<Commit[]>>("list_repo_commits", {
|
| + |
rid: repo.rid,
|
| + |
skip: 0,
|
| + |
take: 50,
|
| + |
}),
|
| + |
invoke<Tag[]>("list_tags", { rid: repo.rid }),
|
| + |
]);
|
| + |
commits = commitsResult.content;
|
| + |
tags = tagsResult;
|
| |
} catch (err) {
|
| - |
console.error("list_repo_commits failed", err);
|
| + |
console.error("loading commits/tags failed", err);
|
| |
}
|
| |
});
|
| |
|
| + |
function onSelectTag(name: string) {
|
| + |
selectedTagName = name;
|
| + |
if (name === "") return;
|
| + |
const tag = tags.find(t => t.name === name);
|
| + |
if (tag) {
|
| + |
// Auto-fill the commit OID from the tag's target. The user can still
|
| + |
// override it manually — and doing so clears the tag selection so we
|
| + |
// don't end up writing a tag OID that doesn't match the commit.
|
| + |
oid = tag.commit;
|
| + |
}
|
| + |
}
|
| + |
|
| + |
function onCommitInput(value: string) {
|
| + |
oid = value;
|
| + |
// If the typed value no longer matches the selected tag's commit, the
|
| + |
// tag and commit are out of sync — clear the tag.
|
| + |
if (selectedTag && selectedTag.commit !== value) {
|
| + |
selectedTagName = "";
|
| + |
}
|
| + |
}
|
| + |
|
| |
async function pickFiles() {
|
| |
const picked = await invoke<string[]>("pick_artifact_files");
|
| |
for (const p of picked) {
|
| |
|
| |
<div class="form">
|
| |
<div class="field">
|
| + |
<label for="release-tag">Tag</label>
|
| + |
<select
|
| + |
id="release-tag"
|
| + |
value={selectedTagName}
|
| + |
onchange={e => onSelectTag((e.currentTarget as HTMLSelectElement).value)}
|
| + |
disabled={submitting}>
|
| + |
<option value="">No tag — release a bare commit</option>
|
| + |
{#each tags as tag (tag.name)}
|
| + |
<option value={tag.name}>
|
| + |
{tag.name}
|
| + |
{tag.annotated ? "(annotated)" : ""}
|
| + |
→ {tag.commit.slice(0, 7)}
|
| + |
</option>
|
| + |
{/each}
|
| + |
</select>
|
| + |
</div>
|
| + |
|
| + |
<div class="field">
|
| |
<label for="release-oid">Commit OID</label>
|
| |
<input
|
| |
id="release-oid"
|
| |
type="text"
|
| |
placeholder="e.g. ec49ecb..."
|
| |
list="release-oid-options"
|
| - |
bind:value={oid}
|
| + |
value={oid}
|
| + |
oninput={e =>
|
| + |
onCommitInput((e.currentTarget as HTMLInputElement).value)}
|
| |
disabled={submitting} />
|
| |
<datalist id="release-oid-options">
|
| |
{#each commits as commit (commit.id)}
|