diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2025-10-20 12:50:37 +0100 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2025-10-20 14:36:24 +0100 |
| commit | 54bbcc3c16b88a01a29bb0f9bd76e9174993e16e (patch) | |
| tree | 70f5625f9d544fda133c12beb9358d094ccbf598 /src | |
| parent | b61f48bcdad6a7d13259bb0c1dfe6c7564b357a1 (diff) | |
fix: grasp server detection
to ensure we dont try and fallback to ssh
Diffstat (limited to 'src')
| -rw-r--r-- | src/bin/git_remote_nostr/push.rs | 4 | ||||
| -rw-r--r-- | src/bin/ngit/sub_commands/sync.rs | 2 | ||||
| -rw-r--r-- | src/lib/repo_ref.rs | 216 |
3 files changed, 205 insertions, 17 deletions
diff --git a/src/bin/git_remote_nostr/push.rs b/src/bin/git_remote_nostr/push.rs index 1645ae8..e880f0d 100644 --- a/src/bin/git_remote_nostr/push.rs +++ b/src/bin/git_remote_nostr/push.rs | |||
| @@ -22,7 +22,7 @@ use ngit::{ | |||
| 22 | list::list_from_remotes, | 22 | list::list_from_remotes, |
| 23 | login::{self, user::UserRef}, | 23 | login::{self, user::UserRef}, |
| 24 | push::{push_to_remote, select_servers_push_refs_and_generate_pr_or_pr_update_event}, | 24 | push::{push_to_remote, select_servers_push_refs_and_generate_pr_or_pr_update_event}, |
| 25 | repo_ref::{self, get_repo_config_from_yaml, is_grasp_server_in_list}, | 25 | repo_ref::{self, get_repo_config_from_yaml, is_grasp_server_clone_url}, |
| 26 | repo_state, | 26 | repo_state, |
| 27 | utils::{ | 27 | utils::{ |
| 28 | find_proposal_and_patches_by_branch_name, get_all_proposals, get_remote_name_by_url, | 28 | find_proposal_and_patches_by_branch_name, get_all_proposals, get_remote_name_by_url, |
| @@ -159,7 +159,7 @@ pub async fn run_push( | |||
| 159 | &repo_ref.to_nostr_git_url(&None), | 159 | &repo_ref.to_nostr_git_url(&None), |
| 160 | &remote_refspecs, | 160 | &remote_refspecs, |
| 161 | &term, | 161 | &term, |
| 162 | is_grasp_server_in_list(&git_server_url, &repo_ref.grasp_servers()), | 162 | is_grasp_server_clone_url(&git_server_url), |
| 163 | ); | 163 | ); |
| 164 | } | 164 | } |
| 165 | } | 165 | } |
diff --git a/src/bin/ngit/sub_commands/sync.rs b/src/bin/ngit/sub_commands/sync.rs index 0860cc4..b7eb812 100644 --- a/src/bin/ngit/sub_commands/sync.rs +++ b/src/bin/ngit/sub_commands/sync.rs | |||
| @@ -185,7 +185,7 @@ pub async fn launch(args: &SubCommandArgs) -> Result<()> { | |||
| 185 | &decoded_nostr_url, | 185 | &decoded_nostr_url, |
| 186 | &refspecs, | 186 | &refspecs, |
| 187 | &term, | 187 | &term, |
| 188 | *is_grasp_server, | 188 | *is_grasp_server || is_grasp_server_clone_url(url), |
| 189 | ) { | 189 | ) { |
| 190 | Err(error) => { | 190 | Err(error) => { |
| 191 | term.write_line(&format!( | 191 | term.write_line(&format!( |
diff --git a/src/lib/repo_ref.rs b/src/lib/repo_ref.rs index eb0964f..9573238 100644 --- a/src/lib/repo_ref.rs +++ b/src/lib/repo_ref.rs | |||
| @@ -736,26 +736,56 @@ pub fn extract_npub(s: &str) -> Result<&str> { | |||
| 736 | 736 | ||
| 737 | pub fn is_grasp_server_in_list(url: &str, grasp_servers: &[String]) -> bool { | 737 | pub fn is_grasp_server_in_list(url: &str, grasp_servers: &[String]) -> bool { |
| 738 | if !grasp_servers.is_empty() { | 738 | if !grasp_servers.is_empty() { |
| 739 | if let Ok(url) = normalize_grasp_server_url(url) { | 739 | grasp_servers |
| 740 | grasp_servers.iter().any(|s| { | 740 | .iter() |
| 741 | if let Ok(s) = normalize_grasp_server_url(s) { | 741 | .any(|s| s.trim_end_matches('/') == url.trim_end_matches('/')) |
| 742 | s == url | ||
| 743 | } else { | ||
| 744 | false | ||
| 745 | } | ||
| 746 | }) | ||
| 747 | } else { | ||
| 748 | false | ||
| 749 | } | ||
| 750 | } else { | 742 | } else { |
| 751 | false | 743 | false |
| 752 | } | 744 | } |
| 753 | } | 745 | } |
| 754 | 746 | ||
| 755 | pub fn is_grasp_server_clone_url(url: &str) -> bool { | 747 | pub fn is_grasp_server_clone_url(url: &str) -> bool { |
| 756 | extract_npub(url).is_ok() | 748 | // Must start with http:// or https:// |
| 757 | && (url.ends_with(".git") || url.ends_with(".git/")) | 749 | if !url.starts_with("http://") && !url.starts_with("https://") { |
| 758 | && url.starts_with("http") | 750 | return false; |
| 751 | } | ||
| 752 | |||
| 753 | // Must end with .git or .git/ | ||
| 754 | if !url.ends_with(".git") && !url.ends_with(".git/") { | ||
| 755 | return false; | ||
| 756 | } | ||
| 757 | |||
| 758 | // Must contain a valid npub | ||
| 759 | let npub = match extract_npub(url) { | ||
| 760 | Ok(npub) => npub, | ||
| 761 | Err(_) => return false, | ||
| 762 | }; | ||
| 763 | |||
| 764 | // Must have format: /{npub}/<repo-name>.git | ||
| 765 | // The npub must be followed by a slash and then a non-empty repo name | ||
| 766 | let npub_pattern = format!("/{}/", npub); | ||
| 767 | if let Some(npub_pos) = url.find(&npub_pattern) { | ||
| 768 | // Get the part after /{npub}/ | ||
| 769 | let after_npub = &url[npub_pos + npub_pattern.len()..]; | ||
| 770 | |||
| 771 | // Remove trailing slash if present | ||
| 772 | let after_npub = after_npub.trim_end_matches('/'); | ||
| 773 | |||
| 774 | // Must have a non-empty repo name that ends with .git | ||
| 775 | if after_npub.is_empty() || after_npub == ".git" { | ||
| 776 | return false; | ||
| 777 | } | ||
| 778 | |||
| 779 | // Repo name must be at least 1 character before .git | ||
| 780 | if !after_npub.ends_with(".git") { | ||
| 781 | return false; | ||
| 782 | } | ||
| 783 | |||
| 784 | let repo_name = &after_npub[..after_npub.len() - 4]; // Remove .git | ||
| 785 | !repo_name.is_empty() | ||
| 786 | } else { | ||
| 787 | false | ||
| 788 | } | ||
| 759 | } | 789 | } |
| 760 | 790 | ||
| 761 | pub fn format_grasp_server_url_as_relay_url(url: &str) -> Result<String> { | 791 | pub fn format_grasp_server_url_as_relay_url(url: &str) -> Result<String> { |
| @@ -1100,4 +1130,162 @@ mod tests { | |||
| 1100 | } | 1130 | } |
| 1101 | Ok(()) | 1131 | Ok(()) |
| 1102 | } | 1132 | } |
| 1133 | |||
| 1134 | mod is_grasp_server_in_list { | ||
| 1135 | use super::*; | ||
| 1136 | |||
| 1137 | #[test] | ||
| 1138 | fn detects_in_list() { | ||
| 1139 | assert!(is_grasp_server_in_list( | ||
| 1140 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/example-repo.git", | ||
| 1141 | &[ | ||
| 1142 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/example-repo.git".to_string(), | ||
| 1143 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/example-repo2.git".to_string(), | ||
| 1144 | ], | ||
| 1145 | )) | ||
| 1146 | } | ||
| 1147 | |||
| 1148 | #[test] | ||
| 1149 | fn ignores_not_in_list() { | ||
| 1150 | assert!(!is_grasp_server_in_list( | ||
| 1151 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/example-repo3.git", | ||
| 1152 | &[ | ||
| 1153 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/example-repo.git".to_string(), | ||
| 1154 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/example-repo2.git".to_string(), | ||
| 1155 | ], | ||
| 1156 | )) | ||
| 1157 | } | ||
| 1158 | } | ||
| 1159 | |||
| 1160 | mod is_grasp_server_clone_url { | ||
| 1161 | use super::*; | ||
| 1162 | |||
| 1163 | #[test] | ||
| 1164 | fn valid_https_url() { | ||
| 1165 | assert!(is_grasp_server_clone_url( | ||
| 1166 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/my-repo.git" | ||
| 1167 | )); | ||
| 1168 | } | ||
| 1169 | |||
| 1170 | #[test] | ||
| 1171 | fn valid_http_url() { | ||
| 1172 | assert!(is_grasp_server_clone_url( | ||
| 1173 | "http://localhost:8080/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/test-repo.git" | ||
| 1174 | )); | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | #[test] | ||
| 1178 | fn valid_with_trailing_slash() { | ||
| 1179 | assert!(is_grasp_server_clone_url( | ||
| 1180 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/my-repo.git/" | ||
| 1181 | )); | ||
| 1182 | } | ||
| 1183 | |||
| 1184 | #[test] | ||
| 1185 | fn valid_with_nested_path() { | ||
| 1186 | assert!(is_grasp_server_clone_url( | ||
| 1187 | "https://relay.ngit.dev/path/to/server/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/my-repo.git" | ||
| 1188 | )); | ||
| 1189 | } | ||
| 1190 | |||
| 1191 | #[test] | ||
| 1192 | fn valid_with_port() { | ||
| 1193 | assert!(is_grasp_server_clone_url( | ||
| 1194 | "https://relay.ngit.dev:8080/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/my-repo.git" | ||
| 1195 | )); | ||
| 1196 | } | ||
| 1197 | |||
| 1198 | #[test] | ||
| 1199 | fn invalid_missing_git_extension() { | ||
| 1200 | assert!(!is_grasp_server_clone_url( | ||
| 1201 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/my-repo" | ||
| 1202 | )); | ||
| 1203 | } | ||
| 1204 | |||
| 1205 | #[test] | ||
| 1206 | fn invalid_no_npub() { | ||
| 1207 | assert!(!is_grasp_server_clone_url( | ||
| 1208 | "https://relay.ngit.dev/my-repo.git" | ||
| 1209 | )); | ||
| 1210 | } | ||
| 1211 | |||
| 1212 | #[test] | ||
| 1213 | fn invalid_npub_not_in_path() { | ||
| 1214 | // npub exists but not in the path structure (e.g., in query string or fragment) | ||
| 1215 | assert!(!is_grasp_server_clone_url( | ||
| 1216 | "https://relay.ngit.dev/my-repo.git?npub=npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr" | ||
| 1217 | )); | ||
| 1218 | } | ||
| 1219 | |||
| 1220 | #[test] | ||
| 1221 | fn invalid_wrong_protocol() { | ||
| 1222 | assert!(!is_grasp_server_clone_url( | ||
| 1223 | "ftp://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/my-repo.git" | ||
| 1224 | )); | ||
| 1225 | } | ||
| 1226 | |||
| 1227 | #[test] | ||
| 1228 | fn invalid_no_protocol() { | ||
| 1229 | assert!(!is_grasp_server_clone_url( | ||
| 1230 | "relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/my-repo.git" | ||
| 1231 | )); | ||
| 1232 | } | ||
| 1233 | |||
| 1234 | #[test] | ||
| 1235 | fn invalid_wss_protocol() { | ||
| 1236 | assert!(!is_grasp_server_clone_url( | ||
| 1237 | "wss://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/my-repo.git" | ||
| 1238 | )); | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | #[test] | ||
| 1242 | fn invalid_npub_not_followed_by_slash() { | ||
| 1243 | // npub must be followed by a slash before the repo name | ||
| 1244 | assert!(!is_grasp_server_clone_url( | ||
| 1245 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejrmy-repo.git" | ||
| 1246 | )); | ||
| 1247 | } | ||
| 1248 | |||
| 1249 | #[test] | ||
| 1250 | fn invalid_no_repo_name_after_npub() { | ||
| 1251 | assert!(!is_grasp_server_clone_url( | ||
| 1252 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/.git" | ||
| 1253 | )); | ||
| 1254 | } | ||
| 1255 | |||
| 1256 | #[test] | ||
| 1257 | fn invalid_empty_repo_name() { | ||
| 1258 | assert!(!is_grasp_server_clone_url( | ||
| 1259 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr.git" | ||
| 1260 | )); | ||
| 1261 | } | ||
| 1262 | |||
| 1263 | #[test] | ||
| 1264 | fn invalid_malformed_npub() { | ||
| 1265 | assert!(!is_grasp_server_clone_url( | ||
| 1266 | "https://relay.ngit.dev/npub123invalid/my-repo.git" | ||
| 1267 | )); | ||
| 1268 | } | ||
| 1269 | |||
| 1270 | #[test] | ||
| 1271 | fn valid_repo_name_with_hyphens() { | ||
| 1272 | assert!(is_grasp_server_clone_url( | ||
| 1273 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/my-awesome-repo.git" | ||
| 1274 | )); | ||
| 1275 | } | ||
| 1276 | |||
| 1277 | #[test] | ||
| 1278 | fn valid_repo_name_with_underscores() { | ||
| 1279 | assert!(is_grasp_server_clone_url( | ||
| 1280 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/my_repo.git" | ||
| 1281 | )); | ||
| 1282 | } | ||
| 1283 | |||
| 1284 | #[test] | ||
| 1285 | fn valid_repo_name_with_numbers() { | ||
| 1286 | assert!(is_grasp_server_clone_url( | ||
| 1287 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/repo123.git" | ||
| 1288 | )); | ||
| 1289 | } | ||
| 1290 | } | ||
| 1103 | } | 1291 | } |