upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/git_mirror.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/git_mirror.rs')
-rw-r--r--src/git_mirror.rs104
1 files changed, 104 insertions, 0 deletions
diff --git a/src/git_mirror.rs b/src/git_mirror.rs
index 6866de3..c486887 100644
--- a/src/git_mirror.rs
+++ b/src/git_mirror.rs
@@ -1,9 +1,12 @@
1use crate::db::MirrorDb; 1use crate::db::MirrorDb;
2use crate::discovery::DiscoveredRepo; 2use crate::discovery::DiscoveredRepo;
3use crate::health::GraspServer; 3use crate::health::GraspServer;
4use crate::nip46::Nip46Client;
4use anyhow::{Context, Result}; 5use anyhow::{Context, Result};
5use git2::RemoteCallbacks; 6use git2::RemoteCallbacks;
7use nostr_sdk::prelude::*;
6use std::path::Path; 8use std::path::Path;
9use std::sync::Arc;
7 10
8pub struct GitMirror { 11pub struct GitMirror {
9 mirror_dir: std::path::PathBuf, 12 mirror_dir: std::path::PathBuf,
@@ -27,6 +30,7 @@ impl GitMirror {
27 db: &MirrorDb, 30 db: &MirrorDb,
28 repo: &DiscoveredRepo, 31 repo: &DiscoveredRepo,
29 target_servers: &[GraspServer], 32 target_servers: &[GraspServer],
33 nip46_client: Option<&Arc<Nip46Client>>,
30 ) -> Result<()> { 34 ) -> Result<()> {
31 if target_servers.is_empty() { 35 if target_servers.is_empty() {
32 tracing::debug!( 36 tracing::debug!(
@@ -43,6 +47,25 @@ impl GitMirror {
43 self.clone_bare(&repo_path, &repo.clone_urls)?; 47 self.clone_bare(&repo_path, &repo.clone_urls)?;
44 } 48 }
45 49
50 let state_event = match self.build_state_event(&repo_path, repo, nip46_client).await {
51 Ok(Some(event)) => Some(event),
52 Ok(None) => {
53 tracing::warn!(
54 identifier = %repo.identifier,
55 "could not build state event — push may be rejected by GRASP servers"
56 );
57 None
58 }
59 Err(e) => {
60 tracing::error!(
61 identifier = %repo.identifier,
62 error = %e,
63 "failed to build state event"
64 );
65 None
66 }
67 };
68
46 for server in target_servers { 69 for server in target_servers {
47 let target_url = server.clone_url(&pk_hex, &repo.identifier); 70 let target_url = server.clone_url(&pk_hex, &repo.identifier);
48 71
@@ -53,6 +76,31 @@ impl GitMirror {
53 "mirroring git data" 76 "mirroring git data"
54 ); 77 );
55 78
79 if let Some(ref event) = state_event {
80 let relay_url = server.relay_url();
81 if let Ok(url) = RelayUrl::parse(&relay_url) {
82 let urls = vec![url];
83 if let Err(e) = nip46_client
84 .map_or(Ok(()), |_| {
85 Err(anyhow::anyhow!("need nostr client to send state event"))
86 })
87 {
88 let _ = e;
89 }
90
91 let nostr_client = nostr_sdk::Client::default();
92 let _ = nostr_client.add_relay(&relay_url).await;
93 nostr_client.connect().await;
94 if let Err(e) = nostr_client.send_event_to(urls, event.clone()).await {
95 tracing::warn!(
96 server = %server.domain,
97 error = %e,
98 "failed to publish state event to server relay"
99 );
100 }
101 }
102 }
103
56 let repo_id = db.get_all_repos().await.ok().and_then(|repos| { 104 let repo_id = db.get_all_repos().await.ok().and_then(|repos| {
57 repos 105 repos
58 .iter() 106 .iter()
@@ -88,6 +136,62 @@ impl GitMirror {
88 Ok(()) 136 Ok(())
89 } 137 }
90 138
139 async fn build_state_event(
140 &self,
141 repo_path: &std::path::PathBuf,
142 repo: &DiscoveredRepo,
143 nip46_client: Option<&Arc<Nip46Client>>,
144 ) -> Result<Option<Event>> {
145 let nip46 = match nip46_client {
146 Some(c) => c,
147 None => return Ok(None),
148 };
149
150 let git_repo = git2::Repository::open(repo_path)
151 .with_context(|| format!("failed to open bare repo at {:?}", repo_path))?;
152
153 let mut tags: Vec<Tag> = vec![
154 Tag::custom(TagKind::Custom("d".into()), [&repo.identifier]),
155 ];
156
157 let refs = git_repo.references()?;
158 for reference in refs {
159 let reference = reference?;
160 let name = reference.name().unwrap_or("");
161 if name.is_empty() {
162 continue;
163 }
164 if let Some(oid) = reference.target() {
165 tags.push(Tag::custom(
166 TagKind::Custom("ref".into()),
167 [name, &oid.to_string()],
168 ));
169 }
170 }
171
172 let builder = EventBuilder::new(Kind::Custom(30618), "").tags(tags);
173 let unsigned = builder.build(repo.pubkey);
174
175 match nip46.sign_event(&repo.pubkey, &unsigned).await {
176 Ok(signed) => {
177 tracing::info!(
178 identifier = %repo.identifier,
179 event_id = %signed.id.to_hex(),
180 "signed kind:30618 state event via NIP-46"
181 );
182 Ok(Some(signed))
183 }
184 Err(e) => {
185 tracing::error!(
186 identifier = %repo.identifier,
187 error = %e,
188 "NIP-46 signing failed for state event"
189 );
190 Err(e)
191 }
192 }
193 }
194
91 fn clone_bare(&self, repo_path: &Path, clone_urls: &[String]) -> Result<()> { 195 fn clone_bare(&self, repo_path: &Path, clone_urls: &[String]) -> Result<()> {
92 if let Some(parent) = repo_path.parent() { 196 if let Some(parent) = repo_path.parent() {
93 std::fs::create_dir_all(parent) 197 std::fs::create_dir_all(parent)