@@ -220,22 +220,17 @@ def run_impl_within_lock(
220220 milestone = Milestones .NOTIFICATIONS_SENT
221221
222222 log .info ("Starting notifications" , extra = {"commit" : commitid , "repoid" : repoid })
223- commits_query = db_session .query (Commit ).filter (
224- Commit .repoid == repoid , Commit .commitid == commitid
225- )
226- commit : Commit = commits_query .first ()
227- assert commit , "Commit not found in database."
228-
223+ commit = self ._fetch_commit (db_session , repoid , commitid )
229224 any_failures , all_tests_passed = get_test_status (commit .repoid , commit .commitid )
230225
231- # This functionality is disabled for now because it's too noisy for customers
226+ # TA error messaging is disabled for now ( too noisy for customers)
232227 ta_error_msg = None
233228
234229 if any_failures and not all_tests_passed :
235230 self ._call_upload_breadcrumb_task (
236231 commit_sha = commit .commitid ,
237232 repo_id = commit .repoid ,
238- milestone = Milestones . NOTIFICATIONS_SENT ,
233+ milestone = milestone ,
239234 error = Errors .SKIPPED_NOTIFICATIONS ,
240235 )
241236 return {
@@ -244,158 +239,28 @@ def run_impl_within_lock(
244239 "reason" : "test_failures" ,
245240 }
246241
247- try :
248- installation_name_to_use = get_installation_name_for_owner_for_task (
249- self .name , commit .repository .author
250- )
251- repository_service = get_repo_provider_service_for_specific_commit (
252- commit , installation_name_to_use
253- )
254- except RepositoryWithoutValidBotError :
255- save_commit_error (
256- commit , error_code = CommitErrorTypes .REPO_BOT_INVALID .value
257- )
258-
259- log .warning (
260- "Unable to start notifications because repo doesn't have a valid bot" ,
261- extra = {"repoid" : repoid , "commit" : commitid },
262- )
263- self .log_checkpoint (UploadFlow .NOTIF_NO_VALID_INTEGRATION )
264- self ._call_upload_breadcrumb_task (
265- commit_sha = commitid ,
266- repo_id = repoid ,
267- milestone = milestone ,
268- error = Errors .REPO_MISSING_VALID_BOT ,
269- )
270- return {"notified" : False , "notifications" : None , "reason" : "no_valid_bot" }
271- except NoConfiguredAppsAvailable as exp :
272- if exp .rate_limited_count > 0 :
273- # There's at least 1 app that we can use to communicate with GitHub,
274- # but this app happens to be rate limited now. We try again later.
275- # Min wait time of 1 minute
276- retry_delay_seconds = max (60 , get_seconds_to_next_hour ())
277- log .warning (
278- "Unable to start notifications. Retrying again later." ,
279- extra = {
280- "repoid" : repoid ,
281- "commit" : commitid ,
282- "apps_available" : exp .apps_count ,
283- "apps_rate_limited" : exp .rate_limited_count ,
284- "apps_suspended" : exp .suspended_count ,
285- "countdown_seconds" : retry_delay_seconds ,
286- },
287- )
288- self ._call_upload_breadcrumb_task (
289- commit_sha = commitid ,
290- repo_id = repoid ,
291- milestone = milestone ,
292- error = Errors .INTERNAL_APP_RATE_LIMITED ,
293- )
294- return self ._attempt_retry (
295- max_retries = 10 ,
296- countdown = retry_delay_seconds ,
297- current_yaml = current_yaml ,
298- commit = commit ,
299- ** kwargs ,
300- )
301- # Maybe we have apps that are suspended. We can't communicate with github.
302- log .warning (
303- "We can't find an app to communicate with GitHub. Not notifying." ,
304- extra = {
305- "repoid" : repoid ,
306- "commit" : commitid ,
307- "apps_available" : exp .apps_count ,
308- "apps_suspended" : exp .suspended_count ,
309- },
310- )
311- self .log_checkpoint (UploadFlow .NOTIF_NO_APP_INSTALLATION )
312- return {
313- "notified" : False ,
314- "notifications" : None ,
315- "reason" : "no_valid_github_app_found" ,
316- }
242+ result = self ._get_repository_service_for_notify (
243+ commit , repoid , commitid , milestone , current_yaml = current_yaml , ** kwargs
244+ )
245+ if isinstance (result , dict ):
246+ return result
247+ repository_service , installation_name_to_use = result
317248
318249 if current_yaml is None :
319250 current_yaml = async_to_sync (get_current_yaml )(commit , repository_service )
320251 else :
321252 current_yaml = UserYaml .from_dict (current_yaml )
322253
323- try :
324- ci_results = self .fetch_and_update_whether_ci_passed (
325- repository_service , commit , current_yaml
326- )
327- except TorngitClientError as ex :
328- log .info (
329- "Unable to fetch CI results due to a client problem. Not notifying user" ,
330- extra = {
331- "repoid" : commit .repoid ,
332- "commit" : commit .commitid ,
333- "code" : ex .code ,
334- },
335- )
336- self .log_checkpoint (UploadFlow .NOTIF_GIT_CLIENT_ERROR )
337- self ._call_upload_breadcrumb_task (
338- commit_sha = commit .commitid ,
339- repo_id = commit .repoid ,
340- milestone = milestone ,
341- error = Errors .GIT_CLIENT_ERROR ,
342- )
343- return {
344- "notified" : False ,
345- "notifications" : None ,
346- "reason" : "not_able_fetch_ci_result" ,
347- }
348- except TorngitServerFailureError :
349- log .info (
350- "Unable to fetch CI results due to server issues. Not notifying user" ,
351- extra = {"repoid" : commit .repoid , "commit" : commit .commitid },
352- )
353- self .log_checkpoint (UploadFlow .NOTIF_GIT_SERVICE_ERROR )
354- self ._call_upload_breadcrumb_task (
355- commit_sha = commit .commitid ,
356- repo_id = commit .repoid ,
357- milestone = milestone ,
358- error = Errors .GIT_CLIENT_ERROR ,
359- )
360- return {
361- "notified" : False ,
362- "notifications" : None ,
363- "reason" : "server_issues_ci_result" ,
364- }
254+ ci_result = self ._fetch_ci_results_or_error (
255+ repository_service , commit , current_yaml , milestone
256+ )
257+ if isinstance (ci_result , dict ):
258+ return ci_result
259+ ci_results = ci_result
260+
365261 if self .should_wait_longer (current_yaml , commit , ci_results ):
366- log .info (
367- "Not sending notifications yet because we are waiting for CI to finish" ,
368- extra = {"repoid" : commit .repoid , "commit" : commit .commitid },
369- )
370- ghapp_default_installations = list (
371- filter (
372- lambda obj : obj .name == installation_name_to_use
373- and obj .is_configured (),
374- commit .repository .author .github_app_installations or [],
375- )
376- )
377- rely_on_webhook_ghapp = ghapp_default_installations != [] and any (
378- obj .is_repo_covered_by_integration (commit .repository )
379- for obj in ghapp_default_installations
380- )
381- rely_on_webhook_legacy = commit .repository .using_integration
382- if (
383- rely_on_webhook_ghapp
384- or rely_on_webhook_legacy
385- or commit .repository .hookid
386- ):
387- # rely on the webhook, but still retry in case we miss the webhook
388- max_retries = 5
389- countdown = (60 * 3 ) * 2 ** self .request .retries
390- else :
391- max_retries = 10
392- countdown = 15 * 2 ** self .request .retries
393- return self ._attempt_retry (
394- max_retries = max_retries ,
395- countdown = countdown ,
396- current_yaml = current_yaml ,
397- commit = commit ,
398- ** kwargs ,
262+ return self ._handle_ci_wait_retry (
263+ commit , current_yaml , installation_name_to_use , ** kwargs
399264 )
400265
401266 report_service = ReportService (
@@ -531,6 +396,191 @@ def run_impl_within_lock(
531396 )
532397 return {"notified" : False , "notifications" : None }
533398
399+ def _fetch_commit (self , db_session : Session , repoid : int , commitid : str ) -> Commit :
400+ """Fetch and validate the commit from the database."""
401+ commit = (
402+ db_session .query (Commit )
403+ .filter (Commit .repoid == repoid , Commit .commitid == commitid )
404+ .first ()
405+ )
406+ assert commit , "Commit not found in database."
407+ return commit
408+
409+ def _get_repository_service_for_notify (
410+ self , commit , repoid , commitid , milestone , current_yaml = None , ** kwargs
411+ ):
412+ """Get the repository service for notification, handling bot/app errors.
413+
414+ Returns (repository_service, installation_name) on success, or a dict
415+ result for early return on error.
416+ """
417+ try :
418+ installation_name_to_use = get_installation_name_for_owner_for_task (
419+ self .name , commit .repository .author
420+ )
421+ repository_service = get_repo_provider_service_for_specific_commit (
422+ commit , installation_name_to_use
423+ )
424+ return (repository_service , installation_name_to_use )
425+ except RepositoryWithoutValidBotError :
426+ save_commit_error (
427+ commit , error_code = CommitErrorTypes .REPO_BOT_INVALID .value
428+ )
429+ log .warning (
430+ "Unable to start notifications because repo doesn't have a valid bot" ,
431+ extra = {"repoid" : repoid , "commit" : commitid },
432+ )
433+ self .log_checkpoint (UploadFlow .NOTIF_NO_VALID_INTEGRATION )
434+ self ._call_upload_breadcrumb_task (
435+ commit_sha = commitid ,
436+ repo_id = repoid ,
437+ milestone = milestone ,
438+ error = Errors .REPO_MISSING_VALID_BOT ,
439+ )
440+ return {"notified" : False , "notifications" : None , "reason" : "no_valid_bot" }
441+ except NoConfiguredAppsAvailable as exp :
442+ return self ._handle_no_configured_apps (
443+ commit , exp , repoid , commitid , milestone , current_yaml , ** kwargs
444+ )
445+
446+ def _handle_no_configured_apps (
447+ self , commit , exp , repoid , commitid , milestone , current_yaml , ** kwargs
448+ ):
449+ """Handle the case where no configured GitHub apps are available.
450+
451+ Either retries (if apps are rate-limited) or returns an error result.
452+ """
453+ if exp .rate_limited_count > 0 :
454+ # There's at least 1 app that we can use to communicate with GitHub,
455+ # but this app happens to be rate limited now. We try again later.
456+ # Min wait time of 1 minute
457+ retry_delay_seconds = max (60 , get_seconds_to_next_hour ())
458+ log .warning (
459+ "Unable to start notifications. Retrying again later." ,
460+ extra = {
461+ "repoid" : repoid ,
462+ "commit" : commitid ,
463+ "apps_available" : exp .apps_count ,
464+ "apps_rate_limited" : exp .rate_limited_count ,
465+ "apps_suspended" : exp .suspended_count ,
466+ "countdown_seconds" : retry_delay_seconds ,
467+ },
468+ )
469+ self ._call_upload_breadcrumb_task (
470+ commit_sha = commitid ,
471+ repo_id = repoid ,
472+ milestone = milestone ,
473+ error = Errors .INTERNAL_APP_RATE_LIMITED ,
474+ )
475+ return self ._attempt_retry (
476+ max_retries = 10 ,
477+ countdown = retry_delay_seconds ,
478+ current_yaml = current_yaml ,
479+ commit = commit ,
480+ ** kwargs ,
481+ )
482+ # Maybe we have apps that are suspended. We can't communicate with github.
483+ log .warning (
484+ "We can't find an app to communicate with GitHub. Not notifying." ,
485+ extra = {
486+ "repoid" : repoid ,
487+ "commit" : commitid ,
488+ "apps_available" : exp .apps_count ,
489+ "apps_suspended" : exp .suspended_count ,
490+ },
491+ )
492+ self .log_checkpoint (UploadFlow .NOTIF_NO_APP_INSTALLATION )
493+ return {
494+ "notified" : False ,
495+ "notifications" : None ,
496+ "reason" : "no_valid_github_app_found" ,
497+ }
498+
499+ def _fetch_ci_results_or_error (
500+ self , repository_service , commit , current_yaml , milestone
501+ ):
502+ """Fetch CI results, handling provider errors.
503+
504+ Returns ci_results on success, or a dict result for early return on error.
505+ """
506+ try :
507+ return self .fetch_and_update_whether_ci_passed (
508+ repository_service , commit , current_yaml
509+ )
510+ except TorngitClientError as ex :
511+ log .info (
512+ "Unable to fetch CI results due to a client problem. Not notifying user" ,
513+ extra = {
514+ "repoid" : commit .repoid ,
515+ "commit" : commit .commitid ,
516+ "code" : ex .code ,
517+ },
518+ )
519+ self .log_checkpoint (UploadFlow .NOTIF_GIT_CLIENT_ERROR )
520+ self ._call_upload_breadcrumb_task (
521+ commit_sha = commit .commitid ,
522+ repo_id = commit .repoid ,
523+ milestone = milestone ,
524+ error = Errors .GIT_CLIENT_ERROR ,
525+ )
526+ return {
527+ "notified" : False ,
528+ "notifications" : None ,
529+ "reason" : "not_able_fetch_ci_result" ,
530+ }
531+ except TorngitServerFailureError :
532+ log .info (
533+ "Unable to fetch CI results due to server issues. Not notifying user" ,
534+ extra = {"repoid" : commit .repoid , "commit" : commit .commitid },
535+ )
536+ self .log_checkpoint (UploadFlow .NOTIF_GIT_SERVICE_ERROR )
537+ self ._call_upload_breadcrumb_task (
538+ commit_sha = commit .commitid ,
539+ repo_id = commit .repoid ,
540+ milestone = milestone ,
541+ error = Errors .GIT_CLIENT_ERROR ,
542+ )
543+ return {
544+ "notified" : False ,
545+ "notifications" : None ,
546+ "reason" : "server_issues_ci_result" ,
547+ }
548+
549+ def _handle_ci_wait_retry (
550+ self , commit , current_yaml , installation_name_to_use , ** kwargs
551+ ):
552+ """Handle retrying when CI hasn't finished yet."""
553+ log .info (
554+ "Not sending notifications yet because we are waiting for CI to finish" ,
555+ extra = {"repoid" : commit .repoid , "commit" : commit .commitid },
556+ )
557+ ghapp_default_installations = list (
558+ filter (
559+ lambda obj : obj .name == installation_name_to_use
560+ and obj .is_configured (),
561+ commit .repository .author .github_app_installations or [],
562+ )
563+ )
564+ rely_on_webhook_ghapp = ghapp_default_installations != [] and any (
565+ obj .is_repo_covered_by_integration (commit .repository )
566+ for obj in ghapp_default_installations
567+ )
568+ rely_on_webhook_legacy = commit .repository .using_integration
569+ if rely_on_webhook_ghapp or rely_on_webhook_legacy or commit .repository .hookid :
570+ # rely on the webhook, but still retry in case we miss the webhook
571+ max_retries = 5
572+ countdown = (60 * 3 ) * 2 ** self .request .retries
573+ else :
574+ max_retries = 10
575+ countdown = 15 * 2 ** self .request .retries
576+ return self ._attempt_retry (
577+ max_retries = max_retries ,
578+ countdown = countdown ,
579+ current_yaml = current_yaml ,
580+ commit = commit ,
581+ ** kwargs ,
582+ )
583+
534584 def is_using_codecov_commenter (
535585 self , repository_service : TorngitBaseAdapter
536586 ) -> bool :
0 commit comments