Skip to content

Commit 7ae27aa

Browse files
Zimmi48erikmd
andcommitted
Introduce support for multiple GitLab instances.
Distinguish the bot's name on GitHub and on each GitLab instance. Introduce ppx_expect test for new function. Co-authored-by: Erik Martin-Dorel <[email protected]>
1 parent 8101013 commit 7ae27aa

26 files changed

+494
-266
lines changed

README.md

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -253,14 +253,21 @@ Once you finish the installation, follow these steps:
253253
By default, **@coqbot** considers that both GitHub and GitLab repositories
254254
share the same URL except for the "lab" replacing the "hub" part. If
255255
that is not the case, assuming you created a GitLab repository whose
256-
URL is <https://gitlab.com/owner/repo/>, add a file `coqbot.toml` at
256+
URL is <https://mygitlab.example.com/owner/repo/>, add a file `coqbot.toml` at
257257
the root of your GitHub repository and in its default branch (most often
258258
named `master`), containing:
259259
```
260260
[mapping]
261261
gitlab = "owner/repo"
262+
gitlab_domain = "mygitlab.example.com"
262263
```
263-
If you use other instance of **@coqbot**, this repository-specific
264+
If omitted, the `gitlab_domain` value defaults to `"gitlab.com"`.
265+
Note that the value of `gitlab_domain` must be a supported GitLab
266+
instance, i.e., it needs to be defined in the bot's own configuration
267+
file (check [coqbot-config.toml](coqbot-config.toml) for the coqbot
268+
instance configuration).
269+
270+
If you use another instance of **@coqbot**, this repository-specific
264271
configuration file becomes `BOT_NAME.toml` where `BOT_NAME` is the name
265272
of the bot.
266273

@@ -345,11 +352,14 @@ to [Heroku](https://www.heroku.com/). Simply follow the official
345352
The bot will need to read a few environment variables so make sure
346353
these are configured in your Heroku app:
347354

348-
- `GITLAB_ACCESS_TOKEN`
349-
- `GITHUB_ACCESS_TOKEN`
350-
- `GITHUB_WEBHOOK_SECRET`
355+
- `GITHUB_ACCESS_TOKEN` (can also be defined in the configuration file as `github.api_token`)
356+
- `GITLAB_ACCESS_TOKEN` (can also be defined for each GitLab instance through the configuration file as `api_token` or through an environment variable whose name is defined in the configuration file as `api_token_env_var`)
357+
- `GITHUB_WEBHOOK_SECRET` (can also be defined in the configuration file as `github.webhook_secret`)
358+
- `GITLAB_WEBHOOK_SECRET` (can also be defined in the configuration file as `gitlab.webhook_secret`, will default to `GITHUB_WEBHOOK_SECRET` if not defined)
359+
- `DAILY_SCHEDULE_SECRET` (can also be defined in the configuration file as `github.daily_schedule_secret`, will default to `GITHUB_WEBHOOK_SECRET` if not defined)
360+
- `GITHUB_APP_ID` (can also be defined in the configuration file as `github.app_id`)
351361
- `GITHUB_PRIVATE_KEY` (a private key of your GitHub app)
352-
- `GITHUB_APP_ID` (your GitHub App ID)
362+
- `PORT` (can also be defined in the configuration file as `server.port`)
353363

354364
Then, you must configure the bot with a configuration file. Here is an example
355365
to adapt to your needs [`example-config.toml`](example-config.toml)).

