@@ -148,20 +148,20 @@ module Action (Github_api : Api.Github) (Slack_api : Api.Slack) = struct
148
148
let partition_status (ctx : Context.t ) (n : status_notification ) =
149
149
let repo = n.repository in
150
150
let cfg = Context. find_repo_config_exn ctx repo.url in
151
- let pipeline = n.context in
152
- let current_status = n.state in
151
+ let context = n.context in
153
152
let rules = cfg.status_rules.rules in
154
- let is_main_branch =
155
- match cfg.main_branch_name with
156
- | None -> false
157
- | Some main_branch -> List. exists (fun ({ name } : branch ) -> String. equal name main_branch) n.branches
158
- in
153
+ let repo_state = State. find_or_add_repo ctx.state repo.url in
159
154
let action_on_match (branches : branch list ) ~notify_channels ~notify_dm =
155
+ let is_main_branch =
156
+ match cfg.main_branch_name with
157
+ | None -> false
158
+ | Some main_branch -> List. exists (fun ({ name } : branch ) -> String. equal name main_branch) n.branches
159
+ in
160
160
let % lwt direct_message =
161
161
if notify_dm then begin
162
162
match % lwt Slack_api. lookup_user ~ctx ~cfg ~email: n.commit.commit.author.email () with
163
163
| Ok res ->
164
- State. set_repo_pipeline_commit ctx.state repo.url ~pipeline ~commit: n.sha ;
164
+ State. set_repo_pipeline_commit ctx.state n ;
165
165
(* To send a DM, channel parameter is set to the user id of the recipient *)
166
166
Lwt. return [ Slack_user_id. to_channel_id res.user.id ]
167
167
| Error e ->
@@ -175,30 +175,37 @@ module Action (Github_api : Api.Github) (Slack_api : Api.Slack) = struct
175
175
| false , _ | _ , [] -> Lwt. return []
176
176
| _ ->
177
177
(* non-main branch build notifications go to default channel to reduce spam in topic channels *)
178
- match cfg.main_branch_name, is_main_branch with
179
- | None , _ | _ , false ->
178
+ match is_main_branch with
179
+ | false ->
180
180
Lwt. return (Option. map_default (fun c -> [ Slack_channel. to_any c ]) [] cfg.prefix_rules.default_channel)
181
- | Some _ , true ->
182
- let sha = n.commit.sha in
183
- (match % lwt Github_api. get_api_commit ~ctx ~repo ~sha with
181
+ | true ->
182
+ (match % lwt Github_api. get_api_commit ~ctx ~repo ~sha: n.commit.sha with
184
183
| Error e -> action_error e
185
184
| Ok commit ->
186
185
let chans = partition_commit cfg commit.files in
187
186
Lwt. return (List. map Slack_channel. to_any chans))
188
187
in
188
+ (* only notify the failed builds channels for full failed builds with new failed steps on the main branch *)
189
189
let notify_failed_builds_channel =
190
- (* we only notify the failed builds channels for failed builds on the main branch *)
191
- Util.Build. is_failed_build n && Option. is_some cfg.status_rules.allowed_pipelines && is_main_branch
190
+ is_main_branch
191
+ && Util.Build. is_failed_build n
192
+ && Util.Build. new_failed_steps n repo_state <> []
193
+ && Option. map_default
194
+ (fun allowed_pipelines ->
195
+ List. exists
196
+ (fun { failed_builds_channel; name } -> name = context && Option. is_some failed_builds_channel)
197
+ allowed_pipelines)
198
+ false cfg.status_rules.allowed_pipelines
192
199
in
193
200
match notify_failed_builds_channel, cfg.status_rules.allowed_pipelines with
194
- | false , _ | true , None -> Lwt. return (direct_message @ chans)
201
+ | false , _ | _ , None -> Lwt. return (direct_message @ chans)
195
202
| true , Some allowed_pipelines ->
196
- (* if we have a failed build and a failed builds channel, we send one notification there too,
203
+ (* if we have a failed builds channel configured , we send one notification there too,
197
204
but we don't notify the same channel twice *)
198
205
let chans =
199
206
List. find_map
200
207
(fun ({ name; failed_builds_channel } : Config_t.pipeline ) ->
201
- match String. equal name n. context, failed_builds_channel with
208
+ match String. equal name context, failed_builds_channel with
202
209
| true , Some failed_builds_channel -> Some (Slack_channel. to_any failed_builds_channel :: chans)
203
210
| _ -> None )
204
211
allowed_pipelines
@@ -208,32 +215,50 @@ module Action (Github_api : Api.Github) (Slack_api : Api.Slack) = struct
208
215
Lwt. return (direct_message @ chans)
209
216
in
210
217
let % lwt recipients =
211
- if Context. is_pipeline_allowed ctx repo.url ~pipeline then begin
212
- let repo_state = State. find_or_add_repo ctx.state repo.url in
218
+ if Context. is_pipeline_allowed ctx repo.url ~context then begin
213
219
match Rule.Status. match_rules ~rules n with
214
220
| Some (Ignore , _ , _ ) | None -> Lwt. return []
215
221
| Some (Allow, notify_channels , notify_dm ) -> action_on_match n.branches ~notify_channels ~notify_dm
216
222
| Some (Allow_once, notify_channels , notify_dm ) ->
217
223
let branches =
218
- match StringMap. find_opt pipeline repo_state.pipeline_statuses with
219
- | Some branch_statuses ->
220
- let has_same_status_as_prev (branch : branch ) =
221
- match StringMap. find_opt branch.name branch_statuses with
222
- | Some { status; _ } when status = current_status -> true
223
- | _ -> false
224
- in
225
- let branches = List. filter (Fun. negate has_same_status_as_prev) n.branches in
226
- branches
224
+ match n.target_url with
227
225
| None -> n.branches
226
+ | Some build_url ->
227
+ let pipeline_name =
228
+ (* We only need to track messages for the base pipeline, not the steps *)
229
+ match Util.Build. parse_context ~context ~build_url with
230
+ | Ok { Util.Build. pipeline_name; _ } -> pipeline_name
231
+ | Error _ -> context
232
+ in
233
+ (match StringMap. find_opt pipeline_name repo_state.pipeline_statuses with
234
+ | None ->
235
+ (* this is the first notification for a pipeline, so no need to filter branches *)
236
+ n.branches
237
+ | Some branch_statuses ->
238
+ let has_same_status (branch : branch ) =
239
+ match StringMap. find_opt branch.name branch_statuses with
240
+ | Some build_statuses ->
241
+ let current = Util.Build. get_build_number_exn ~context ~build_url in
242
+ let previous_builds = StringMap. filter (fun build_num _ -> build_num < current) build_statuses in
243
+ (match StringMap. is_empty previous_builds with
244
+ | true ->
245
+ (* if we have no previous builds, it means they were successful and cleaned from state *)
246
+ n.state = Github_t. Success
247
+ | false ->
248
+ let _, previous_build = StringMap. max_binding previous_builds in
249
+ previous_build.status = n.state)
250
+ | None ->
251
+ (* if we don't have any builds for this branch yet, it's the first notification for this pipeline *)
252
+ false
253
+ in
254
+ List. filter (Fun. negate has_same_status) n.branches)
228
255
in
229
- let notify_dm =
230
- notify_dm && not (State. mem_repo_pipeline_commits ctx.state repo.url ~pipeline ~commit: n.sha)
231
- in
256
+ let notify_dm = notify_dm && not (State. mem_repo_pipeline_commits ctx.state n) in
232
257
action_on_match branches ~notify_channels ~notify_dm
233
258
end
234
259
else Lwt. return []
235
260
in
236
- State. set_repo_pipeline_status ctx.state repo.url ~pipeline n;
261
+ State. set_repo_pipeline_status ctx.state n;
237
262
Lwt. return recipients
238
263
239
264
let partition_commit_comment (ctx : Context.t ) n =
0 commit comments