Skip to content

Commit e0c23ba

Browse files
committed
add force notify option
1 parent 69d34ab commit e0c23ba

File tree

6 files changed

+144
-57
lines changed

6 files changed

+144
-57
lines changed

apps/worker/services/comparison/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class ComparisonContext:
3333
# We need to send commit statuses to this other commit, to guarantee that the check is not ignored.
3434
# See https://docs.gitlab.com/ee/ci/pipelines/merged_results_pipelines.html#successful-merged-results-pipeline-overrides-a-failed-branch-pipeline
3535
gitlab_extra_shas: set[str] | None = None
36+
force_notify: bool = False
3637

3738

3839
NOT_RESOLVED: Any = object()

apps/worker/services/notification/notifiers/checks/base.py

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,18 @@ def notify(
9595
comparison: ComparisonProxy,
9696
status_or_checks_helper_text: dict[str, str] | None = None,
9797
) -> NotificationResult:
98+
force_notify = getattr(comparison.context, "force_notify", False)
99+
100+
if not force_notify:
101+
validation_result = self._perform_validation_checks(comparison)
102+
if validation_result:
103+
return validation_result
104+
105+
return self._perform_notification(comparison)
106+
107+
def _perform_validation_checks(
108+
self, comparison: ComparisonProxy
109+
) -> NotificationResult | None:
98110
if comparison.pull is None or ():
99111
log.debug(
100112
"Falling back to commit_status: Not a pull request",
@@ -132,7 +144,7 @@ def notify(
132144
data_sent=None,
133145
data_received=None,
134146
)
135-
if comparison.pull.state != "open":
147+
if comparison.pull and comparison.pull.state != "open":
136148
log.debug(
137149
"Falling back to commit_status: Pull request closed",
138150
extra={
@@ -187,28 +199,37 @@ def notify(
187199
data_sent=None,
188200
data_received=None,
189201
)
202+
203+
flag_coverage_not_uploaded_behavior = (
204+
self.determine_status_check_behavior_to_apply(
205+
comparison, "flag_coverage_not_uploaded_behavior"
206+
)
207+
)
208+
if (
209+
flag_coverage_not_uploaded_behavior == "exclude"
210+
and not self.flag_coverage_was_uploaded(comparison)
211+
):
212+
return NotificationResult(
213+
notification_attempted=False,
214+
notification_successful=None,
215+
explanation="exclude_flag_coverage_not_uploaded_checks",
216+
data_sent=None,
217+
data_received=None,
218+
)
219+
220+
return None
221+
222+
def _perform_notification(self, comparison: ComparisonProxy) -> NotificationResult:
190223
payload = None
191224
try:
192225
with nullcontext():
193-
# If flag coverage wasn't uploaded, apply the appropriate behavior
194226
flag_coverage_not_uploaded_behavior = (
195227
self.determine_status_check_behavior_to_apply(
196228
comparison, "flag_coverage_not_uploaded_behavior"
197229
)
198230
)
199231
if not comparison.has_head_report():
200232
payload = self.build_payload(comparison)
201-
elif (
202-
flag_coverage_not_uploaded_behavior == "exclude"
203-
and not self.flag_coverage_was_uploaded(comparison)
204-
):
205-
return NotificationResult(
206-
notification_attempted=False,
207-
notification_successful=None,
208-
explanation="exclude_flag_coverage_not_uploaded_checks",
209-
data_sent=None,
210-
data_received=None,
211-
)
212233
elif (
213234
flag_coverage_not_uploaded_behavior == "pass"
214235
and not self.flag_coverage_was_uploaded(comparison)

apps/worker/services/notification/notifiers/comment/__init__.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -92,19 +92,25 @@ def notify(
9292
},
9393
)
9494

95-
for condition in self.notify_conditions:
96-
condition_result = condition.check_condition(
97-
notifier=self, comparison=comparison
98-
)
99-
if condition_result == False:
100-
side_effect_result = condition.on_failure_side_effect(self, comparison)
101-
default_result = NotificationResult(
102-
notification_attempted=False,
103-
explanation=condition.failure_explanation,
104-
data_sent=None,
105-
data_received=None,
95+
force_notify = getattr(comparison.context, "force_notify", False)
96+
97+
if not force_notify:
98+
for condition in self.notify_conditions:
99+
condition_result = condition.check_condition(
100+
notifier=self, comparison=comparison
106101
)
107-
return default_result.merge(side_effect_result)
102+
if condition_result == False:
103+
side_effect_result = condition.on_failure_side_effect(
104+
self, comparison
105+
)
106+
default_result = NotificationResult(
107+
notification_attempted=False,
108+
explanation=condition.failure_explanation,
109+
data_sent=None,
110+
data_received=None,
111+
)
112+
return default_result.merge(side_effect_result)
113+
108114
pull = comparison.pull
109115
try:
110116
message = self.build_message(

apps/worker/services/notification/notifiers/generics.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,10 @@ def notify(
8888
filtered_comparison = comparison.get_filtered_comparison(
8989
**self.get_notifier_filters()
9090
)
91-
if self.should_notify_comparison(filtered_comparison):
91+
92+
force_notify = getattr(comparison.context, "force_notify", False)
93+
94+
if force_notify or self.should_notify_comparison(filtered_comparison):
9295
result = self.do_notify(filtered_comparison)
9396
else:
9497
result = NotificationResult(

apps/worker/services/notification/notifiers/status/base.py

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,18 @@ def notify(
148148
comparison: ComparisonProxy,
149149
status_or_checks_helper_text: dict[str, str] | None = None,
150150
) -> NotificationResult:
151-
payload = None
151+
force_notify = getattr(comparison.context, "force_notify", False)
152+
153+
if not force_notify:
154+
validation_result = self._perform_validation_checks(comparison)
155+
if validation_result:
156+
return validation_result
157+
158+
return self._perform_notification(comparison)
159+
160+
def _perform_validation_checks(
161+
self, comparison: ComparisonProxy
162+
) -> NotificationResult | None:
152163
if not self.can_we_set_this_status(comparison):
153164
return NotificationResult(
154165
notification_attempted=False,
@@ -163,6 +174,28 @@ def notify(
163174
explanation="need_more_builds",
164175
data_sent=None,
165176
)
177+
178+
# Check flag coverage behavior when force_notify is False
179+
flag_coverage_not_uploaded_behavior = (
180+
self.determine_status_check_behavior_to_apply(
181+
comparison, "flag_coverage_not_uploaded_behavior"
182+
)
183+
)
184+
if (
185+
flag_coverage_not_uploaded_behavior == "exclude"
186+
and not self.flag_coverage_was_uploaded(comparison)
187+
):
188+
return NotificationResult(
189+
notification_attempted=False,
190+
notification_successful=None,
191+
explanation="exclude_flag_coverage_not_uploaded_checks",
192+
data_sent=None,
193+
data_received=None,
194+
)
195+
196+
return None
197+
198+
def _perform_notification(self, comparison: ComparisonProxy) -> NotificationResult:
166199
# Filter the coverage report based on fields in this notification's YAML settings
167200
# e.g. if "paths" is specified, exclude the coverage not on those paths
168201
try:
@@ -174,17 +207,6 @@ def notify(
174207
)
175208
if not comparison.has_head_report():
176209
payload = self.build_payload(comparison)
177-
elif (
178-
flag_coverage_not_uploaded_behavior == "exclude"
179-
and not self.flag_coverage_was_uploaded(comparison)
180-
):
181-
return NotificationResult(
182-
notification_attempted=False,
183-
notification_successful=None,
184-
explanation="exclude_flag_coverage_not_uploaded_checks",
185-
data_sent=None,
186-
data_received=None,
187-
)
188210
elif (
189211
flag_coverage_not_uploaded_behavior == "pass"
190212
and not self.flag_coverage_was_uploaded(comparison)
@@ -262,6 +284,11 @@ def get_status_external_name(self) -> str:
262284
def maybe_send_notification(
263285
self, comparison: ComparisonProxy, payload: dict
264286
) -> NotificationResult:
287+
force_notify = getattr(comparison.context, "force_notify", False)
288+
289+
if force_notify:
290+
return self.send_notification(comparison, payload)
291+
265292
base_commit = (
266293
comparison.project_coverage_base.commit
267294
if comparison.project_coverage_base
@@ -306,14 +333,19 @@ def maybe_send_notification(
306333
)
307334

308335
def send_notification(self, comparison: ComparisonProxy, payload):
336+
force_notify = getattr(comparison.context, "force_notify", False)
337+
309338
repository_service = self.repository_service
310339
title = self.get_status_external_name()
311340
head_commit_sha = comparison.head.commit.commitid
312341
head_report = comparison.head.report
313342
state = payload["state"]
314343
message = payload["message"]
315344
url = payload["url"]
316-
if self.status_already_exists(comparison, title, state, message):
345+
346+
if not force_notify and self.status_already_exists(
347+
comparison, title, state, message
348+
):
317349
log.info(
318350
"Status already set",
319351
extra={"context": title, "description": message, "state": state},

apps/worker/tasks/notify.py

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@
7373
class NotifyTask(BaseCodecovTask, name=notify_task_name):
7474
throws = (SoftTimeLimitExceeded,)
7575

76+
def __init__(self, *args, **kwargs):
77+
super().__init__(*args, **kwargs)
78+
self.force_notify = False
79+
7680
def run_impl(
7781
self,
7882
db_session: Session,
@@ -81,10 +85,12 @@ def run_impl(
8185
commitid: str,
8286
current_yaml=None,
8387
empty_upload=None,
88+
force_notify: bool = False,
8489
**kwargs,
8590
):
91+
self.force_notify = force_notify
8692
redis_connection = get_redis_connection()
87-
if self.has_upcoming_notifies_according_to_redis(
93+
if not self.force_notify and self.has_upcoming_notifies_according_to_redis(
8894
redis_connection, repoid, commitid
8995
):
9096
log.info(
@@ -198,12 +204,14 @@ def run_impl_within_lock(
198204
assert commit, "Commit not found in database."
199205

200206
test_result_commit_report = commit.commit_report(ReportType.TEST_RESULTS)
201-
if (
207+
test_failures_detected = (
202208
test_result_commit_report is not None
203209
and test_result_commit_report.test_result_totals is not None
204210
and not test_result_commit_report.test_result_totals.error
205211
and test_result_commit_report.test_result_totals.failed > 0
206-
):
212+
)
213+
214+
if test_failures_detected and not self.force_notify:
207215
return {
208216
"notify_attempted": False,
209217
"notifications": None,
@@ -288,23 +296,31 @@ def run_impl_within_lock(
288296
},
289297
)
290298
self.log_checkpoint(UploadFlow.NOTIF_GIT_CLIENT_ERROR)
291-
return {
292-
"notified": False,
293-
"notifications": None,
294-
"reason": "not_able_fetch_ci_result",
295-
}
299+
if not self.force_notify:
300+
return {
301+
"notified": False,
302+
"notifications": None,
303+
"reason": "not_able_fetch_ci_result",
304+
}
305+
else:
306+
ci_results = None
296307
except TorngitServerFailureError:
297308
log.info(
298309
"Unable to fetch CI results due to server issues. Not notifying user",
299310
extra={"repoid": commit.repoid, "commit": commit.commitid},
300311
)
301312
self.log_checkpoint(UploadFlow.NOTIF_GIT_SERVICE_ERROR)
302-
return {
303-
"notified": False,
304-
"notifications": None,
305-
"reason": "server_issues_ci_result",
306-
}
307-
if self.should_wait_longer(current_yaml, commit, ci_results):
313+
if not self.force_notify:
314+
return {
315+
"notified": False,
316+
"notifications": None,
317+
"reason": "server_issues_ci_result",
318+
}
319+
else:
320+
ci_results = None
321+
if (not self.force_notify) and self.should_wait_longer(
322+
current_yaml, commit, ci_results
323+
):
308324
log.info(
309325
"Not sending notifications yet because we are waiting for CI to finish",
310326
extra={"repoid": commit.repoid, "commit": commit.commitid},
@@ -346,9 +362,15 @@ def run_impl_within_lock(
346362
head_report = report_service.get_existing_report_for_commit(
347363
commit, report_class=ReadOnlyReport
348364
)
349-
if self.should_send_notifications(
350-
current_yaml, commit, ci_results, head_report
351-
):
365+
366+
if self.force_notify:
367+
should_notify = True
368+
else:
369+
should_notify = self.should_send_notifications(
370+
current_yaml, commit, ci_results, head_report
371+
)
372+
373+
if should_notify:
352374
enriched_pull = async_to_sync(
353375
fetch_and_update_pull_request_information_from_commit
354376
)(repository_service, commit, current_yaml)
@@ -360,7 +382,8 @@ def run_impl_within_lock(
360382
base_commit = self.fetch_parent(commit)
361383

362384
if (
363-
enriched_pull
385+
not self.force_notify
386+
and enriched_pull
364387
and not self.send_notifications_if_commit_differs_from_pulls_head(
365388
commit, enriched_pull, current_yaml
366389
)
@@ -640,6 +663,7 @@ def submit_third_party_notifications(
640663
gh_app_installation_name=installation_name_to_use,
641664
gh_is_using_codecov_commenter=gh_is_using_codecov_commenter,
642665
gitlab_extra_shas=gitlab_extra_shas_to_notify,
666+
force_notify=self.force_notify,
643667
),
644668
)
645669

0 commit comments

Comments
 (0)