Skip to content

Commit 0542c90

Browse files
authored
Merge pull request #2159 from Urgau/gh-range-diff-all-range
Add full ranges support for our range-diff feature
2 parents 9473f65 + ca97826 commit 0542c90

File tree

3 files changed

+119
-15
lines changed

3 files changed

+119
-15
lines changed

src/gh_range_diff.rs

Lines changed: 112 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use imara_diff::{
2020
use pulldown_cmark_escape::FmtWriter;
2121
use regex::Regex;
2222

23+
use crate::github::GithubCompare;
2324
use crate::{github, handlers::Context, utils::AppError};
2425

2526
static MARKER_RE: LazyLock<Regex> =
@@ -31,7 +32,7 @@ static MARKER_RE: LazyLock<Regex> =
3132
pub async fn gh_range_diff(
3233
Path((owner, repo, basehead)): Path<(String, String, String)>,
3334
State(ctx): State<Arc<Context>>,
34-
Host(host): Host,
35+
host: Host,
3536
) -> axum::response::Result<impl IntoResponse, AppError> {
3637
let Some((oldhead, newhead)) = basehead.split_once("..") else {
3738
return Ok((
@@ -41,9 +42,6 @@ pub async fn gh_range_diff(
4142
));
4243
};
4344

44-
// Configure unified diff
45-
let config = UnifiedDiffConfig::default();
46-
4745
let repos = ctx
4846
.team
4947
.repos()
@@ -96,18 +94,14 @@ pub async fn gh_range_diff(
9694
.sha;
9795

9896
// Get the comparison between the oldbase..oldhead
99-
let mut old = ctx
97+
let old = ctx
10098
.github
10199
.compare(&issue_repo, &oldbase, oldhead)
102100
.await
103101
.with_context(|| {
104102
format!("failed to retrive the comparison between {oldbase} and {oldhead}")
105103
})?;
106104

107-
// Sort by filename, so it's consistent with GitHub UI
108-
old.files
109-
.sort_unstable_by(|f1, f2| f1.filename.cmp(&f2.filename));
110-
111105
anyhow::Result::<_>::Ok((oldbase, old))
112106
};
113107

@@ -125,24 +119,128 @@ pub async fn gh_range_diff(
125119
.sha;
126120

127121
// Get the comparison between the newbase..newhead
128-
let mut new = ctx
122+
let new = ctx
129123
.github
130124
.compare(&issue_repo, &newbase, newhead)
131125
.await
132126
.with_context(|| {
133127
format!("failed to retrive the comparison between {newbase} and {newhead}")
134128
})?;
135129

136-
// Sort by filename, so it's consistent with GitHub UI
137-
new.files
138-
.sort_unstable_by(|f1, f2| f1.filename.cmp(&f2.filename));
139-
140130
anyhow::Result::<_>::Ok((newbase, new))
141131
};
142132

143133
// Wait for both futures and early exit if there is an error
144134
let ((oldbase, old), (newbase, new)) = futures::try_join!(old, new)?;
145135

136+
process_old_new(
137+
host,
138+
(&owner, &repo),
139+
(&oldbase, oldhead, old),
140+
(&newbase, newhead, new),
141+
)
142+
}
143+
144+
/// Compute and renders an emulated `git range-diff` between two pushes (old and new).
145+
///
146+
/// - `oldbasehead` is `OLDBASE..OLDHEAD`
147+
/// - `newbasehead` is `NEWBASE..NEWHEAD`
148+
pub async fn gh_ranges_diff(
149+
Path((owner, repo, oldbasehead, newbasehead)): Path<(String, String, String, String)>,
150+
State(ctx): State<Arc<Context>>,
151+
host: Host,
152+
) -> axum::response::Result<impl IntoResponse, AppError> {
153+
let Some((oldbase, oldhead)) = oldbasehead.split_once("..") else {
154+
return Ok((
155+
StatusCode::BAD_REQUEST,
156+
HeaderMap::new(),
157+
format!("`{oldbasehead}` is not in the form `base..head`"),
158+
));
159+
};
160+
161+
let Some((newbase, newhead)) = newbasehead.split_once("..") else {
162+
return Ok((
163+
StatusCode::BAD_REQUEST,
164+
HeaderMap::new(),
165+
format!("`{newbasehead}` is not in the form `base..head`"),
166+
));
167+
};
168+
169+
let repos = ctx
170+
.team
171+
.repos()
172+
.await
173+
.context("unable to retrieve team repos")?;
174+
175+
// Verify that the request org is part of the Rust project
176+
let Some(repos) = repos.repos.get(&owner) else {
177+
return Ok((
178+
StatusCode::BAD_REQUEST,
179+
HeaderMap::new(),
180+
format!("organization `{owner}` is not part of the Rust Project team repos"),
181+
));
182+
};
183+
184+
// Verify that the request repo is part of the Rust project
185+
if !repos.iter().any(|r| r.name == repo) {
186+
return Ok((
187+
StatusCode::BAD_REQUEST,
188+
HeaderMap::new(),
189+
format!("repository `{owner}` is not part of the Rust Project team repos"),
190+
));
191+
}
192+
193+
let issue_repo = github::IssueRepository {
194+
organization: owner.to_string(),
195+
repository: repo.to_string(),
196+
};
197+
198+
// Get the comparison between the oldbase..oldhead
199+
let old = async {
200+
ctx.github
201+
.compare(&issue_repo, &oldbase, oldhead)
202+
.await
203+
.with_context(|| {
204+
format!("failed to retrive the comparison between {oldbase} and {oldhead}")
205+
})
206+
};
207+
208+
// Get the comparison between the newbase..newhead
209+
let new = async {
210+
ctx.github
211+
.compare(&issue_repo, &newbase, newhead)
212+
.await
213+
.with_context(|| {
214+
format!("failed to retrive the comparison between {newbase} and {newhead}")
215+
})
216+
};
217+
218+
// Wait for both futures and early exit if there is an error
219+
let (old, new) = futures::try_join!(old, new)?;
220+
221+
process_old_new(
222+
host,
223+
(&owner, &repo),
224+
(&oldbase, oldhead, old),
225+
(&newbase, newhead, new),
226+
)
227+
}
228+
229+
fn process_old_new(
230+
Host(host): Host,
231+
(owner, repo): (&str, &str),
232+
(oldbase, oldhead, mut old): (&str, &str, GithubCompare),
233+
(newbase, newhead, mut new): (&str, &str, GithubCompare),
234+
) -> axum::response::Result<(StatusCode, HeaderMap, String), AppError> {
235+
// Configure unified diff
236+
let config = UnifiedDiffConfig::default();
237+
238+
// Sort by filename, so it's consistent with GitHub UI
239+
old.files
240+
.sort_unstable_by(|f1, f2| f1.filename.cmp(&f2.filename));
241+
new.files
242+
.sort_unstable_by(|f1, f2| f1.filename.cmp(&f2.filename));
243+
146244
// Create the HTML buffer with a very rough approximation for the capacity
147245
let mut html: String = String::with_capacity(800 + old.files.len() * 100);
148246

src/handlers/check_commits/force_push_range_diff.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,12 @@ pub(super) async fn handle_event(
5151
};
5252

5353
let branch = &base.git_ref;
54+
let (oldbase, oldhead) = (&compare_before.merge_base_commit.sha, before);
55+
let (newbase, newhead) = (&compare_after.merge_base_commit.sha, after);
5456

5557
// Rebase detected, post a comment to our range-diff.
5658
event.issue.post_comment(&ctx.github,
57-
&format!(r#"This PR was rebased onto a different {branch} commit! Check out the changes with our [`range-diff`]({protocol}://{host}/gh-range-diff/{org}/{repo}/{before}..{after})."#)
59+
&format!(r#"This PR was rebased onto a different {branch} commit! Check out the changes with our [`range-diff`]({protocol}://{host}/gh-range-diff/{org}/{repo}/{oldbase}..{oldhead}/{newbase}..{newhead})."#)
5860
).await.context("failed to post range-diff comment")?;
5961

6062
Ok(())

src/main.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,10 @@ async fn run_server(addr: SocketAddr) -> anyhow::Result<()> {
189189
"/gh-range-diff/{owner}/{repo}/{basehead}",
190190
get(triagebot::gh_range_diff::gh_range_diff),
191191
)
192+
.route(
193+
"/gh-range-diff/{owner}/{repo}/{oldbasehead}/{newbasehead}",
194+
get(triagebot::gh_range_diff::gh_ranges_diff),
195+
)
192196
.nest("/agenda", agenda)
193197
.route("/bors-commit-list", get(triagebot::bors::bors_commit_list))
194198
.route(

0 commit comments

Comments
 (0)