Merge pull request 'only show active jobs, fixes #162' (#176) from orphan-old-builds into main

Reviewed-on: #176
This commit is contained in:
Reynir Björnsson 2023-09-26 11:18:40 +00:00
commit 46f93c28ea
4 changed files with 68 additions and 39 deletions

View file

@ -81,28 +81,28 @@ let init_influx name data =
let run_batch_viz ~cachedir ~datadir ~configdir = let run_batch_viz ~cachedir ~datadir ~configdir =
let open Rresult.R.Infix in let open Rresult.R.Infix in
begin begin
let script = Fpath.(configdir / "batch-viz.sh") let script = Fpath.(configdir / "batch-viz.sh")
and script_log = Fpath.(cachedir / "batch-viz.log") and script_log = Fpath.(cachedir / "batch-viz.log")
and viz_script = Fpath.(configdir / "upload-hooks" / "visualizations.sh") and viz_script = Fpath.(configdir / "upload-hooks" / "visualizations.sh")
in in
Bos.OS.File.exists script >>= fun script_exists -> Bos.OS.File.exists script >>= fun script_exists ->
if not script_exists then begin if not script_exists then begin
Logs.warn (fun m -> m "Didn't find %s" (Fpath.to_string script)); Logs.warn (fun m -> m "Didn't find %s" (Fpath.to_string script));
Ok () Ok ()
end else end else
let args = let args =
[ "--cache-dir=" ^ Fpath.to_string cachedir; [ "--cache-dir=" ^ Fpath.to_string cachedir;
"--data-dir=" ^ Fpath.to_string datadir; "--data-dir=" ^ Fpath.to_string datadir;
"--viz-script=" ^ Fpath.to_string viz_script ] "--viz-script=" ^ Fpath.to_string viz_script ]
|> List.map (fun s -> "\"" ^ String.escaped s ^ "\"") |> List.map (fun s -> "\"" ^ String.escaped s ^ "\"")
|> String.concat " " |> String.concat " "
in in
(*> Note: The reason for appending, is that else a new startup could (*> Note: The reason for appending, is that else a new startup could
overwrite an existing running batch's log*) overwrite an existing running batch's log*)
(Fpath.to_string script ^ " " ^ args (Fpath.to_string script ^ " " ^ args
^ " 2>&1 >> " ^ Fpath.to_string script_log ^ " 2>&1 >> " ^ Fpath.to_string script_log
^ " &") ^ " &")
|> Sys.command |> Sys.command
|> ignore |> ignore
|> Result.ok |> Result.ok
end end
@ -113,7 +113,7 @@ let run_batch_viz ~cachedir ~datadir ~configdir =
m "Error while starting batch-viz.sh: %a" m "Error while starting batch-viz.sh: %a"
Rresult.R.pp_msg err) Rresult.R.pp_msg err)
let setup_app level influx port host datadir cachedir configdir run_batch_viz_flag = let setup_app level influx port host datadir cachedir configdir run_batch_viz_flag expired_jobs =
let dbpath = Printf.sprintf "%s/builder.sqlite3" datadir in let dbpath = Printf.sprintf "%s/builder.sqlite3" datadir in
let datadir = Fpath.v datadir in let datadir = Fpath.v datadir in
let cachedir = let cachedir =
@ -159,7 +159,7 @@ let setup_app level influx port host datadir cachedir configdir run_batch_viz_fl
let error_handler = Dream.error_template Builder_web.error_template in let error_handler = Dream.error_template Builder_web.error_template in
Dream.initialize_log ?level (); Dream.initialize_log ?level ();
let dream_routes = Builder_web.( let dream_routes = Builder_web.(
routes ~datadir ~cachedir ~configdir routes ~datadir ~cachedir ~configdir ~expired_jobs
|> to_dream_routes |> to_dream_routes
) )
in in
@ -241,11 +241,15 @@ let run_batch_viz =
log is written to CACHE_DIR/batch-viz.log" in log is written to CACHE_DIR/batch-viz.log" in
Arg.(value & flag & info [ "run-batch-viz" ] ~doc) Arg.(value & flag & info [ "run-batch-viz" ] ~doc)
let expired_jobs =
let doc = "Amount of days after which a job is considered to be inactive if \
no successful build has been achieved (use 0 for infinite)" in
Arg.(value & opt int 30 & info [ "expired-jobs" ] ~doc)
let () = let () =
let term = let term =
Term.(const setup_app $ Logs_cli.level () $ influx $ port $ host $ datadir $ Term.(const setup_app $ Logs_cli.level () $ influx $ port $ host $ datadir $
cachedir $ configdir $ run_batch_viz) cachedir $ configdir $ run_batch_viz $ expired_jobs)
in in
let info = Cmd.info "Builder web" ~doc:"Builder web" ~man:[] in let info = Cmd.info "Builder web" ~doc:"Builder web" ~man:[] in
Cmd.v info term Cmd.v info term

