diff --git a/dune-project b/dune-project index dad6dc0b..8ef8aff7 100644 --- a/dune-project +++ b/dune-project @@ -1,4 +1,5 @@ (lang dune 2.5) +(implicit_transitive_deps false) (formatting (enabled_for ocaml reason)) diff --git a/lib/action.ml b/lib/action.ml index 6a5c6cb6..0d298d94 100644 --- a/lib/action.ml +++ b/lib/action.ml @@ -9,9 +9,52 @@ open Github_j let log = Log.from "action" -let touching_prefix rule name = - let has_prefix s = List.exists ~f:(fun prefix -> String.is_prefix s ~prefix) in - (List.is_empty rule.prefix || has_prefix name rule.prefix) && not (has_prefix name rule.ignore) +type prefix_match = + | Match of int + | NoMatch + +let chan_of_prefix_rule (r : prefix_rule) = r.chan + +let touching_prefix (rule : Notabot_t.prefix_rule) name = + let match_lengths filename prefixes = + List.filter_map + ~f:(fun prefix -> if String.is_prefix filename ~prefix then Some (String.length prefix) else None) + prefixes + in + match match_lengths name rule.ignore with + | _ :: _ -> NoMatch + | [] -> + match rule.prefix with + | [] -> Match 0 + | _ -> + match List.max_elt (match_lengths name rule.prefix) ~compare:Int.compare with + | Some x -> Match x + | None -> NoMatch + +let longest_touching_prefix_rule rules name = + let get_m rule = touching_prefix rule name in + let reduce_to_longest_match longest_rule_match_pair current_rule = + let _, longest_match = longest_rule_match_pair in + let current_match = get_m current_rule in + let current_rule_match_pair = current_rule, current_match in + match longest_match with + | NoMatch -> current_rule_match_pair + | Match x -> + match current_match with + | NoMatch -> longest_rule_match_pair + | Match y -> if y > x then current_rule_match_pair else longest_rule_match_pair + in + match rules with + | [] -> None + | (x : prefix_rule) :: xs -> + match List.fold_left xs ~init:(x, get_m x) ~f:reduce_to_longest_match with + | _, NoMatch -> None + | r, Match _ -> Some r + +let chan_of_file rules file = Option.map ~f:chan_of_prefix_rule @@ longest_touching_prefix_rule rules file + +let unique_chans_of_files rules files = + List.dedup_and_sort ~compare:String.compare @@ List.filter_map files ~f:(chan_of_file rules) let touching_label rule name = let name_lc = String.lowercase name in @@ -23,6 +66,15 @@ let touching_label rule name = let is_main_merge_message ~msg:message ~branch cfg = match cfg.main_branch_name with + | Some main_branch when String.equal branch main_branch -> + (* + handle "Merge
into " commits when they are merged into main branch + we should have already seen these commits on the feature branch but for some reason they are distinct:true + *) + let prefix = sprintf "Merge branch '%s' into " main_branch in + let prefix2 = sprintf "Merge remote-tracking branch 'origin/%s' into " main_branch in + let title = first_line message in + String.is_prefix title ~prefix || String.is_prefix title ~prefix:prefix2 | Some main_branch -> let expect = sprintf "Merge branch '%s' into %s" main_branch branch in let expect2 = sprintf "Merge remote-tracking branch 'origin/%s' into %s" main_branch branch in @@ -31,18 +83,12 @@ let is_main_merge_message ~msg:message ~branch cfg = | _ -> false let filter_push rules commit = - let matching_push rule files = List.exists files ~f:(fun file -> touching_prefix rule file) in - List.filter_map rules ~f:(fun rule -> - let filter = - matching_push rule commit.added || matching_push rule commit.removed || matching_push rule commit.modified - in - match filter with - | false -> None - | true -> Some (rule.chan, commit)) + let files = List.concat [ commit.added; commit.removed; commit.modified ] in + List.map ~f:(fun chan -> chan, commit) @@ unique_chans_of_files rules files -let group_commit webhook l = - List.filter_map l ~f:(fun (chan, commit) -> - match String.equal webhook chan with +let group_commit chan l = + List.filter_map l ~f:(fun (chan', commit) -> + match String.equal chan chan' with | false -> None | true -> Some commit) @@ -61,15 +107,14 @@ let partition_push cfg n = match filter_push rules commit with | [] -> default commit | l -> l) + |> List.concat in - let concat_chan = List.concat channels in let prefix_chans = - let chans = List.map rules ~f:(fun (rule : prefix_rule) -> rule.chan) in - let chans = Option.value_map cfg.prefix_rules.default ~default:chans ~f:(fun default -> default :: chans) in + let chans = Option.to_list cfg.prefix_rules.default @ List.map rules ~f:(fun (rule : prefix_rule) -> rule.chan) in List.dedup_and_sort chans ~compare:String.compare in List.filter_map prefix_chans ~f:(fun chan -> - match group_commit chan concat_chan with + match group_commit chan channels with | [] -> None | l -> Some (chan, { n with commits = l })) @@ -81,7 +126,7 @@ let filter_label rules (label : Github_j.label) = | true -> Some rule.chan) let partition_label cfg (labels : Github_j.label list) = - let default = Option.value_map cfg.label_rules.default ~default:[] ~f:(fun webhook -> [ webhook ]) in + let default = Option.to_list cfg.label_rules.default in match labels with | [] -> default | labels -> @@ -131,41 +176,33 @@ let partition_pr_review cfg (n : pr_review_notification) = | Submitted, _, _ -> partition_label cfg n.pull_request.labels | _ -> [] -let filter_commit rules filename = - rules - |> List.filter_map ~f:(fun rule -> - match touching_prefix rule filename with - | false -> None - | true -> Some rule.chan) - let partition_commit cfg files = - let default = Option.value_map cfg.prefix_rules.default ~default:[] ~f:(fun webhook -> [ webhook ]) in - match files with - | [] -> - log#error "this commit contains no files"; - [] - | files -> - let rules = cfg.prefix_rules.rules in - let channels = - files - |> List.map ~f:(fun file -> - match filter_commit rules file.filename with - | [] -> default - | l -> l) - in - List.dedup_and_sort ~compare:String.compare (List.concat channels) + let names = List.map ~f:(fun f -> f.filename) files in + match unique_chans_of_files cfg.prefix_rules.rules names with + | _ :: _ as xs -> xs + | [] -> Option.to_list cfg.prefix_rules.default let hide_cancelled (notification : status_notification) cfg = - let is_cancelled_status = + let find_cancelled status_state = + match status_state with + | Config.Cancelled r -> Some r + | _ -> None + in + let regexp_opt = List.find_map cfg.status_rules.status ~f:find_cancelled in + match regexp_opt with + | None -> false + | Some regexp -> let { state; description; _ } = notification in - let r = Re.Str.regexp_case_fold "^\\(Build #[0-9]+ canceled by .+\\|Failed (exit status 255)\\)$" in - match description, state with + let r = Re.Str.regexp_case_fold regexp in + ( match description, state with | Some s, Failure when Re.Str.string_match r s 0 -> true | _ -> false - in - is_cancelled_status && cfg.suppress_cancelled_events + ) let hide_success (n : status_notification) (ctx : Context.t) = + match List.exists ctx.cfg.status_rules.status ~f:(Poly.equal Config.HideConsecutiveSuccess) with + | false -> false + | true -> match n.state with | Success -> List.exists @@ -179,21 +216,37 @@ let hide_success (n : status_notification) (ctx : Context.t) = let partition_status (ctx : Context.t) (n : status_notification) = let cfg = ctx.cfg in let get_commit_info () = - match%lwt Github.generate_query_commmit cfg ~url:n.commit.url ~sha:n.commit.sha with - | None -> - let default = Option.value_map cfg.prefix_rules.default ~default:[] ~f:(fun webhook -> [ webhook ]) in - Lwt.return default - | Some commit -> - match - List.exists n.branches ~f:(fun { name } -> is_main_merge_message ~msg:commit.commit.message ~branch:name cfg) - with + let default () = Lwt.return @@ Option.to_list cfg.prefix_rules.default in + match cfg.main_branch_name with + | None -> default () + | Some main_branch_name -> + (* non-main branch build notifications go to default channel to reduce spam in topic channels *) + match List.exists n.branches ~f:(fun { name } -> String.equal name main_branch_name) with + | false -> default () | true -> - log#info "main branch merge, ignoring status event %s: %s" n.context (first_line commit.commit.message); - Lwt.return [] - | false -> Lwt.return (partition_commit cfg commit.files) + ( match%lwt Github.generate_query_commmit cfg ~url:n.commit.url ~sha:n.commit.sha with + | None -> default () + | Some commit -> + (* + match + List.exists n.branches ~f:(fun { name } -> is_main_merge_message ~msg:commit.commit.message ~branch:name cfg) + with + | true -> + log#info "main branch merge, ignoring status event %s: %s" n.context (first_line commit.commit.message); + Lwt.return [] + | false -> +*) + Lwt.return (partition_commit cfg commit.files) + ) in let res = - match List.exists cfg.status_rules.status ~f:(Poly.equal n.state) with + match + List.exists cfg.status_rules.status ~f:(fun x -> + match x with + | State s -> Poly.equal s n.state + | HideConsecutiveSuccess -> Poly.equal Success n.state + | _ -> false) + with | false -> Lwt.return [] | true -> match List.exists ~f:id [ hide_cancelled n cfg; hide_success n ctx ] with @@ -210,7 +263,7 @@ let partition_status (ctx : Context.t) (n : status_notification) = res let partition_commit_comment cfg n = - let default = Option.value_map cfg.prefix_rules.default ~default:[] ~f:(fun webhook -> [ webhook ]) in + let default = Option.to_list cfg.prefix_rules.default in match n.comment.path with | None -> ( match%lwt Github.generate_commit_from_commit_comment cfg n with @@ -218,9 +271,9 @@ let partition_commit_comment cfg n = | Some commit -> Lwt.return (partition_commit cfg commit.files) ) | Some p -> - match filter_commit cfg.prefix_rules.rules p with - | [] -> Lwt.return default - | l -> Lwt.return l + match chan_of_file cfg.prefix_rules.rules p with + | None -> Lwt.return default + | Some chan -> Lwt.return [ chan ] let generate_notifications (ctx : Context.t) req = let cfg = ctx.cfg in @@ -248,7 +301,7 @@ let generate_notifications (ctx : Context.t) req = Lwt.return notifs | Status n -> let%lwt webhooks = partition_status ctx n in - let notifs = List.map ~f:(fun webhook -> webhook, generate_status_notification n) webhooks in + let notifs = List.map ~f:(fun webhook -> webhook, generate_status_notification cfg n) webhooks in Lwt.return notifs | _ -> Lwt.return [] diff --git a/lib/common.ml b/lib/common.ml index db088ff1..b1e8205c 100644 --- a/lib/common.ml +++ b/lib/common.ml @@ -4,3 +4,15 @@ let first_line s = match String.split ~on:'\n' s with | x :: _ -> x | [] -> s + +module Tristate : Atdgen_runtime.Json_adapter.S = struct + let normalize = function + | `Bool true -> `String "true" + | `Bool false -> `String "false" + | x -> x + + let restore = function + | `String "true" -> `Bool true + | `String "false" -> `Bool false + | x -> x +end diff --git a/lib/config.ml b/lib/config.ml index f20bfcb6..d0ed9a31 100644 --- a/lib/config.ml +++ b/lib/config.ml @@ -1,9 +1,14 @@ open Devkit module Chan_map = Map.Make (String) +type config_status_state = + | State of Github_t.status_state + | Cancelled of string + | HideConsecutiveSuccess + type status_rules = { title : string list option; - status : Github_t.status_state list; + status : config_status_state list; } type t = { @@ -15,7 +20,6 @@ type t = { gh_token : string option; offline : string option; status_rules : status_rules; - suppress_cancelled_events : bool; } let make (json_config : Notabot_t.config) (secrets : Notabot_t.secrets) = @@ -65,15 +69,22 @@ let make (json_config : Notabot_t.config) (secrets : Notabot_t.secrets) = let j = json_config.status_rules.status in List.filter_map id [ - (if j.success then Some Success else None); - (if j.failure then Some Failure else None); - (if j.pending then Some Pending else None); - (if j.error then Some Error else None); + ( match j.success with + | False -> None + | True -> Some (State Success) + | Once -> Some HideConsecutiveSuccess + ); + (if j.failure then Some (State Failure) else None); + (if j.pending then Some (State Pending) else None); + (if j.error then Some (State Error) else None); + ( match j.cancelled with + | Some r -> Some (Cancelled r) + | None -> None + ); ] in { title = json_config.status_rules.title; status } in - let suppress_cancelled_events = Option.default true json_config.suppress_cancelled_events in { chans; prefix_rules = json_config.prefix_rules; @@ -83,7 +94,6 @@ let make (json_config : Notabot_t.config) (secrets : Notabot_t.secrets) = gh_token = secrets.gh_token; offline = json_config.offline; status_rules; - suppress_cancelled_events; } let load_config_file ~config_path = Notabot_j.config_of_string @@ Stdio.In_channel.read_all config_path diff --git a/lib/dune b/lib/dune index 49a1512c..a6c44cf3 100644 --- a/lib/dune +++ b/lib/dune @@ -1,7 +1,8 @@ (library (name lib) - (libraries curl curl.lwt nocrypto hex atdgen base stdio lwt lwt.unix uri - devkit devkit.core omd base64) + (libraries atdgen atdgen-runtime base base.caml base64 biniou cstruct curl + curl.lwt devkit devkit.core hex lwt lwt.unix nocrypto omd re stdio uri + yojson) (preprocess (pps lwt_ppx))) @@ -15,7 +16,7 @@ (targets github_j.ml github_j.mli) (deps github.atd) (action - (run atdgen -j %{deps}))) + (run atdgen -j -j-std %{deps}))) (rule (targets slack_t.ml slack_t.mli) @@ -27,7 +28,7 @@ (targets slack_j.ml slack_j.mli) (deps slack.atd) (action - (run atdgen -j %{deps}))) + (run atdgen -j -j-std %{deps}))) (rule (targets notabot_t.ml notabot_t.mli) @@ -39,4 +40,4 @@ (targets notabot_j.ml notabot_j.mli) (deps notabot.atd) (action - (run atdgen -j %{deps}))) + (run atdgen -j -j-std %{deps}))) diff --git a/lib/mrkdwn.ml b/lib/mrkdwn.ml index 21ccd534..6c01b84c 100644 --- a/lib/mrkdwn.ml +++ b/lib/mrkdwn.ml @@ -1,9 +1,20 @@ open Omd open Base -let escape_url_chars str = - let repl c = String.substr_replace_all ~pattern:(Char.to_string c) ~with_:(Printf.sprintf "\\%c" c) in - str |> repl '<' |> repl '>' |> repl '|' +let escape_url_chars = Staged.unstage @@ String.Escaping.escape ~escapeworthy:[ '<'; '>'; '|' ] ~escape_char:'\\' + +(* https://api.slack.com/reference/surfaces/formatting#escaping *) +let escape_mrkdwn = + String.concat_map ~f:(function + | '<' -> "<" + | '>' -> ">" + | '&' -> "&" + | c -> String.make 1 c) + +let unescape_omd = + Staged.unstage @@ String.Escaping.unescape_gen_exn ~escapeworthy_map:[ '(', '('; ')', ')' ] ~escape_char:'\\' + +let transform_text = escape_mrkdwn let rec transform_list = List.map ~f:transform @@ -13,13 +24,12 @@ and surround s t = let t = to_markdown @@ transform_list t in Raw (Printf.sprintf "%s%s%s" s t s) +(** massage markdown AST so that rendered result looks like slack mrkdwn *) and transform = function - | H1 t -> H1 (transform_list t) - | H2 t -> H2 (transform_list t) - | H3 t | H4 t | H5 t | H6 t -> Paragraph [ Bold (transform_list t) ] + | H1 t | H2 t | H3 t | H4 t | H5 t | H6 t -> Paragraph (transform_list [ Bold t ]) | Paragraph t -> Paragraph (transform_list t) - | Emph t -> surround "_" t - | Bold t -> surround "*" t + | Emph t -> surround "_" (transform_list t) + | Bold t -> surround "*" (transform_list t) | Ul ts -> Ul (transform_flatten ts) | Ol ts -> Ol (transform_flatten ts) | Ulp ts -> Ulp (transform_flatten ts) @@ -27,17 +37,15 @@ and transform = function | Url (href, label, title) -> let label = escape_url_chars @@ to_markdown @@ transform_list label in let title = if String.length title > 0 then Printf.sprintf "%s - " @@ escape_url_chars title else title in - Raw (Printf.sprintf "<%s%s|%s>" title label href) + Raw (Printf.sprintf "<%s|%s%s>" href title label) | Html _ as e -> Raw (Printf.sprintf "`%s`" @@ to_markdown [ e ]) | Html_comment _ -> Br | Html_block _ as e -> Code_block ("", to_markdown [ e ]) | Blockquote t -> Blockquote (transform_list t) - | Img (alt, src, title) -> Url (src, [ Text alt ], title) + | Img (alt, src, title) -> transform @@ Url (src, [ Text alt ], title) | Code_block (_, str) -> Code_block ("", str) - | (Text _ | Code _ | Br | Hr | NL | Ref _ | Img_ref _ | Raw _ | Raw_block _ | X _) as e -> e - -let of_doc t = transform_list t - -let to_mrkdwn doc = to_markdown @@ of_doc doc + | Text s -> Text (transform_text s) + | (Code _ | Br | Hr | NL | Ref _ | Img_ref _ | Raw _ | Raw_block _ | X _) as e -> e -let mrkdwn_of_markdown str = to_mrkdwn @@ of_string str +(* unescaping here is a workaround of to_markdown escaping parentheses in text (bug?) *) +let mrkdwn_of_markdown str = unescape_omd @@ to_markdown @@ transform_list @@ of_string str diff --git a/lib/notabot.atd b/lib/notabot.atd index a457263e..d02d9f2e 100644 --- a/lib/notabot.atd +++ b/lib/notabot.atd @@ -51,11 +51,14 @@ type label_config = { rules: label_rule list; } +type tristate = [ True | False | Once ] + type status_state = { - success: bool; + success: tristate; failure: bool; pending: bool; error: bool; + ?cancelled: string option; (* if specified, it will use the string as regex to match the description from the payload to determine if the status_state is cancelled *) } type status_config = { @@ -67,7 +70,6 @@ type config = { prefix_rules : prefix_config; label_rules : label_config; status_rules : status_config; - ?suppress_cancelled_events : bool option; (* supresses status cancelled events; if not specified - true *) ?main_branch_name: string option; (* used to filter out notifications about merges of main branch into other branches *) ?offline: string option; (* where to find github api data when http calls are not allowed *) } diff --git a/lib/slack.ml b/lib/slack.ml index db60081c..7a33bde7 100644 --- a/lib/slack.ml +++ b/lib/slack.ml @@ -28,24 +28,21 @@ let empty_attachments = let mrkdwn_of_markdown str = String.strip @@ Mrkdwn.mrkdwn_of_markdown str -let mrkdwn_of_markdown_opt str_opt = Option.map ~f:mrkdwn_of_markdown str_opt +let mrkdwn_of_markdown_opt = Option.map ~f:mrkdwn_of_markdown + +let show_labels = function + | [] -> None + | (labels: label list) -> Some (sprintf "Labels: %s" @@ String.concat ~sep:", " (List.map ~f:(fun x -> x.name) labels)) let generate_pull_request_notification notification = let { action; number; sender; pull_request; repository } = notification in let ({ body; title; html_url; labels; _ } : pull_request) = pull_request in - let label_str = - match labels with - | [] -> None - | labels -> - let value = String.concat ~sep:", " (List.map ~f:(fun x -> x.name) labels) in - Some (sprintf "Labels: %s" value) - in - let action_str = + let action, body = match action with - | Opened -> "opened" - | Closed -> "closed" - | Reopened -> "reopened" - | Labeled -> "labeled" + | Opened -> "opened", Some body + | Closed -> "closed", None + | Reopened -> "reopened", None + | Labeled -> "labeled", show_labels labels | _ -> invalid_arg (sprintf "Notabot doesn't know how to generate notification for the unexpected event %s" @@ -53,8 +50,8 @@ let generate_pull_request_notification notification = in let summary = Some - (sprintf "<%s|[%s]> Pull request #%d <%s|%s> %s by %s" repository.url repository.full_name number html_url title - action_str sender.login) + (sprintf "<%s|[%s]> Pull request #%d <%s|%s> %s by *%s*" repository.url repository.full_name number html_url title + action sender.login) in { text = None; @@ -67,12 +64,7 @@ let generate_pull_request_notification notification = fallback = summary; color = Some "#ccc"; pretext = summary; - text = - ( match action with - | Labeled -> label_str - | Closed -> None - | _ -> Some (mrkdwn_of_markdown body) - ); + text = mrkdwn_of_markdown_opt body; }; ]; blocks = None; @@ -97,7 +89,7 @@ let generate_pr_review_notification notification = in let summary = Some - (sprintf "<%s|[%s]> %s <%s|%s> #%d <%s|%s>" repository.url repository.full_name sender.login review.html_url + (sprintf "<%s|[%s]> *%s* <%s|%s> #%d <%s|%s>" repository.url repository.full_name sender.login review.html_url action_str number html_url title) in { @@ -130,7 +122,7 @@ let generate_pr_review_comment_notification notification = in let summary = Some - (sprintf "<%s|[%s]> %s %s on #%d <%s|%s>" repository.url repository.full_name sender.login action_str number + (sprintf "<%s|[%s]> *%s* %s on #%d <%s|%s>" repository.url repository.full_name sender.login action_str number html_url title) in let file = @@ -158,13 +150,13 @@ let generate_pr_review_comment_notification notification = let generate_issue_notification notification = let ({ action; sender; issue; repository } : issue_notification) = notification in - let { number; body; title; html_url; _ } = issue in - let action_str = + let { number; body; title; html_url; labels; _ } = issue in + let action, body = match action with - | Opened -> "opened" - | Closed -> "closed" - | Reopened -> "reopened" - | Labeled -> "labeled" + | Opened -> "opened", Some body + | Closed -> "closed", None + | Reopened -> "reopened", None + | Labeled -> "labeled", show_labels labels | _ -> invalid_arg (sprintf "Notabot doesn't know how to generate notification for the unexpected event %s" @@ -172,8 +164,8 @@ let generate_issue_notification notification = in let summary = Some - (sprintf "<%s|[%s]> Issue #%d <%s|%s> %s by %s" repository.url repository.full_name number html_url title - action_str sender.login) + (sprintf "<%s|[%s]> Issue #%d <%s|%s> %s by *%s*" repository.url repository.full_name number html_url title action + sender.login) in { text = None; @@ -186,7 +178,7 @@ let generate_issue_notification notification = fallback = summary; color = Some "#ccc"; pretext = summary; - text = Some (mrkdwn_of_markdown body); + text = mrkdwn_of_markdown_opt body; }; ]; blocks = None; @@ -206,7 +198,7 @@ let generate_issue_comment_notification notification = in let summary = Some - (sprintf "<%s|[%s]> %s <%s|%s> on #%d <%s|%s>" repository.url repository.full_name sender.login comment.html_url + (sprintf "<%s|[%s]> *%s* <%s|%s> on #%d <%s|%s>" repository.url repository.full_name sender.login comment.html_url action_str number issue.html_url title) in { @@ -267,7 +259,7 @@ let generate_push_notification notification = blocks = None; } -let generate_status_notification (notification : status_notification) = +let generate_status_notification (cfg : Config.t) (notification : status_notification) = let { commit; state; description; target_url; context; repository; _ } = notification in let ({ commit : inner_commit; sha; author; html_url; _ } : status_commit) = commit in let ({ message; _ } : inner_commit) = commit in @@ -292,11 +284,17 @@ let generate_status_notification (notification : status_notification) = | Some s -> Some (sprintf "*Description*: %s." s) in let commit_info = - sprintf "*Commit*: `<%s|%s>` %s - %s" html_url (git_short_sha_hash sha) (first_line message) author.login + [ sprintf "*Commit*: `<%s|%s>` %s - %s" html_url (git_short_sha_hash sha) (first_line message) author.login ] in let branches_info = - let branches = notification.branches |> List.map ~f:(fun ({ name } : branch) -> name) |> String.concat ~sep:", " in - sprintf "*Branches*: %s" branches + match List.map ~f:(fun { name } -> name) notification.branches with + | [] -> [] (* happens when branch is force-pushed by the time CI notification arrives *) + | branches -> + match cfg.main_branch_name with + | Some main when List.mem branches main ~equal:String.equal -> + (* happens when new branches are branched before CI finishes *) + [ sprintf "*Branch*: %s" main ] + | _ -> [ sprintf "*Branches*: %s" (String.concat ~sep:", " branches) ] in let summary = match target_url with @@ -320,7 +318,8 @@ let generate_status_notification (notification : status_notification) = pretext = summary; color = Some color_info; text = description_info; - fields = Some [ { title = None; value = String.concat ~sep:"\n" [ commit_info; branches_info ] } ]; + fields = + Some [ { title = None; value = String.concat ~sep:"\n" @@ List.concat [ commit_info; branches_info ] } ]; }; ]; blocks = None; @@ -339,7 +338,7 @@ let generate_commit_comment_notification cfg notification = in let summary = Some - (sprintf "<%s|[%s]> %s commented on `<%s|%s>` %s" repository.url repository.full_name sender.login + (sprintf "<%s|[%s]> *%s* commented on `<%s|%s>` %s" repository.url repository.full_name sender.login comment.html_url (git_short_sha_hash commit_id) (first_line commit.message)) in let path = @@ -369,10 +368,7 @@ let generate_commit_comment_notification cfg notification = Lwt.return notifs let send_notification webhook_url data = - let data = Slack_j.string_of_webhook_notification data in let body = `Raw ("application/json", data) in match%lwt Web.http_request_lwt ~verbose:true ~body `POST webhook_url with | `Ok _ -> Lwt.return_unit - | `Error e -> - log#error "error when posting notification to slack: %s" e; - Exn_lwt.fail "unable to send notification to slack" + | `Error e -> Exn_lwt.fail "failed to send notification to slack : %s" e diff --git a/mock_payloads/push.two_commits_longest_match.json b/mock_payloads/push.two_commits_longest_match.json new file mode 100644 index 00000000..3d91cb4b --- /dev/null +++ b/mock_payloads/push.two_commits_longest_match.json @@ -0,0 +1,204 @@ +{ + "ref": "refs/heads/master", + "before": "fb245e2a6d52d10025c8bd4f36f6e3134d85ae18", + "after": "e2173f38ae43865433a182c1fc1b5442d9763b54", + "created": false, + "deleted": false, + "forced": false, + "base_ref": null, + "compare": "https://github.com/thatportugueseguy/monorepo/compare/fb245e2a6d52...e2173f38ae43", + "commits": [ + { + "id": "80452f696a8988e7234063d47720a62e902f2afc", + "tree_id": "bf7530bb0b13d0b9f471ff5d1951c59488b5704c", + "distinct": true, + "message": "Update readme", + "timestamp": "2019-07-06T11:47:56+01:00", + "url": "https://github.com/thatportugueseguy/monorepo/commit/80452f696a8988e7234063d47720a62e902f2afc", + "author": { + "name": "jose nogueira", + "email": "mail@example.org", + "username": "thatportugueseguy" + }, + "committer": { + "name": "jose nogueira", + "email": "mail@example.org", + "username": "thatportugueseguy" + }, + "added": [], + "removed": [], + "modified": [ + "README.md" + ] + }, + { + "id": "e2173f38ae43865433a182c1fc1b5442d9763b54", + "tree_id": "6dc065fdfacfc72cd7b1f5e7a9391c238e4fb74e", + "distinct": true, + "message": "Add TESTING.md", + "timestamp": "2019-07-06T11:48:38+01:00", + "url": "https://github.com/thatportugueseguy/monorepo/commit/e2173f38ae43865433a182c1fc1b5442d9763b54", + "author": { + "name": "jose nogueira", + "email": "mail@example.org", + "username": "thatportugueseguy" + }, + "committer": { + "name": "jose nogueira", + "email": "mail@example.org", + "username": "thatportugueseguy" + }, + "added": [ + "TESTING.md", + "backend/a1/longest/test.ml" + ], + "removed": [], + "modified": [] + } + ], + "head_commit": { + "id": "e2173f38ae43865433a182c1fc1b5442d9763b54", + "tree_id": "6dc065fdfacfc72cd7b1f5e7a9391c238e4fb74e", + "distinct": true, + "message": "Add TESTING.md", + "timestamp": "2019-07-06T11:48:38+01:00", + "url": "https://github.com/thatportugueseguy/monorepo/commit/e2173f38ae43865433a182c1fc1b5442d9763b54", + "author": { + "name": "jose nogueira", + "email": "mail@example.org", + "username": "thatportugueseguy" + }, + "committer": { + "name": "jose nogueira", + "email": "mail@example.org", + "username": "thatportugueseguy" + }, + "added": [ + "TESTING.md" + ], + "removed": [], + "modified": [] + }, + "repository": { + "id": 0, + "node_id": "00000000000000000000", + "name": "monorepo", + "full_name": "thatportugueseguy/monorepo", + "private": true, + "owner": { + "name": "thatportugueseguy", + "email": "mail@example.org", + "login": "thatportugueseguy", + "id": 0, + "node_id": "00000000000000000000", + "avatar_url": "https://avatars1.githubusercontent.com/u/000000?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/thatportugueseguy", + "html_url": "https://github.com/thatportugueseguy", + "followers_url": "https://api.github.com/users/thatportugueseguy/followers", + "following_url": "https://api.github.com/users/thatportugueseguy/following{/other_user}", + "gists_url": "https://api.github.com/users/thatportugueseguy/gists{/gist_id}", + "starred_url": "https://api.github.com/users/thatportugueseguy/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/thatportugueseguy/subscriptions", + "organizations_url": "https://api.github.com/users/thatportugueseguy/orgs", + "repos_url": "https://api.github.com/users/thatportugueseguy/repos", + "events_url": "https://api.github.com/users/thatportugueseguy/events{/privacy}", + "received_events_url": "https://api.github.com/users/thatportugueseguy/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/thatportugueseguy/monorepo", + "description": null, + "fork": false, + "url": "https://github.com/thatportugueseguy/monorepo", + "forks_url": "https://api.github.com/repos/thatportugueseguy/monorepo/forks", + "keys_url": "https://api.github.com/repos/thatportugueseguy/monorepo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/thatportugueseguy/monorepo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/thatportugueseguy/monorepo/teams", + "hooks_url": "https://api.github.com/repos/thatportugueseguy/monorepo/hooks", + "issue_events_url": "https://api.github.com/repos/thatportugueseguy/monorepo/issues/events{/number}", + "events_url": "https://api.github.com/repos/thatportugueseguy/monorepo/events", + "assignees_url": "https://api.github.com/repos/thatportugueseguy/monorepo/assignees{/user}", + "branches_url": "https://api.github.com/repos/thatportugueseguy/monorepo/branches{/branch}", + "tags_url": "https://api.github.com/repos/thatportugueseguy/monorepo/tags", + "blobs_url": "https://api.github.com/repos/thatportugueseguy/monorepo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/thatportugueseguy/monorepo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/thatportugueseguy/monorepo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/thatportugueseguy/monorepo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/thatportugueseguy/monorepo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/thatportugueseguy/monorepo/languages", + "stargazers_url": "https://api.github.com/repos/thatportugueseguy/monorepo/stargazers", + "contributors_url": "https://api.github.com/repos/thatportugueseguy/monorepo/contributors", + "subscribers_url": "https://api.github.com/repos/thatportugueseguy/monorepo/subscribers", + "subscription_url": "https://api.github.com/repos/thatportugueseguy/monorepo/subscription", + "commits_url": "https://api.github.com/repos/thatportugueseguy/monorepo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/thatportugueseguy/monorepo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/thatportugueseguy/monorepo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/thatportugueseguy/monorepo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/thatportugueseguy/monorepo/contents/{+path}", + "compare_url": "https://api.github.com/repos/thatportugueseguy/monorepo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/thatportugueseguy/monorepo/merges", + "archive_url": "https://api.github.com/repos/thatportugueseguy/monorepo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/thatportugueseguy/monorepo/downloads", + "issues_url": "https://api.github.com/repos/thatportugueseguy/monorepo/issues{/number}", + "pulls_url": "https://api.github.com/repos/thatportugueseguy/monorepo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/thatportugueseguy/monorepo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/thatportugueseguy/monorepo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/thatportugueseguy/monorepo/labels{/name}", + "releases_url": "https://api.github.com/repos/thatportugueseguy/monorepo/releases{/id}", + "deployments_url": "https://api.github.com/repos/thatportugueseguy/monorepo/deployments", + "created_at": 1562409695, + "updated_at": "2019-07-06T10:42:28Z", + "pushed_at": 1562410128, + "git_url": "git://github.com/thatportugueseguy/monorepo.git", + "ssh_url": "git@github.com:thatportugueseguy/monorepo.git", + "clone_url": "https://github.com/thatportugueseguy/monorepo.git", + "svn_url": "https://github.com/thatportugueseguy/monorepo", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 0, + "license": null, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "stargazers": 0, + "master_branch": "master" + }, + "pusher": { + "name": "thatportugueseguy", + "email": "mail@example.org" + }, + "sender": { + "login": "thatportugueseguy", + "id": 0, + "node_id": "00000000000000000000", + "avatar_url": "https://avatars1.githubusercontent.com/u/000000?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/thatportugueseguy", + "html_url": "https://github.com/thatportugueseguy", + "followers_url": "https://api.github.com/users/thatportugueseguy/followers", + "following_url": "https://api.github.com/users/thatportugueseguy/following{/other_user}", + "gists_url": "https://api.github.com/users/thatportugueseguy/gists{/gist_id}", + "starred_url": "https://api.github.com/users/thatportugueseguy/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/thatportugueseguy/subscriptions", + "organizations_url": "https://api.github.com/users/thatportugueseguy/orgs", + "repos_url": "https://api.github.com/users/thatportugueseguy/repos", + "events_url": "https://api.github.com/users/thatportugueseguy/events{/privacy}", + "received_events_url": "https://api.github.com/users/thatportugueseguy/received_events", + "type": "User", + "site_admin": false + } +} \ No newline at end of file diff --git a/notabot.opam b/notabot.opam index 7ee12647..6341d647 100644 --- a/notabot.opam +++ b/notabot.opam @@ -13,16 +13,20 @@ depends: [ "dune" {>= "2.5.0"} "atdgen" "base" + "biniou" "cmdliner" + "cstruct" "devkit" "hex" "lwt" "lwt_ppx" "nocrypto" + "re" "stdio" "uri" "ocamlformat" {dev & = "0.13.0"} "base64" "omd" {< "2.0.0"} + "yojson" ] build: ["dune" "build" "-p" name] diff --git a/src/dune b/src/dune index 62680a81..7df15051 100644 --- a/src/dune +++ b/src/dune @@ -1,5 +1,6 @@ (executable - (libraries lib cmdliner devkit devkit.core) + (libraries lib base base.caml base64 cmdliner devkit devkit.core lwt.unix + stdio uri) (preprocess (pps lwt_ppx)) (public_name notabot)) diff --git a/src/notabot.ml b/src/notabot.ml index 27a35014..8c33f1c9 100644 --- a/src/notabot.ml +++ b/src/notabot.ml @@ -26,7 +26,7 @@ let send_slack_notification webhook file = let data = Stdio.In_channel.read_all file in match Slack_j.webhook_notification_of_string data with | exception exn -> log#error ~exn "unable to parse notification" - | data -> Lwt_main.run (Slack.send_notification webhook data) + | _ -> Lwt_main.run (Slack.send_notification webhook data) let check_common file print config secrets state_path = let ctx_thunk = diff --git a/src/request_handler.ml b/src/request_handler.ml index 56ed8707..6abc427f 100644 --- a/src/request_handler.ml +++ b/src/request_handler.ml @@ -26,7 +26,9 @@ let process_github_notification (ctx_thunk : Lib.Context.context_thunk) headers Lwt_list.iter_s (fun (chan, msg) -> let url = Config.Chan_map.find chan cfg.chans in - Slack.send_notification url msg) + let data = Slack_j.string_of_webhook_notification msg in + log#info "sending to %s : %s" chan data; + Slack.send_notification url data) notifications with | Lib.Context.Context_Error s -> @@ -56,7 +58,6 @@ let setup_http ~ctx_thunk ~signature ~port ~ip = body @@ serve ~status ?extra request typ r in let ret_err status s = body @@ serve_text ~status request s in - log#info "http: %s" (show_request request); try%lwt let path = match Stre.nsplitc request.path '/' with diff --git a/test/dune b/test/dune index 9511a626..d7df070b 100644 --- a/test/dune +++ b/test/dune @@ -1,6 +1,7 @@ (executable (name test) - (libraries lib) + (libraries lib base base.caml base64 devkit devkit.core lwt.unix stdio + yojson) (preprocess (pps lwt_ppx))) diff --git a/test/notabot.json b/test/notabot.json index f11e940e..8f545e41 100644 --- a/test/notabot.json +++ b/test/notabot.json @@ -8,9 +8,10 @@ ], "status": { "pending": false, - "success": true, "failure": true, - "error": true + "error": true, + "cancelled": "^\\(Build #[0-9]+ canceled by .+\\|Failed (exit status 255)\\)$", + "success": "once" } }, "prefix_rules": { @@ -23,6 +24,13 @@ "ignore": [], "chan": "a1" }, + { + "prefix": [ + "backend/a1/longest" + ], + "ignore": [], + "chan": "longest-a1" + }, { "prefix": [ "backend/a5", @@ -72,6 +80,5 @@ "chan": "frontend-bot" } ] - }, - "suppress_cancelled_events": true + } } diff --git a/test/secrets.json b/test/secrets.json index 35223adf..f29896fd 100644 --- a/test/secrets.json +++ b/test/secrets.json @@ -8,6 +8,10 @@ "url": "https://slack_webhook_url", "channel": "a1" }, + { + "url": "https://slack_webhook_url", + "channel": "longest-a1" + }, { "url": "https://slack_webhook_url", "channel": "backend" diff --git a/test/slack_payloads.expected b/test/slack_payloads.expected index 8710a0ff..1b22f7f9 100644 --- a/test/slack_payloads.expected +++ b/test/slack_payloads.expected @@ -4,11 +4,11 @@ will notify #all-push-events "attachments": [ { "fallback": - " xinyuluo commented on `` Update README.md", + " *xinyuluo* commented on `` Update README.md", "mrkdwn_in": [ "pretext", "text" ], "color": "#ccc", "pretext": - " xinyuluo commented on `` Update README.md", + " *xinyuluo* commented on `` Update README.md", "text": "comment here", "footer": "New comment by xinyuluo in " @@ -21,11 +21,11 @@ will notify #all-push-events "attachments": [ { "fallback": - " xinyuluo commented on `` add new line at EOF", + " *xinyuluo* commented on `` add new line at EOF", "mrkdwn_in": [ "pretext", "text" ], "color": "#ccc", "pretext": - " xinyuluo commented on `` add new line at EOF", + " *xinyuluo* commented on `` add new line at EOF", "text": "add a commit comment" } ] @@ -36,11 +36,11 @@ will notify #a1 "attachments": [ { "fallback": - " xinyuluo commented on `` Add files via upload", + " *xinyuluo* commented on `` Add files via upload", "mrkdwn_in": [ "pretext", "text" ], "color": "#ccc", "pretext": - " xinyuluo commented on `` Add files via upload", + " *xinyuluo* commented on `` Add files via upload", "text": "this is a test for general commit comment with multiple paths" } ] @@ -50,11 +50,11 @@ will notify #all-push-events "attachments": [ { "fallback": - " xinyuluo commented on `` Add files via upload", + " *xinyuluo* commented on `` Add files via upload", "mrkdwn_in": [ "pretext", "text" ], "color": "#ccc", "pretext": - " xinyuluo commented on `` Add files via upload", + " *xinyuluo* commented on `` Add files via upload", "text": "this is a test for general commit comment with multiple paths" } ] @@ -65,11 +65,11 @@ will notify #all-push-events "attachments": [ { "fallback": - " xinyuluo commented on `` add new line at EOF", + " *xinyuluo* commented on `` add new line at EOF", "mrkdwn_in": [ "pretext", "text" ], "color": "#ccc", "pretext": - " xinyuluo commented on `` add new line at EOF", + " *xinyuluo* commented on `` add new line at EOF", "text": "this can be written shorter:\n`\n| Some serpDomain -> Domain.is_subdomain serpDomain domain\n| None -> false`", "footer": @@ -83,13 +83,13 @@ will notify #all-push-events "attachments": [ { "fallback": - " xinyuluo commented on `` add new line at EOF", + " *xinyuluo* commented on `` add new line at EOF", "mrkdwn_in": [ "pretext", "text" ], "color": "#ccc", "pretext": - " xinyuluo commented on `` add new line at EOF", + " *xinyuluo* commented on `` add new line at EOF", "text": - "*bold*, _italic_\n\n> blockquote\n\n\n- list-element 1\n- list-element 2\n - list-element2.1\n - \n\n\n```\n
html block
\n```\n```\nStdio.printf \"hello ocaml\"\n```", + "*bold*, _italic_\n\n> blockquote\n\n\n- list-element 1\n- list-element 2\n - list-element2.1\n - \n\n\n```\n
html block
\n```\n```\nStdio.printf \"hello ocaml\"\n```", "footer": "New comment by xinyuluo in " } @@ -101,11 +101,11 @@ will notify #all-push-events "attachments": [ { "fallback": - " xinyuluo commented on `` make changes in 2 files", + " *xinyuluo* commented on `` make changes in 2 files", "mrkdwn_in": [ "pretext", "text" ], "color": "#ccc", "pretext": - " xinyuluo commented on `` make changes in 2 files", + " *xinyuluo* commented on `` make changes in 2 files", "text": "make a comment" } ] @@ -116,11 +116,11 @@ will notify #all-push-events "attachments": [ { "fallback": - " xinyuluo commented on `` add new line at EOF", + " *xinyuluo* commented on `` add new line at EOF", "mrkdwn_in": [ "pretext", "text" ], "color": "#ccc", "pretext": - " xinyuluo commented on `` add new line at EOF", + " *xinyuluo* commented on `` add new line at EOF", "text": "add a single comment on the code in a commit", "footer": "New comment by xinyuluo in " @@ -133,11 +133,11 @@ will notify #default "attachments": [ { "fallback": - " xinyuluo on #5 ", + " *xinyuluo* on #5 ", "mrkdwn_in": [ "text" ], "color": "#ccc", "pretext": - " xinyuluo on #5 ", + " *xinyuluo* on #5 ", "text": "handle issue comment" } ] @@ -148,11 +148,11 @@ will notify #a1-bot "attachments": [ { "fallback": - " xinyuluo on #4 ", + " *xinyuluo* on #4 ", "mrkdwn_in": [ "text" ], "color": "#ccc", "pretext": - " xinyuluo on #4 ", + " *xinyuluo* on #4 ", "text": "comment example" } ] @@ -162,11 +162,11 @@ will notify #backend "attachments": [ { "fallback": - " xinyuluo on #4 ", + " *xinyuluo* on #4 ", "mrkdwn_in": [ "text" ], "color": "#ccc", "pretext": - " xinyuluo on #4 ", + " *xinyuluo* on #4 ", "text": "comment example" } ] @@ -178,11 +178,11 @@ will notify #frontend-bot "attachments": [ { "fallback": - " yasunariw on #6168 ", + " *yasunariw* on #6168 ", "mrkdwn_in": [ "text" ], "color": "#ccc", "pretext": - " yasunariw on #6168 ", + " *yasunariw* on #6168 ", "text": "> I do not understand this\n\n\nDo you have access to the logs on the website?" } @@ -195,13 +195,11 @@ will notify #backend "attachments": [ { "fallback": - " Issue #6 closed by xinyuluo", + " Issue #6 closed by *xinyuluo*", "mrkdwn_in": [ "text" ], "color": "#ccc", "pretext": - " Issue #6 closed by xinyuluo", - "text": - "consider multiple cases including labeled/opened/closed/reopened" + " Issue #6 closed by *xinyuluo*" } ] } @@ -211,13 +209,12 @@ will notify #backend "attachments": [ { "fallback": - " Issue #6 labeled by xinyuluo", + " Issue #6 labeled by *xinyuluo*", "mrkdwn_in": [ "text" ], "color": "#ccc", "pretext": - " Issue #6 labeled by xinyuluo", - "text": - "consider multiple cases including labeled/opened/closed/reopened" + " Issue #6 labeled by *xinyuluo*", + "text": "Labels: backend" } ] } @@ -227,11 +224,11 @@ will notify #default "attachments": [ { "fallback": - " Issue #6 opened by xinyuluo", + " Issue #6 opened by *xinyuluo*", "mrkdwn_in": [ "text" ], "color": "#ccc", "pretext": - " Issue #6 opened by xinyuluo", + " Issue #6 opened by *xinyuluo*", "text": "consider multiple cases including labeled/opened/closed/reopened" } @@ -243,13 +240,11 @@ will notify #backend "attachments": [ { "fallback": - " Issue #6 reopened by xinyuluo", + " Issue #6 reopened by *xinyuluo*", "mrkdwn_in": [ "text" ], "color": "#ccc", "pretext": - " Issue #6 reopened by xinyuluo", - "text": - "consider multiple cases including labeled/opened/closed/reopened" + " Issue #6 reopened by *xinyuluo*" } ] } @@ -259,11 +254,11 @@ will notify #a1-bot "attachments": [ { "fallback": - " Pull request #8 closed by xinyuluo", + " Pull request #8 closed by *xinyuluo*", "mrkdwn_in": [ "text" ], "color": "#ccc", "pretext": - " Pull request #8 closed by xinyuluo" + " Pull request #8 closed by *xinyuluo*" } ] } @@ -273,11 +268,11 @@ will notify #default "attachments": [ { "fallback": - " Pull request #2 closed by xinyuluo", + " Pull request #2 closed by *xinyuluo*", "mrkdwn_in": [ "text" ], "color": "#ccc", "pretext": - " Pull request #2 closed by xinyuluo" + " Pull request #2 closed by *xinyuluo*" } ] } @@ -287,11 +282,11 @@ will notify #a1-bot "attachments": [ { "fallback": - " Pull request #4 labeled by xinyuluo", + " Pull request #4 labeled by *xinyuluo*", "mrkdwn_in": [ "text" ], "color": "#ccc", "pretext": - " Pull request #4 labeled by xinyuluo", + " Pull request #4 labeled by *xinyuluo*", "text": "Labels: a1, backend" } ] @@ -301,11 +296,11 @@ will notify #backend "attachments": [ { "fallback": - " Pull request #4 labeled by xinyuluo", + " Pull request #4 labeled by *xinyuluo*", "mrkdwn_in": [ "text" ], "color": "#ccc", "pretext": - " Pull request #4 labeled by xinyuluo", + " Pull request #4 labeled by *xinyuluo*", "text": "Labels: a1, backend" } ] @@ -316,11 +311,11 @@ will notify #frontend-bot "attachments": [ { "fallback": - " Pull request #3 opened by xinyuluo", + " Pull request #3 opened by *xinyuluo*", "mrkdwn_in": [ "text" ], "color": "#ccc", "pretext": - " Pull request #3 opened by xinyuluo", + " Pull request #3 opened by *xinyuluo*", "text": "" } ] @@ -332,11 +327,11 @@ will notify #default "attachments": [ { "fallback": - " Khady #6 ", + " *Khady* #6 ", "mrkdwn_in": [ "text" ], "color": "#ccc", "pretext": - " Khady #6 ", + " *Khady* #6 ", "text": "approve review" } ] @@ -347,11 +342,11 @@ will notify #frontend-bot "attachments": [ { "fallback": - " xinyuluo #3 ", + " *xinyuluo* #3 ", "mrkdwn_in": [ "text" ], "color": "#ccc", "pretext": - " xinyuluo #3 ", + " *xinyuluo* #3 ", "text": "submit a PR review" } ] @@ -364,11 +359,11 @@ will notify #default "attachments": [ { "fallback": - " Khady #6 ", + " *Khady* #6 ", "mrkdwn_in": [ "text" ], "color": "#ccc", "pretext": - " Khady #6 ", + " *Khady* #6 ", "text": "request changes review" } ] @@ -381,11 +376,11 @@ will notify #a1-bot "attachments": [ { "fallback": - " xinyuluo commented on #4 ", + " *xinyuluo* commented on #4 ", "mrkdwn_in": [ "text" ], "color": "#ccc", "pretext": - " xinyuluo commented on #4 ", + " *xinyuluo* commented on #4 ", "text": "PR review comment example", "footer": "New comment by xinyuluo in " @@ -397,11 +392,11 @@ will notify #backend "attachments": [ { "fallback": - " xinyuluo commented on #4 ", + " *xinyuluo* commented on #4 ", "mrkdwn_in": [ "text" ], "color": "#ccc", "pretext": - " xinyuluo commented on #4 ", + " *xinyuluo* commented on #4 ", "text": "PR review comment example", "footer": "New comment by xinyuluo in " @@ -429,9 +424,46 @@ will notify #all-push-events } ] } +===== file ../mock_payloads/push.two_commits_longest_match.json ===== +will notify #all-push-events +{ + "text": + " pushed by thatportugueseguy", + "attachments": [ + { + "fallback": "Commit pushed notification", + "mrkdwn_in": [ "fields" ], + "color": "#ccc", + "fields": [ + { + "value": + "`` Update readme - jose nogueira\n`` Add TESTING.md - jose nogueira" + } + ] + } + ] +} +will notify #longest-a1 +{ + "text": + " pushed by thatportugueseguy", + "attachments": [ + { + "fallback": "Commit pushed notification", + "mrkdwn_in": [ "fields" ], + "color": "#ccc", + "fields": [ + { + "value": + "`` Add TESTING.md - jose nogueira" + } + ] + } + ] +} ===== file ../mock_payloads/status.cancelled_test.json ===== ===== file ../mock_payloads/status.failure_test.json ===== -will notify #all-push-events +will notify #default { "attachments": [ { @@ -455,7 +487,7 @@ will notify #all-push-events ===== file ../mock_payloads/status.pending_test.json ===== ===== file ../mock_payloads/status.state_hide_success_test.json ===== ===== file ../mock_payloads/status.success_public_repo_no_buildkite.json ===== -will notify #all-push-events +will notify #default { "attachments": [ { @@ -475,7 +507,7 @@ will notify #all-push-events ] } ===== file ../mock_payloads/status.success_test.json ===== -will notify #all-push-events +will notify #default { "attachments": [ {