Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions src/bors/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pub enum BorsCommand {
Try {
/// Parent commit which should be used as the merge base.
parent: Option<Parent>,
/// The CI workflow to run.
jobs: Option<Vec<String>>,
},
/// Cancel a try build.
TryCancel,
Expand Down
98 changes: 88 additions & 10 deletions src/bors/command/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,14 +140,15 @@ fn parser_try<'a>(command: &'a str, parts: &[CommandPart<'a>]) -> ParseResult<'a
}

let mut parent = None;
let mut jobs = None;

for part in parts {
match part {
CommandPart::Bare(key) => {
return Some(Err(CommandParseError::UnknownArg(key)));
}
CommandPart::KeyValue { key, value } => {
if *key == "parent" {
CommandPart::KeyValue { key, value } => match *key {
"parent" => {
parent = if *value == "last" {
Some(Parent::Last)
} else {
Expand All @@ -160,13 +161,30 @@ fn parser_try<'a>(command: &'a str, parts: &[CommandPart<'a>]) -> ParseResult<'a
}
}
}
} else {
}
"jobs" => {
let raw_jobs: Vec<_> = value.split(',').map(|s| s.to_string()).collect();
if raw_jobs.is_empty() {
return Some(Err(CommandParseError::ValidationError(format!(
"Try jobs must not be empty"
))));
}

// rust ci currently allows specifying 10 jobs max
if raw_jobs.len() > 10 {
return Some(Err(CommandParseError::ValidationError(format!(
"Try jobs must not have more than 10 jobs"
))));
}
jobs = Some(raw_jobs);
}
_ => {
return Some(Err(CommandParseError::UnknownArg(key)));
}
}
},
}
}
Some(Ok(BorsCommand::Try { parent }))
Some(Ok(BorsCommand::Try { parent, jobs }))
}

/// Parses "@bors try cancel".
Expand Down Expand Up @@ -262,14 +280,26 @@ line two
"#,
);
assert_eq!(cmds.len(), 1);
assert!(matches!(cmds[0], Ok(BorsCommand::Try { parent: None })));
assert!(matches!(
cmds[0],
Ok(BorsCommand::Try {
parent: None,
jobs: None
})
));
}

#[test]
fn parse_try() {
let cmds = parse_commands("@bors try");
assert_eq!(cmds.len(), 1);
assert!(matches!(cmds[0], Ok(BorsCommand::Try { parent: None })));
assert!(matches!(
cmds[0],
Ok(BorsCommand::Try {
parent: None,
jobs: None
})
));
}

#[test]
Expand All @@ -281,7 +311,8 @@ line two
Ok(BorsCommand::Try {
parent: Some(Parent::CommitSha(CommitSha(
"ea9c1b050cc8b420c2c211d2177811e564a4dc60".to_string()
)))
))),
jobs: None
})
);
}
Expand All @@ -293,7 +324,8 @@ line two
assert_eq!(
cmds[0],
Ok(BorsCommand::Try {
parent: Some(Parent::Last)
parent: Some(Parent::Last),
jobs: None
})
);
}
Expand All @@ -311,6 +343,46 @@ line two
"###);
}

#[test]
fn parse_try_jobs() {
let cmds = parse_commands("@bors try jobs=ci,lint");
assert_eq!(cmds.len(), 1);
assert_eq!(
cmds[0],
Ok(BorsCommand::Try {
parent: None,
jobs: Some(vec!["ci".to_string(), "lint".to_string()])
})
);
}

#[test]
fn parse_try_jobs_empty() {
let cmds = parse_commands("@bors try jobs=");
assert_eq!(cmds.len(), 1);
insta::assert_debug_snapshot!(cmds[0], @r###"
Err(
MissingArgValue {
arg: "jobs",
},
)
"###);
}

#[test]
fn parse_try_jobs_too_many() {
let cmds =
parse_commands("@bors try jobs=ci,lint,foo,bar,baz,qux,quux,corge,grault,garply,waldo");
assert_eq!(cmds.len(), 1);
insta::assert_debug_snapshot!(cmds[0], @r###"
Err(
ValidationError(
"Try jobs must not have more than 10 jobs",
),
)
"###);
}

#[test]
fn parse_try_unknown_arg() {
let cmds = parse_commands("@bors try a");
Expand All @@ -333,7 +405,13 @@ line two
"#,
);
assert_eq!(cmds.len(), 1);
assert!(matches!(cmds[0], Ok(BorsCommand::Try { parent: None })));
assert!(matches!(
cmds[0],
Ok(BorsCommand::Try {
parent: None,
jobs: None
})
));
}

#[test]
Expand Down
15 changes: 11 additions & 4 deletions src/bors/handlers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,18 @@ async fn handle_comment<Client: RepositoryClient>(
let span = tracing::info_span!("Ping");
command_ping(repo, &pull_request).instrument(span).await
}
BorsCommand::Try { parent } => {
BorsCommand::Try { parent, jobs } => {
let span = tracing::info_span!("Try");
command_try_build(repo, database, &pull_request, &comment.author, parent)
.instrument(span)
.await
command_try_build(
repo,
database,
&pull_request,
&comment.author,
parent,
jobs,
)
.instrument(span)
.await
}
BorsCommand::TryCancel => {
let span = tracing::info_span!("Cancel try");
Expand Down
19 changes: 15 additions & 4 deletions src/bors/handlers/trybuild.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub(super) async fn command_try_build<Client: RepositoryClient>(
pr: &PullRequest,
author: &GithubUser,
parent: Option<Parent>,
jobs: Option<Vec<String>>,
) -> anyhow::Result<()> {
let repo = repo.as_ref();
if !check_try_permissions(repo, pr, author).await? {
Expand Down Expand Up @@ -94,7 +95,7 @@ pub(super) async fn command_try_build<Client: RepositoryClient>(
.merge_branches(
TRY_MERGE_BRANCH_NAME,
&pr.head.sha,
&auto_merge_commit_message(pr, "<try>"),
&auto_merge_commit_message(pr, "<try>", jobs),
)
.await
{
Expand Down Expand Up @@ -232,17 +233,27 @@ fn get_pending_build(pr: PullRequestModel) -> Option<BuildModel> {
.and_then(|b| (b.status == BuildStatus::Pending).then_some(b))
}

fn auto_merge_commit_message(pr: &PullRequest, reviewer: &str) -> String {
fn auto_merge_commit_message(
pr: &PullRequest,
reviewer: &str,
jobs: Option<Vec<String>>,
) -> String {
let pr_number = pr.number;
format!(
let mut message = format!(
r#"Auto merge of #{pr_number} - {pr_label}, r={reviewer}
{pr_title}

{pr_message}"#,
pr_label = pr.head_label,
pr_title = pr.title,
pr_message = pr.message
)
);
if let Some(jobs) = jobs {
for job in jobs {
message.push_str(&format!("\nci-job: {}", job));
}
}
message
}

fn merge_conflict_message(branch: &str) -> String {
Expand Down