diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2024-07-26 10:29:33 +0100 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2024-07-26 10:36:58 +0100 |
| commit | 0134ab8eb413b8b81ec8e179897ddb8ea63e134e (patch) | |
| tree | 1d3ef8786fa806a4d39378286c68cda722cc017d /src/git_remote_helper.rs | |
| parent | 52f9efa50f81142da013c4da5f3cd3091e07916b (diff) | |
feat(remote): add nostr git remote helper
as a simple proxy to the first git server listed in announcement
parse clone url as `nostr://naddr123...`
Diffstat (limited to 'src/git_remote_helper.rs')
| -rw-r--r-- | src/git_remote_helper.rs | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/src/git_remote_helper.rs b/src/git_remote_helper.rs new file mode 100644 index 0000000..6050d1a --- /dev/null +++ b/src/git_remote_helper.rs | |||
| @@ -0,0 +1,156 @@ | |||
| 1 | #![cfg_attr(not(test), warn(clippy::pedantic))] | ||
| 2 | #![allow(clippy::large_futures)] | ||
| 3 | // better solution to dead_code error on multiple binaries than https://stackoverflow.com/a/66196291 | ||
| 4 | #![allow(dead_code)] | ||
| 5 | #![cfg_attr(not(test), warn(clippy::expect_used))] | ||
| 6 | |||
| 7 | use core::str; | ||
| 8 | use std::{ | ||
| 9 | collections::HashSet, | ||
| 10 | env, | ||
| 11 | io::{self}, | ||
| 12 | path::PathBuf, | ||
| 13 | }; | ||
| 14 | |||
| 15 | use anyhow::{bail, Context, Result}; | ||
| 16 | #[cfg(not(test))] | ||
| 17 | use client::Connect; | ||
| 18 | use client::{fetching_with_report, get_repo_ref_from_cache}; | ||
| 19 | use git::RepoActions; | ||
| 20 | use nostr::nips::nip01::Coordinate; | ||
| 21 | use nostr_sdk::Url; | ||
| 22 | |||
| 23 | #[cfg(not(test))] | ||
| 24 | use crate::client::Client; | ||
| 25 | #[cfg(test)] | ||
| 26 | use crate::client::MockConnect; | ||
| 27 | use crate::git::Repo; | ||
| 28 | |||
| 29 | mod cli; | ||
| 30 | mod cli_interactor; | ||
| 31 | mod client; | ||
| 32 | mod config; | ||
| 33 | mod git; | ||
| 34 | mod key_handling; | ||
| 35 | mod login; | ||
| 36 | mod repo_ref; | ||
| 37 | mod sub_commands; | ||
| 38 | |||
| 39 | #[tokio::main] | ||
| 40 | async fn main() -> Result<()> { | ||
| 41 | let args = env::args(); | ||
| 42 | let args = args.skip(1).take(2).collect::<Vec<_>>(); | ||
| 43 | |||
| 44 | let ([_, url] | [url]) = args.as_slice() else { | ||
| 45 | bail!("invalid arguments - no url"); | ||
| 46 | }; | ||
| 47 | if env::args().nth(1).as_deref() == Some("--version") { | ||
| 48 | println!("v0.0.1"); | ||
| 49 | } | ||
| 50 | |||
| 51 | let git_repo = Repo::from_path(&PathBuf::from( | ||
| 52 | std::env::var("GIT_DIR").context("git should set GIT_DIR when remote helper is called")?, | ||
| 53 | ))?; | ||
| 54 | let git_repo_path = git_repo.get_path()?; | ||
| 55 | |||
| 56 | #[cfg(not(test))] | ||
| 57 | let client = Client::default(); | ||
| 58 | #[cfg(test)] | ||
| 59 | let client = <MockConnect as std::default::Default>::default(); | ||
| 60 | |||
| 61 | let repo_coordinates = nostr_git_url_to_repo_coordinates(url).context("invalid nostr url")?; | ||
| 62 | |||
| 63 | fetching_with_report(git_repo_path, &client, &repo_coordinates).await?; | ||
| 64 | |||
| 65 | let repo_ref = get_repo_ref_from_cache(git_repo_path, &repo_coordinates).await?; | ||
| 66 | |||
| 67 | let stdin = io::stdin(); | ||
| 68 | let mut line = String::new(); | ||
| 69 | |||
| 70 | let temp_remote_url = repo_ref | ||
| 71 | .git_server | ||
| 72 | .first() | ||
| 73 | .context("no git server listed in nostr repository announcement")?; | ||
| 74 | |||
| 75 | let mut temp_remote = git_repo.git_repo.remote_anonymous(temp_remote_url)?; | ||
| 76 | |||
| 77 | loop { | ||
| 78 | let tokens = read_line(&stdin, &mut line)?; | ||
| 79 | |||
| 80 | match tokens.as_slice() { | ||
| 81 | ["capabilities"] => { | ||
| 82 | println!("option"); | ||
| 83 | println!("push"); | ||
| 84 | println!("fetch"); | ||
| 85 | println!(); | ||
| 86 | } | ||
| 87 | ["option", "verbosity"] => { | ||
| 88 | println!("ok"); | ||
| 89 | } | ||
| 90 | ["option", ..] => { | ||
| 91 | println!("unsupported"); | ||
| 92 | } | ||
| 93 | ["fetch", _oid, refstr] => { | ||
| 94 | temp_remote.connect(git2::Direction::Fetch)?; | ||
| 95 | temp_remote.download(&[refstr], None)?; | ||
| 96 | temp_remote.disconnect()?; | ||
| 97 | println!(); | ||
| 98 | } | ||
| 99 | ["push", refspec] => { | ||
| 100 | temp_remote.connect(git2::Direction::Push)?; | ||
| 101 | temp_remote.push(&[refspec], None)?; | ||
| 102 | temp_remote.disconnect()?; | ||
| 103 | println!(); | ||
| 104 | } | ||
| 105 | ["list"] => { | ||
| 106 | temp_remote.connect(git2::Direction::Fetch)?; | ||
| 107 | for head in temp_remote.list()? { | ||
| 108 | println!("{} {}", head.oid(), head.name()); | ||
| 109 | } | ||
| 110 | temp_remote.disconnect()?; | ||
| 111 | println!(); | ||
| 112 | } | ||
| 113 | ["list", "for-push"] => { | ||
| 114 | temp_remote.connect(git2::Direction::Fetch)?; | ||
| 115 | for head in temp_remote.list()? { | ||
| 116 | if head.name() != "HEAD" { | ||
| 117 | println!("{} {}", head.oid(), head.name()); | ||
| 118 | } | ||
| 119 | } | ||
| 120 | temp_remote.disconnect()?; | ||
| 121 | println!(); | ||
| 122 | } | ||
| 123 | [] => { | ||
| 124 | return Ok(()); | ||
| 125 | } | ||
| 126 | _ => { | ||
| 127 | bail!(format!("unknown command: {}", line.trim().to_owned())); | ||
| 128 | } | ||
| 129 | } | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | /// Read one line from stdin, and split it into tokens. | ||
| 134 | pub(crate) fn read_line<'a>(stdin: &io::Stdin, line: &'a mut String) -> io::Result<Vec<&'a str>> { | ||
| 135 | line.clear(); | ||
| 136 | |||
| 137 | let read = stdin.read_line(line)?; | ||
| 138 | if read == 0 { | ||
| 139 | return Ok(vec![]); | ||
| 140 | } | ||
| 141 | let line = line.trim(); | ||
| 142 | let tokens = line.split(' ').filter(|t| !t.is_empty()).collect(); | ||
| 143 | |||
| 144 | Ok(tokens) | ||
| 145 | } | ||
| 146 | |||
| 147 | fn nostr_git_url_to_repo_coordinates(url: &str) -> Result<HashSet<Coordinate>> { | ||
| 148 | let mut repo_coordinattes = HashSet::new(); | ||
| 149 | let coordinate = Coordinate::parse(Url::parse(url)?.domain().context("no naddr")?)?; | ||
| 150 | if coordinate.kind.eq(&nostr_sdk::Kind::GitRepoAnnouncement) { | ||
| 151 | repo_coordinattes.insert(coordinate); | ||
| 152 | } else { | ||
| 153 | bail!("naddr doesnt point to a git repository announcement"); | ||
| 154 | } | ||
| 155 | Ok(repo_coordinattes) | ||
| 156 | } | ||