Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T03:32:40.203Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

remote_output.rs

356 lines · 12.7 KB · rust
1use anyhow::Context as _;
2
3use git::repository::{Remote, RemoteCommandOutput};
4use ui::SharedString;
5use util::ResultExt as _;
6
7const PULL_REQUEST_HINTS: &[(&str, &str)] = &[
8    // GitHub: "Create a pull request for 'branch' on GitHub by visiting:"
9    ("Create a pull request", "Create Pull Request"),
10    // Bitbucket: "Create pull request for branch:"
11    ("Create pull request", "Create Pull Request"),
12    // GitLab: "To create a merge request for branch, visit:"
13    ("create a merge request", "Create Merge Request"),
14    // GitLab: "View merge request for branch:"
15    ("View merge request", "View Merge Request"),
16];
17
18#[derive(Clone)]
19pub enum RemoteAction {
20    Fetch(Option<Remote>),
21    Pull(Remote),
22    Push(SharedString, Remote),
23}
24
25impl RemoteAction {
26    pub fn name(&self) -> &'static str {
27        match self {
28            RemoteAction::Fetch(_) => "fetch",
29            RemoteAction::Pull(_) => "pull",
30            RemoteAction::Push(_, _) => "push",
31        }
32    }
33}
34
35pub enum SuccessStyle {
36    Toast,
37    ToastWithLog { output: RemoteCommandOutput },
38    PushPrLink { label: &'static str, url: String },
39}
40
41pub struct SuccessMessage {
42    pub message: String,
43    pub style: SuccessStyle,
44}
45
46fn extract_pull_request_link(output: &RemoteCommandOutput) -> Option<(&'static str, String)> {
47    let mut pending_label: Option<&'static str> = None;
48
49    for line in output.stderr.lines() {
50        let Some(remote_line) = line.trim_start().strip_prefix("remote:") else {
51            pending_label = None;
52            continue;
53        };
54
55        if let Some((_, label)) = PULL_REQUEST_HINTS
56            .iter()
57            .find(|(hint, _)| remote_line.contains(hint))
58        {
59            pending_label = Some(label);
60        }
61
62        if let Some(url) = extract_url(remote_line)
63            && let Some(label) = pending_label
64        {
65            return Some((label, url));
66        }
67    }
68
69    None
70}
71
72fn extract_url(line: &str) -> Option<String> {
73    let http_index = line.find("https://").or_else(|| line.find("http://"))?;
74    let url = line[http_index..]
75        .split_whitespace()
76        .next()?
77        .trim_end_matches(|character| matches!(character, ',' | '.' | ')' | ']' | '>'));
78
79    Some(url.to_string())
80}
81
82pub fn format_output(action: &RemoteAction, output: RemoteCommandOutput) -> SuccessMessage {
83    match action {
84        RemoteAction::Fetch(remote) => {
85            if output.stderr.is_empty() {
86                SuccessMessage {
87                    message: "Fetch: Already up to date".into(),
88                    style: SuccessStyle::Toast,
89                }
90            } else {
91                let message = match remote {
92                    Some(remote) => format!("Synchronized with {}", remote.name),
93                    None => "Synchronized with remotes".into(),
94                };
95                SuccessMessage {
96                    message,
97                    style: SuccessStyle::ToastWithLog { output },
98                }
99            }
100        }
101        RemoteAction::Pull(remote_ref) => {
102            let get_changes = |output: &RemoteCommandOutput| -> anyhow::Result<u32> {
103                let last_line = output
104                    .stdout
105                    .lines()
106                    .last()
107                    .context("Failed to get last line of output")?
108                    .trim();
109
110                let files_changed = last_line
111                    .split_whitespace()
112                    .next()
113                    .context("Failed to get first word of last line")?
114                    .parse()?;
115
116                Ok(files_changed)
117            };
118            if output.stdout.ends_with("Already up to date.\n") {
119                SuccessMessage {
120                    message: "Pull: Already up to date".into(),
121                    style: SuccessStyle::Toast,
122                }
123            } else if output.stdout.starts_with("Updating") {
124                let files_changed = get_changes(&output).log_err();
125                let message = if let Some(files_changed) = files_changed {
126                    format!(
127                        "Received {} file change{} from {}",
128                        files_changed,
129                        if files_changed == 1 { "" } else { "s" },
130                        remote_ref.name
131                    )
132                } else {
133                    format!("Fast forwarded from {}", remote_ref.name)
134                };
135                SuccessMessage {
136                    message,
137                    style: SuccessStyle::ToastWithLog { output },
138                }
139            } else if output.stdout.starts_with("Merge") {
140                let files_changed = get_changes(&output).log_err();
141                let message = if let Some(files_changed) = files_changed {
142                    format!(
143                        "Merged {} file change{} from {}",
144                        files_changed,
145                        if files_changed == 1 { "" } else { "s" },
146                        remote_ref.name
147                    )
148                } else {
149                    format!("Merged from {}", remote_ref.name)
150                };
151                SuccessMessage {
152                    message,
153                    style: SuccessStyle::ToastWithLog { output },
154                }
155            } else if output.stdout.contains("Successfully rebased") {
156                SuccessMessage {
157                    message: format!("Successfully rebased from {}", remote_ref.name),
158                    style: SuccessStyle::ToastWithLog { output },
159                }
160            } else {
161                SuccessMessage {
162                    message: format!("Successfully pulled from {}", remote_ref.name),
163                    style: SuccessStyle::ToastWithLog { output },
164                }
165            }
166        }
167        RemoteAction::Push(branch_name, remote_ref) => {
168            if output.stderr.ends_with("Everything up-to-date\n") {
169                SuccessMessage {
170                    message: "Push: Everything is up-to-date".to_string(),
171                    style: SuccessStyle::Toast,
172                }
173            } else if let Some((label, url)) = extract_pull_request_link(&output) {
174                SuccessMessage {
175                    message: format!("Pushed {} to {}", branch_name, remote_ref.name),
176                    style: SuccessStyle::PushPrLink { label, url },
177                }
178            } else {
179                SuccessMessage {
180                    message: format!("Pushed {} to {}", branch_name, remote_ref.name),
181                    style: SuccessStyle::ToastWithLog { output },
182                }
183            }
184        }
185    }
186}
187
188#[cfg(test)]
189mod tests {
190    use super::*;
191    use indoc::indoc;
192
193    #[test]
194    fn test_push_new_branch_pull_request() {
195        let action = RemoteAction::Push(
196            SharedString::new_static("test_branch"),
197            Remote {
198                name: SharedString::new_static("test_remote"),
199            },
200        );
201
202        let output = RemoteCommandOutput {
203            stdout: String::new(),
204            stderr: indoc! { "
205                Total 0 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
206                remote:
207                remote: Create a pull request for 'test' on GitHub by visiting:
208                remote:      https://example.com/test/test/pull/new/test
209                remote:
210                To example.com:test/test.git
211                 * [new branch]      test -> test
212                "}
213            .to_string(),
214        };
215
216        let msg = format_output(&action, output);
217        if let SuccessStyle::PushPrLink { label, url } = msg.style {
218            assert_eq!(msg.message, "Pushed test_branch to test_remote");
219            assert_eq!(label, "Create Pull Request");
220            assert_eq!(url, "https://example.com/test/test/pull/new/test");
221        } else {
222            panic!("Expected PushPrLink variant");
223        }
224    }
225
226    #[test]
227    fn test_push_new_branch_merge_request() {
228        let action = RemoteAction::Push(
229            SharedString::new_static("test_branch"),
230            Remote {
231                name: SharedString::new_static("test_remote"),
232            },
233        );
234
235        let output = RemoteCommandOutput {
236            stdout: String::new(),
237            stderr: indoc! {"
238                Total 0 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
239                remote:
240                remote: To create a merge request for test, visit:
241                remote:   https://example.com/test/test/-/merge_requests/new?merge_request%5Bsource_branch%5D=test
242                remote:
243                To example.com:test/test.git
244                 * [new branch]      test -> test
245                "}
246            .to_string()
247            };
248
249        let msg = format_output(&action, output);
250
251        if let SuccessStyle::PushPrLink { label, url } = msg.style {
252            assert_eq!(msg.message, "Pushed test_branch to test_remote");
253            assert_eq!(label, "Create Merge Request");
254            assert_eq!(
255                url,
256                "https://example.com/test/test/-/merge_requests/new?merge_request%5Bsource_branch%5D=test"
257            )
258        } else {
259            panic!("Expected PushPrLink variant")
260        }
261    }
262
263    #[test]
264    fn test_push_new_branch_bitbucket_pull_request() {
265        let output = RemoteCommandOutput {
266            stdout: String::new(),
267            stderr: indoc! {"
268                remote:
269                remote: Create pull request for test:
270                remote:   https://bitbucket.example.com/projects/TEST/repos/test/pull-requests?create&sourceBranch=refs/heads/test
271                "}
272            .to_string(),
273        };
274
275        assert_eq!(
276            extract_pull_request_link(&output),
277            Some((
278                "Create Pull Request",
279                "https://bitbucket.example.com/projects/TEST/repos/test/pull-requests?create&sourceBranch=refs/heads/test".to_string()
280            ))
281        );
282    }
283
284    #[test]
285    fn test_push_branch_existing_merge_request() {
286        let action = RemoteAction::Push(
287            SharedString::new_static("test_branch"),
288            Remote {
289                name: SharedString::new_static("test_remote"),
290            },
291        );
292
293        let output = RemoteCommandOutput {
294            stdout: String::new(),
295            // Include an unrelated URL outside of the `remote:` lines, in this
296            // case, an OpenSSH warning, to ensure that it is not mistaken for
297            // the merge request link.
298            stderr: indoc! {"
299                ** WARNING: connection is not using a post-quantum key exchange algorithm.
300                ** This session may be vulnerable to \"store now, decrypt later\" attacks.
301                ** The server may need to be upgraded. See https://openssh.com/pq.html
302                Total 0 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
303                remote:
304                remote: View merge request for test:
305                remote:    https://example.com/test/test/-/merge_requests/99999
306                remote:
307                To example.com:test/test.git
308                    + 80bd3c83be...e03d499d2e test -> test
309                "}
310            .to_string(),
311        };
312
313        let msg = format_output(&action, output);
314
315        if let SuccessStyle::PushPrLink { label, url } = msg.style {
316            assert_eq!(msg.message, "Pushed test_branch to test_remote");
317            assert_eq!(label, "View Merge Request");
318            assert_eq!(url, "https://example.com/test/test/-/merge_requests/99999");
319        } else {
320            panic!("Expected PushPrLink variant")
321        }
322    }
323
324    #[test]
325    fn test_push_new_branch_no_link() {
326        let action = RemoteAction::Push(
327            SharedString::new_static("test_branch"),
328            Remote {
329                name: SharedString::new_static("test_remote"),
330            },
331        );
332
333        let output = RemoteCommandOutput {
334            stdout: String::new(),
335            stderr: indoc! { "
336                To http://example.com/test/test.git
337                 * [new branch]      test -> test
338                ",
339            }
340            .to_string(),
341        };
342
343        let msg = format_output(&action, output);
344
345        if let SuccessStyle::ToastWithLog { output } = &msg.style {
346            assert_eq!(
347                output.stderr,
348                "To http://example.com/test/test.git\n * [new branch]      test -> test\n"
349            );
350            assert_eq!(extract_pull_request_link(output), None);
351        } else {
352            panic!("Expected ToastWithLog variant");
353        }
354    }
355}
356
Served at tenant.openagents/omega Member data and write actions are omitted.