Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 0 additions & 27 deletions site/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -619,33 +619,6 @@ pub mod github {
issue: Issue,
comment: Comment,
},
Push(Push),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Push {
pub r#ref: String,
pub head_commit: HeadCommit,
pub before: String,
pub commits: Vec<Commit>,
pub repository: Repository,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Repository {
pub default_branch: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Commit {
#[serde(rename = "id")]
pub sha: String,
pub message: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HeadCommit {
pub message: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down
207 changes: 0 additions & 207 deletions site/src/github.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
pub mod client;
pub mod comparison_summary;

use crate::api::github::Commit;
use crate::job_queue::build_queue;
use crate::load::{SiteCtxt, TryCommit};
use chrono::Utc;
use serde::Deserialize;
use std::sync::LazyLock;
use std::time::Duration;

type BoxedError = Box<dyn std::error::Error + Send + Sync>;
Expand All @@ -19,213 +17,8 @@ pub const RUST_REPO_GITHUB_API_URL: &str = "https://api.github.com/repos/rust-la
/// They are removed once a perf. run comparison summary is posted on a PR.
pub const COMMENT_MARK_TEMPORARY: &str = "<!-- rust-timer: temporary -->";

/// Used for comment that contains unrolled commits for merged rolled-up PRs.
pub const COMMENT_MARK_ROLLUP: &str = "<!-- rust-timer: rollup -->";

use database::{BenchmarkJobStatus, BenchmarkRequestStatus, Connection};

/// Enqueues try build artifacts and posts a message about them on the original rollup PR
pub async fn unroll_rollup(
gh_client: client::Client,
rollup_merges: impl Iterator<Item = &Commit>,
previous_master: &str,
rollup_pr_number: u32,
) -> Result<(), String> {
let commit_link = |sha: &str| format!("https://github.com/rust-lang/rust/commit/{sha}");

let format_commit = |s: &str, truncate: bool| {
let display = if truncate { s.split_at(10).0 } else { s };
format!("[{display}]({})", commit_link(s))
};

let mapping = enqueue_unrolled_try_builds(&gh_client, rollup_merges, previous_master)
.await?
.into_iter()
.fold(String::new(), |mut string, c| {
use std::fmt::Write;
let commit = c
.sha
.as_deref()
.map(|s| {
// Format the SHA as a code block to make it easy to copy-paste verbatim
let link = commit_link(s);
format!("`{s}` ([link]({link}))")
})
.unwrap_or_else(|| {
let head = format_commit(&c.rolled_up_head, true);
format!("❌ conflicts merging '{head}' into previous master ❌")
});
let message = c
.rollup_merge
.message
.split('\n')
// Skip over "Rollup merge of ..." and an empty line
.nth(2)
.map(|m| {
if m.len() <= 60 {
m.to_string()
} else {
format!("{}…", m.split_at(59).0)
}
})
.unwrap_or_else(|| format!("#{}", c.original_pr_number))
.replace('|', "\\|");
writeln!(
&mut string,
"|#{pr}|{message}|{commit}|",
pr = c.original_pr_number
)
.unwrap();
string
});
let previous_master = format_commit(previous_master, true);
let msg =
format!("📌 Perf builds for each rolled up PR:\n\n\
| PR# | Message | Perf Build Sha |\n|----|----|:-----:|\n\
{mapping}\n\n*previous master*: {previous_master}\n\nIn the case of a perf regression, \
run the following command for each PR you suspect might be the cause: `@rust-timer build $SHA`\n\
{COMMENT_MARK_ROLLUP}");
gh_client.post_comment(rollup_pr_number, msg).await;
Ok(())
}

/// Enqueues try builds on the try-perf branch for every rollup merge in `rollup_merges`.
/// Returns a mapping between the rollup merge commit and the try build sha.
async fn enqueue_unrolled_try_builds<'a>(
client: &client::Client,
rollup_merges: impl Iterator<Item = &'a Commit>,
previous_master: &str,
) -> Result<Vec<UnrolledCommit<'a>>, String> {
let mut mapping = Vec::new();
for rollup_merge in rollup_merges {
// Grab the number of the rolled up PR from its commit message
let original_pr_number = ROLLEDUP_PR_NUMBER
.captures(&rollup_merge.message)
.and_then(|c| c.get(1))
.map(|m| m.as_str())
.ok_or_else(|| {
format!(
"Could not get PR number from message: '{}'",
rollup_merge.message
)
})?;

// Fetch the rollup merge commit which should have two parents.
// The first parent is in the chain of rollup merge commits all the way back to `previous_master`.
// The second parent is the head of the PR that was rolled up. We want the second parent.
let commit = client.get_commit(&rollup_merge.sha).await.map_err(|e| {
format!(
"Error getting rollup merge commit '{}': {e:?}",
rollup_merge.sha
)
})?;
assert!(
commit.parents.len() == 2,
"What we thought was a merge commit was not a merge commit. sha: {}",
rollup_merge.sha
);
let rolled_up_head = commit.parents[1].sha.clone();

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

// Try to merge in the rolled up PR's head commit into the previous master
let sha = client
.merge_branch(
"perf-tmp",
&rolled_up_head,
&format!("Unrolled build for #{original_pr_number}\n{}", rollup_merge.message),
)
.await
.map_err(|e| {
format!("Error merging #{original_pr_number}'s commit '{rolled_up_head}' into perf-tmp: {e:?}")
})?;

// Handle success and merge conflicts
match &sha {
Some(s) => {
// Force the `try-perf` branch to point to what the perf-tmp branch points to
client
.update_branch("try-perf", s)
.await
.map_err(|e| format!("Error updating the try-perf branch: {e:?}"))?;
}
None => {
// Merge conflict
log::debug!(
"Could not create unrolled commit for #{original_pr_number}. \
Merging the rolled up HEAD '{rolled_up_head}' into the previous master \
'{previous_master}' leads to a merge conflict."
);
}
};

mapping.push(UnrolledCommit {
original_pr_number,
rollup_merge,
rolled_up_head,
sha,
});
// Wait to ensure there's enough time for GitHub to checkout these changes before they are overwritten
tokio::time::sleep(std::time::Duration::from_secs(15)).await
}

Ok(mapping)
}

/// A commit representing a rolled up PR as if it had been merged into master directly
pub struct UnrolledCommit<'a> {
/// The PR number that was rolled up
pub original_pr_number: &'a str,
/// The original rollup merge commit
pub rollup_merge: &'a Commit,
/// The HEAD commit for the rolled up PR
pub rolled_up_head: String,
/// The sha of the new unrolled merge commit. `None` when creation failed due to merge conflicts.
pub sha: Option<String>,
}

static ROLLUP_PR_NUMBER: LazyLock<regex::Regex> =
LazyLock::new(|| regex::Regex::new(r"^Auto merge of #(\d+)").unwrap());
static ROLLEDUP_PR_NUMBER: LazyLock<regex::Regex> =
LazyLock::new(|| regex::Regex::new(r"^Rollup merge of #(\d+)").unwrap());

// Gets the pr number for the associated rollup PR message. Returns None if this is not a rollup PR
pub async fn rollup_pr_number(
client: &client::Client,
message: &str,
) -> Result<Option<u32>, String> {
if !message.starts_with("Auto merge of") {
return Ok(None);
}

let number = ROLLUP_PR_NUMBER
.captures(message)
.and_then(|c| c.get(1))
.map(|m| m.as_str().parse::<u64>())
.transpose()
.map_err(|e| format!("Error parsing PR number from '{message}': {e:?}"))?;

let number = match number {
Some(n) => n,
None => return Ok(None),
};

let issue = client
.get_issue(number)
.await
.map_err(|e| format!("Error fetching PR #{number} {e:?}"))?;

Ok(issue
.labels
.iter()
.any(|l| l.name == "rollup")
.then_some(issue.number))
}

/// Enqueues the given SHA and returns a message that should be sent as a comment to the corresponding PR.
/// If not benchmark reques was found to which the commit SHA could be attached, returns `Ok(None)`.
pub async fn enqueue_sha(
Expand Down
Loading
Loading