View file

@ -258,8 +258,17 @@ module Viz_aux = struct
end end
let routes ~datadir ~cachedir ~configdir = let routes ~datadir ~cachedir ~configdir ~expired_jobs =
let builds req = let builds ~all ?(filter_builds_later_than = 0) req =
let than =
if filter_builds_later_than = 0 then
Ptime.epoch
else
let n = Ptime.Span.v (filter_builds_later_than, 0L) in
let now = Ptime_clock.now () in
Ptime.Span.sub (Ptime.to_span now) n |> Ptime.of_span |>
Option.fold ~none:Ptime.epoch ~some:Fun.id
in
Dream.sql req Model.jobs_with_section_synopsis Dream.sql req Model.jobs_with_section_synopsis
|> if_error "Error getting jobs" |> if_error "Error getting jobs"
~log:(fun e -> Log.warn (fun m -> m "Error getting jobs: %a" pp_error e)) ~log:(fun e -> Log.warn (fun m -> m "Error getting jobs: %a" pp_error e))
@ -272,20 +281,26 @@ let routes ~datadir ~cachedir ~configdir =
r >>= fun acc -> r >>= fun acc ->
Dream.sql req (Model.build_with_main_binary job_id platform) >>= function Dream.sql req (Model.build_with_main_binary job_id platform) >>= function
| Some (build, artifact) -> | Some (build, artifact) ->
Lwt_result.return ((platform, build, artifact) :: acc) if Ptime.is_later ~than build.finish then
Lwt_result.return ((platform, build, artifact) :: acc)
else
Lwt_result.return acc
| None -> | None ->
Log.warn (fun m -> m "Job without builds: %s" job_name); Log.warn (fun m -> m "Job without builds: %s" job_name);
Lwt_result.return acc) Lwt_result.return acc)
ps (Lwt_result.return []) >>= fun platform_builds -> ps (Lwt_result.return []) >>= fun platform_builds ->
let v = (job_name, synopsis, platform_builds) in if platform_builds = [] then
let section = Option.value ~default:"Uncategorized" section in Lwt_result.return acc
Lwt_result.return (Utils.String_map.add_or_create section v acc)) else
let v = (job_name, synopsis, platform_builds) in
let section = Option.value ~default:"Uncategorized" section in
Lwt_result.return (Utils.String_map.add_or_create section v acc))
jobs jobs
(Lwt_result.return Utils.String_map.empty) (Lwt_result.return Utils.String_map.empty)
|> if_error "Error getting jobs" |> if_error "Error getting jobs"
~log:(fun e -> Log.warn (fun m -> m "Error getting jobs: %a" pp_error e)) ~log:(fun e -> Log.warn (fun m -> m "Error getting jobs: %a" pp_error e))
>>= fun jobs -> >>= fun jobs ->
Views.Builds.make jobs |> string_of_html |> Dream.html |> Lwt_result.ok Views.Builds.make ~all jobs |> string_of_html |> Dream.html |> Lwt_result.ok
in in
let job req = let job req =
@ -605,7 +620,7 @@ let routes ~datadir ~cachedir ~configdir =
let w f req = or_error_response (f req) in let w f req = or_error_response (f req) in
[ [
`Get, "/", (w builds); `Get, "/", (w (builds ~all:false ~filter_builds_later_than:expired_jobs));
`Get, "/job/:job", (w job); `Get, "/job/:job", (w job);
`Get, "/job/:job/failed", (w job_with_failed); `Get, "/job/:job/failed", (w job_with_failed);
`Get, "/job/:job/build/latest/**", (w redirect_latest); `Get, "/job/:job/build/latest/**", (w redirect_latest);
@ -619,6 +634,7 @@ let routes ~datadir ~cachedir ~configdir =
`Get, "/job/:job/build/:build/console", (w (job_build_static_file `Console)); `Get, "/job/:job/build/:build/console", (w (job_build_static_file `Console));
`Get, "/job/:job/build/:build/all.tar.gz", (w job_build_targz); `Get, "/job/:job/build/:build/all.tar.gz", (w job_build_targz);
`Get, "/failed-builds", (w failed_builds); `Get, "/failed-builds", (w failed_builds);
`Get, "/all-builds", (w (builds ~all:true));
`Get, "/hash", (w hash); `Get, "/hash", (w hash);
`Get, "/compare/:build_left/:build_right", (w compare_builds); `Get, "/compare/:build_left/:build_right", (w compare_builds);
`Post, "/upload", (Authorization.authenticate (w upload)); `Post, "/upload", (Authorization.authenticate (w upload));

View file

@ -98,7 +98,7 @@ let make_breadcrumbs nav =
txtf "Job %s" job_name, Link.Job.make ~job_name (); txtf "Job %s" job_name, Link.Job.make ~job_name ();
( (
txtf "%a" pp_platform platform, txtf "%a" pp_platform platform,
Link.Job.make ~job_name ~queries () Link.Job.make ~job_name ~queries ()
) )
] ]
| `Build (job_name, build) -> | `Build (job_name, build) ->
@ -122,7 +122,7 @@ let make_breadcrumbs nav =
txtf "Comparison between %s@%a and %s@%a" txtf "Comparison between %s@%a and %s@%a"
job_left pp_ptime build_left.Builder_db.Build.start job_left pp_ptime build_left.Builder_db.Build.start
job_right pp_ptime build_right.Builder_db.Build.start, job_right pp_ptime build_right.Builder_db.Build.start,
Link.Compare_builds.make Link.Compare_builds.make
~left:build_left.uuid ~left:build_left.uuid
~right:build_right.uuid () ~right:build_right.uuid ()
); );
@ -218,7 +218,7 @@ let page_not_found ~target ~referer =
| None -> [] | None -> []
| Some prev_url -> [ | Some prev_url -> [
H.p [ H.p [
H.txt "Go back to "; H.txt "Go back to ";
H.a ~a:H.[ a_href prev_url ] [ H.txt prev_url ]; H.a ~a:H.[ a_href prev_url ] [ H.txt prev_url ];
]; ];
] ]
@ -361,14 +361,23 @@ have questions or suggestions.
H.txt "View the latest failed builds "; H.txt "View the latest failed builds ";
H.a ~a:H.[a_href "/failed-builds"] H.a ~a:H.[a_href "/failed-builds"]
[H.txt "here"]; [H.txt "here"];
H.txt "." H.txt ".";
]] ]]
let make section_job_map = let make_all_or_active all =
[ H.p [
H.txt (if all then "View active jobs " else "View all jobs ");
H.a ~a:H.[a_href (if all then "/" else "/all-builds")]
[H.txt "here"];
H.txt ".";
]]
let make ~all section_job_map =
layout ~title:"Reproducible OPAM builds" layout ~title:"Reproducible OPAM builds"
(make_header (make_header
@ make_body section_job_map @ make_body section_job_map
@ make_failed_builds) @ make_failed_builds
@ make_all_or_active all)
end end
@ -393,7 +402,7 @@ module Job = struct
check_icon build.Builder_db.Build.result; check_icon build.Builder_db.Build.result;
txtf " %s " build.platform; txtf " %s " build.platform;
H.a ~a:H.[ H.a ~a:H.[
a_href @@ Link.Job_build.make a_href @@ Link.Job_build.make
~job_name ~job_name
~build:build.Builder_db.Build.uuid () ] ~build:build.Builder_db.Build.uuid () ]
[ [
@ -431,7 +440,7 @@ module Job = struct
H.txt "." ] H.txt "." ]
else else
H.p [ H.p [
H.txt "Including failed builds " ; H.txt "Including failed builds " ;
H.a ~a:H.[ H.a ~a:H.[
a_href @@ Link.Job.make_failed ~job_name ~queries () a_href @@ Link.Job.make_failed ~job_name ~queries ()
] ]
@ -582,7 +591,7 @@ module Job_build = struct
| Some b when not (Uuidm.equal build.uuid b.Builder_db.Build.uuid) -> | Some b when not (Uuidm.equal build.uuid b.Builder_db.Build.uuid) ->
[ H.li [ H.txt ctx; [ H.li [ H.txt ctx;
H.a ~a:[ H.a ~a:[
H.a_href @@ Link.Compare_builds.make H.a_href @@ Link.Compare_builds.make
~left:b.uuid ~left:b.uuid
~right:build.uuid () ] ~right:build.uuid () ]
[txtf "%a" pp_ptime b.start]] [txtf "%a" pp_ptime b.start]]
@ -679,10 +688,10 @@ module Job_build = struct
font-weight: bold;\ font-weight: bold;\
" "
] ]
let make_viz_section ~job_name ~artifacts ~uuid = let make_viz_section ~job_name ~artifacts ~uuid =
let viz_deps = let viz_deps =
let iframe = let iframe =
let src = Link.Job_build_artifact.make ~job_name ~build:uuid let src = Link.Job_build_artifact.make ~job_name ~build:uuid
~artifact:`Viz_dependencies () in ~artifact:`Viz_dependencies () in
H.iframe ~a:H.[ H.iframe ~a:H.[
@ -693,11 +702,11 @@ module Job_build = struct
in in
let descr_txt = "\ let descr_txt = "\
This is an interactive visualization of dependencies, \ This is an interactive visualization of dependencies, \
focusing on how shared dependencies are. focusing on how shared dependencies are.
In the middle you see the primary package. \ In the middle you see the primary package. \
Edges shoot out to its direct \ Edges shoot out to its direct \
dependencies, including build dependencies. dependencies, including build dependencies.
From these direct dependencies, edges shoot out to sets \ From these direct dependencies, edges shoot out to sets \
of their own respective direct dependencies. \ of their own respective direct dependencies. \
@ -714,7 +723,7 @@ dependency.\
[ iframe; H.br (); make_description descr_txt ] [ iframe; H.br (); make_description descr_txt ]
in in
let viz_treemap = lazy ( let viz_treemap = lazy (
let iframe = let iframe =
let src = Link.Job_build_artifact.make ~job_name ~build:uuid let src = Link.Job_build_artifact.make ~job_name ~build:uuid
~artifact:`Viz_treemap () in ~artifact:`Viz_treemap () in
H.iframe ~a:H.[ H.iframe ~a:H.[
@ -726,7 +735,7 @@ dependency.\
let descr_txt = "\ let descr_txt = "\
This interactive treemap shows the space-usage of modules/libraries inside the \ This interactive treemap shows the space-usage of modules/libraries inside the \
ELF binary. You can get more info from each block by \ ELF binary. You can get more info from each block by \
hovering over them. hovering over them.
On top of the treemap there is a scale, showing how much space the \ On top of the treemap there is a scale, showing how much space the \
treemap itself constitutes of the binary, the excluded symbols/modules \ treemap itself constitutes of the binary, the excluded symbols/modules \

View file

@ -2,7 +2,7 @@
module Param_verification = struct module Param_verification = struct
(*> None is 'verified'*) (*> None is 'verified'*)
type t = wrong_type option type t = wrong_type option
[@@deriving yojson,show,eq] [@@deriving yojson,show,eq]
and wrong_type = { and wrong_type = {
@ -24,9 +24,9 @@ module Param_verification = struct
param; param;
expected = "Uuidm.t" expected = "Uuidm.t"
} }
end end
let verify parameters req = let verify parameters req =
let verified_params = let verified_params =
List.fold_left (fun acc p -> List.fold_left (fun acc p ->
@ -53,13 +53,13 @@ let find_parameters path =
else else
None) None)
(String.split_on_char '/' path) (String.split_on_char '/' path)
let router = let router =
(* XXX: this relies on [Builder_web.routes] only using {data,cache,config}dir (* XXX: this relies on [Builder_web.routes] only using {data,cache,config}dir
* in the handlers which are never called here. The path /nonexistant is * in the handlers which are never called here. The path /nonexistant is
* assumed to not exist. *) * assumed to not exist. *)
let nodir = Fpath.v "/nonexistant" in let nodir = Fpath.v "/nonexistant" in
Builder_web.routes ~datadir:nodir ~cachedir:nodir ~configdir:nodir Builder_web.routes ~datadir:nodir ~cachedir:nodir ~configdir:nodir ~expired_jobs:0
|> List.map (fun (meth, route, _handler) -> |> List.map (fun (meth, route, _handler) ->
meth, route, Param_verification.verify (find_parameters route)) meth, route, Param_verification.verify (find_parameters route))
|> Builder_web.to_dream_routes |> Builder_web.to_dream_routes
@ -83,7 +83,7 @@ let test_link method_ target () =
Alcotest.(check' (result Param_verification.alcotyp string) ~msg:"param-verification" Alcotest.(check' (result Param_verification.alcotyp string) ~msg:"param-verification"
~actual:body ~expected:(Ok None)) ~actual:body ~expected:(Ok None))
let test_link_artifact artifact = let test_link_artifact artifact =
let job_name = "test" in let job_name = "test" in
let build = Uuidm.v `V4 in let build = Uuidm.v `V4 in
test_link `GET @@ test_link `GET @@
@ -147,7 +147,7 @@ let () =
end; end;
test_case "Link.Failed_builds.make" `Quick begin test_case "Link.Failed_builds.make" `Quick begin
test_link `GET @@ test_link `GET @@
Builder_web.Link.Failed_builds.make ~count:2 ~start:1 () Builder_web.Link.Failed_builds.make ~count:2 ~start:1 ()
end; end;
]; ];
(* this doesn't actually test the redirects, unfortunately *) (* this doesn't actually test the redirects, unfortunately *)