Skip to content

Commit d771ab5

Browse files
authored
Support coqbot resume ci minimize ci-foo url (#298)
Some notes on usage: - Both `ci minimize ci-foo https://...` and `ci minimize ci-foo [description](url)` are supported. - you can directly link to the artifacts of previous runs, such as the `tmp.v` file, and coqbot will unpack the artifact - other links need to be to text files that are the buggy file (you can upload it as .txt or .v.txt or .log or w/e) - plausibly in the future we can support non-artifact links to .zip files and .tar.gz files, etc; in the interim, I don't expect this to be a big issue
2 parents 302337a + 1dfcc64 commit d771ab5

12 files changed

+789
-460
lines changed

bot-components.opam

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ depends: [
2323
"x509" {>= "0.11.2"}
2424
"cstruct" {>= "5.0.0"}
2525
"ISO8601" {>= "0.2.0"}
26+
"camlzip" {>= "1.08"}
2627
"odoc" {>= "1.5.2" & with-doc}
2728
]
2829
build: [

bot-components/GitHub_queries.ml

+6
Original file line numberDiff line numberDiff line change
@@ -1021,3 +1021,9 @@ let get_project_field_values ~bot_info ~organization ~project ~field ~options =
10211021
Lwt.return_error (f "Organization %s does not exist." organization) )
10221022
| Error err ->
10231023
Lwt.return_error err
1024+
1025+
let get_artifact_blob ~bot_info ~owner ~repo ~artifact_id =
1026+
generic_get_zip ~bot_info
1027+
(f "repos/%s/%s/actions/artifacts/%s/zip" owner repo artifact_id)
1028+
(let open Zip in
1029+
List.map ~f:(fun (entry, contents) -> (entry.filename, contents)) )

bot-components/GitHub_queries.mli

+7
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,10 @@ val get_project_field_values :
151151
, string )
152152
result
153153
Lwt.t
154+
155+
val get_artifact_blob :
156+
bot_info:Bot_info.t
157+
-> owner:string
158+
-> repo:string
159+
-> artifact_id:string
160+
-> ((string * string) list, string) result Lwt.t

bot-components/Utils.ml

+82-7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ open Bot_info
33
open Cohttp
44
open Cohttp_lwt_unix
55
open Lwt
6+
open Zip
67

78
let f = Printf.sprintf
89

@@ -43,6 +44,30 @@ let handle_json action body =
4344
| Yojson.Basic.Util.Type_error (err, _) ->
4445
Error (f "Json type error: %s\n" err)
4546

47+
let handle_zip action body =
48+
let open Lwt_result.Infix in
49+
Lwt_io.with_temp_file (fun (tmp_name, tmp_channel) ->
50+
let open Lwt.Infix in
51+
Lwt_io.write tmp_channel body
52+
>>= fun () ->
53+
Lwt_io.close tmp_channel
54+
>>= Lwt_preemptive.detach (fun () ->
55+
try
56+
let zip_entries =
57+
let zf = Zip.open_in tmp_name in
58+
let entries =
59+
Zip.entries zf
60+
|> List.filter ~f:(fun entry -> not entry.is_directory)
61+
|> List.map ~f:(fun entry ->
62+
(entry, Zip.read_entry zf entry) )
63+
in
64+
Zip.close_in zf ; entries
65+
in
66+
Ok zip_entries
67+
with Zip.Error (zip_name, entry_name, message) ->
68+
Error (f "Zip.Error(%s, %s, %s)" zip_name entry_name message) ) )
69+
>|= action
70+
4671
(* GitHub specific *)
4772

4873
let project_api_preview_header =
@@ -51,14 +76,64 @@ let project_api_preview_header =
5176
let app_api_preview_header =
5277
[("Accept", "application/vnd.github.machine-man-preview+json")]
5378

79+
let api_json_header = [("Accept", "application/vnd.github+json")]
80+
5481
let github_header bot_info =
5582
[("Authorization", "bearer " ^ github_token bot_info)]
5683

