Skip to content

Commit 79ba00d

Browse files
authored
Merge pull request #1426 from rylev/handle-merge-conflicts
Handle merge conflicts when unrolling up PR
2 parents 53fb804 + 42d1e9e commit 79ba00d

File tree

2 files changed

+62
-26
lines changed

2 files changed

+62
-26
lines changed

site/src/github.rs

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,31 @@ pub async fn unroll_rollup(
1818
previous_master: &str,
1919
rollup_pr_number: u32,
2020
) -> Result<(), String> {
21+
let format_commit = |s: &str, truncate: bool| {
22+
let display = truncate.then(|| s.split_at(10).0).unwrap_or(s);
23+
format!("[{display}](https://github.com/rust-lang-ci/rust/commit/{s})")
24+
};
2125
let mapping = enqueue_unrolled_try_builds(ci_client, rollup_merges, previous_master)
2226
.await?
2327
.into_iter()
2428
.fold(String::new(), |mut string, c| {
2529
use std::fmt::Write;
26-
write!(
27-
&mut string,
28-
"|#{pr}|[{commit}](https://github.com/rust-lang-ci/rust/commit/{commit})|\n",
29-
pr = c.original_pr_number,
30-
commit = c.sha
31-
)
32-
.unwrap();
30+
let commit = c
31+
.sha
32+
.as_deref()
33+
.map(|s| format_commit(s, false))
34+
.unwrap_or_else(|| {
35+
let head = format_commit(&c.rolled_up_head, true);
36+
format!("❌ conflicts merging '{head}' into previous master ❌")
37+
});
38+
write!(&mut string, "|#{pr}|{commit}|\n", pr = c.original_pr_number).unwrap();
3339
string
3440
});
41+
let previous_master = format_commit(previous_master, true);
3542
let msg =
3643
format!("📌 Perf builds for each rolled up PR:\n\n\
37-
|PR# | Perf Build Sha|\n|----|-----|\n\
38-
{mapping}\nIn the case of a perf regression, \
44+
|PR# | Perf Build Sha|\n|----|:-----:|\n\
45+
{mapping}\n\n*previous master*: {previous_master}\n\nIn the case of a perf regression, \
3946
run the following command for each PR you suspect might be the cause: `@rust-timer build $SHA`");
4047
main_repo_client.post_comment(rollup_pr_number, msg).await;
4148
Ok(())
@@ -76,35 +83,49 @@ async fn enqueue_unrolled_try_builds<'a>(
7683
"What we thought was a merge commit was not a merge commit. sha: {}",
7784
rollup_merge.sha
7885
);
79-
let rolled_up_head = &commit.parents[1].sha;
86+
let rolled_up_head = commit.parents[1].sha.clone();
8087

8188
// Reset perf-tmp to the previous master
8289
client
8390
.update_branch("perf-tmp", previous_master)
8491
.await
8592
.map_err(|e| format!("Error updating perf-tmp with previous master: {e:?}"))?;
8693

87-
// Merge in the rolled up PR's head commit into the previous master
94+
// Try to merge in the rolled up PR's head commit into the previous master
8895
let sha = client
8996
.merge_branch(
9097
"perf-tmp",
91-
rolled_up_head,
92-
&format!("Unrolled build for #{}", original_pr_number),
98+
&rolled_up_head,
99+
&format!("Unrolled build for #{original_pr_number}"),
93100
)
94101
.await
95102
.map_err(|e| {
96103
format!("Error merging #{original_pr_number}'s commit '{rolled_up_head}' into perf-tmp: {e:?}")
97104
})?;
98105

99-
// Force the `try-perf` branch to point to what the perf-tmp branch points to
100-
client
101-
.update_branch("try-perf", &sha)
102-
.await
103-
.map_err(|e| format!("Error updating the try-perf branch: {e:?}"))?;
106+
// Handle success and merge conflicts
107+
match &sha {
108+
Some(s) => {
109+
// Force the `try-perf` branch to point to what the perf-tmp branch points to
110+
client
111+
.update_branch("try-perf", s)
112+
.await
113+
.map_err(|e| format!("Error updating the try-perf branch: {e:?}"))?;
114+
}
115+
None => {
116+
// Merge conflict
117+
log::debug!(
118+
"Could not create unrolled commit for #{original_pr_number}. \
119+
Merging the rolled up HEAD '{rolled_up_head}' into the previous master \
120+
'{previous_master}' leads to a merge conflict."
121+
);
122+
}
123+
};
104124

105125
mapping.push(UnrolledCommit {
106126
original_pr_number,
107127
rollup_merge,
128+
rolled_up_head,
108129
sha,
109130
});
110131
// Wait to ensure there's enough time for GitHub to checkout these changes before they are overwritten
@@ -120,8 +141,10 @@ pub struct UnrolledCommit<'a> {
120141
pub original_pr_number: &'a str,
121142
/// The original rollup merge commit
122143
pub rollup_merge: &'a Commit,
123-
/// The sha of the new unrolled merge commit
124-
pub sha: String,
144+
/// The HEAD commit for the rolled up PR
145+
pub rolled_up_head: String,
146+
/// The sha of the new unrolled merge commit. `None` when creation failed due to merge conflicts.
147+
pub sha: Option<String>,
125148
}
126149

127150
lazy_static::lazy_static! {

site/src/github/client.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,15 @@ impl Client {
107107
Ok(())
108108
}
109109

110+
/// Merge the given sha into the given branch with the given commit message
111+
///
112+
/// Returns `None` if the sha cannot be merged due to a merge conflict.
110113
pub async fn merge_branch(
111114
&self,
112115
branch: &str,
113116
sha: &str,
114117
commit_message: &str,
115-
) -> anyhow::Result<String> {
118+
) -> anyhow::Result<Option<String>> {
116119
#[derive(serde::Serialize)]
117120
struct MergeBranchRequest<'a> {
118121
base: &'a str,
@@ -125,12 +128,22 @@ impl Client {
125128
head: sha,
126129
commit_message,
127130
});
128-
let response = self.send(req).await.context("PATCH /merges failed")?;
129-
if !response.status().is_success() {
130-
anyhow::bail!("{:?} != 201 CREATED", response.status());
131-
}
131+
let response = self
132+
.send(req)
133+
.await
134+
.context("POST /merges failed to send")?;
132135

133-
Ok(response.json::<MergeBranchResponse>().await?.sha)
136+
if response.status() == 409 {
137+
// Return `None` on merge conflicts which are signaled by 409s
138+
Ok(None)
139+
} else if !response.status().is_success() {
140+
Err(anyhow::format_err!(
141+
"response has non-successful status: {:?} ",
142+
response.status()
143+
))
144+
} else {
145+
Ok(Some(response.json::<MergeBranchResponse>().await?.sha))
146+
}
134147
}
135148

136149
pub async fn create_commit(

0 commit comments

Comments
 (0)