Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@bors info command #215

Merged
merged 1 commit into from
Mar 11, 2025
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
target/
.env

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

2 changes: 2 additions & 0 deletions src/bors/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ pub enum BorsCommand {
TryCancel,
/// Set the priority of a PR.
SetPriority(Priority),
/// Get information about the current PR.
Info,
/// Delegate approval authority to the pull request author.
Delegate,
/// Revoke any previously granted delegation.
Expand Down
24 changes: 24 additions & 0 deletions src/bors/command/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const PARSERS: &[for<'b> fn(&CommandPart<'b>, &[CommandPart<'b>]) -> ParseResult
parser_try_cancel,
parser_try,
parser_delegation,
parser_info,
parser_help,
parser_ping,
];
Expand Down Expand Up @@ -303,6 +304,15 @@ fn parser_rollup<'a>(command: &CommandPart<'a>, _parts: &[CommandPart<'a>]) -> P
parse_rollup(std::slice::from_ref(command)).map(|res| res.map(BorsCommand::SetRollupMode))
}

/// Parses "@bors info"
fn parser_info<'a>(command: &CommandPart<'a>, _parts: &[CommandPart<'a>]) -> ParseResult<'a> {
if *command == CommandPart::Bare("info") {
Some(Ok(BorsCommand::Info))
} else {
None
}
}

#[cfg(test)]
mod tests {
use crate::bors::command::parser::{CommandParseError, CommandParser};
Expand Down Expand Up @@ -958,6 +968,20 @@ line two
"###)
}

#[test]
fn parse_info() {
let cmds = parse_commands("@bors info");
assert_eq!(cmds.len(), 1);
assert!(matches!(cmds[0], Ok(BorsCommand::Info)));
}

#[test]
fn parse_info_unknown_arg() {
let cmds = parse_commands("@bors info a");
assert_eq!(cmds.len(), 1);
assert!(matches!(cmds[0], Ok(BorsCommand::Info)));
}

#[test]
fn parse_try_cancel() {
let cmds = parse_commands("@bors try cancel");
Expand Down
10 changes: 8 additions & 2 deletions src/bors/handlers/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ pub(super) async fn command_help(
jobs: vec![],
},
BorsCommand::TryCancel,
BorsCommand::SetRollupMode(RollupMode::Always),
BorsCommand::Info,
BorsCommand::Ping,
BorsCommand::Help,
BorsCommand::SetRollupMode(RollupMode::Always),
]
.into_iter()
.map(|help| format!("- {}", get_command_help(help)))
Expand Down Expand Up @@ -79,6 +80,10 @@ fn get_command_help(command: BorsCommand) -> String {
BorsCommand::SetRollupMode(_) => {
"`rollup=<never/iffy/maybe/always>`: Mark the rollup status of the PR"
}
BorsCommand::Info => {
"`info`: Get information about the current PR including delegation, priority, merge status, and try build status"
}

};
help.to_string()
}
Expand All @@ -100,9 +105,10 @@ mod tests {
- `delegate-`: Remove any previously granted delegation
- `try [parent=<parent>] [jobs=<jobs>]`: Start a try build. Optionally, you can specify a `<parent>` SHA or a list of `<jobs>` to run
- `try cancel`: Cancel a running try build
- `rollup=<never/iffy/maybe/always>`: Mark the rollup status of the PR
- `info`: Get information about the current PR including delegation, priority, merge status, and try build status
- `ping`: Check if the bot is alive
- `help`: Print this help message
- `rollup=<never/iffy/maybe/always>`: Mark the rollup status of the PR
");
Ok(tester)
})
Expand Down
172 changes: 172 additions & 0 deletions src/bors/handlers/info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
use crate::bors::Comment;
use crate::bors::RepositoryState;
use crate::database::PgDbClient;
use crate::github::PullRequest;
use std::sync::Arc;

pub(super) async fn command_info(
repo: Arc<RepositoryState>,
pr: &PullRequest,
db: Arc<PgDbClient>,
) -> anyhow::Result<()> {
// Geting PR info from database
let pr_model = db
.get_or_create_pull_request(repo.client.repository(), pr.number)
.await?;

// Building the info message
let mut info_lines = Vec::new();

// Approval info
if let Some(approved_by) = pr_model.approved_by {
info_lines.push(format!("- **Approved by:** @{}", approved_by));
} else {
info_lines.push("- **Not Approved:**".to_string());
}

// Priority info
if let Some(priority) = pr_model.priority {
info_lines.push(format!("- **Priority:** {}", priority));
} else {
info_lines.push("- **Priority:** Not set".to_string());
}

// Build status
if let Some(try_build) = pr_model.try_build {
info_lines.push(format!("- **Try build branch:** {}", try_build.branch));

if let Ok(urls) = db.get_workflow_urls_for_build(&try_build).await {
info_lines.extend(
urls.into_iter()
.map(|url| format!("- **Workflow URL:** {}", url)),
);
}
}

// Joining all lines
let info = info_lines.join("\n");

// Post the comment
repo.client
.post_comment(pr.number, Comment::new(info))
.await?;

Ok(())
}

#[cfg(test)]
mod tests {
use crate::tests::mocks::{create_world_with_approve_config, BorsBuilder, World};

#[sqlx::test]
async fn info_for_unapproved_pr(pool: sqlx::PgPool) {
BorsBuilder::new(pool)
.world(World::default())
.run_test(|mut tester| async {
tester.post_comment("@bors info").await?;
insta::assert_snapshot!(
tester.get_comment().await?,
@r"
- **Not Approved:**
- **Priority:** Not set
"
);
Ok(tester)
})
.await;
}

#[sqlx::test]
async fn info_for_approved_pr(pool: sqlx::PgPool) {
BorsBuilder::new(pool)
.world(create_world_with_approve_config())
.run_test(|mut tester| async {
tester.post_comment("@bors r+").await?;
tester.expect_comments(1).await;

tester.post_comment("@bors info").await?;
insta::assert_snapshot!(
tester.get_comment().await?,
@r"
- **Approved by:** @default-user
- **Priority:** Not set
"
);
Ok(tester)
})
.await;
}

#[sqlx::test]
async fn info_for_pr_with_priority(pool: sqlx::PgPool) {
BorsBuilder::new(pool)
.world(create_world_with_approve_config())
.run_test(|mut tester| async {
tester.post_comment("@bors p=5").await?;
tester
.wait_for(|| async {
let pr = tester.get_default_pr().await?;
Ok(pr.priority == Some(5))
})
.await?;

tester.post_comment("@bors info").await?;
insta::assert_snapshot!(
tester.get_comment().await?,
@r"
- **Not Approved:**
- **Priority:** 5
"
);
Ok(tester)
})
.await;
}

#[sqlx::test]
async fn info_for_pr_with_try_build(pool: sqlx::PgPool) {
BorsBuilder::new(pool)
.world(create_world_with_approve_config())
.run_test(|mut tester| async {
tester.post_comment("@bors try").await?;
tester.expect_comments(1).await;

tester.post_comment("@bors info").await?;
insta::assert_snapshot!(
tester.get_comment().await?,
@r"
- **Not Approved:**
- **Priority:** Not set
- **Try build branch:** automation/bors/try
"
);
Ok(tester)
})
.await;
}

#[sqlx::test]
async fn info_for_pr_with_everything(pool: sqlx::PgPool) {
BorsBuilder::new(pool)
.world(create_world_with_approve_config())
.run_test(|mut tester| async {
tester.post_comment("@bors r+ p=10").await?;
tester.expect_comments(1).await;

tester.post_comment("@bors try").await?;
tester.expect_comments(1).await;

tester.post_comment("@bors info").await?;
insta::assert_snapshot!(
tester.get_comment().await?,
@r"
- **Approved by:** @default-user
- **Priority:** 10
- **Try build branch:** automation/bors/try
"
);
Ok(tester)
})
.await;
}
}
8 changes: 8 additions & 0 deletions src/bors/handlers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::sync::Arc;
use crate::bors::command::{BorsCommand, CommandParseError};
use crate::bors::event::{BorsGlobalEvent, BorsRepositoryEvent, PullRequestComment};
use crate::bors::handlers::help::command_help;
use crate::bors::handlers::info::command_info;
use crate::bors::handlers::ping::command_ping;
use crate::bors::handlers::refresh::refresh_repository;
use crate::bors::handlers::review::{command_approve, command_unapprove};
Expand All @@ -24,6 +25,7 @@ use tracing::Instrument;
use crate::tests::util::TestSyncMarker;

mod help;
mod info;
mod labels;
mod ping;
mod pr_events;
Expand Down Expand Up @@ -282,6 +284,12 @@ async fn handle_comment(
.instrument(span)
.await
}
BorsCommand::Info => {
let span = tracing::info_span!("Info");
command_info(repo, &pull_request, database)
.instrument(span)
.await
}
BorsCommand::SetRollupMode(rollup) => {
let span = tracing::info_span!("Rollup");
command_set_rollup(repo, database, &pull_request, &comment.author, rollup)
Expand Down
12 changes: 10 additions & 2 deletions src/database/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ use crate::github::{CommitSha, GithubRepoName};
use super::operations::{
approve_pull_request, create_build, create_pull_request, create_workflow,
delegate_pull_request, find_build, find_pr_by_build, get_pull_request, get_running_builds,
get_workflows_for_build, set_pr_priority, set_pr_rollup, unapprove_pull_request,
undelegate_pull_request, update_build_status, update_pr_build_id, update_workflow_status,
get_workflow_urls_for_build, get_workflows_for_build, set_pr_priority, set_pr_rollup,
unapprove_pull_request, undelegate_pull_request, update_build_status, update_pr_build_id,
update_workflow_status,
};
use super::RunId;

Expand Down Expand Up @@ -183,6 +184,13 @@ impl PgDbClient {
get_workflows_for_build(&self.pool, build.id).await
}

pub async fn get_workflow_urls_for_build(
&self,
build: &BuildModel,
) -> anyhow::Result<Vec<String>> {
get_workflow_urls_for_build(&self.pool, build.id).await
}

pub async fn get_pending_workflows_for_build(
&self,
build: &BuildModel,
Expand Down
18 changes: 18 additions & 0 deletions src/database/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,24 @@ WHERE build.id = $1
Ok(workflows)
}

pub(crate) async fn get_workflow_urls_for_build(
executor: impl PgExecutor<'_>,
build_id: i32,
) -> anyhow::Result<Vec<String>> {
let results = sqlx::query!(
r#"
SELECT url
FROM workflow
WHERE build_id = $1
"#,
build_id
)
.fetch_all(executor)
.await?;

Ok(results.into_iter().map(|r| r.url).collect())
}

#[cfg(test)]
pub(crate) async fn get_all_workflows(
executor: impl PgExecutor<'_>,
Expand Down