From 5ac09c580272d5116c2f57b4370e274b204f21bb Mon Sep 17 00:00:00 2001

From: DanConwayDev

Date: Mon, 18 Nov 2024 16:39:47 +0000

Subject: [PATCH] feat(blossom): blossom as remote via external bin

following conversations with

nostr:npub1elta7cneng3w8p9y4dw633qzdjr4kyvaparuyuttyrx6e8xp7xnq32cume

this minimum viable attempt at adapting git-remote-nostr so that his

approach / work on git via blossom can be intergrated.

it requires 2 binaries in PATH called:

1. git-remote-blossom-fetch which must take multiple commit ids as

`--input` args and either write an errot to `stderr` or return a

success code after downloading and adding missing state data related

to these commit ids into the local .git folder.

2. git-remote-blossom-push which must take multiple commit ids as

`--input` args and either write an errot to `stderr` or upload

downloading and uploading any missing state data to (a) blossom

server(s) in a way that is retrievable by the fetch binary.

this is just an untested starting point.

---

src/bin/git_remote_nostr/fetch.rs | 30 ++++++++++++++++++++++++++++++

src/bin/git_remote_nostr/list.rs | 24 +++++++++++++++++++++++-

src/bin/git_remote_nostr/push.rs | 47 ++++++++++++++++++++++++++++++++++++++++++++---

src/lib/repo_state.rs | 1 +

4 files changed, 98 insertions(+), 4 deletions(-)

diff --git a/src/bin/git_remote_nostr/fetch.rs b/src/bin/git_remote_nostr/fetch.rs

index a972a2f..df2158b 100644

--- a/src/bin/git_remote_nostr/fetch.rs

+++ b/src/bin/git_remote_nostr/fetch.rs

@@ -2,6 +2,7 @@ use core::str;

use std::{

collections::HashMap,

io::Stdin,

+ process::Command,

sync::{Arc, Mutex},

time::Instant,

};

@@ -171,6 +172,10 @@ pub fn fetch_from_git_server(

return Ok(());

}

+ if git_server_url == "blossom" {

+ return fetch_from_blossom(oids, term);

+ }

+

let server_url = git_server_url.parse::()?;

let protocols_to_attempt = get_read_protocols_to_try(git_repo, &server_url, decoded_nostr_url);

@@ -228,6 +233,31 @@ pub fn fetch_from_git_server(

}

}

+fn fetch_from_blossom(oids: &[String], term: &console::Term) -> Result<()> {

+ term.write_line("fetching over blossom...")?;

+ let args: Vec = oids

+ .iter()

+ .flat_map(|value| vec!["--input".to_string(), value.clone()])

+ .collect();

+ let res = Command::new("git-remote-blossom-fetch").args(args).output();

+ match res {

+ Ok(output) => {

+ if output.status.success() {

+ term.write_line("fetch: succeeded over blossom")?;

+ } else {

+ let error = String::from_utf8_lossy(&output.stderr).to_string();

+ term.write_line(format!("fetch: failed over blossom: {}", &error).as_str())?;

+ bail!(error);

+ }

+ }

+ Err(error) => {

+ term.write_line(format!("fetch: failed over blossom: {error}").as_str())?;

+ bail!(error);

+ }

+ }

+ Ok(())

+}

+

#[allow(clippy::cast_precision_loss)]

#[allow(clippy::float_cmp)]

#[allow(clippy::needless_pass_by_value)]

diff --git a/src/bin/git_remote_nostr/list.rs b/src/bin/git_remote_nostr/list.rs

index 92faa6b..b556897 100644

--- a/src/bin/git_remote_nostr/list.rs

+++ b/src/bin/git_remote_nostr/list.rs

@@ -14,6 +14,7 @@ use ngit::{

git_events::event_to_cover_letter,

login::get_curent_user,

repo_ref,

+ repo_state::RepoState,

};

use nostr_sdk::hashes::sha1::Hash as Sha1Hash;

use repo_ref::RepoRef;

