@@ -12,10 +12,13 @@ use crate::bors::comment::{
1212 merge_conflict_comment,
1313} ;
1414use crate :: bors:: { PullRequestStatus , RepositoryState } ;
15- use crate :: database:: { BuildStatus , MergeableState , OctocrabMergeableState , PullRequestModel } ;
15+ use crate :: database:: {
16+ ApprovalInfo , BuildModel , BuildStatus , MergeableState , OctocrabMergeableState ,
17+ PullRequestModel , QueueStatus ,
18+ } ;
1619use crate :: github:: api:: client:: CheckRunOutput ;
1720use crate :: github:: api:: operations:: { BranchUpdateError , ForcePush } ;
18- use crate :: github:: { CommitSha , PullRequest } ;
21+ use crate :: github:: { CommitSha , PullRequest , PullRequestNumber } ;
1922use crate :: github:: { MergeResult , attempt_merge} ;
2023use crate :: utils:: sort_queue:: sort_queue_prs;
2124
@@ -130,114 +133,126 @@ async fn process_repository(repo: &RepositoryState, ctx: &BorsContext) -> anyhow
130133 for pr in prs {
131134 let pr_num = pr. number ;
132135
133- let Some ( auto_build) = & pr. auto_build else {
134- // No build exists for this PR - start a new auto build.
135- match start_auto_build ( repo, ctx, & pr) . await {
136- Ok ( ( ) ) => {
137- tracing:: info!( "Starting auto build for PR {pr_num}" ) ;
138- break ;
139- }
140- Err ( StartAutoBuildError :: MergeConflict ) => {
141- let gh_pr = repo. client . get_pull_request ( pr. number ) . await ?;
142-
143- tracing:: debug!(
144- "Failed to start auto build for PR {pr_num} due to merge conflict"
145- ) ;
146-
147- ctx. db
148- . update_pr_mergeable_state ( & pr, MergeableState :: Unknown )
149- . await ?;
150- repo. client
151- . post_comment ( pr. number , merge_conflict_comment ( & gh_pr. head . name ) )
152- . await ?;
153- continue ;
154- }
155- Err ( StartAutoBuildError :: SanityCheckFailed ( error) ) => {
156- tracing:: info!( "Sanity check failed for PR {pr_num}: {error:?}" ) ;
157- break ;
158- }
159- Err ( StartAutoBuildError :: GitHubError ( error) ) => {
160- tracing:: debug!(
161- "Failed to start auto build for PR {pr_num} due to a GitHub error: {error:?}"
162- ) ;
163- break ;
164- }
165- Err ( StartAutoBuildError :: DatabaseError ( error) ) => {
166- tracing:: debug!(
167- "Failed to start auto build for PR {pr_num} due to database error: {error:?}"
168- ) ;
169- break ;
170- }
171- }
172- } ;
173-
174- let commit_sha = CommitSha ( auto_build. commit_sha . clone ( ) ) ;
175-
176- match auto_build. status {
177- // Build successful - point the base branch to the merged commit.
178- BuildStatus :: Success => {
179- let workflows = ctx. db . get_workflows_for_build ( auto_build) . await ?;
180- let comment = auto_build_succeeded_comment (
181- & workflows,
182- pr. approver ( ) . unwrap_or ( "<unknown>" ) ,
183- & commit_sha,
184- & pr. base_branch ,
185- ) ;
186-
187- if let Err ( error) = repo
188- . client
189- . set_branch_to_sha ( & pr. base_branch , & commit_sha, ForcePush :: No )
190- . await
191- {
192- tracing:: error!(
193- "Failed to fast-forward base branch for PR {pr_num}: {error:?}"
194- ) ;
195-
196- let comment = match & error {
197- BranchUpdateError :: Conflict ( branch_name) => auto_build_push_failed_comment (
198- & format ! ( "this PR has conflicts with the `{branch_name}` branch" ) ,
199- ) ,
200- BranchUpdateError :: ValidationFailed ( branch_name) => {
201- auto_build_push_failed_comment ( & format ! (
202- "the tested commit was behind the `{branch_name}` branch"
203- ) )
204- }
205- error => auto_build_push_failed_comment ( & error. to_string ( ) ) ,
206- } ;
207-
208- ctx. db
209- . update_build_status ( auto_build, BuildStatus :: Failure )
210- . await ?;
211-
212- repo. client . post_comment ( pr_num, comment) . await ?;
213- } else {
214- tracing:: info!( "Auto build succeeded and merged for PR {pr_num}" ) ;
215-
216- ctx. db
217- . set_pr_status ( & pr. repository , pr. number , PullRequestStatus :: Merged )
218- . await ?;
219-
220- repo. client . post_comment ( pr. number , comment) . await ?;
221- }
222-
223- // Break to give GitHub time to update the base branch.
136+ match pr. queue_status ( ) {
137+ QueueStatus :: NotApproved => unreachable ! ( ) ,
138+ QueueStatus :: Stalled ( ..) => unreachable ! ( ) ,
139+ QueueStatus :: Pending ( ..) => {
140+ // Build in progress - stop queue. We can only have one PR being built
141+ // at a time.
142+ tracing:: info!( "PR {pr_num} has a pending build - blocking queue" ) ;
224143 break ;
225144 }
226- // Build in progress - stop queue. We can only have one PR being built
227- // at a time.
228- BuildStatus :: Pending => {
229- tracing:: info!( "PR {pr_num} has a pending build - blocking queue" ) ;
145+ QueueStatus :: ReadyForMerge ( approval_info, auto_build) => {
146+ handle_successful_build ( repo, ctx, & pr, & auto_build, & approval_info, pr_num)
147+ . await ?;
230148 break ;
231149 }
232- BuildStatus :: Failure | BuildStatus :: Cancelled | BuildStatus :: Timeouted => {
233- unreachable ! ( "Failed auto builds should be filtered out by SQL query" ) ;
150+ QueueStatus :: Approved ( ..) => {
151+ if handle_start_auto_build ( repo, ctx, & pr, pr_num) . await ? {
152+ break ;
153+ }
234154 }
235155 }
236156 }
237157
238158 Ok ( ( ) )
239159}
240160
161+ /// Handle a successful auto build by pointing the base branch to the merged commit.
162+ async fn handle_successful_build (
163+ repo : & RepositoryState ,
164+ ctx : & BorsContext ,
165+ pr : & PullRequestModel ,
166+ auto_build : & BuildModel ,
167+ approval_info : & ApprovalInfo ,
168+ pr_num : PullRequestNumber ,
169+ ) -> anyhow:: Result < ( ) > {
170+ let commit_sha = CommitSha ( auto_build. commit_sha . clone ( ) ) ;
171+ let workflows = ctx. db . get_workflows_for_build ( auto_build) . await ?;
172+ let comment = auto_build_succeeded_comment (
173+ & workflows,
174+ & approval_info. approver ,
175+ & commit_sha,
176+ & pr. base_branch ,
177+ ) ;
178+
179+ if let Err ( error) = repo
180+ . client
181+ . set_branch_to_sha ( & pr. base_branch , & commit_sha, ForcePush :: No )
182+ . await
183+ {
184+ tracing:: error!( "Failed to fast-forward base branch for PR {pr_num}: {error:?}" ) ;
185+
186+ let error_comment = match & error {
187+ BranchUpdateError :: Conflict ( branch_name) => auto_build_push_failed_comment ( & format ! (
188+ "this PR has conflicts with the `{branch_name}` branch"
189+ ) ) ,
190+ BranchUpdateError :: ValidationFailed ( branch_name) => auto_build_push_failed_comment (
191+ & format ! ( "the tested commit was behind the `{branch_name}` branch" ) ,
192+ ) ,
193+ error => auto_build_push_failed_comment ( & error. to_string ( ) ) ,
194+ } ;
195+
196+ ctx. db
197+ . update_build_status ( auto_build, BuildStatus :: Failure )
198+ . await ?;
199+ repo. client . post_comment ( pr_num, error_comment) . await ?;
200+ } else {
201+ tracing:: info!( "Auto build succeeded and merged for PR {pr_num}" ) ;
202+ ctx. db
203+ . set_pr_status ( & pr. repository , pr. number , PullRequestStatus :: Merged )
204+ . await ?;
205+ repo. client . post_comment ( pr. number , comment) . await ?;
206+ }
207+
208+ Ok ( ( ) )
209+ }
210+
211+ /// Handle starting a new auto build for an approved PR.
212+ /// Returns true if the queue should break, false to continue.
213+ async fn handle_start_auto_build (
214+ repo : & RepositoryState ,
215+ ctx : & BorsContext ,
216+ pr : & PullRequestModel ,
217+ pr_num : PullRequestNumber ,
218+ ) -> anyhow:: Result < bool > {
219+ let Err ( error) = start_auto_build ( repo, ctx, pr) . await else {
220+ tracing:: info!( "Starting auto build for PR {pr_num}" ) ;
221+ return Ok ( true ) ;
222+ } ;
223+
224+ match error {
225+ StartAutoBuildError :: MergeConflict => {
226+ let gh_pr = repo. client . get_pull_request ( pr. number ) . await ?;
227+ tracing:: debug!( "Failed to start auto build for PR {pr_num} due to merge conflict" ) ;
228+
229+ ctx. db
230+ . update_pr_mergeable_state ( pr, MergeableState :: Unknown )
231+ . await ?;
232+ repo. client
233+ . post_comment ( pr. number , merge_conflict_comment ( & gh_pr. head . name ) )
234+ . await ?;
235+ Ok ( false )
236+ }
237+ StartAutoBuildError :: SanityCheckFailed ( error) => {
238+ tracing:: info!( "Sanity check failed for PR {pr_num}: {error:?}" ) ;
239+ Ok ( true )
240+ }
241+ StartAutoBuildError :: GitHubError ( error) => {
242+ tracing:: debug!(
243+ "Failed to start auto build for PR {pr_num} due to a GitHub error: {error:?}"
244+ ) ;
245+ Ok ( true )
246+ }
247+ StartAutoBuildError :: DatabaseError ( error) => {
248+ tracing:: debug!(
249+ "Failed to start auto build for PR {pr_num} due to database error: {error:?}"
250+ ) ;
251+ Ok ( true )
252+ }
253+ }
254+ }
255+
241256#[ must_use]
242257pub enum StartAutoBuildError {
243258 /// Merge conflict between PR head and base branch.
0 commit comments