@@ -10,6 +10,46 @@ let action_error msg = raise (Action_error msg)
10
10
let log = Log. from " action"
11
11
12
12
module Action (Github_api : Api.Github ) (Slack_api : Api.Slack ) = struct
13
+ let canonical_regex = Re2. create_exn {| \.| \-| \+. *|@.*| }
14
+ (* Match email domain, everything after '+', as well as dots and hyphens *)
15
+
16
+ let username_to_slack_id_tbl = Stringtbl. empty ()
17
+
18
+ let canonicalize_email_username email =
19
+ email |> Re2. rewrite_exn ~template: " " canonical_regex |> String. lowercase_ascii
20
+
21
+ let refresh_username_to_slack_id_tbl ~ctx =
22
+ log#info " updating github to slack username mapping" ;
23
+ match % lwt Slack_api. list_users ~ctx () with
24
+ | Error e ->
25
+ log#warn " couldn't fetch list of Slack users: %s" e;
26
+ Lwt. return_unit
27
+ | Ok res ->
28
+ List. iter
29
+ (fun (user : Slack_t.user ) ->
30
+ match user.profile.email with
31
+ | None -> ()
32
+ | Some email ->
33
+ let username = canonicalize_email_username email in
34
+ Stringtbl. replace username_to_slack_id_tbl username user.id
35
+ )
36
+ res.members;
37
+ Lwt. return_unit
38
+
39
+ let rec refresh_username_to_slack_id_tbl_background_lwt ~ctx : unit Lwt. t =
40
+ let % lwt () = refresh_username_to_slack_id_tbl ~ctx in
41
+ let % lwt () = Lwt_unix. sleep (Time. days 1 ) in
42
+ (* Updates mapping every 24 hours *)
43
+ refresh_username_to_slack_id_tbl_background_lwt ~ctx
44
+
45
+ let match_github_login_to_slack_id cfg_opt login =
46
+ let login =
47
+ match cfg_opt with
48
+ | None -> login
49
+ | Some cfg -> List. assoc_opt login cfg.user_mappings |> Option. default login
50
+ in
51
+ login |> canonicalize_email_username |> Stringtbl. find_opt username_to_slack_id_tbl
52
+
13
53
let partition_push (cfg : Config_t.config ) n =
14
54
let default = Stdlib.Option. to_list cfg.prefix_rules.default_channel in
15
55
let rules = cfg.prefix_rules.rules in
@@ -191,21 +231,27 @@ module Action (Github_api : Api.Github) (Slack_api : Api.Slack) = struct
191
231
let generate_notifications (ctx : Context.t ) (req : Github.t ) =
192
232
let repo = Github. repo_of_notification req in
193
233
let cfg = Context. find_repo_config_exn ctx repo.url in
234
+ let slack_match_func = match_github_login_to_slack_id (Some cfg) in
194
235
match ignore_notifications_from_user cfg req with
195
236
| true -> Lwt. return []
196
237
| false ->
197
238
match req with
198
239
| Github. Push n ->
199
240
partition_push cfg n |> List. map (fun (channel , n ) -> generate_push_notification n channel) |> Lwt. return
200
- | Pull_request n -> partition_pr cfg n |> List. map (generate_pull_request_notification n) |> Lwt. return
201
- | PR_review n -> partition_pr_review cfg n |> List. map (generate_pr_review_notification n) |> Lwt. return
241
+ | Pull_request n ->
242
+ partition_pr cfg n |> List. map (generate_pull_request_notification ~slack_match_func n) |> Lwt. return
243
+ | PR_review n ->
244
+ partition_pr_review cfg n |> List. map (generate_pr_review_notification ~slack_match_func n) |> Lwt. return
202
245
| PR_review_comment n ->
203
- partition_pr_review_comment cfg n |> List. map (generate_pr_review_comment_notification n) |> Lwt. return
204
- | Issue n -> partition_issue cfg n |> List. map (generate_issue_notification n) |> Lwt. return
205
- | Issue_comment n -> partition_issue_comment cfg n |> List. map (generate_issue_comment_notification n) |> Lwt. return
246
+ partition_pr_review_comment cfg n
247
+ |> List. map (generate_pr_review_comment_notification ~slack_match_func n)
248
+ |> Lwt. return
249
+ | Issue n -> partition_issue cfg n |> List. map (generate_issue_notification ~slack_match_func n) |> Lwt. return
250
+ | Issue_comment n ->
251
+ partition_issue_comment cfg n |> List. map (generate_issue_comment_notification ~slack_match_func n) |> Lwt. return
206
252
| Commit_comment n ->
207
253
let % lwt channels, api_commit = partition_commit_comment ctx n in
208
- let notifs = List. map (generate_commit_comment_notification api_commit n) channels in
254
+ let notifs = List. map (generate_commit_comment_notification ~slack_match_func api_commit n) channels in
209
255
Lwt. return notifs
210
256
| Status n ->
211
257
let % lwt channels = partition_status ctx n in
@@ -220,28 +266,28 @@ module Action (Github_api : Api.Github) (Slack_api : Api.Slack) = struct
220
266
in
221
267
Lwt_list. iter_s notify notifications
222
268
269
+ let fetch_config ~ctx ~repo =
270
+ match % lwt Github_api. get_config ~ctx ~repo with
271
+ | Ok config ->
272
+ Context. set_repo_config ctx repo.url config;
273
+ Context. print_config ctx repo.url;
274
+ Lwt. return @@ Ok ()
275
+ | Error e -> action_error e
276
+
223
277
(* * [refresh_repo_config ctx n] fetches the latest repo config if it's
224
278
uninitialized, or if the incoming request [n] is a push
225
279
notification containing commits that touched the config file. *)
226
280
let refresh_repo_config (ctx : Context.t ) notification =
227
281
let repo = Github. repo_of_notification notification in
228
- let fetch_config () =
229
- match % lwt Github_api. get_config ~ctx ~repo with
230
- | Ok config ->
231
- Context. set_repo_config ctx repo.url config;
232
- Context. print_config ctx repo.url;
233
- Lwt. return @@ Ok ()
234
- | Error e -> action_error e
235
- in
236
282
match Context. find_repo_config ctx repo.url with
237
- | None -> fetch_config ()
283
+ | None -> fetch_config ~ctx ~repo
238
284
| Some _ ->
239
285
match notification with
240
286
| Github. Push commit_pushed_notification ->
241
287
let commits = commit_pushed_notification.commits in
242
288
let modified_files = List. concat_map Github. modified_files_of_commit commits in
243
289
let config_was_modified = List. exists (String. equal ctx.config_filename) modified_files in
244
- if config_was_modified then fetch_config () else Lwt. return @@ Ok ()
290
+ if config_was_modified then fetch_config ~ctx ~repo else Lwt. return @@ Ok ()
245
291
| _ -> Lwt. return @@ Ok ()
246
292
247
293
let do_github_tasks ctx (repo : repository ) (req : Github.t ) =
@@ -265,15 +311,15 @@ module Action (Github_api : Api.Github) (Slack_api : Api.Slack) = struct
265
311
end
266
312
| _ -> Lwt. return_unit
267
313
314
+ let repo_is_supported secrets (repo : Github_t.repository ) =
315
+ List. exists (fun (r : repo_config ) -> String. equal r.url repo.url) secrets.repos
316
+
268
317
let process_github_notification (ctx : Context.t ) headers body =
269
318
let validate_signature secrets payload =
270
319
let repo = Github. repo_of_notification payload in
271
320
let signing_key = Context. gh_hook_secret_token_of_secrets secrets repo.url in
272
321
Github. validate_signature ?signing_key ~headers body
273
322
in
274
- let repo_is_supported secrets (repo : Github_t.repository ) =
275
- List. exists (fun (r : repo_config ) -> String. equal r.url repo.url) secrets.repos
276
- in
277
323
try % lwt
278
324
let secrets = Context. get_secrets_exn ctx in
279
325
match Github. parse_exn headers body with
@@ -337,9 +383,7 @@ module Action (Github_api : Api.Github) (Slack_api : Api.Slack) = struct
337
383
Lwt. return_none
338
384
in
339
385
let process link =
340
- let with_gh_result_populate_slack (type a ) ~(api_result : (a, string) Result.t )
341
- ~(populate : repository -> a -> Slack_t.message_attachment ) ~repo
342
- =
386
+ let with_gh_result_populate_slack (type a ) ~(api_result : (a, string) Result.t ) ~populate ~repo =
343
387
match api_result with
344
388
| Error _ -> Lwt. return_none
345
389
| Ok item -> Lwt. return_some @@ (link, populate repo item)
0 commit comments