Skip to content

Commit

Permalink
wd
Browse files Browse the repository at this point in the history
  • Loading branch information
TroyKomodo committed Dec 16, 2024
1 parent f2f9e76 commit e4be781
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 31 deletions.
6 changes: 6 additions & 0 deletions .github/brawl.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ try_branch_prefix = "automation/brawl/try/"
# Like: `automation/brawl/merge/main`
merge_branch_prefix = "automation/brawl/merge/"

# The branch prefix for temporary branches created when performing merges
# Will append the target branch name to the end of the prefix:
# `automation/brawl/temp/<uuid>`
# Like: `automation/brawl/temp/79570502-0e92-4c6c-87fe-636b2490c77d`
temp_branch_prefix = "automation/brawl/temp/"

# By default only users with `write` access can merge PRs
merge_permissions = [
"role:write", # include anyone with write access
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,4 @@ hmac = "0.12.1"
sha2 = "0.10.6"
toml = "0.8.19"
thiserror = "2.0.6"
uuid = "1.5.0"
4 changes: 4 additions & 0 deletions server/src/github/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ pub struct GitHubBrawlQueueConfig {
/// "automation/brawl/merge/")
#[default("automation/brawl/merge/")]
pub merge_branch_prefix: String,
/// The branch prefix for temp branches used when performing merges (default:
/// "automation/brawl/temp/")
#[default("automation/brawl/temp/")]
pub temp_branch_prefix: String,
/// The permissions required to merge a PR (default: ["role:write"])
#[default(vec![Permission::Role(Role::Push)])]
pub merge_permissions: Vec<Permission>,
Expand Down
58 changes: 57 additions & 1 deletion server/src/github/installation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use octocrab::models::pulls::PullRequest;
use octocrab::models::repos::{CommitAuthor, Object, Ref, RepoCommit};
use octocrab::models::{Installation, InstallationRepositories, Repository, RepositoryId, UserId, UserProfile};
use octocrab::params::repos::Reference;
use octocrab::{GitHubError, Octocrab};
use octocrab::{params, GitHubError, Octocrab};
use parking_lot::Mutex;

use super::config::{GitHubBrawlRepoConfig, Permission, Role};
Expand Down Expand Up @@ -379,6 +379,39 @@ impl<'a> RepoClient<'a> {
.context("get commit")
}

pub async fn create_merge(
&self,
message: &str,
tmp_branch_prefix: &str,
base_sha: &str,
head_sha: &str,
) -> anyhow::Result<Commit> {
let repo = self.get()?;

let tmp_branch = format!("{}/{}", tmp_branch_prefix.trim_end_matches('/'), uuid::Uuid::new_v4());

self.push_branch(&tmp_branch, base_sha, true).await.context("push tmp branch")?;

let commit = self.installation
.client()
.post::<_, Commit>(
format!("/repos/{}/{}/merges", repo.owner.unwrap().login, repo.name),
Some(&serde_json::json!({
"base": tmp_branch,
"head": head_sha,
"commit_message": message,
})),
)
.await
.context("create commit");

if let Err(e) = self.delete_branch(&tmp_branch).await {
tracing::error!("failed to delete tmp branch: {:#}", e);
}

commit
}

pub async fn create_commit(
&self,
message: String,
Expand Down Expand Up @@ -445,6 +478,15 @@ impl<'a> RepoClient<'a> {
Ok(())
}

pub async fn delete_branch(&self, branch: &str) -> anyhow::Result<()> {
self.installation
.client()
.repos_by_id(self.repo_id)
.delete_ref(&Reference::Branch(branch.to_owned()))
.await
.context("delete branch")
}

pub async fn get_commit_by_sha(&self, sha: &str) -> anyhow::Result<Option<Commit>> {
let repo = self.get()?;

Expand All @@ -468,6 +510,20 @@ impl<'a> RepoClient<'a> {
Err(e) => Err(e).context("get commit by sha"),
}
}

pub async fn get_ref(&self, gh_ref: &params::repos::Reference) -> anyhow::Result<Option<Ref>> {
match self.installation.client().repos_by_id(self.repo_id).get_ref(gh_ref).await {
Ok(r) => Ok(Some(r)),
Err(octocrab::Error::GitHub {
source: GitHubError {
status_code: http::StatusCode::NOT_FOUND,
..
},
..
}) => Ok(None),
Err(e) => Err(e).context("get ref"),
}
}
}

fn is_object_sha(object: &Object, sha: &str) -> bool {
Expand Down
79 changes: 49 additions & 30 deletions server/src/github/webhook/command/dry_run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use std::sync::Arc;
use anyhow::Context;
use diesel_async::AsyncPgConnection;
use octocrab::models::pulls::PullRequest;
use octocrab::models::repos::Object;
use octocrab::params::repos::Reference;

use super::utils::commit_link;
use super::BrawlCommandContext;
Expand Down Expand Up @@ -98,23 +100,57 @@ pub async fn handle(
};

let head = repo_client.get_commit(head_sha).await.context("get head commit")?;
let base = if let Some(base_sha) = &command.base_sha {
Some(repo_client.get_commit(base_sha).await.context("get base commit")?)
} else if let Some(pr) = &context.pr {
let Some(gh_ref) = repo_client.get_ref(&Reference::Branch(pr.base.ref_field.clone())).await? else {
anyhow::bail!("head ref not found");
};

let base_sha = match gh_ref.object {
Object::Commit { sha, .. } => sha,
Object::Tag { sha, .. } => sha,
r => anyhow::bail!("head ref object is not a commit or tag: {:?}", r),
};

Some(repo_client.get_commit(&base_sha).await.context("get base commit")?)
} else {
None
};

let commit_sha = if let Some(base) = base {
let mut base_link = commit_link(&repo_owner.login, &repo.name, &base.sha);
let mut head_link = head_link.clone();
if let Some(pr) = &context.pr {
base_link = format!("{base_link} ({})", pr.base.label.as_ref().map(|l| l.as_str()).unwrap_or(&pr.base.ref_field));
head_link = format!("{head_link} ({})", pr.head.label.as_ref().map(|l| l.as_str()).unwrap_or(&pr.head.ref_field));
}

let mut items = vec![];

if let Some(pr) = &context.pr {
items.push(format!("{}", pr.head.label.as_ref().map(|l| l.as_str()).unwrap_or(&pr.head.ref_field)));
}

items.push(format!("r={user}", user = context.user.login.to_lowercase()));

let commit = if let Some(base_sha) = command.base_sha {
repo_client
.create_commit(
format!(
"Dry run from #{issue} - r={user}\n\nTrying commit: {head_link} into {base_link}",
.create_merge(
&format!(
"Dry run from #{issue} - {items}\n\nTrying commit: {head_link} into {base_link}",
issue = context.issue_number,
user = context.user.login.to_lowercase(),
items = items.join(", "),
head_link = head_link,
base_link = commit_link(&repo_owner.login, &repo.name, &base_sha),
base_link = base_link,
),
vec![head_sha.to_owned(), base_sha.to_owned()],
head.commit.tree.sha,
&context.config.queue.temp_branch_prefix,
&base.sha,
&head_sha,
)
.await
.context("create commit")?
} else if command.head_sha.is_some() {
.context("create merge")?
.sha
} else {
repo_client
.create_commit(
format!(
Expand All @@ -128,24 +164,7 @@ pub async fn handle(
)
.await
.context("create commit")?
} else if let Some(pr) = &context.pr {
repo_client
.create_commit(
format!(
"Dry run from #{issue} - {head}, r={user}\n\nTrying commit: {head_link} into {base_link}",
issue = context.issue_number,
head = pr.head.ref_field,
user = context.user.login.to_lowercase(),
head_link = head_link,
base_link = commit_link(&repo_owner.login, &repo.name, &pr.base.sha),
),
vec![head_sha.to_owned(), pr.base.sha.clone()],
head.commit.tree.sha,
)
.await
.context("create commit")?
} else {
anyhow::bail!("no commit sha provided");
.sha
};

let prefix = context.config.queue.try_branch_prefix.trim_end_matches('/');
Expand All @@ -157,7 +176,7 @@ pub async fn handle(

let branch = format!("{}/{}", prefix, context.issue_number);

if let Err(err) = repo_client.push_branch(&branch, &commit.sha, true).await {
if let Err(err) = repo_client.push_branch(&branch, &commit_sha, true).await {
tracing::error!("push branch failed: {:#}", err);
repo_client
.send_message(
Expand All @@ -174,7 +193,7 @@ pub async fn handle(
format!(
"⌛ Testing commit {} with merge {}...",
commit_link(&repo_owner.login, &repo.name, head_sha),
commit_link(&repo_owner.login, &repo.name, &commit.sha),
commit_link(&repo_owner.login, &repo.name, &commit_sha),
),
)
.await?;
Expand Down

0 comments on commit e4be781

Please sign in to comment.