bot-components/Bot_info.ml

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
open Base
2+
13
type t =
2-
{ gitlab_token: string
4+
{ gitlab_instances: (string, string * string) Hashtbl.t
35
; github_pat: string
46
; github_install_token: string option
5-
; name: string
7+
; github_name: string
68
; email: string
79
; domain: string
810
; app_id: int }
@@ -13,3 +15,15 @@ let github_token bot_info =
1315
t
1416
| None ->
1517
bot_info.github_pat
18+
19+
let gitlab_name_and_token bot_info gitlab_domain =
20+
match Hashtbl.find bot_info.gitlab_instances gitlab_domain with
21+
| Some t ->
22+
Ok t
23+
| None ->
24+
Error
25+
( "I don't know about GitLab domain " ^ gitlab_domain
26+
^ " (not in my configuration file)" )
27+
28+
let gitlab_token bot_info gitlab_domain =
29+
gitlab_name_and_token bot_info gitlab_domain |> Result.map ~f:snd

bot-components/Bot_info.mli

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
type t =
2-
{ gitlab_token: string
2+
{ gitlab_instances: (string, string * string) Base.Hashtbl.t
33
; github_pat: string
44
; github_install_token: string option
5-
; name: string
5+
; github_name: string
66
; email: string
77
; domain: string
88
; app_id: int }
99

1010
val github_token : t -> string
11+
12+
val gitlab_token : t -> string -> (string, string) Result.t
13+
14+
val gitlab_name_and_token : t -> string -> (string * string, string) Result.t

bot-components/GitHub_app.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@ let make_jwt ~key ~app_id =
3939

4040
let get ~bot_info ~token ~url =
4141
Stdio.print_endline ("Making get request to " ^ url) ;
42-
let headers = headers ~bot_info (github_headers token) in
42+
let headers = headers (github_headers token) bot_info.Bot_info.github_name in
4343
Client.get ~headers (Uri.of_string url)
4444
>>= fun (_response, body) -> Cohttp_lwt.Body.to_string body
4545

4646
let post ~bot_info ~body ~token ~url =
4747
Stdio.print_endline ("Making post request to " ^ url) ;
48-
let headers = headers ~bot_info (github_headers token) in
48+
let headers = headers (github_headers token) bot_info.Bot_info.github_name in
4949
let body =
5050
(match body with None -> "{}" | Some json -> Yojson.to_string json)
5151
|> Cohttp_lwt.Body.of_string

bot-components/GitHub_mutations.ml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ open Cohttp_lwt_unix
44
open Lwt
55
open Utils
66

7-
let send_graphql_query = GraphQL_query.send_graphql_query ~api:`GitHub
7+
let send_graphql_query = GraphQL_query.send_graphql_query ~api:GitHub
88

99
let mv_card_to_column ~bot_info ({card_id; column_id} : mv_card_to_column_input)
1010
=
@@ -213,7 +213,7 @@ let remove_labels ~bot_info ~labels ~issue =
213213
(* TODO: use GraphQL API *)
214214

215215
let update_milestone ~bot_info new_milestone (issue : issue) =
216-
let headers = headers (github_header bot_info) ~bot_info in
216+
let headers = headers (github_header bot_info) bot_info.github_name in
217217
let uri =
218218
f "https://api.github.com/repos/%s/%s/issues/%d" issue.owner issue.repo
219219
issue.number
@@ -245,7 +245,7 @@ let send_status_check ~bot_info ~repo_full_name ~commit ~state ~url ~context
245245
"https://api.github.com/repos/" ^ repo_full_name ^ "/statuses/" ^ commit
246246
|> Uri.of_string
247247
in
248-
send_request ~body ~uri (github_header bot_info) ~bot_info
248+
send_request ~body ~uri (github_header bot_info) bot_info.github_name
249249

250250
let add_pr_to_column ~bot_info ~pr_id ~column_id =
251251
let body =
@@ -265,4 +265,4 @@ let add_pr_to_column ~bot_info ~pr_id ~column_id =
265265
in
266266
send_request ~body ~uri
267267
(project_api_preview_header @ github_header bot_info)
268-
~bot_info
268+
bot_info.github_name

bot-components/GitHub_queries.ml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@ open GitHub_types
44
open Lwt
55
open Utils
66

7-
let send_graphql_query = GraphQL_query.send_graphql_query ~api:`GitHub
7+
let send_graphql_query = GraphQL_query.send_graphql_query ~api:GitHub
88

99
let extract_backport_info ~(bot_info : Bot_info.t) description :
1010
full_backport_info option =
1111
let project_column_regexp =
1212
"https://github.com/[^/]*/[^/]*/projects/[0-9]+#column-\\([0-9]+\\)"
1313
in
1414
let regexp =
15-
bot_info.name ^ ": backport to \\([^ ]*\\) (request inclusion column: "
15+
bot_info.github_name
16+
^ ": backport to \\([^ ]*\\) (request inclusion column: "
1617
^ project_column_regexp ^ "; backported column: " ^ project_column_regexp
1718
^ "; move rejected PRs to: "
1819
^ "https://github.com/[^/]*/[^/]*/milestone/\\([0-9]+\\)" ^ ")"
@@ -29,7 +30,7 @@ let extract_backport_info ~(bot_info : Bot_info.t) description :
2930
[{backport_to; request_inclusion_column; backported_column}]
3031
; rejected_milestone }
3132
else
32-
let begin_regexp = bot_info.name ^ ": \\(.*\\)$" in
33+
let begin_regexp = bot_info.github_name ^ ": \\(.*\\)$" in
3334
let backport_info_unit =
3435
"backport to \\([^ ]*\\) (request inclusion column: "
3536
^ project_column_regexp ^ "; backported column: " ^ project_column_regexp

bot-components/GitLab_mutations.ml

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,48 @@ open Base
22
open Bot_info
33
open Utils
44

5-
let generic_retry ~bot_info ~url_part =
5+
let generic_retry ~bot_info ~gitlab_domain ~url_part =
66
let uri =
7-
"https://gitlab.com/api/v4/" ^ url_part ^ "/retry" |> Uri.of_string
7+
f "https://%s/api/v4/%s/retry" gitlab_domain url_part |> Uri.of_string
88
in
9-
let gitlab_header = [("Private-Token", bot_info.gitlab_token)] in
10-
Utils.send_request ~body:Cohttp_lwt.Body.empty ~uri gitlab_header ~bot_info
9+
match gitlab_name_and_token bot_info gitlab_domain with
10+
| Error err ->
11+
Lwt_io.printlf "Error when retrying job %s: %s." url_part err
12+
| Ok (name, token) ->
13+
let gitlab_header = [("Private-Token", token)] in
14+
Utils.send_request ~body:Cohttp_lwt.Body.empty ~uri gitlab_header name
1115

12-
let retry_job ~bot_info ~project_id ~build_id =
13-
generic_retry ~bot_info
16+
let retry_job ~bot_info ~gitlab_domain ~project_id ~build_id =
17+
generic_retry ~bot_info ~gitlab_domain
1418
~url_part:
1519
( "projects/" ^ Int.to_string project_id ^ "/jobs/"
1620
^ Int.to_string build_id )
1721

18-
let play_job ~bot_info ~project_id ~build_id ?(key_value_pairs = []) () =
22+
let play_job ~bot_info ~gitlab_domain ~project_id ~build_id
23+
?(key_value_pairs = []) () =
1924
let uri =
2025
Uri.of_string
21-
@@ Printf.sprintf "https://gitlab.com/api/v4/projects/%d/jobs/%d/play"
26+
@@ Printf.sprintf "https://%s/api/v4/projects/%d/jobs/%d/play" gitlab_domain
2227
project_id build_id
2328
in
24-
let gitlab_header =
25-
[ ("Private-Token", bot_info.gitlab_token)
26-
; ("Content-Type", "application/json") ]
27-
in
28-
let body =
29-
match key_value_pairs with
30-
| [] ->
31-
Cohttp_lwt.Body.empty
32-
| _ ->
33-
key_value_pairs
34-
|> List.map ~f:(fun (k, v) -> f {|{ "key": "%s", "value": "%s" }|} k v)
35-
|> String.concat ~sep:","
36-
|> f {|{ "job_variables_attributes": [%s] }|}
37-
|> Cohttp_lwt.Body.of_string
38-
in
39-
Utils.send_request ~body ~uri gitlab_header ~bot_info
29+
match gitlab_name_and_token bot_info gitlab_domain with
30+
| Error err ->
31+
Lwt_io.printlf "Error when playing job %d of project %d: %s." build_id
32+
project_id err
33+
| Ok (name, token) ->
34+
let gitlab_header =
35+
[("Private-Token", token); ("Content-Type", "application/json")]
36+
in
37+
let body =
38+
match key_value_pairs with
39+
| [] ->
40+
Cohttp_lwt.Body.empty
41+
| _ ->
42+
key_value_pairs
43+
|> List.map ~f:(fun (k, v) ->
44+
f {|{ "key": "%s", "value": "%s" }|} k v )
45+
|> String.concat ~sep:","
46+
|> f {|{ "job_variables_attributes": [%s] }|}
47+
|> Cohttp_lwt.Body.of_string
48+
in
49+
Utils.send_request ~body ~uri gitlab_header name

bot-components/GitLab_mutations.mli

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
val retry_job :
2-
bot_info:Bot_info.t -> project_id:int -> build_id:int -> unit Lwt.t
2+
bot_info:Bot_info.t
3+
-> gitlab_domain:string
4+
-> project_id:int
5+
-> build_id:int
6+
-> unit Lwt.t
37

4-
val generic_retry : bot_info:Bot_info.t -> url_part:string -> unit Lwt.t
8+
val generic_retry :
9+
bot_info:Bot_info.t -> gitlab_domain:string -> url_part:string -> unit Lwt.t
510

611
val play_job :
712
bot_info:Bot_info.t
13+
-> gitlab_domain:string
814
-> project_id:int
915
-> build_id:int
1016
-> ?key_value_pairs:(string * string) list

bot-components/GitLab_queries.ml

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,37 @@
11
open Base
22
open Cohttp_lwt_unix
3-
open Lwt
43
open Bot_info
54
open Utils
65

7-
let send_graphql_query = GraphQL_query.send_graphql_query ~api:`GitLab
6+
let send_graphql_query ~gitlab_domain =
7+
GraphQL_query.send_graphql_query ~api:(GitLab gitlab_domain)
88

9-
let get_build_trace ~bot_info ~project_id ~build_id =
9+
let get_build_trace ~bot_info ~gitlab_domain ~project_id ~build_id =
1010
let uri =
11-
"https://gitlab.com/api/v4/projects/" ^ Int.to_string project_id ^ "/jobs/"
12-
^ Int.to_string build_id ^ "/trace"
11+
f "https://%s/api/v4/projects/%d/jobs/%d/trace" gitlab_domain project_id
12+
build_id
1313
|> Uri.of_string
1414
in
15-
let gitlab_header = [("Private-Token", bot_info.gitlab_token)] in
16-
let headers = Utils.headers gitlab_header ~bot_info in
15+
let open Lwt_result.Infix in
16+
gitlab_name_and_token bot_info gitlab_domain
17+
|> Lwt.return
18+
>>= fun (name, token) ->
19+
let gitlab_header = [("Private-Token", token)] in
20+
let headers = Utils.headers gitlab_header name in
21+
let open Lwt.Infix in
1722
Client.get ~headers uri
18-
>>= fun (_response, body) -> Cohttp_lwt.Body.to_string body
23+
>>= fun (_response, body) ->
24+
Cohttp_lwt.Body.to_string body |> Lwt.map Result.return
1925

20-
let get_retry_nb ~bot_info ~full_name ~build_id ~build_name =
26+
let get_retry_nb ~bot_info ~gitlab_domain ~full_name ~build_id ~build_name =
2127
let open GitLab_GraphQL.GetRetriedJobs in
28+
let open Lwt.Infix in
2229
makeVariables ~fullPath:full_name
2330
~jobId:
2431
(build_id |> f {|"gid://gitlab/Ci::Build/%d"|} |> Yojson.Basic.from_string)
2532
()
2633
|> serializeVariables |> variablesToJson
27-
|> send_graphql_query ~bot_info ~query
34+
|> send_graphql_query ~bot_info ~gitlab_domain ~query
2835
~parse:(Fn.compose parse unsafe_fromJson)
2936
>|= function
3037
| Ok {project= Some {job= Some {pipeline= Some {jobs= Some {count= 0}}}}} ->

bot-components/GitLab_queries.mli

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
val get_build_trace :
2-
bot_info:Bot_info.t -> project_id:int -> build_id:int -> string Lwt.t
2+
bot_info:Bot_info.t
3+
-> gitlab_domain:string
4+
-> project_id:int
5+
-> build_id:int
6+
-> (string, string) Lwt_result.t
37

48
val get_retry_nb :
59
bot_info:Bot_info.t
10+
-> gitlab_domain:string
611
-> full_name:string
712
-> build_id:int
813
-> build_name:string

bot-components/GitLab_subscriptions.ml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ let job_info_of_json json =
3232
let project_id = json |> member "project_id" |> to_int in
3333
let base_commit, head_commit = json |> extract_commit in
3434
let branch = json |> member "ref" |> to_string in
35-
let repo_url = json |> member "repository" |> member "url" |> to_string in
35+
let http_repo_url =
36+
json |> member "repository" |> member "homepage" |> to_string
37+
in
3638
let stage = json |> member "build_stage" |> to_string in
3739
let failure_reason =
3840
json |> member "build_failure_reason" |> to_string |> Option.some
@@ -44,7 +46,8 @@ let job_info_of_json json =
4446
; stage
4547
; failure_reason
4648
; allow_fail
47-
; common_info= {base_commit; head_commit; branch; repo_url; project_id} }
49+
; common_info= {base_commit; head_commit; branch; http_repo_url; project_id}
50+
}
4851

4952
(* For use to decode builds inside a pipeline webhook *)
5053
let build_info_of_json json =
@@ -70,8 +73,7 @@ let pipeline_info_of_json json =
7073
let base_commit, head_commit = json |> extract_commit in
7174
let branch = pipeline_json |> member "ref" |> to_string in
7275
let project = json |> member "project" in
73-
let repo_url = project |> member "web_url" |> to_string in
74-
let project_path = project |> member "path_with_namespace" |> to_string in
76+
let http_repo_url = project |> member "web_url" |> to_string in
7577
let project_id = project |> member "id" |> to_int in
7678
let variables =
7779
pipeline_json |> member "variables" |> to_list
@@ -88,8 +90,7 @@ let pipeline_info_of_json json =
8890
in
8991
{ state
9092
; pipeline_id
91-
; project_path
92-
; common_info= {head_commit; base_commit; branch; repo_url; project_id}
93+
; common_info= {head_commit; base_commit; branch; http_repo_url; project_id}
9394
; variables
9495
; stages
9596
; builds }

bot-components/GitLab_types.mli

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ type ci_common_info =
22
{ head_commit: string
33
; base_commit: string option
44
; branch: string
5-
; repo_url: string
5+
; http_repo_url: string
66
; project_id: int }
77

88
type 'a job_info =
@@ -17,7 +17,6 @@ type 'a job_info =
1717
type pipeline_info =
1818
{ state: string
1919
; pipeline_id: int
20-
; project_path: string
2120
; common_info: ci_common_info
2221
; variables: (string * string) list
2322
; stages: string list

0 commit comments

Comments
 (0)