Radish alpha
r
Radicle desktop app
Radicle
Git (anonymous pull)
Log in to clone via SSH
Add tag picker to the release form
Daniel Norman committed 7 days ago
commit de62ac0beb4c0f3b8f7968ec557cde5c97b7605b
parent 8c62a43574f831d2eb34a4abd5628591e5393f92
2 files changed +76 -8
modified src/views/repo/NewRelease.svelte
@@ -11,6 +11,7 @@
  import type { PaginatedQuery } from "@bindings/cob/PaginatedQuery";
  import type { Commit } from "@bindings/repo/Commit";
  import type { RepoInfo } from "@bindings/repo/RepoInfo";
+
  import type { Tag } from "@bindings/repo/Tag";

  import { onMount } from "svelte";

@@ -33,12 +34,26 @@
  };

  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;
@@ -46,16 +61,42 @@

  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) {
@@ -107,6 +148,7 @@
      const releaseId = await invoke<string>("create_or_open_release", {
        rid: repo.rid,
        oid: oid.trim(),
+
        tag: tagOid,
      });
      for (const f of files) {
        if (!f.cid) continue;
@@ -165,7 +207,8 @@
    font: var(--txt-body-s-semibold);
    color: var(--color-text-secondary);
  }
-
  input[type="text"] {
+
  input[type="text"],
+
  select {
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--border-radius-sm);
@@ -229,13 +272,33 @@

<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)}
modified src/views/repo/Release.svelte
@@ -236,6 +236,11 @@

  <div class="header">
    <div class="oid">{release.oid}</div>
+
    {#if release.tag}
+
      <div class="oid" style:margin-top="0.25rem">
+
        annotated tag {release.tag.slice(0, 7)}
+
      </div>
+
    {/if}
  </div>

  {#each release.artifacts as artifact (artifact.cid)}