@@ -3,6 +3,41 @@ open Devkit
33open Common
44open Util
55
6+ module Github_app = struct
7+ let jwt_token ({ client_id; pem; _ } : Config_t.app_installation_cfg ) =
8+ let b64enc s = Base64. encode_string ~alphabet: Base64. uri_safe_alphabet ~pad: false s in
9+ let now = Unix. gettimeofday () in
10+ (* Issues 60 seconds in the past *)
11+ let iat = int_of_float (now -. 60.0 ) in
12+ (* Expires 1 minute from now *)
13+ let exp = int_of_float (now +. 60.0 ) in
14+ let header_json = {| {" typ" :" JWT" ," alg" :" RS256" }| } in
15+ let payload_json = sprintf {| {" iat" :% d," exp" :% d," iss" :" %s" }| } iat exp client_id in
16+ let header_payload = sprintf " %s.%s" (b64enc header_json) (b64enc payload_json) in
17+ let key =
18+ match X509.Private_key. decode_pem pem with
19+ | Ok (`RSA k ) -> k
20+ | Ok _ -> failwith " Expected RSA key for app installation auth"
21+ | Error (`Message e ) -> failwith @@ " Failed to parse app installation private key: " ^ e
22+ | _ -> failwith " Failed to parse app installation private key"
23+ in
24+ let signature = Mirage_crypto_pk.Rsa.PKCS1. sign ~hash: `SHA256 ~key (`Message header_payload) |> b64enc in
25+ sprintf " %s.%s" header_payload signature
26+
27+ let get_installation_token (app : Config_t.app_installation_cfg ) =
28+ let headers = [ " Accept: application/vnd.github.v3+json" ; sprintf " Authorization: Bearer %s" (jwt_token app) ] in
29+ let url = sprintf " https://api.github.com/app/installations/%s/access_tokens" app.installation_id in
30+ let % lwt res =
31+ http_request ~headers `POST url ~body: (`Raw (" application/json" , " " ))
32+ |> Lwt_result. map_error (fun e -> sprintf " Error while authenticating with GitHub app: %s" e)
33+ in
34+ match res with
35+ | Ok res ->
36+ let { Github_t. token; _ } = Github_j. installation_token_response_of_string res in
37+ Lwt. return token
38+ | Error e -> failwith e
39+ end
40+
641module Github : Api .Github = struct
742 let commits_url ~(repo : Github_t.repository ) ~sha =
843 let _, url = ExtLib.String. replace ~sub: " {/sha}" ~by: (" /" ^ sha) ~str: repo.commits_url in
@@ -29,7 +64,14 @@ module Github : Api.Github = struct
2964 Option. map_default (fun v -> sprintf " Authorization: token %s" v :: headers) headers token
3065
3166 let prepare_request ~secrets ~repo_url url =
32- let token = Context. gh_token_of_secrets secrets repo_url in
67+ let % lwt token =
68+ match Context. gh_auth_of_secrets secrets repo_url with
69+ | None -> Lwt. return_none
70+ | Some (GH_token token ) -> Lwt. return_some token
71+ | Some (AppInstallation gh_app ) ->
72+ let % lwt token = Github_app. get_installation_token gh_app in
73+ Lwt. return_some token
74+ in
3375 let headers = build_headers ?token () in
3476 let url =
3577 match Context. gh_repo_of_secrets secrets repo_url with
@@ -40,15 +82,15 @@ module Github : Api.Github = struct
4082 let repo_config_url_scheme = repo_config.url |> Uri. of_string |> Uri. scheme in
4183 url |> Uri. of_string |> flip Uri. with_scheme repo_config_url_scheme |> Uri. to_string
4284 in
43- headers, url
85+ Lwt. return ( headers, url)
4486
4587 let get_resource ~secrets ~repo_url url =
46- let headers, url = prepare_request ~secrets ~repo_url url in
88+ let % lwt headers, url = prepare_request ~secrets ~repo_url url in
4789 http_request ~headers `GET url
4890 |> Lwt_result. map_error (fun e -> sprintf " error while querying remote: %s\n failed to get resource from %s" e url)
4991
5092 let post_resource ~secrets ~repo_url body url =
51- let headers, url = prepare_request ~secrets ~repo_url url in
93+ let % lwt headers, url = prepare_request ~secrets ~repo_url url in
5294 http_request ~headers ~body: (`Raw (" application/json; charset=utf-8" , body)) `POST url
5395 |> Lwt_result. map_error (sprintf " POST to %s failed : %s" url)
5496
0 commit comments