upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2023-05-21 11:24:35 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2023-05-21 11:24:35 +0000
commitd8dbd5fe94219b9df7f0aa5f6f810009335f0f46 (patch)
treeefb863061583957e02c03909ed08746c7099517f /src
parent73f0333128b5296c7ad718a7b4f2b3db8c7e8a0f (diff)
init
Diffstat (limited to 'src')
-rw-r--r--src/sub_commands/init.rs247
1 files changed, 247 insertions, 0 deletions
diff --git a/src/sub_commands/init.rs b/src/sub_commands/init.rs
new file mode 100644
index 0000000..e5fa52c
--- /dev/null
+++ b/src/sub_commands/init.rs
@@ -0,0 +1,247 @@
1use std::{str::FromStr, fs::{OpenOptions, File}, io::Write};
2use dialoguer::{theme::ColorfulTheme, Confirm, Input};
3use clap::{Args};
4use indicatif::ProgressBar;
5use nostr_sdk::prelude::*;
6
7use crate::{config::{load_config, save_conifg}, groups::{init::{InitializeGroup}, group::{Group}}, repos::{init::InitializeRepo, repo::Repo}, utils::{save_event, create_client, get_or_generate_keys}, cli_helpers::select_relays, repo_config::RepoConfig};
8
9#[derive(Args)]
10struct InitRepo {
11 // Repo Name
12 #[arg(short, long)]
13 name: String,
14 /// Admin Group ID
15 #[arg(long)]
16 admin_group_id: Option<String>,
17 /// Relays
18 #[arg(short, long)]
19 relays: Option<String>,
20}
21
22#[derive(Args)]
23pub struct InitSubCommand {
24 /// Repo Name
25 #[arg(short, long)]
26 name: Option<String>,
27}
28
29pub fn create_and_broadcast_init(
30 relays: Vec<String>,
31 sub_command_args: &InitSubCommand,
32) -> Result<()> {
33
34 let mut cfg = load_config();
35
36 let repo_dir_path = std::env::current_dir().unwrap();
37
38 // check for potential problems
39 let ngit_path = repo_dir_path.clone().join(".ngit");
40 if ngit_path.is_dir() && (
41 !Confirm::with_theme(&ColorfulTheme::default())
42 .with_prompt("ngit already initialized! Do you want overwrite it with a fresh repoisotry?")
43 .default(false)
44 .interact()
45 .unwrap()
46 || !Confirm::with_theme(&ColorfulTheme::default())
47 .with_prompt("Are you sure?")
48 .default(false)
49 .interact()
50 .unwrap()
51 ) { panic!("aborted as ngit repository already exists."); };
52
53 let git_path = repo_dir_path.clone().join(".git");
54 if git_path.is_dir() && (
55 !Confirm::with_theme(&ColorfulTheme::default())
56 .with_prompt("git has already been initialized here. For this alpha ngit prototype its best to start with a fresh repository. Continue anyway?")
57 .default(false)
58 .interact()
59 .unwrap()
60 ) { panic!("aborted as git repository already initialized."); };
61
62 // collect information from user
63
64 let dir_name: String =String::from(repo_dir_path.file_name().unwrap().to_string_lossy());
65 let repo_name: String = match &sub_command_args.name {
66 Some(name) => name.clone(),
67 None => {
68 Input::with_theme(&ColorfulTheme::default())
69 .with_prompt("Repo Name")
70 .default(dir_name)
71 .interact_text()
72 .unwrap()
73 },
74 };
75 let repo_relays = select_relays(&mut cfg, &relays)?;
76
77 let mut repo_group_members: Vec<String> = vec![];
78 loop {
79 if Confirm::with_theme(&ColorfulTheme::default())
80 .with_prompt("Would you like add other maintainers now?")
81 .default(false)
82 .interact()
83 .unwrap()
84 {
85 let member_key_input: String = Input::with_theme(&ColorfulTheme::default())
86 .with_prompt("npub or hex (n to abort)")
87 .interact_text()
88 .unwrap()
89 ;
90 if member_key_input.starts_with("npub") {
91 match XOnlyPublicKey::from_bech32(member_key_input) {
92 Ok(k) => { repo_group_members.push(k.to_string()); },
93 Err(e) => { println!("{}",e) },
94 }}
95 else {
96 match XOnlyPublicKey::from_str(member_key_input.as_str()) {
97 Ok(k) => { repo_group_members.push(k.to_string()); },
98 Err(e) => { println!("{}",e) },
99 }
100 }
101 }
102 else { break; }
103 }
104
105 let keys = get_or_generate_keys(&mut cfg);
106 let mut events_to_broadcast: Vec<Event> = vec![];
107
108 // delay adding user as group member so keys are the last thing asked for
109 repo_group_members.push(keys.public_key().to_string());
110
111 let admin_group_event = match cfg.default_admin_group_event_serialized {
112 None => {
113 let new_admin_group = Group::new(
114 &InitializeGroup::new()
115 .members(
116 vec![
117 keys.public_key().to_string(),
118 ],
119 vec![],
120 )
121 .relays(&repo_relays),
122 &keys,
123 ).unwrap();
124 cfg.default_admin_group_event_serialized = Some(new_admin_group.events[0].as_json());
125 save_conifg(&cfg);
126 events_to_broadcast.push(new_admin_group.events[0].clone());
127 new_admin_group.events[0].clone()
128 },
129 Some(admin) => Group::new_from_json_event(admin.clone())
130 .expect("default_admin_group_event_serialized in MyConfig to load into Group")
131 .events[0].clone(),
132 };
133
134 let new_repo_group = Group::new(
135 &InitializeGroup::new()
136 .name(format!("{repo_name} maintainers (ngit)"))
137 .members(
138 repo_group_members,
139 vec![],
140 )
141 .relays(&repo_relays)
142 ,
143 &keys,
144 ).unwrap();
145 events_to_broadcast.push(new_repo_group.events[0].clone());
146
147 let new_repo = Repo::new(
148 &InitializeRepo::new()
149 .name(&repo_name)
150 .relays(&repo_relays)
151 .maintainers_group(new_repo_group.get_ref())
152 ,
153 &keys,
154 ).unwrap();
155 events_to_broadcast.push(new_repo.events[0].clone());
156
157 // crate .ngit folder and store the repo and group reference and associated events (?)
158 for p in [
159 "groups",
160 "branches",
161 "patches",
162 "merges",
163 "prs",
164 "issues",
165 "comments",
166 ] { std::fs::create_dir_all(ngit_path.join(p)).unwrap(); }
167
168 // save repo event locally
169 save_event(
170 ngit_path.join(format!("groups/{}.json",admin_group_event.id.to_string())),
171 &admin_group_event,
172 ).unwrap();
173
174 save_event(
175 ngit_path.join(format!("groups/{}.json",new_repo_group.id.to_string())),
176 &new_repo_group.events[0],
177 ).unwrap();
178 save_event(
179 ngit_path.join("repo.json"),
180 &new_repo.events[0],
181 ).unwrap();
182
183 // set repo config
184 let mut repo_config = RepoConfig::open(&repo_dir_path);
185 for b in ["main", "master"] {
186 repo_config.set_mapping(&b.to_string(), &new_repo.events[0].id.to_string());
187
188 }
189 repo_config.set_last_branch_ref_update_time(new_repo.events[0].created_at.clone());
190
191 // initialise git
192 git2::Repository::init(repo_dir_path.clone()).unwrap();
193
194 // add .gitignore
195 let gitignore_path = repo_dir_path.join(".gitignore");
196 let mut gitignore_file = if gitignore_path.is_file() {
197 OpenOptions::new()
198 .write(true)
199 .append(true)
200 .open(&gitignore_path)
201 .expect(".gitignore to open")
202 } else {
203 File::create(gitignore_path)
204 .expect("create and open .gitignore file")
205 };
206 writeln!(gitignore_file, ".ngit")
207 .expect(".ngit added to gitignore");
208
209 let spinner = ProgressBar::new_spinner();
210 spinner.set_message("Broadcasting... if this takes 20s+, there was a problem broadcasting to one or more relays even if it says 'Repository Initialised'.");
211
212 let client = create_client(&keys, repo_relays.clone())?;
213 for e in &events_to_broadcast {
214 match client.send_event(e.clone()) {
215 Ok(_) => (),
216 // TODO: this isn't working - if a relay is specified with a type it will wait 30ish secs and then return successful
217 Err(e) => { println!("error broadcasting repo event: {}",e); },
218 }
219 // TODO: better error handling here / reporting. potentially warn if taking a while and report on troublesome relays
220 }
221 spinner.finish_with_message(format!(
222 "Repository Initialised! id: {}",
223 Nip19Event::new(
224 new_repo.id.clone(),
225 vec![&repo_relays[0]],
226 )
227 .to_bech32()
228 .expect("Nip19Event to convert to to_bech32")
229
230 ));
231 // println!("Hint: only maintainers can push to master branch and merge PRs but anyone can clone and push a forked branch.");
232
233 // Instructions:
234 // 1. make some commits on 'master' or 'main' branch.
235 // 2. 'ngit push' will push them to the nostr repo (via a patch).
236 // 3. make a branch called 'feature-1'
237 // 3. 'ngit push' will create a PR
238
239 // repo
240 // repo
241 // PRs
242
243 // patch
244
245
246 Ok(())
247}