Skip to content

Commit 1c57795

Browse files
committed
Reload unknown mergeable PRs on refresh
1 parent c888eda commit 1c57795

File tree

6 files changed

+274
-20
lines changed

6 files changed

+274
-20
lines changed

.sqlx/query-07ad0343b05020b1df6f21b8863ad848f096688a8e41e74f9481e1c27b381bb9.json

+124
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/bors/handlers/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ pub async fn handle_bors_global_event(
219219
ctx: Arc<BorsContext>,
220220
gh_client: &Octocrab,
221221
team_api_client: &TeamApiClient,
222+
mergeable_queue_tx: MergeableQueueSender,
222223
) -> anyhow::Result<()> {
223224
let db = Arc::clone(&ctx.db);
224225
match event {
@@ -234,9 +235,10 @@ pub async fn handle_bors_global_event(
234235
ctx.repositories.read().unwrap().values().cloned().collect();
235236
futures::future::join_all(repos.into_iter().map(|repo| {
236237
let repo = Arc::clone(&repo);
238+
let mergeable_queue_tx = mergeable_queue_tx.clone();
237239
async {
238240
let subspan = tracing::info_span!("Repo", repo = repo.repository().to_string());
239-
refresh_repository(repo, Arc::clone(&db), team_api_client)
241+
refresh_repository(repo, Arc::clone(&db), team_api_client, mergeable_queue_tx)
240242
.instrument(subspan)
241243
.await
242244
}

src/bors/handlers/refresh.rs

+83-6
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,33 @@ use std::time::Duration;
33

44
use anyhow::Context;
55
use chrono::{DateTime, Utc};
6+
use futures::FutureExt;
7+
use futures::future::join_all;
68

79
use crate::bors::Comment;
810
use crate::bors::RepositoryState;
911
use crate::bors::handlers::trybuild::cancel_build_workflows;
12+
use crate::bors::mergeable_queue::MergeableQueueSender;
1013
use crate::database::BuildStatus;
1114
use crate::{PgDbClient, TeamApiClient};
1215

1316
pub async fn refresh_repository(
1417
repo: Arc<RepositoryState>,
1518
db: Arc<PgDbClient>,
1619
team_api_client: &TeamApiClient,
20+
mergeable_queue_tx: MergeableQueueSender,
1721
) -> anyhow::Result<()> {
1822
let repo = repo.as_ref();
19-
if let (Ok(_), _, Ok(_)) = tokio::join!(
20-
cancel_timed_out_builds(repo, db.as_ref()),
21-
reload_permission(repo, team_api_client),
22-
reload_config(repo)
23-
) {
23+
24+
let results = join_all([
25+
cancel_timed_out_builds(repo, db.as_ref()).boxed(),
26+
reload_permission(repo, team_api_client).boxed(),
27+
reload_config(repo).boxed(),
28+
reload_unknown_mergeable_prs(repo, db.as_ref(), mergeable_queue_tx).boxed(),
29+
])
30+
.await;
31+
32+
if results.iter().all(|result| result.is_ok()) {
2433
Ok(())
2534
} else {
2635
tracing::error!("Failed to refresh repository");
@@ -79,6 +88,27 @@ async fn reload_permission(
7988
Ok(())
8089
}
8190

91+
async fn reload_unknown_mergeable_prs(
92+
repo: &RepositoryState,
93+
db: &PgDbClient,
94+
mergeable_queue: MergeableQueueSender,
95+
) -> anyhow::Result<()> {
96+
let prs = db
97+
.get_prs_with_unknown_mergeable_state(repo.repository())
98+
.await?;
99+
100+
tracing::info!(
101+
"Refreshing {} PR(s) with unknown mergeable state",
102+
prs.len()
103+
);
104+
105+
for pr in prs {
106+
mergeable_queue.enqueue(repo.repository().clone(), pr.number);
107+
}
108+
109+
Ok(())
110+
}
111+
82112
async fn reload_config(repo: &RepositoryState) -> anyhow::Result<()> {
83113
let config = repo.client.load_config().await?;
84114
repo.config.store(Arc::new(config));
@@ -109,8 +139,9 @@ fn elapsed_time(date: DateTime<Utc>) -> Duration {
109139
mod tests {
110140
use crate::bors::handlers::WAIT_FOR_WORKFLOW_STARTED;
111141
use crate::bors::handlers::refresh::MOCK_TIME;
142+
use crate::database::{MergeableState, OctocrabMergeableState};
112143
use crate::tests::mocks::{
113-
BorsBuilder, GitHubState, WorkflowEvent, default_repo_name, run_test,
144+
BorsBuilder, GitHubState, WorkflowEvent, default_pr_number, default_repo_name, run_test,
114145
};
115146
use chrono::Utc;
116147
use std::future::Future;
@@ -133,6 +164,7 @@ timeout = 3600
133164
"#,
134165
)
135166
}
167+
136168
#[sqlx::test]
137169
async fn refresh_do_nothing_before_timeout(pool: sqlx::PgPool) {
138170
BorsBuilder::new(pool)
@@ -207,6 +239,51 @@ timeout = 3600
207239
gh.check_cancelled_workflows(default_repo_name(), &[1]);
208240
}
209241

242+
#[sqlx::test]
243+
async fn refresh_enqueues_unknown_mergeable_prs(pool: sqlx::PgPool) {
244+
run_test(pool, |mut tester| async {
245+
tester
246+
.edit_pr(default_repo_name(), default_pr_number(), |pr| {
247+
pr.mergeable_state = OctocrabMergeableState::Unknown
248+
})
249+
.await?;
250+
tester
251+
.wait_for_default_pr(|pr| pr.mergeable_state == MergeableState::Unknown)
252+
.await?;
253+
tester.refresh().await;
254+
tester
255+
.default_repo()
256+
.lock()
257+
.get_pr_mut(default_pr_number())
258+
.mergeable_state = OctocrabMergeableState::Dirty;
259+
tester
260+
.wait_for_default_pr(|pr| pr.mergeable_state == MergeableState::HasConflicts)
261+
.await?;
262+
Ok(tester)
263+
})
264+
.await;
265+
}
266+
267+
#[sqlx::test]
268+
async fn refresh_enqueues_no_known_mergeable_prs(pool: sqlx::PgPool) {
269+
run_test(pool, |mut tester| async {
270+
tester
271+
.edit_pr(default_repo_name(), default_pr_number(), |pr| {
272+
pr.mergeable_state = OctocrabMergeableState::Clean
273+
})
274+
.await?;
275+
tester
276+
.wait_for_default_pr(|pr| pr.mergeable_state == MergeableState::Mergeable)
277+
.await?;
278+
tester.refresh().await;
279+
tester
280+
.wait_for_default_pr(|pr| pr.mergeable_state == MergeableState::Mergeable)
281+
.await?;
282+
Ok(tester)
283+
})
284+
.await;
285+
}
286+
210287
async fn with_mocked_time<Fut: Future<Output = ()>>(in_future: Duration, future: Fut) {
211288
// It is important to use this function only with a single threaded runtime,
212289
// otherwise the `MOCK_TIME` variable might get mixed up between different threads.

src/database/client.rs

+13-5
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ use crate::github::{CommitSha, GithubRepoName};
1111
use super::operations::{
1212
approve_pull_request, create_build, create_pull_request, create_workflow,
1313
delegate_pull_request, find_build, find_pr_by_build,
14-
get_nonclosed_pull_requests_by_base_branch, get_pull_request, get_repository,
15-
get_running_builds, get_workflow_urls_for_build, get_workflows_for_build, set_pr_priority,
16-
set_pr_rollup, set_pr_status, unapprove_pull_request, undelegate_pull_request,
17-
update_build_status, update_mergeable_states_by_base_branch, update_pr_build_id,
18-
update_pr_mergeable_state, update_workflow_status, upsert_pull_request, upsert_repository,
14+
get_nonclosed_pull_requests_by_base_branch, get_prs_with_unknown_mergeable_state,
15+
get_pull_request, get_repository, get_running_builds, get_workflow_urls_for_build,
16+
get_workflows_for_build, set_pr_priority, set_pr_rollup, set_pr_status, unapprove_pull_request,
17+
undelegate_pull_request, update_build_status, update_mergeable_states_by_base_branch,
18+
update_pr_build_id, update_pr_mergeable_state, update_workflow_status, upsert_pull_request,
19+
upsert_repository,
1920
};
2021
use super::{ApprovalInfo, DelegatedPermission, MergeableState, RunId};
2122

@@ -121,6 +122,13 @@ impl PgDbClient {
121122
get_nonclosed_pull_requests_by_base_branch(&self.pool, repo, base_branch).await
122123
}
123124

125+
pub async fn get_prs_with_unknown_mergeable_state(
126+
&self,
127+
repo: &GithubRepoName,
128+
) -> anyhow::Result<Vec<PullRequestModel>> {
129+
get_prs_with_unknown_mergeable_state(&self.pool, repo).await
130+
}
131+
124132
pub async fn create_pull_request(
125133
&self,
126134
repo: &GithubRepoName,

src/database/operations.rs

+40
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,46 @@ pub(crate) async fn update_pr_mergeable_state(
225225
.await
226226
}
227227

228+
pub(crate) async fn get_prs_with_unknown_mergeable_state(
229+
executor: impl PgExecutor<'_>,
230+
repo: &GithubRepoName,
231+
) -> anyhow::Result<Vec<PullRequestModel>> {
232+
measure_db_query("get_prs_with_unknown_mergeable_state", || async {
233+
let records = sqlx::query_as!(
234+
PullRequestModel,
235+
r#"
236+
SELECT
237+
pr.id,
238+
pr.repository as "repository: GithubRepoName",
239+
pr.number as "number!: i64",
240+
(
241+
pr.approved_by,
242+
pr.approved_sha
243+
) AS "approval_status!: ApprovalStatus",
244+
pr.status as "pr_status: PullRequestStatus",
245+
pr.priority,
246+
pr.rollup as "rollup: RollupMode",
247+
pr.delegated_permission as "delegated_permission: DelegatedPermission",
248+
pr.base_branch,
249+
pr.mergeable_state as "mergeable_state: MergeableState",
250+
pr.created_at as "created_at: DateTime<Utc>",
251+
build AS "try_build: BuildModel"
252+
FROM pull_request as pr
253+
LEFT JOIN build ON pr.build_id = build.id
254+
WHERE pr.repository = $1
255+
AND pr.mergeable_state = 'unknown'
256+
AND pr.status IN ('open', 'draft')
257+
"#,
258+
repo as &GithubRepoName
259+
)
260+
.fetch_all(executor)
261+
.await?;
262+
263+
Ok(records)
264+
})
265+
.await
266+
}
267+
228268
pub(crate) async fn update_mergeable_states_by_base_branch(
229269
executor: impl PgExecutor<'_>,
230270
repo: &GithubRepoName,

0 commit comments

Comments
 (0)