Skip to repository content356 lines · 12.7 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T03:32:40.203Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
remote_output.rs
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