57-
let generic_get ~bot_info relative_uri ?(header_list = []) json_handler =
58-
let uri = "https://api.github.com/" ^ relative_uri |> Uri.of_string in
59-
let headers =
60-
headers (header_list @ github_header bot_info) bot_info.github_name
61-
in
84+
let headers_of_list = headers
85+
86+
(* when following a redirect from GitHub to Azure, passing along the
87+
Authorization header results in 403 Forbidden. So we strip the
88+
headers when we recurse by default. *)
89+
let rec client_get ?(follow_redirects = true)
90+
?(include_headers_in_redirects = false) ~user_agent ~headers uri =
6291
Client.get ~headers uri
63-
>>= (fun (_response, body) -> Cohttp_lwt.Body.to_string body)
64-
>|= handle_json json_handler
92+
>>= fun (resp, body) ->
93+
match Response.status resp with
94+
| `OK ->
95+
Lwt.return_ok body
96+
| `Moved_permanently
97+
| `Found
98+
| `See_other
99+
| `Temporary_redirect
100+
| `Permanent_redirect
101+
when follow_redirects -> (
102+
let headers =
103+
if include_headers_in_redirects then headers
104+
else headers_of_list [] user_agent
105+
in
106+
match Header.get_location (Response.headers resp) with
107+
| Some new_uri ->
108+
Lwt_io.printlf "Following redirect to %s" (Uri.to_string new_uri)
109+
>>= fun () ->
110+
client_get ~follow_redirects ~include_headers_in_redirects ~headers
111+
~user_agent new_uri
112+
| None ->
113+
let msg =
114+
f "Redirected from %s, but no Location header found"
115+
(Uri.to_string uri)
116+
in
117+
Lwt.return_error msg )
118+
| status_code ->
119+
let msg =
120+
f "HTTP request to %s failed with status code: %s" (Uri.to_string uri)
121+
(Code.string_of_status status_code)
122+
in
123+
Lwt.return_error msg
124+
125+
let generic_get ~bot_info relative_uri ?(header_list = []) handler =
126+
let open Lwt_result.Infix in
127+
let uri = "https://api.github.com/" ^ relative_uri |> Uri.of_string in
128+
let user_agent = bot_info.github_name in
129+
let headers = headers (header_list @ github_header bot_info) user_agent in
130+
client_get ~headers ~user_agent uri
131+
>>= (fun body -> Cohttp_lwt.Body.to_string body |> Lwt_result.ok)
132+
>>= handler
133+
134+
let generic_get_json ~bot_info relative_uri ?(header_list = []) json_handler =
135+
generic_get ~bot_info relative_uri ~header_list (fun body ->
136+
body |> handle_json json_handler |> Lwt.return )
137+
138+
let generic_get_zip ~bot_info relative_uri ?(header_list = []) zip_handler =
139+
generic_get ~bot_info relative_uri ~header_list (handle_zip zip_handler)

bot-components/Utils.mli

+10-1
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,20 @@ val project_api_preview_header : (string * string) list
1717

1818
val app_api_preview_header : (string * string) list
1919

20+
val api_json_header : (string * string) list
21+
2022
val github_header : Bot_info.t -> (string * string) list
2123

22-
val generic_get :
24+
val generic_get_json :
2325
bot_info:Bot_info.t
2426
-> string
2527
-> ?header_list:(string * string) list
2628
-> (Yojson.Basic.t -> 'a)
2729
-> ('a, string) result Lwt.t
30+
31+
val generic_get_zip :
32+
bot_info:Bot_info.t
33+
-> string
34+
-> ?header_list:(string * string) list
35+
-> ((Zip.entry * string) list -> 'a)
36+
-> ('a, string) result Lwt.t

bot-components/dune

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
(library
22
(name Bot_components)
33
(public_name bot-components)
4-
(libraries base cohttp-lwt-unix cstruct eqaf hex mirage-crypto stdio str
5-
x509 yojson ISO8601)
4+
(libraries base camlzip cohttp-lwt-unix cstruct eqaf hex mirage-crypto stdio
5+
str x509 yojson ISO8601)
66
(private_modules GraphQL_query GitHub_GraphQL Utils)
77
(modules_without_implementation GitHub_types GitLab_types)
88
(preprocess

dune-project

+1
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,6 @@
5050
(x509 (>= 0.11.2))
5151
(cstruct (>= 5.0.0))
5252
(ISO8601 (>= 0.2.0))
53+
(camlzip (>= 1.08))
5354
(odoc (and (>= 1.5.2) :with-doc)))
5455
)

0 commit comments

Comments
 (0)