Skip to content

Commit 4ad34de

Browse files
committed
cfg: allow repo-specific configuration of gh_token and gh_hook_token
Define custom getters for retrieving GH secret values. As the getter for each token type defaults to looking for a global value if a repo-specific vaue isn't found, existing deployments don't need to change.
1 parent 3774234 commit 4ad34de

File tree

4 files changed

+44
-18
lines changed

4 files changed

+44
-18
lines changed

lib/action.ml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -232,16 +232,17 @@ module Action (Github_api : Api.Github) (Slack_api : Api.Slack) = struct
232232
| _ -> Lwt.return_unit
233233

234234
let process_github_notification (ctx : Context.t) headers body =
235-
let validate_signature secrets =
236-
let signing_key = secrets.gh_hook_token in
235+
let validate_signature secrets payload =
236+
let repo = Github.repo_of_notification payload in
237+
let signing_key = Context.gh_hook_token_of_secrets secrets repo.url in
237238
Github.validate_signature ?signing_key ~headers body
238239
in
239240
try%lwt
240241
let secrets = Context.get_secrets_exn ctx in
241242
match Github.parse_exn headers body with
242243
| exception exn -> Exn_lwt.fail ~exn "failed to parse payload"
243244
| payload ->
244-
match validate_signature secrets with
245+
match validate_signature secrets payload with
245246
| Error e -> action_error e
246247
| Ok () ->
247248
( match%lwt refresh_repo_config ctx payload with

lib/api_remote.ml

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ module Github : Api.Github = struct
2323
let get_config ~(ctx : Context.t) ~repo =
2424
let secrets = Context.get_secrets_exn ctx in
2525
let url = contents_url ~repo ~path:ctx.config_filename in
26-
let headers = build_headers ?token:secrets.gh_token () in
26+
let token = Context.gh_token_of_secrets secrets repo.url in
27+
let headers = build_headers ?token () in
2728
match%lwt http_request ~headers `GET url with
2829
| Error e -> Lwt.return @@ fmt_error "error while querying remote: %s\nfailed to get config from file %s" e url
2930
| Ok res ->
@@ -44,35 +45,38 @@ module Github : Api.Github = struct
4445
@@ fmt_error "unexpected encoding '%s' in Github response\nfailed to get config from file %s" encoding url
4546
)
4647

47-
let get_resource (ctx : Context.t) url =
48-
let secrets = Context.get_secrets_exn ctx in
49-
let headers = build_headers ?token:secrets.gh_token () in
48+
let get_resource ~secrets ~repo_url url =
49+
let token = Context.gh_token_of_secrets secrets repo_url in
50+
let headers = build_headers ?token () in
5051
match%lwt http_request ~headers `GET url with
5152
| Ok res -> Lwt.return @@ Ok res
5253
| Error e -> Lwt.return @@ fmt_error "error while querying remote: %s\nfailed to get resource from %s" e url
5354

54-
let post_resource (ctx : Context.t) body url =
55-
let secrets = Context.get_secrets_exn ctx in
56-
let headers = build_headers ?token:secrets.gh_token () in
55+
let post_resource ~secrets ~repo_url body url =
56+
let token = Context.gh_token_of_secrets secrets repo_url in
57+
let headers = build_headers ?token () in
5758
match%lwt http_request ~headers ~body:(`Raw ("application/json; charset=utf-8", body)) `POST url with
5859
| Ok res -> Lwt.return @@ Ok res
5960
| Error e -> Lwt.return @@ fmt_error "POST to %s failed : %s" url e
6061

61-
let get_api_commit ~(ctx : Context.t) ~repo ~sha =
62-
let%lwt res = commits_url ~repo ~sha |> get_resource ctx in
62+
let get_api_commit ~(ctx : Context.t) ~(repo : Github_t.repository) ~sha =
63+
let%lwt res = commits_url ~repo ~sha |> get_resource ~secrets:(Context.get_secrets_exn ctx) ~repo_url:repo.url in
6364
Lwt.return @@ Result.map res ~f:Github_j.api_commit_of_string
6465

65-
let get_pull_request ~(ctx : Context.t) ~repo ~number =
66-
let%lwt res = pulls_url ~repo ~number |> get_resource ctx in
66+
let get_pull_request ~(ctx : Context.t) ~(repo : Github_t.repository) ~number =
67+
let%lwt res = pulls_url ~repo ~number |> get_resource ~secrets:(Context.get_secrets_exn ctx) ~repo_url:repo.url in
6768
Lwt.return @@ Result.map res ~f:Github_j.pull_request_of_string
6869

69-
let get_issue ~(ctx : Context.t) ~repo ~number =
70-
let%lwt res = issues_url ~repo ~number |> get_resource ctx in
70+
let get_issue ~(ctx : Context.t) ~(repo : Github_t.repository) ~number =
71+
let%lwt res = issues_url ~repo ~number |> get_resource ~secrets:(Context.get_secrets_exn ctx) ~repo_url:repo.url in
7172
Lwt.return @@ Result.map res ~f:Github_j.issue_of_string
7273

7374
let request_reviewers ~(ctx : Context.t) ~repo ~number ~reviewers =
7475
let body = Github_j.string_of_request_reviewers_req reviewers in
75-
let%lwt res = pulls_url ~repo ~number ^ "/requested_reviewers" |> post_resource ctx body in
76+
let%lwt res =
77+
pulls_url ~repo ~number ^ "/requested_reviewers"
78+
|> post_resource ~secrets:(Context.get_secrets_exn ctx) ~repo_url:repo.url body
79+
in
7680
Lwt.return @@ Result.map res ~f:(fun _ -> ())
7781
end
7882

lib/config.atd

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ type status_rule <ocaml from="Rule"> = abstract
22
type prefix_rule <ocaml from="Rule"> = abstract
33
type label_rule <ocaml from="Rule"> = abstract
44
type project_owners_rule <ocaml from="Rule"> = abstract
5+
type 'v map_as_object <ocaml from="Common"> = abstract
56

67
(* This type of rule is used for CI build notifications. *)
78
type status_rules = {
@@ -44,9 +45,18 @@ type webhook = {
4445
channel : string; (* name of the Slack channel to post the message *)
4546
}
4647

48+
type gh_repo_secrets = {
49+
(* GitHub personal access token, if repo access requires it *)
50+
?gh_token : string nullable;
51+
(* GitHub webhook token to secure the webhook *)
52+
?gh_hook_token : string nullable;
53+
}
54+
4755
(* This is the structure of the secrets file which stores sensitive information, and
4856
shouldn't be checked into version control. *)
4957
type secrets = {
58+
(* repo-specific secrets; overrides global values if defined for a given repo *)
59+
~repo_secrets <ocaml default="Common.StringMap.empty"> : gh_repo_secrets map_as_object;
5060
(* GitHub personal access token, if repo access requires it *)
5161
?gh_token : string nullable;
5262
(* GitHub webhook token to secure the webhook *)

lib/context.ml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ let find_repo_config_exn ctx repo_url =
4343

4444
let set_repo_config ctx repo_url config = Stringtbl.set ctx.config ~key:repo_url ~data:config
4545

46+
let gh_token_of_secrets (secrets : Config_t.secrets) repo_url =
47+
match Map.find secrets.repo_secrets repo_url with
48+
| None -> secrets.gh_token
49+
| Some repo_secrets -> repo_secrets.gh_token
50+
51+
let gh_hook_token_of_secrets (secrets : Config_t.secrets) repo_url =
52+
match Map.find secrets.repo_secrets repo_url with
53+
| None -> secrets.gh_hook_token
54+
| Some repo_secrets -> repo_secrets.gh_hook_token
55+
4656
let hook_of_channel ctx channel_name =
4757
let secrets = get_secrets_exn ctx in
4858
match List.find secrets.slack_hooks ~f:(fun webhook -> String.equal webhook.channel channel_name) with
@@ -93,8 +103,9 @@ let refresh_state ctx =
93103
let print_config ctx repo_url =
94104
let cfg = find_repo_config_exn ctx repo_url in
95105
let secrets = get_secrets_exn ctx in
106+
let token = gh_hook_token_of_secrets secrets repo_url in
96107
log#info "using prefix routing:";
97108
Rule.Prefix.print_prefix_routing cfg.prefix_rules.rules;
98109
log#info "using label routing:";
99110
Rule.Label.print_label_routing cfg.label_rules.rules;
100-
log#info "signature checking %s" (if Option.is_some secrets.gh_hook_token then "enabled" else "disabled")
111+
log#info "signature checking %s" (if Option.is_some token then "enabled" else "disabled")

0 commit comments

Comments
 (0)