Skip to content

Commit ea91f11

Browse files
authored
Merge pull request #180 from vohoanglong0107/refactor-cleanup-repository-loader
refactor: clean up RepositoryLoader
2 parents 469fc0c + b1ff073 commit ea91f11

File tree

9 files changed

+82
-89
lines changed

9 files changed

+82
-89
lines changed

src/bin/bors.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use std::time::Duration;
66

77
use anyhow::Context;
88
use bors::{
9-
create_app, create_bors_process, create_github_client, BorsContext, BorsGlobalEvent,
10-
CommandParser, PgDbClient, RepositoryLoader, ServerState, TeamApiClient, WebhookSecret,
9+
create_app, create_bors_process, create_github_client, load_repositories, BorsContext,
10+
BorsGlobalEvent, CommandParser, PgDbClient, ServerState, TeamApiClient, WebhookSecret,
1111
};
1212
use clap::Parser;
1313
use sqlx::postgres::PgConnectOptions;
@@ -88,9 +88,7 @@ fn try_main(opts: Opts) -> anyhow::Result<()> {
8888
"https://api.github.com".to_string(),
8989
opts.private_key.into(),
9090
)?;
91-
let repos = RepositoryLoader::new(client.clone())
92-
.load_repositories(&team_api)
93-
.await?;
91+
let repos = load_repositories(&client, &team_api).await?;
9492
Ok::<_, anyhow::Error>((client, repos))
9593
})?;
9694

src/bors/comment.rs

+8
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,11 @@ Build commit: {} (`{}`)"#,
4747
}),
4848
}
4949
}
50+
51+
pub fn try_build_in_progress_comment() -> Comment {
52+
Comment::new(":exclamation: A try build is currently in progress. You can cancel it using @bors try cancel.".to_string())
53+
}
54+
55+
pub fn cant_find_last_parent_comment() -> Comment {
56+
Comment::new(":exclamation: There was no previous build. Please set an explicit parent or remove the `parent=last` argument to use the default parent.".to_string())
57+
}

src/bors/handlers/mod.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::sync::Arc;
22

33
use anyhow::Context;
4+
use octocrab::Octocrab;
45
use tracing::Instrument;
56

