Add list_canonical_tags command

- Load the release modal's tags with it
- Sort them with the shared sortedTags helper
- Test that peers' tags are left out
This commit is contained in:
Daniel Norman
2026-10-08 18:21:48 +02:00
committed by Rūdolfs Ošiņš
parent 6f57b811ef
commit 040906ba6d
7 changed files with 92 additions and 57 deletions
+10
View File
@@ -1,3 +1,5 @@
use std::collections::BTreeMap;
use radicle::git;
use radicle::identity::RepoId;
use radicle::node::NodeId;
@@ -37,6 +39,14 @@ pub async fn list_repo_refs(
blocking(ctx, move |ctx| ctx.list_repo_refs(rid)).await
}
#[tauri::command]
pub async fn list_canonical_tags(
ctx: tauri::State<'_, AppState>,
rid: RepoId,
) -> Result<BTreeMap<String, types::repo::Tag>, Error> {
blocking(ctx, move |ctx| ctx.list_canonical_tags(rid)).await
}
#[tauri::command]
pub async fn repo_by_id(
ctx: tauri::State<'_, AppState>,
+1
View File
@@ -122,6 +122,7 @@ pub fn run() {
repo::clean,
repo::diff_stats,
repo::list_commits,
repo::list_canonical_tags,
repo::list_repo_commits,
repo::list_repo_refs,
repo::list_repos,
+64
View File
@@ -655,6 +655,17 @@ pub trait Repo: Profile {
Ok(repo::RepoRefs { canonical, remotes })
}
/// The canonical tags, without the per-peer refs that `list_repo_refs`
/// loads and verifies for every remote.
fn list_canonical_tags(
&self,
rid: identity::RepoId,
) -> Result<BTreeMap<String, repo::Tag>, Error> {
let repo = self.profile().storage.repository(rid)?;
Ok(canonical_refs(&repo)?.tags)
}
fn repo_by_id(&self, rid: identity::RepoId) -> Result<repo::RepoInfo, Error> {
let profile = self.profile();
let repo = profile.storage.repository(rid)?;
@@ -1309,6 +1320,59 @@ mod test {
);
}
#[test]
fn canonical_tags_ignore_peer_tags() {
use radicle::cob::Title;
use radicle::identity::Identity;
use radicle::identity::doc::PayloadId;
use radicle::storage::{SignRepository, WriteRepository, WriteStorage};
let tmp = tempfile::tempdir().unwrap();
let profile = test::profile(&tmp.path().join("home"), [0xff; 32]);
let signer = SigningKey::from_seed(Seed::new([0xff; 32]));
let (rid, _, _, head) =
fixtures::project(tmp.path().join("working"), &profile.storage, &signer).unwrap();
{
let repo = profile.storage.repository_mut(rid).unwrap();
let mut identity = Identity::load_mut(&repo, &signer).unwrap();
let mut doc = identity.doc().clone().edit();
doc.payload.insert(
PayloadId::canonical_refs(),
serde_json::json!({
"rules": {
"refs/tags/*": { "allow": "delegates", "threshold": 1 },
},
})
.into(),
);
identity
.update(
Title::new("Make tags canonical").unwrap(),
"",
&doc.verified().unwrap(),
)
.unwrap();
repo.sign_refs(&signer).unwrap();
repo.set_identity_head().unwrap();
let other = repo.identity_head().unwrap().into();
let peer = "z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7";
let peer_tags = format!("refs/namespaces/{peer}/refs/tags");
for (name, oid) in [
("refs/tags/v1".to_owned(), head),
(format!("{peer_tags}/v1"), other),
(format!("{peer_tags}/v2"), head),
] {
repo.backend.reference(&name, oid, false, "test").unwrap();
}
}
let tags = AppState { profile }.list_canonical_tags(rid).unwrap();
assert_eq!(tags.keys().collect::<Vec<_>>(), vec!["v1"]);
assert_eq!(tags["v1"].oid, head);
}
#[test]
fn repo_split_tree_path() {
let tmp = tempfile::tempdir().unwrap();
+10
View File
@@ -193,6 +193,7 @@ pub fn router(shared: Shared) -> Router {
)
.route("/repo_by_id", post(repo_handler))
.route("/list_repo_refs", post(list_repo_refs_handler))
.route("/list_canonical_tags", post(list_canonical_tags_handler))
.route("/identity_by_repo", post(identity_handler))
.route("/version", post(version_handler))
.route("/git_info", post(git_info_handler))
@@ -468,6 +469,15 @@ async fn list_repo_refs_handler(
Ok::<_, Error>(Json(refs))
}
async fn list_canonical_tags_handler(
Ctx(ctx): Ctx,
Json(RepoBody { rid }): Json<RepoBody>,
) -> impl IntoResponse {
let tags = ctx.list_canonical_tags(rid)?;
Ok::<_, Error>(Json(tags))
}
async fn git_info_handler() -> impl IntoResponse {
Ok::<_, Error>(Json(types::binaries::git_info()))
}