Radish alpha
r
Radicle desktop app
Radicle
Git (anonymous pull)
Log in to clone via SSH
Add creator, tag name, commit summary to Release DTO
Daniel Norman committed 7 days ago
commit 034509f7fd1bb1de137546f793c9b57b228f206b
parent 31f64e45055a1c23e7d3b9224182436bd5e3d2e9
3 files changed +101 -4
modified crates/radicle-types/bindings/cob/release/Release.ts
@@ -1,4 +1,5 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
+
import type { Author } from "../Author";
import type { Artifact } from "./Artifact";

export type Release = {
@@ -9,6 +10,21 @@ export type Release = {
   * if any. `None` means the release was created from a bare commit.
   */
  tag?: string;
+
  /**
+
   * Refname of the tag pointed at by `tag`, resolved on the server so
+
   * the UI doesn't need to fan out a separate `list_tags` lookup per
+
   * release. Omitted when the tag has been deleted locally.
+
   */
+
  tagName?: string;
+
  /**
+
   * DID of the user who authored this release COB.
+
   */
+
  creator: Author;
+
  /**
+
   * Subject line of the released commit, resolved on the server so the
+
   * list view can render it without an extra round-trip per row.
+
   */
+
  commitSummary?: string;
  timestamp: number;
  artifacts: Array<Artifact>;
};
modified crates/radicle-types/src/cobs/release.rs
@@ -22,6 +22,19 @@ pub struct Release {
    #[serde(skip_serializing_if = "Option::is_none")]
    #[ts(as = "Option<String>", optional)]
    pub tag: Option<radicle::git::Oid>,
+
    /// Refname of the tag pointed at by `tag`, resolved on the server so
+
    /// the UI doesn't need to fan out a separate `list_tags` lookup per
+
    /// release. Omitted when the tag has been deleted locally.
+
    #[serde(skip_serializing_if = "Option::is_none")]
+
    #[ts(optional)]
+
    pub tag_name: Option<String>,
+
    /// DID of the user who authored this release COB.
+
    pub creator: cobs::Author,
+
    /// Subject line of the released commit, resolved on the server so the
+
    /// list view can render it without an extra round-trip per row.
+
    #[serde(skip_serializing_if = "Option::is_none")]
+
    #[ts(optional)]
+
    pub commit_summary: Option<String>,
    #[ts(type = "number")]
    pub timestamp: u64,
    pub artifacts: Vec<Artifact>,
@@ -84,6 +97,8 @@ impl Release {
        release: &radicle_artifact::Release,
        our_did: &Did,
        aliases: &impl AliasStore,
+
        tag_name: Option<String>,
+
        commit_summary: Option<String>,
    ) -> Self {
        let artifacts = release
            .artifacts()
@@ -95,6 +110,9 @@ impl Release {
            id,
            oid: *release.oid(),
            tag: release.tag().copied(),
+
            tag_name,
+
            creator: cobs::Author::new(release.creator(), aliases),
+
            commit_summary,
            timestamp: release.timestamp(),
            artifacts,
        }
modified crates/radicle-types/src/traits/release.rs
@@ -1,11 +1,12 @@
-
use std::collections::{BTreeMap, BTreeSet};
+
use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::path::PathBuf;
use std::str::FromStr;

use radicle::identity;
-
use radicle::storage::ReadStorage;
+
use radicle::storage::{ReadRepository, ReadStorage};
use radicle_artifact::share::cid_utils;
use radicle_artifact::Releases as ReleasesStore;
+
use radicle_surf as surf;

use crate::cobs::release;
use crate::error::Error;
@@ -19,16 +20,26 @@ pub trait Releases: Profile {
        let aliases = profile.aliases();
        let our_did = identity::Did::from(profile.public_key);

+
        let surf_repo = surf::Repository::open(repo.path())?;
+
        // Build the tag-OID → refname index once so the list view can show
+
        // names without an N+1 ref walk per release.
+
        let tag_index = build_tag_index(&surf_repo);
        let releases = ReleasesStore::open(&repo)?;

        let mut out = Vec::new();
        for item in releases.all()? {
            let (id, release) = item?;
+
            let tag_name = release
+
                .tag()
+
                .and_then(|oid| tag_index.get(&oid.to_string()).cloned());
+
            let commit_summary = commit_summary(&surf_repo, *release.oid());
            out.push(release::Release::new(
                radicle_artifact::ReleaseId::from(id),
                &release,
                &our_did,
                &aliases,
+
                tag_name,
+
                commit_summary,
            ));
        }
        Ok(out)
@@ -50,8 +61,21 @@ pub trait Releases: Profile {
            return Ok(None);
        };

+
        let surf_repo = surf::Repository::open(repo.path())?;
+
        let tag_name = release.tag().and_then(|oid| {
+
            build_tag_index(&surf_repo)
+
                .get(&oid.to_string())
+
                .cloned()
+
        });
+
        let commit_summary = commit_summary(&surf_repo, *release.oid());
+

        Ok(Some(release::Release::new(
-
            id, &release, &our_did, &aliases,
+
            id,
+
            &release,
+
            &our_did,
+
            &aliases,
+
            tag_name,
+
            commit_summary,
        )))
    }

@@ -65,12 +89,25 @@ pub trait Releases: Profile {
        let aliases = profile.aliases();
        let our_did = identity::Did::from(profile.public_key);

+
        let surf_repo = surf::Repository::open(repo.path())?;
+
        let tag_index = build_tag_index(&surf_repo);
        let releases = ReleasesStore::open(&repo)?;

        let mut out = Vec::new();
        for item in releases.find_by_commit(oid)? {
            let (id, release) = item?;
-
            out.push(release::Release::new(id, &release, &our_did, &aliases));
+
            let tag_name = release
+
                .tag()
+
                .and_then(|tag_oid| tag_index.get(&tag_oid.to_string()).cloned());
+
            let commit_summary = commit_summary(&surf_repo, *release.oid());
+
            out.push(release::Release::new(
+
                id,
+
                &release,
+
                &our_did,
+
                &aliases,
+
                tag_name,
+
                commit_summary,
+
            ));
        }
        Ok(out)
    }
@@ -116,3 +153,29 @@ pub trait Releases: Profile {
        Ok(artifact.locations().clone())
    }
}
+

+
/// Build a lookup from tag-object OID to short refname so the list view
+
/// can show e.g. `v1.0.0` instead of a 40-char hash. Annotated tag OIDs
+
/// land under their tag-object OID; lightweight tags are keyed by the
+
/// commit they point at, matching `Release::tag`'s storage convention.
+
fn build_tag_index(repo: &surf::Repository) -> HashMap<String, String> {
+
    use surf::{Glob, Tag};
+

+
    let mut out = HashMap::new();
+
    let Ok(tags) = repo.tags(&Glob::all_tags()) else {
+
        return out;
+
    };
+
    for tag in tags.flatten() {
+
        let (id, name) = match tag {
+
            Tag::Light { id, name } => (id, name),
+
            Tag::Annotated { id, name, .. } => (id, name),
+
        };
+
        out.insert(id.to_string(), name.to_string());
+
    }
+
    out
+
}
+

+
fn commit_summary(repo: &surf::Repository, oid: radicle::git::Oid) -> Option<String> {
+
    let surf_oid = crate::oid::into_surf(oid);
+
    repo.commit(surf_oid).ok().map(|c| c.summary)
+
}