67
use crate::bors::command::{BorsCommand, CommandParseError};
@@ -15,8 +16,8 @@ use crate::bors::handlers::trybuild::{command_try_build, command_try_cancel, TRY
1516
use crate::bors::handlers::workflow::{
1617
handle_check_suite_completed, handle_workflow_completed, handle_workflow_started,
1718
};
18-
use crate::bors::{BorsContext, Comment, RepositoryLoader, RepositoryState};
19-
use crate::{PgDbClient, TeamApiClient};
19+
use crate::bors::{BorsContext, Comment, RepositoryState};
20+
use crate::{load_repositories, PgDbClient, TeamApiClient};
2021

2122
#[cfg(test)]
2223
use crate::tests::util::TestSyncMarker;
@@ -141,14 +142,14 @@ pub static WAIT_FOR_REFRESH: TestSyncMarker = TestSyncMarker::new();
141142
pub async fn handle_bors_global_event(
142143
event: BorsGlobalEvent,
143144
ctx: Arc<BorsContext>,
144-
repo_loader: &RepositoryLoader,
145+
gh_client: &Octocrab,
145146
team_api_client: &TeamApiClient,
146147
) -> anyhow::Result<()> {
147148
let db = Arc::clone(&ctx.db);
148149
match event {
149150
BorsGlobalEvent::InstallationsChanged => {
150151
let span = tracing::info_span!("Installations changed");
151-
reload_repos(ctx, repo_loader, team_api_client)
152+
reload_repos(ctx, gh_client, team_api_client)
152153
.instrument(span)
153154
.await?;
154155
}
@@ -275,10 +276,10 @@ async fn handle_comment(
275276

276277
async fn reload_repos(
277278
ctx: Arc<BorsContext>,
278-
repo_loader: &RepositoryLoader,
279+
gh_client: &Octocrab,
279280
team_api_client: &TeamApiClient,
280281
) -> anyhow::Result<()> {
281-
let reloaded_repos = repo_loader.load_repositories(team_api_client).await?;
282+
let reloaded_repos = load_repositories(gh_client, team_api_client).await?;
282283
let mut repositories = ctx.repositories.write().unwrap();
283284
for repo in repositories.values() {
284285
if !reloaded_repos.contains_key(repo.repository()) {

src/bors/handlers/trybuild.rs

+37-28
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use std::sync::Arc;
33
use anyhow::{anyhow, Context};
44

55
use crate::bors::command::Parent;
6+
use crate::bors::comment::cant_find_last_parent_comment;
7+
use crate::bors::comment::try_build_in_progress_comment;
68
use crate::bors::handlers::labels::handle_label_trigger;
79
use crate::bors::Comment;
810
use crate::bors::RepositoryState;
@@ -47,37 +49,17 @@ pub(super) async fn command_try_build(
4749
.await
4850
.context("Cannot find or create PR")?;
4951

50-
let last_parent = if let Some(ref build) = pr_model.try_build {
51-
if build.status == BuildStatus::Pending {
52-
tracing::warn!("Try build already in progress");
53-
repo.client
54-
.post_comment(pr.number, Comment::new(":exclamation: A try build is currently in progress. You can cancel it using @bors try cancel.".to_string()))
55-
.await?;
56-
return Ok(());
57-
}
58-
Some(CommitSha(build.parent.clone()))
59-
} else {
60-
None
61-
};
62-
63-
let base_sha = match parent.clone() {
64-
Some(parent) => match parent {
65-
Parent::Last => match last_parent {
66-
None => {
67-
repo.client
68-
.post_comment(pr.number, Comment::new(":exclamation: There was no previous build. Please set an explicit parent or remove the `parent=last` argument to use the default parent.".to_string()))
69-
.await?;
70-
return Ok(());
71-
}
72-
Some(last_parent) => last_parent,
73-
},
74-
Parent::CommitSha(parent) => parent,
75-
},
76-
None => repo
52+
let base_sha = match get_base_sha(&pr_model, parent) {
53+
Ok(Some(base_sha)) => base_sha,
54+
Ok(None) => repo
7755
.client
7856
.get_branch_sha(&pr.base.name)
7957
.await
80-
.with_context(|| format!("Cannot get SHA for branch {}", pr.base.name))?,
58+
.context(format!("Cannot get SHA for branch {}", pr.base.name))?,
59+
Err(comment) => {
60+
repo.client.post_comment(pr.number, comment).await?;
61+
return Ok(());
62+
}
8163
};
8264

8365
tracing::debug!("Attempting to merge with base SHA {base_sha}");
@@ -140,6 +122,33 @@ pub(super) async fn command_try_build(
140122
}
141123
}
142124

125+
fn get_base_sha(
126+
pr_model: &PullRequestModel,
127+
parent: Option<Parent>,
128+
) -> Result<Option<CommitSha>, Comment> {
129+
let last_parent = if let Some(ref build) = pr_model.try_build {
130+
if build.status == BuildStatus::Pending {
131+
tracing::warn!("Try build already in progress");
132+
return Err(try_build_in_progress_comment());
133+
} else {
134+
Some(CommitSha(build.parent.clone()))
135+
}
136+
} else {
137+
None
138+
};
139+
140+
match parent.clone() {
141+
Some(parent) => match parent {
142+
Parent::Last => match last_parent {
143+
None => Err(cant_find_last_parent_comment()),
144+
Some(last_parent) => Ok(Some(last_parent)),
145+
},
146+
Parent::CommitSha(parent) => Ok(Some(parent)),
147+
},
148+
None => Ok(None),
149+
}
150+
}
151+
143152
pub(super) async fn command_try_cancel(
144153
repo: Arc<RepositoryState>,
145154
db: Arc<PgDbClient>,

src/bors/mod.rs

-24
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
use std::collections::HashMap;
2-
31
use arc_swap::ArcSwap;
4-
use octocrab::Octocrab;
52

63
pub use command::CommandParser;
74
pub use comment::Comment;
@@ -12,36 +9,15 @@ pub use handlers::{handle_bors_global_event, handle_bors_repository_event};
129

1310
use crate::config::RepositoryConfig;
1411
use crate::github::api::client::GithubRepositoryClient;
15-
use crate::github::api::load_repositories;
1612
use crate::github::GithubRepoName;
1713
use crate::permissions::UserPermissions;
18-
use crate::TeamApiClient;
1914

2015
mod command;
2116
pub mod comment;
2217
mod context;
2318
pub mod event;
2419
mod handlers;
2520

26-
/// Loads repositories through a global GitHub client.
27-
pub struct RepositoryLoader {
28-
client: Octocrab,
29-
}
30-
31-
impl RepositoryLoader {
32-
pub fn new(client: Octocrab) -> Self {
33-
Self { client }
34-
}
35-
36-
/// Load state of repositories.
37-
pub async fn load_repositories(
38-
&self,
39-
team_api_client: &TeamApiClient,
40-
) -> anyhow::Result<HashMap<GithubRepoName, anyhow::Result<RepositoryState>>> {
41-
load_repositories(&self.client, team_api_client).await
42-
}
43-
}
44-
4521
#[derive(Clone, Debug)]
4622
pub enum CheckSuiteStatus {
4723
Pending,

src/github/api/client.rs

+2-5
Original file line numberDiff line numberDiff line change
@@ -286,12 +286,12 @@ impl GithubRepositoryClient {
286286

287287
#[cfg(test)]
288288
mod tests {
289+
use crate::github::api::load_repositories;
289290
use crate::github::GithubRepoName;
290291
use crate::permissions::PermissionType;
291292
use crate::tests::mocks::Permissions;
292293
use crate::tests::mocks::{ExternalHttpMock, Repo};
293294
use crate::tests::mocks::{User, World};
294-
use crate::RepositoryLoader;
295295
use octocrab::models::UserId;
296296

297297
#[tokio::test]
@@ -312,10 +312,7 @@ mod tests {
312312
.await;
313313
let client = mock.github_client();
314314
let team_api_client = mock.team_api_client();
315-
let mut repos = RepositoryLoader::new(client)
316-
.load_repositories(&team_api_client)
317-
.await
318-
.unwrap();
315+
let mut repos = load_repositories(&client, &team_api_client).await.unwrap();
319316
assert_eq!(repos.len(), 2);
320317

321318
let repo = repos

src/github/api/mod.rs

+18-11
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use secrecy::{ExposeSecret, SecretString};
1010
use client::GithubRepositoryClient;
1111

1212
use crate::bors::RepositoryState;
13+
use crate::config::RepositoryConfig;
1314
use crate::github::GithubRepoName;
1415
use crate::permissions::TeamApiClient;
1516

@@ -36,6 +37,9 @@ pub fn create_github_client(
3637
}
3738

3839
/// Loads repositories that are connected to the given GitHub App client.
40+
/// The anyhow::Result<RepositoryState> is intended, because we wanted to have
41+
/// a hard error when the repos fail to load when the bot starts, but only log
42+
/// a warning when we reload the state during the bot's execution.
3943
pub async fn load_repositories(
4044
client: &Octocrab,
4145
team_api_client: &TeamApiClient,
@@ -152,21 +156,24 @@ async fn create_repo_state(
152156
.await
153157
.with_context(|| format!("Could not load permissions for repository {name}"))?;
154158

155-
let config = match client.load_config().await {
156-
Ok(config) => {
157-
tracing::info!("Loaded repository config for {name}: {config:#?}");
158-
config
159-
}
160-
Err(error) => {
161-
return Err(anyhow::anyhow!(
162-
"Could not load repository config for {name}: {error:?}"
163-
));
164-
}
165-
};
159+
let config = load_config(&client).await?;
166160

167161
Ok(RepositoryState {
168162
client,
169163
config: ArcSwap::new(Arc::new(config)),
170164
permissions: ArcSwap::new(Arc::new(permissions)),
171165
})
172166
}
167+
168+
async fn load_config(client: &GithubRepositoryClient) -> anyhow::Result<RepositoryConfig> {
169+
let name = client.repository();
170+
match client.load_config().await {
171+
Ok(config) => {
172+
tracing::info!("Loaded repository config for {name}: {config:#?}");
173+
Ok(config)
174+
}
175+
Err(error) => Err(anyhow::anyhow!(
176+
"Could not load repository config for {name}: {error:?}"
177+
)),
178+
}
179+
}

src/github/server.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::bors::event::BorsEvent;
22
use crate::bors::{handle_bors_global_event, handle_bors_repository_event, BorsContext};
33
use crate::github::webhook::GitHubWebhook;
44
use crate::github::webhook::WebhookSecret;
5-
use crate::{BorsGlobalEvent, BorsRepositoryEvent, RepositoryLoader, TeamApiClient};
5+
use crate::{BorsGlobalEvent, BorsRepositoryEvent, TeamApiClient};
66

77
use anyhow::Error;
88
use axum::extract::State;
@@ -92,7 +92,6 @@ pub fn create_bors_process(
9292
) {
9393
let (repository_tx, repository_rx) = mpsc::channel::<BorsRepositoryEvent>(1024);
9494
let (global_tx, global_rx) = mpsc::channel::<BorsGlobalEvent>(1024);
95-
let repo_loader = RepositoryLoader::new(gh_client);
9695

9796
let service = async move {
9897
let ctx = Arc::new(ctx);
@@ -105,7 +104,7 @@ pub fn create_bors_process(
105104
{
106105
tokio::join!(
107106
consume_repository_events(ctx.clone(), repository_rx),
108-
consume_global_events(ctx.clone(), global_rx, repo_loader, team_api)
107+
consume_global_events(ctx.clone(), global_rx, gh_client, team_api)
109108
);
110109
}
111110
// In real execution, the bot runs forever. If there is something that finishes
@@ -117,7 +116,7 @@ pub fn create_bors_process(
117116
_ = consume_repository_events(ctx.clone(), repository_rx) => {
118117
tracing::error!("Repository event handling process has ended");
119118
}
120-
_ = consume_global_events(ctx.clone(), global_rx, repo_loader, team_api) => {
119+
_ = consume_global_events(ctx.clone(), global_rx, gh_client, team_api) => {
121120
tracing::error!("Global event handling process has ended");
122121
}
123122
}
@@ -147,15 +146,15 @@ async fn consume_repository_events(
147146
async fn consume_global_events(
148147
ctx: Arc<BorsContext>,
149148
mut global_rx: mpsc::Receiver<BorsGlobalEvent>,
150-
loader: RepositoryLoader,
149+
gh_client: Octocrab,
151150
team_api: TeamApiClient,
152151
) {
153152
while let Some(event) = global_rx.recv().await {
154153
let ctx = ctx.clone();
155154

156155
let span = tracing::info_span!("GlobalEvent");
157156
tracing::debug!("Received global event: {event:#?}");
158-
if let Err(error) = handle_bors_global_event(event, ctx, &loader, &team_api)
157+
if let Err(error) = handle_bors_global_event(event, ctx, &gh_client, &team_api)
159158
.instrument(span.clone())
160159
.await
161160
{

src/lib.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,11 @@ mod github;
88
mod permissions;
99
mod utils;
1010

11-
pub use bors::{
12-
event::BorsGlobalEvent, event::BorsRepositoryEvent, BorsContext, CommandParser,
13-
RepositoryLoader,
14-
};
11+
pub use bors::{event::BorsGlobalEvent, event::BorsRepositoryEvent, BorsContext, CommandParser};
1512
pub use database::PgDbClient;
1613
pub use github::{
1714
api::create_github_client,
15+
api::load_repositories,
1816
server::{create_app, create_bors_process, ServerState},
1917
WebhookSecret,
2018
};

0 commit comments

Comments
 (0)