|
6 | 6 |
|
7 | 7 | //! [`list`] all the [`Patch`]es for project. |
8 | 8 |
|
| 9 | +use anyhow::Context as _; |
9 | 10 | use either::Either; |
10 | 11 | use radicle_git_ext::Oid; |
11 | | -use radicle_source::surf::git::RefScope; |
12 | 12 | use serde::Serialize; |
13 | 13 |
|
14 | | -use link_crypto::PeerId; |
15 | 14 | use link_identities::git::Urn; |
16 | 15 |
|
17 | 16 | use crate::project; |
@@ -43,106 +42,117 @@ pub struct Patch { |
43 | 42 | /// # Errors |
44 | 43 | /// * Cannot access the monorepo |
45 | 44 | /// * Cannot find references within the monorepo |
46 | | -pub async fn list( |
47 | | - peer: &crate::peer::Peer, |
48 | | - project_urn: Urn, |
49 | | -) -> Result<Vec<Patch>, crate::error::Error> { |
50 | | - let mut patches = Vec::new(); |
51 | | - |
| 45 | +pub async fn list(peer: &crate::peer::Peer, project_urn: Urn) -> anyhow::Result<Vec<Patch>> { |
52 | 46 | let default_branch_head_commit_id = { |
53 | | - let project = crate::daemon::state::get_project(peer.librad_peer(), project_urn.clone()) |
54 | | - .await? |
55 | | - .ok_or_else(|| crate::daemon::state::Error::ProjectNotFound(project_urn.clone()))?; |
56 | | - let delegate = project |
| 47 | + let project = peer |
| 48 | + .librad_peer() |
| 49 | + .using_storage({ |
| 50 | + let project_urn = project_urn.clone(); |
| 51 | + move |store| librad::git::identities::project::get(store, &project_urn) |
| 52 | + }) |
| 53 | + .await |
| 54 | + .context("failed to access storage")? |
| 55 | + .context("failed to get project")? |
| 56 | + .ok_or_else(|| anyhow::anyhow!("project {project_urn} not found"))?; |
| 57 | + let first_delegate = project |
57 | 58 | .delegations() |
58 | 59 | .iter() |
59 | 60 | .flat_map(|either| match either { |
60 | 61 | Either::Left(pk) => Either::Left(std::iter::once(pk)), |
61 | 62 | Either::Right(indirect) => Either::Right(indirect.delegations().iter()), |
62 | 63 | }) |
63 | 64 | .next() |
64 | | - .expect("missing delegation"); |
65 | | - let default_branch = crate::daemon::state::get_branch( |
66 | | - peer.librad_peer(), |
67 | | - project_urn.clone(), |
68 | | - Some(PeerId::from(*delegate)), |
69 | | - None, |
70 | | - ) |
71 | | - .await?; |
72 | | - crate::browser::using(peer, default_branch, move |browser| { |
73 | | - Ok(browser.get().first().clone()) |
74 | | - })? |
75 | | - .id |
| 65 | + .context("project does not have any delegations")?; |
| 66 | + let first_delegate = librad::PeerId::from(*first_delegate); |
| 67 | + let remote = if first_delegate == peer.librad_peer().peer_id() { |
| 68 | + None |
| 69 | + } else { |
| 70 | + Some(first_delegate) |
| 71 | + }; |
| 72 | + |
| 73 | + let default_branch_ref_name = |
| 74 | + if let Some(ref default_branch_name) = project.subject().default_branch { |
| 75 | + default_branch_name |
| 76 | + .parse::<radicle_git_ext::RefLike>() |
| 77 | + .context("invalid default branch name")? |
| 78 | + } else { |
| 79 | + librad::reflike!("main") |
| 80 | + }; |
| 81 | + |
| 82 | + let reference = librad::git::types::Reference::head( |
| 83 | + librad::git::types::Namespace::from(project_urn.clone()), |
| 84 | + remote, |
| 85 | + default_branch_ref_name, |
| 86 | + ); |
| 87 | + peer.monorepo_unblock(move |repo| Ok(reference.oid(&repo)?)) |
| 88 | + .await |
| 89 | + .context("failed to resolve git reference")? |
76 | 90 | }; |
77 | 91 |
|
| 92 | + let mut patches = Vec::new(); |
| 93 | + |
78 | 94 | for project_peer in |
79 | 95 | crate::daemon::state::list_project_peers(peer.librad_peer(), project_urn.clone()).await? |
80 | 96 | { |
81 | | - let remote = match &project_peer { |
82 | | - crate::daemon::project::Peer::Local { .. } => None, |
83 | | - crate::daemon::project::Peer::Remote { peer_id, .. } => Some(*peer_id), |
84 | | - }; |
85 | | - |
86 | | - let ref_scope = match remote { |
87 | | - Some(remote) => RefScope::Remote { |
88 | | - name: Some(remote.to_string()), |
| 97 | + let namespace = project_urn.encode_id(); |
| 98 | + let ref_glob = match &project_peer { |
| 99 | + crate::daemon::project::Peer::Local { .. } => { |
| 100 | + format!("refs/namespaces/{namespace}/refs/tags/{TAG_PREFIX}*") |
89 | 101 | }, |
90 | | - None => RefScope::Local, |
91 | | - }; |
92 | | - |
93 | | - let branch = match crate::daemon::state::get_branch( |
94 | | - peer.librad_peer(), |
95 | | - project_urn.clone(), |
96 | | - remote, |
97 | | - None, |
98 | | - ) |
99 | | - .await |
100 | | - { |
101 | | - Ok(branch) => branch, |
102 | | - Err(crate::daemon::state::Error::MissingRef { .. }) => { |
103 | | - // The peer hasn’t published any branches yet. |
104 | | - continue; |
| 102 | + crate::daemon::project::Peer::Remote { peer_id, .. } => { |
| 103 | + format!("refs/namespaces/{namespace}/refs/remotes/{peer_id}/tags/{TAG_PREFIX}*") |
105 | 104 | }, |
106 | | - Err(e) => return Err(e.into()), |
107 | 105 | }; |
108 | 106 |
|
109 | | - crate::browser::using(peer, branch, { |
110 | | - let patches = &mut patches; |
111 | | - move |browser| { |
112 | | - let tags = browser.list_tags(ref_scope)?; |
113 | | - for tag in tags { |
114 | | - match tag { |
115 | | - radicle_source::surf::git::Tag::Light { .. } => { |
116 | | - continue; |
117 | | - }, |
118 | | - radicle_source::surf::git::Tag::Annotated { |
119 | | - target_id, |
120 | | - name, |
121 | | - message, |
122 | | - .. |
123 | | - } => { |
124 | | - let id = match name.to_string().strip_prefix(TAG_PREFIX) { |
125 | | - Some(id) => id.to_string(), |
126 | | - None => continue, |
127 | | - }; |
| 107 | + let patch = peer |
| 108 | + .monorepo_unblock({ |
| 109 | + move |repo| { |
| 110 | + let mut patches = vec![]; |
| 111 | + for ref_result in repo |
| 112 | + .references_glob(&ref_glob) |
| 113 | + .context("failed to get references from glob")? |
| 114 | + { |
| 115 | + let reference = ref_result.context("failed to resolve reference")?; |
| 116 | + let tag = reference |
| 117 | + .peel_to_tag() |
| 118 | + .context("failed to peel reference to tag")?; |
128 | 119 |
|
129 | | - let merge_base = browser |
130 | | - .merge_base(target_id, default_branch_head_commit_id)? |
131 | | - .map(Oid::from); |
132 | | - patches.push(Patch { |
133 | | - id, |
134 | | - peer: project_peer.clone().into(), |
135 | | - message, |
136 | | - commit: Oid::from(target_id), |
137 | | - merge_base, |
138 | | - }); |
139 | | - }, |
| 120 | + let id = tag |
| 121 | + .name() |
| 122 | + .ok_or_else(|| anyhow::anyhow!("tag name is not valid UTF-8"))? |
| 123 | + .strip_prefix(TAG_PREFIX) |
| 124 | + .expect("tag name must have prefix") |
| 125 | + .to_string(); |
| 126 | + let commit_id = tag.target_id(); |
| 127 | + let merge_base = |
| 128 | + match repo.merge_base(commit_id, default_branch_head_commit_id) { |
| 129 | + Ok(base) => Some(Oid::from(base)), |
| 130 | + Err(err) if err.code() == git2::ErrorCode::NotFound => None, |
| 131 | + Err(err) => { |
| 132 | + return Err(err) |
| 133 | + .context("failed to determine merge base for commits") |
| 134 | + }, |
| 135 | + }; |
| 136 | + patches.push(Patch { |
| 137 | + id, |
| 138 | + peer: project_peer.clone().into(), |
| 139 | + message: Some( |
| 140 | + tag.message() |
| 141 | + .ok_or_else(|| { |
| 142 | + anyhow::anyhow!("tag message is not valid UTF-8") |
| 143 | + })? |
| 144 | + .to_string(), |
| 145 | + ), |
| 146 | + commit: Oid::from(commit_id), |
| 147 | + merge_base, |
| 148 | + }) |
140 | 149 | } |
| 150 | + Ok(patches) |
141 | 151 | } |
| 152 | + }) |
| 153 | + .await?; |
142 | 154 |
|
143 | | - Ok(()) |
144 | | - } |
145 | | - })?; |
| 155 | + patches.extend(patch) |
146 | 156 | } |
147 | 157 |
|
148 | 158 | Ok(patches) |
|
0 commit comments