@@ -43,11 +44,20 @@ pub async fn run_list(

let term = console::Term::stderr();

- let remote_states = list_from_remotes(&term, git_repo, &repo_ref.git_server, decoded_nostr_url);

+ let remote_states = list_from_remotes(

+ &term,

+ git_repo,

+ &repo_ref.git_server,

+ decoded_nostr_url,

+ &nostr_state,

+ );

let mut state = if let Some(nostr_state) = nostr_state {

for (name, value) in &nostr_state.state {

for (url, remote_state) in &remote_states {

+ if url == "blossom" {

+ continue;

+ }

let remote_name = get_short_git_server_name(git_repo, url);

if let Some(remote_value) = remote_state.get(name) {

if value.ne(remote_value) {

@@ -179,10 +189,22 @@ pub fn list_from_remotes(

git_repo: &Repo,

git_servers: &Vec,

decoded_nostr_url: &NostrUrlDecoded, // Add this parameter

+ nostr_state: &Option,

) -> HashMap> {

let mut remote_states = HashMap::new();

let mut errors = HashMap::new();

for url in git_servers {

+ if url == "blossom" {

+ remote_states.insert(

+ "blossom".to_string(),

+ if let Some(nostr_state) = nostr_state {

+ nostr_state.state.clone()

+ } else {

+ HashMap::new()

+ },

+ );

+ continue;

+ }

match list_from_remote(term, git_repo, url, decoded_nostr_url) {

Err(error) => {

errors.insert(url, error);

diff --git a/src/bin/git_remote_nostr/push.rs b/src/bin/git_remote_nostr/push.rs

index db86c04..2bc1cb1 100644

--- a/src/bin/git_remote_nostr/push.rs

+++ b/src/bin/git_remote_nostr/push.rs

@@ -2,6 +2,7 @@ use core::str;

use std::{

collections::{HashMap, HashSet},

io::Stdin,

+ process::Command,

sync::{Arc, Mutex},

time::Instant,

};

@@ -72,13 +73,23 @@ pub async fn run_push(

let term = console::Term::stderr();

+ let nostr_state = get_state_from_cache(git_repo.get_path()?, repo_ref).await;

+

let list_outputs = match list_outputs {

Some(outputs) => outputs,

- _ => list_from_remotes(&term, git_repo, &repo_ref.git_server, decoded_nostr_url),

+ _ => list_from_remotes(

+ &term,

+ git_repo,

+ &repo_ref.git_server,

+ decoded_nostr_url,

+ &if let Ok(nostr_state) = &nostr_state {

+ Some(nostr_state.clone())

+ } else {

+ None

+ },

+ ),

};

- let nostr_state = get_state_from_cache(git_repo.get_path()?, repo_ref).await;

-

let existing_state = {

// if no state events - create from first git server listed

if let Ok(nostr_state) = &nostr_state {

@@ -352,7 +363,12 @@ fn push_to_remote(

remote_refspecs: &[String],

term: &Term,

) -> Result<()> {

+ if git_server_url == "blossom" {

+ return push_to_blossom(remote_refspecs, term);

+ }

+

let server_url = git_server_url.parse::()?;

+

let protocols_to_attempt = get_write_protocols_to_try(git_repo, &server_url, decoded_nostr_url);

let mut failed_protocols = vec![];

@@ -398,6 +414,31 @@ fn push_to_remote(

}

}

+fn push_to_blossom(remote_refspecs: &[String], term: &console::Term) -> Result<()> {

+ term.write_line("push: to blossom...")?;

+ let args: Vec = remote_refspecs

+ .iter()

+ .flat_map(|value| vec!["--input".to_string(), value.clone()])

+ .collect();

+ let res = Command::new("git-remote-blossom-push").args(args).output();

+ match res {

+ Ok(output) => {

+ if output.status.success() {

+ term.write_line("push: succeeded over blossom")?;

+ } else {

+ let error = String::from_utf8_lossy(&output.stderr).to_string();

+ term.write_line(format!("push: failed over blossom: {}", &error).as_str())?;

+ bail!(error);

+ }

+ }

+ Err(error) => {

+ term.write_line(format!("push: failed over blossom: {error}").as_str())?;

+ bail!(error);

+ }

+ }

+ Ok(())

+}

+

fn push_to_remote_url(

git_repo: &Repo,

git_server_url: &str,

diff --git a/src/lib/repo_state.rs b/src/lib/repo_state.rs

index c3a7606..ccd317a 100644

--- a/src/lib/repo_state.rs

+++ b/src/lib/repo_state.rs

@@ -3,6 +3,7 @@ use std::collections::HashMap;

use anyhow::{Context, Result};

use git2::Oid;

+#[derive(Clone)]

pub struct RepoState {

pub identifier: String,

pub state: HashMap,

--

libgit2 1.8.1

Reply to this note

Please Login to reply.

Discussion

here you go nostr:npub1elta7cneng3w8p9y4dw633qzdjr4kyvaparuyuttyrx6e8xp7xnq32cume. I didn't build any dummy binaries to test it with, so don't expect it to work yet or anything.

Sure, no expectations here!

As an initial note, I think I'll need the blossom server to be passed down in args to the scripts.

It can just simply be a positional argument.

Passing commit ID's are fine for pushes. But for fetch, I need the blossom sha256 hashes instead of the commit IDs in order to find the blobs on blossom.

Shouldn't that be caculated from within the fetch script?

The way it works in the current version of git-remote-blossom is that it saves both the sha1 and the sha256 hash in the state event. So a ref tag looks like this:

`["refs/heads/master", "3437fd950d4ba37ba51e3bc788025698cfb20ca1", "3b58e484825e5b96428ab0b0c19cb760d5ebbd39acb4795446a89c99482e2d62"],`

It needs the sha256 to be able to start the graph traversal at the HEAD. All the rest can be calculated.

This is somewhat diverges from the current NIP-34. It needs to be updated with the sha256 - which is right now in the place of the optional parent commit ids.

I'm not crazy about breaking changes to the spec but I don't think anyone is using the optional parent commit ids.

For a minimum viable solution I thought we could just hard code it. I suppose in could initially add it as an CLI argument for `ngit init` for now and store it in blossom tag in 30317.

Yes, we can hardcode it, but then I guess we don't even need to handle it in ngit init nor add it to the blossom tag. Or I might misunderstand something.

Right now it assumes the blossom state is always reflected in the latest 30618 event. This means it will only upload state for branches / tags that have had changes since blossom was added to the `clone` list.

If there is no blossom sha256 for a tag, it cannot be cloned from blossom. I think it is not the expected behaviour from the user. If he makes the move from github to blossom, then removes github.com clone url from the repo announcement even, then he will be disappointed if he cannot clone old versions.

I think blossom keys should be added to each ref upon the first push to blossom to keep the functionality.