diff --git a/lib/views.ml b/lib/views.ml index 1e0f837..6a3d3f8 100644 --- a/lib/views.ml +++ b/lib/views.ml @@ -1,26 +1,26 @@ -open Tyxml.Html +module H = Tyxml.Html let pp_ptime ppf ptime = let (y, m, d), ((hh, mm, ss), _) = Ptime.to_date_time ptime in Fmt.pf ppf "%04d-%02d-%02d %02d:%02d:%02dZ" y m d hh mm ss -let txtf fmt = Fmt.kstr txt fmt -let a_titlef fmt = Fmt.kstr a_title fmt +let txtf fmt = Fmt.kstr H.txt fmt +let a_titlef fmt = Fmt.kstr H.a_title fmt let check_icon result = match result with | Builder.Exited 0 -> - span ~a:[ + H.span ~a:H.[ a_style "color: green; cursor: pointer;"; a_titlef "%a" Builder.pp_execution_result result; ] - [txt "☑"] + [H.txt "☑"] | _ -> - span ~a:[ + H.span ~a:H.[ a_style "color: red; cursor: pointer;"; a_titlef "%a" Builder.pp_execution_result result; ] - [txt "☒"] + [H.txt "☒"] type nav = [ | `Default @@ -80,19 +80,22 @@ h1,h2,h3{line-height:1.2} } |} -let layout ?include_static_css ?nav:(nav_=`Default) ~title:title_ body_ = +let layout ?include_static_css ?(nav=`Default) ~title body = let breadcrumb = let to_nav kvs = - nav [ ul (List.map (fun (desc, href) -> - li [a ~a:[a_href href] [desc]]) - kvs) ] + H.nav [ + H.ul ( + List.map (fun (desc, href) -> + H.li [H.a ~a:H.[a_href href] [desc]] + ) kvs + )] in - match nav_ with + match nav with | `Default -> - to_nav [txt "Home", "/"] + to_nav [H.txt "Home", "/"] | `Job (job_name, platform) -> to_nav [ - txt "Home", "/"; + H.txt "Home", "/"; txtf "Job %s" job_name, Fmt.str "/job/%s/" job_name ; ( txtf "%a" pp_platform platform, @@ -101,7 +104,7 @@ let layout ?include_static_css ?nav:(nav_=`Default) ~title:title_ body_ = ] | `Build (job_name, build) -> to_nav [ - txt "Home", "/"; + H.txt "Home", "/"; txtf "Job %s" job_name, Fmt.str "/job/%s/" job_name; ( txtf "%a" pp_platform (Some build.Builder_db.Build.platform), @@ -116,7 +119,7 @@ let layout ?include_static_css ?nav:(nav_=`Default) ~title:title_ body_ = ] | `Comparison ((job_left, build_left), (job_right, build_right)) -> to_nav [ - txt "Home", "/"; + H.txt "Home", "/"; txtf "Comparison between %s@%a and %s@%a" job_left pp_ptime build_left.Builder_db.Build.start job_right pp_ptime build_right.Builder_db.Build.start, @@ -128,32 +131,32 @@ let layout ?include_static_css ?nav:(nav_=`Default) ~title:title_ body_ = (*> Note: Last declared CSS wins - so one can override here*) let static_css = static_css :: Option.to_list include_static_css in - html - (head (title (txt title_)) - [style ~a:[a_mime_type "text/css"] static_css]) + H.html + (H.head (H.title (H.txt title)) + [H.style ~a:H.[a_mime_type "text/css"] static_css]) - (body [ + (H.body [ breadcrumb; - main body_ + H.main body ]) let toggleable ?(hidden=true) id description content = - let checked = if hidden then [] else [a_checked ()] in - div [ - label - ~a:[ + let checked = if hidden then [] else H.[a_checked ()] in + H.div [ + H.label + ~a:H.[ a_label_for id; a_class ["toggleable-descr"]; ] - [txt description]; - input - ~a:(checked @ [ + [H.txt description]; + H.input + ~a:(checked @ H.[ a_input_type `Checkbox; a_id id; a_style "display: none;"; ]) (); - div - ~a:[ + H.div + ~a:H.[ a_class ["toggleable"] ] content; @@ -172,13 +175,13 @@ let artifact Fpath.pp filepath in [ - a ~a:[a_href artifact_link] + H.a ~a:H.[a_href artifact_link] [ - if basename then txt (Fpath.basename filepath) + if basename then H.txt (Fpath.basename filepath) else txtf "%a" Fpath.pp filepath ]; - txt " "; - code [txtf "SHA256:%a" Hex.pp (Hex.of_cstruct sha256)]; + H.txt " "; + H.code [txtf "SHA256:%a" Hex.pp (Hex.of_cstruct sha256)]; txtf " (%a)" Fmt.byte_size size; ] @@ -187,34 +190,37 @@ module Builds = struct let make section_job_map = layout ~title:"Reproducible OPAM builds" ([ - h1 [txt "Reproducible OPAM builds"]; - p [ txt "This website offers binary MirageOS unikernels and \ + H.h1 [ H.txt "Reproducible OPAM builds" ]; + H.p [ H.txt "This website offers binary MirageOS unikernels and \ supplementary OS packages." ]; - p [ txt "Following is a list of jobs that are built daily. A \ + H.p [ + H.txt "Following is a list of jobs that are built daily. A \ persistent link to the latest successful build is available \ as /job/*jobname*/build/latest/. All builds can be \ reproduced with "; - a ~a:[a_href "https://github.com/roburio/orb/"] [txt "orb"]; - txt ". The builds are scheduled and executed by "; - a ~a:[a_href "https://github.com/roburio/builder/"] [txt "builder"]; - txt ". The web interface is "; - a ~a:[a_href "https://git.robur.io/robur/builder-web/"] - [txt "builder-web"]; - txt ". Contact team@robur.coop if you have any questions or \ + H.a ~a:H.[a_href "https://github.com/roburio/orb/"] + [H.txt "orb"]; + H.txt ". The builds are scheduled and executed by "; + H.a ~a:H.[a_href "https://github.com/roburio/builder/"] + [H.txt "builder"]; + H.txt ". The web interface is "; + H.a ~a:H.[a_href "https://git.robur.io/robur/builder-web/"] + [H.txt "builder-web"]; + H.txt ". Contact team@robur.coop if you have any questions or \ suggestions."; ]; - form ~a:[a_action "/hash"; a_method `Get] + H.form ~a:H.[a_action "/hash"; a_method `Get] [ - label [ - txt "Search artifact by SHA256"; - br (); - input ~a:[ + H.label [ + H.txt "Search artifact by SHA256"; + H.br (); + H.input ~a:H.[ a_input_type `Search; a_id "sha256"; a_name "sha256"; ] (); ]; - input ~a:[ + H.input ~a:H.[ a_input_type `Submit; a_value "Search"; ] (); @@ -222,36 +228,37 @@ module Builds = struct ] @ Utils.String_map.fold (fun section jobs acc -> acc @ [ - h2 [ txt section ]; - ul (List.map (fun (job_name, synopsis, platform_builds) -> - li ([ - a ~a:[a_href ("job/" ^ job_name ^ "/")] [txt job_name]; - br (); - txt (Option.value ~default:"" synopsis); - br () + H.h2 [ H.txt section ]; + H.ul (List.map (fun (job_name, synopsis, platform_builds) -> + H.li ([ + H.a ~a:H.[a_href ("job/" ^ job_name ^ "/")] + [H.txt job_name]; + H.br (); + H.txt (Option.value ~default:"" synopsis); + H.br () ] @ List.concat_map (fun (platform, latest_build, latest_artifact) -> [ check_icon latest_build.Builder_db.Build.result; - txt " "; - a ~a:[ - Fmt.kstr a_href "job/%s/%a" + H.txt " "; + H.a ~a:[ + Fmt.kstr H.a_href "job/%s/%a" job_name pp_platform_query (Some platform)] - [txt platform]; - txt " "; - a ~a:[ - Fmt.kstr a_href "job/%s/build/%a/" + [H.txt platform]; + H.txt " "; + H.a ~a:[ + Fmt.kstr H.a_href "job/%s/build/%a/" job_name Uuidm.pp latest_build.Builder_db.Build.uuid] [txtf "%a" pp_ptime latest_build.Builder_db.Build.start]; - txt " "; + H.txt " "; ] @ (match latest_artifact with | Some main_binary -> artifact ~basename:true job_name latest_build main_binary | None -> [ txtf "Build failure: %a" Builder.pp_execution_result latest_build.Builder_db.Build.result ] - ) @ [ br () ] + ) @ [ H.br () ] ) platform_builds) ) @@ -259,11 +266,11 @@ module Builds = struct ]) section_job_map [] @ - [ p [ - txt "View the latest failed builds "; - a ~a:[a_href "/failed-builds/"] - [txt "here"]; - txt "." + [ H.p [ + H.txt "View the latest failed builds "; + H.a ~a:H.[a_href "/failed-builds/"] + [H.txt "here"]; + H.txt "." ]]) end @@ -274,30 +281,30 @@ module Job = struct layout ~nav:(`Job (name, platform)) ~title:(Fmt.str "Job %s %a" name pp_platform platform) - ((h1 [txtf "Job %s %a" name pp_platform platform] :: + ((H.h1 [txtf "Job %s %a" name pp_platform platform] :: (match readme with | None -> [] | Some data -> [ - h2 ~a:[a_id "readme"] [txt "README"]; - a ~a:[a_href "#builds"] [txt "Skip to builds"]; - Unsafe.data (Utils.Omd.html_of_string data) + H.h2 ~a:H.[a_id "readme"] [H.txt "README"]; + H.a ~a:H.[a_href "#builds"] [H.txt "Skip to builds"]; + H.Unsafe.data (Utils.Omd.html_of_string data) ])) @ [ - h2 ~a:[a_id "builds"] [txt "Builds"]; - a ~a:[a_href "#readme"] [txt "Back to readme"]; - ul (List.map (fun (build, main_binary) -> - li ([ + H.h2 ~a:H.[a_id "builds"] [H.txt "Builds"]; + H.a ~a:H.[a_href "#readme"] [H.txt "Back to readme"]; + H.ul (List.map (fun (build, main_binary) -> + H.li ([ check_icon build.Builder_db.Build.result; txtf " %s " build.platform; - a ~a:[ + H.a ~a:H.[ Fmt.kstr a_href "/job/%s/build/%a/" name Uuidm.pp build.Builder_db.Build.uuid ] [ txtf "%a" pp_ptime build.Builder_db.Build.start; ]; - txt " "; + H.txt " "; ] @ match main_binary with | Some main_binary -> artifact ~basename:true name build main_binary @@ -306,15 +313,15 @@ module Job = struct build.Builder_db.Build.result ])) builds); if failed then - p [ - txt "Excluding failed builds " ; - a ~a:[a_href "../"] [txt "here"] ; - txt "." ] + H.p [ + H.txt "Excluding failed builds " ; + H.a ~a:H.[a_href "../"] [H.txt "here"] ; + H.txt "." ] else - p [ - txt "Including failed builds " ; - a ~a:[a_href "failed/"] [txt "here"] ; - txt "." ] + H.p [ + H.txt "Including failed builds " ; + H.a ~a:H.[a_href "failed/"] [H.txt "here"] ; + H.txt "." ] ]) end @@ -338,48 +345,48 @@ module Job_build = struct ~latest ~next ~previous = [ - h2 ~a:[a_id "build"] [txtf "Build %a" pp_ptime build.start]; - p [txtf "Built on platform %s" build.platform ]; - p [txtf "Build took %a." Ptime.Span.pp delta ]; - p [txtf "Execution result: %a." Builder.pp_execution_result build.result]; - h3 [txt "Build info"]; - ul [ - li [ a ~a:[Fmt.kstr a_href "/job/%s/build/%a/console" name Uuidm.pp build.uuid] - [txt "Console output"]; + H.h2 ~a:H.[a_id "build"] [txtf "Build %a" pp_ptime build.start]; + H.p [txtf "Built on platform %s" build.platform ]; + H.p [txtf "Build took %a." Ptime.Span.pp delta ]; + H.p [txtf "Execution result: %a." Builder.pp_execution_result build.result]; + H.h3 [H.txt "Build info"]; + H.ul [ + H.li [ H.a ~a:H.[Fmt.kstr a_href "/job/%s/build/%a/console" name Uuidm.pp build.uuid] + [H.txt "Console output"]; ]; - li [ a ~a:[Fmt.kstr a_href "/job/%s/build/%a/script" name Uuidm.pp build.uuid] - [txt "Build script"]; + H.li [ H.a ~a:H.[Fmt.kstr a_href "/job/%s/build/%a/script" name Uuidm.pp build.uuid] + [H.txt "Build script"]; ] ]; - h3 [txt "Build artifacts"]; - dl (List.concat_map + H.h3 [H.txt "Build artifacts"]; + H.dl (List.concat_map (fun { Builder_db.filepath; localpath=_; sha256; size } -> let (`Hex sha256_hex) = Hex.of_cstruct sha256 in [ - dt [a - ~a:[Fmt.kstr a_href "f/%a" Fpath.pp filepath] - [code [txtf "%a" Fpath.pp filepath]]]; - dd [ - code [txt "SHA256:"; txt sha256_hex]; + H.dt [H.a + ~a:H.[Fmt.kstr a_href "f/%a" Fpath.pp filepath] + [H.code [txtf "%a" Fpath.pp filepath]]]; + H.dd [ + H.code [H.txt "SHA256:"; H.txt sha256_hex]; txtf " (%a)" Fmt.byte_size size; ]; ]) artifacts); - h3 [ + H.h3 [ txtf "Reproduced by %d builds" (List.length (same_input_same_output @ different_input_same_output))] ; - ul + H.ul ((List.map (fun { Builder_db.Build.start ; uuid ; platform ; _ } -> - li [ + H.li [ txtf "on %s, same input, " platform; - a ~a:[Fmt.kstr a_href "/job/%s/build/%a/" name Uuidm.pp uuid] + H.a ~a:H.[Fmt.kstr a_href "/job/%s/build/%a/" name Uuidm.pp uuid] [txtf "%a" pp_ptime start] ]) same_input_same_output) @ List.map (fun { Builder_db.Build.start ; uuid = other_uuid ; platform ; _ } -> - li [ + H.li [ txtf "on %s, different input, " platform; - a ~a:[ + H.a ~a:H.[ Fmt.kstr a_href "/compare/%a/%a/" Uuidm.pp other_uuid Uuidm.pp build.uuid] @@ -390,12 +397,12 @@ module Job_build = struct @ (if same_input_different_output = [] then [] else - [ h3 [txt "Same input, different output (not reproducible!)"]; - ul ( + [ H.h3 [H.txt "Same input, different output (not reproducible!)"]; + H.ul ( List.map (fun { Builder_db.Build.start ; uuid = other_uuid ; platform ; _ } -> - li [ + H.li [ txtf "on %s, " platform ; - a ~a:[ + H.a ~a:H.[ Fmt.kstr a_href "/compare/%a/%a/" Uuidm.pp other_uuid Uuidm.pp build.uuid] @@ -405,18 +412,20 @@ module Job_build = struct ] ) @ [ - h3 [txt "Comparisons with other builds on the same platform"]; + H.h3 [H.txt "Comparisons with other builds on the same platform"]; let opt_build (ctx, build') = match build' with | Some b when not (Uuidm.equal build.uuid b.Builder_db.Build.uuid) -> - [ li [ txt ctx; - a ~a:[Fmt.kstr a_href "/compare/%a/%a/" - Uuidm.pp b.uuid Uuidm.pp build.uuid] + [ H.li [ H.txt ctx; + H.a ~a:[ + Fmt.kstr H.a_href "/compare/%a/%a/" + Uuidm.pp b.uuid + Uuidm.pp build.uuid ] [txtf "%a" pp_ptime b.start]] ] | _ -> [] in - ul + H.ul (List.concat_map opt_build [ ("Latest build ", latest) ; ("Later build with different output ", next) ; @@ -443,19 +452,19 @@ module Job_build = struct let make_viz_section ~name ~artifacts ~uuid = [ - (* [ h3 [txt "Analysis"] ]; *) - [ p [ + (* [ H.h3 [txt "Analysis"] ]; *) + [ H.p [ let src = Fmt.str "/job/%s/build/%a/vizdependencies" name Uuidm.pp uuid in - iframe ~a:[ + H.iframe ~a:H.[ a_src src; a_title "Opam dependencies"; a_style viz_style_deps ] [] ]]; if not @@ contains_debug_bin artifacts then [] else [ - p [ + H.p [ let src = Fmt.str "/job/%s/build/%a/viztreemap" name Uuidm.pp uuid in - iframe ~a:[ + H.iframe ~a:H.[ a_src src; a_title "Binary dissection"; a_style viz_style_treemap @@ -485,26 +494,26 @@ module Job_build = struct ~same_input_different_output ~latest ~next ~previous in - let style_grid = a_style "display: flex; " in - let style_grid_container = a_style "\ + let style_grid = H.a_style "display: flex; " in + let style_grid_container = H.a_style "\ display: flex; align-items: center; justify-content: center; min-width: 83em; " in - let style_col_container = a_style "" in + let style_col_container = H.a_style "" in let style_col_left = - a_style "width: 45em; min-width: 43em; padding-left: 2%" in - let style_col_right = a_style "width: 50%" in + H.a_style "width: 45em; min-width: 43em; padding-left: 2%" in + let style_col_right = H.a_style "width: 50%" in let body = [ - div ~a:[ style_grid_container ] [ - div ~a:[ style_col_container ] [ - h1 [txtf "Job %s" name]; - div ~a:[ style_grid ] [ - (* div ~a:[ style_col_padding ] []; *) - div ~a:[ style_col_left ] left_column; - div ~a:[ style_col_right ] right_column + H.div~a:[ style_grid_container ] [ + H.div~a:[ style_col_container ] [ + H.h1 [txtf "Job %s" name]; + H.div~a:[ style_grid ] [ + (* H.div~a:H.[ style_col_padding ] []; *) + H.div~a:[ style_col_left ] left_column; + H.div~a:[ style_col_right ] right_column ] ] ] @@ -518,53 +527,53 @@ module Job_build = struct end let key_values xs = - List.concat_map (fun (k, v) -> [ txtf "%s %s" k v ; br () ]) xs + List.concat_map (fun (k, v) -> [ txtf "%s %s" k v ; H.br () ]) xs let key_value_changes xs = - List.concat_map (fun (k, v, v') -> [ txtf "%s %s->%s" k v v' ; br () ]) xs + List.concat_map (fun (k, v, v') -> [ txtf "%s %s->%s" k v v' ; H.br () ]) xs let packages packages = OpamPackage.Set.elements packages |> List.concat_map (fun p -> [ txtf "%a" Opamdiff.pp_opampackage p; - br (); + H.br (); ]) let package_diffs diffs = List.concat_map (fun pd -> [ txtf "%a" Opamdiff.pp_version_diff pd; - br (); + H.br (); ]) diffs let opam_diffs diffs = List.concat_map (fun pd -> - h4 [ txtf "%a" Opamdiff.pp_opam_diff pd ] :: + H.h4 [ txtf "%a" Opamdiff.pp_opam_diff pd ] :: (match pd.Opamdiff.build with None -> [] | Some a -> let l, r = Opamdiff.commands_to_strings a in [ - h5 [ txt "build instruction (without common prefix) modifications, old:" ] ; - code (List.concat_map (fun s -> [ txt s ; br () ]) l) ; - h5 [ txt "new" ] ; - code (List.concat_map (fun s -> [ txt s ; br () ]) r) + H.h5 [ H.txt "build instruction (without common prefix) modifications, old:" ] ; + H.code (List.concat_map (fun s -> [ H.txt s ; H.br () ]) l) ; + H.h5 [ H.txt "new" ] ; + H.code (List.concat_map (fun s -> [ H.txt s ; H.br () ]) r) ]) @ (match pd.Opamdiff.install with None -> [] | Some a -> let l, r = Opamdiff.commands_to_strings a in [ - h5 [ txt "install instruction (without common prefix) modifications, old:" ] ; - code (List.concat_map (fun s -> [ txt s ; br () ]) l) ; - h5 [ txt "new" ] ; - code (List.concat_map (fun s -> [ txt s ; br () ]) r) + H.h5 [ H.txt "install instruction (without common prefix) modifications, old:" ] ; + H.code (List.concat_map (fun s -> [ H.txt s ; H.br () ]) l) ; + H.h5 [ H.txt "new" ] ; + H.code (List.concat_map (fun s -> [ H.txt s ; H.br () ]) r) ]) @ (match pd.Opamdiff.url with None -> [] | Some a -> let l, r = Opamdiff.opt_url_to_string a in [ - h5 [ txt "URL" ] ; + H.h5 [ H.txt "URL" ] ; txtf "old: %s" l; - br (); + H.br (); txtf "new: %s" r ]) @ - [ br () ]) + [ H.br () ]) diffs let compare_builds job_left job_right @@ -577,10 +586,10 @@ let compare_builds job_left job_right ~title:(Fmt.str "Comparing builds %a and %a" Uuidm.pp build_left.uuid Uuidm.pp build_right.uuid) ([ - h1 [txt "Comparing builds"]; - h2 [ - txt "Builds "; - a ~a:[a_href + H.h1 [H.txt "Comparing builds"]; + H.h2 [ + H.txt "Builds "; + H.a ~a:H.[a_href (Fmt.str "/job/%s/build/%a/" job_left Uuidm.pp build_left.uuid)] @@ -588,8 +597,8 @@ let compare_builds job_left job_right job_left pp_ptime build_left.start pp_platform (Some build_left.platform)]; - txt " and "; - a ~a:[a_href + H.txt " and "; + H.a ~a:H.[a_href (Fmt.str "/job/%s/build/%a/" job_right Uuidm.pp build_right.uuid)] @@ -598,110 +607,110 @@ let compare_builds job_left job_right pp_ptime build_right.start pp_platform (Some build_right.platform)]; ]; - h3 [ a ~a:[ + H.h3 [ H.a ~a:H.[ Fmt.kstr a_href "/compare/%a/%a/" Uuidm.pp build_right.uuid Uuidm.pp build_left.uuid ] - [txt "Compare in reverse direction"]] ; - ul [ - li [ - a ~a:[a_href "#opam-packages-removed"] + [H.txt "Compare in reverse direction"]] ; + H.ul [ + H.li [ + H.a ~a:H.[a_href "#opam-packages-removed"] [txtf "%d opam packages removed" (OpamPackage.Set.cardinal left)] ]; - li [ - a ~a:[a_href "#opam-packages-installed"] + H.li [ + H.a ~a:H.[a_href "#opam-packages-installed"] [txtf "%d new opam packages installed" (OpamPackage.Set.cardinal right)] ]; - li [ - a ~a:[a_href "#opam-packages-version-diff"] + H.li [ + H.a ~a:H.[a_href "#opam-packages-version-diff"] [txtf "%d opam packages with version changes" (List.length version_diff)] ]; - li [ - a ~a:[a_href "#opam-packages-opam-diff"] + H.li [ + H.a ~a:H.[a_href "#opam-packages-opam-diff"] [txtf "%d opam packages with changes in their opam file" (List.length opam_diff)] ]; - li [ - a ~a:[a_href "#opam-packages-unchanged"] + H.li [ + H.a ~a:H.[a_href "#opam-packages-unchanged"] [txtf "%d opam packages unchanged" (OpamPackage.Set.cardinal same)] ]; - li [ - a ~a:[a_href "#env-added"] + H.li [ + H.a ~a:H.[a_href "#env-added"] [ txtf "%d environment variables added" (List.length added_env)] ]; - li [ - a ~a:[a_href "#env-removed"] + H.li [ + H.a ~a:H.[a_href "#env-removed"] [ txtf "%d environment variables removed" (List.length removed_env)] ]; - li [ - a ~a:[a_href "#env-changed"] + H.li [ + H.a ~a:H.[a_href "#env-changed"] [ txtf "%d environment variables changed" (List.length changed_env)] ]; - li [ - a ~a:[a_href "#pkgs-added"] + H.li [ + H.a ~a:H.[a_href "#pkgs-added"] [ txtf "%d system packages added" (List.length added_pkgs)] ]; - li [ - a ~a:[a_href "#pkgs-removed"] + H.li [ + H.a ~a:H.[a_href "#pkgs-removed"] [ txtf "%d system packages removed" (List.length removed_pkgs)] ]; - li [ - a ~a:[a_href "#pkgs-changed"] + H.li [ + H.a ~a:H.[a_href "#pkgs-changed"] [ txtf "%d system packages changed" (List.length changed_pkgs)] ]; ]; - h3 ~a:[a_id "opam-packages-removed"] - [txt "Opam packages removed"]; - code (packages left); - h3 ~a:[a_id "opam-packages-installed"] - [txt "New opam packages installed"]; - code (packages right); - h3 ~a:[a_id "opam-packages-version-diff"] - [txt "Opam packages with version changes"]; - code (package_diffs version_diff); - h3 ~a:[a_id "opam-packages-opam-diff"] - [txt "Opam packages with changes in their opam file"]] @ + H.h3 ~a:H.[a_id "opam-packages-removed"] + [H.txt "Opam packages removed"]; + H.code (packages left); + H.h3 ~a:H.[a_id "opam-packages-installed"] + [H.txt "New opam packages installed"]; + H.code (packages right); + H.h3 ~a:H.[a_id "opam-packages-version-diff"] + [H.txt "Opam packages with version changes"]; + H.code (package_diffs version_diff); + H.h3 ~a:H.[a_id "opam-packages-opam-diff"] + [H.txt "Opam packages with changes in their opam file"]] @ opam_diffs opam_diff @ [ - h3 ~a:[a_id "opam-packages-unchanged"] - [txt "Unchanged opam packages"]; - code (packages same); - h3 ~a:[a_id "env-added"] [txt "Environment variables added"]; - code (key_values added_env); - h3 ~a:[a_id "env-removed"] [txt "Environment variables removed"]; - code (key_values removed_env); - h3 ~a:[a_id "env-changed"] [txt "Environment variables changed"]; - code (key_value_changes changed_env); - h3 ~a:[a_id "pkgs-added"] [txt "System packages added"]; - code (key_values added_pkgs); - h3 ~a:[a_id "pkgs-removed"] [txt "System packages removed"]; - code (key_values removed_pkgs); - h3 ~a:[a_id "pkgs-changed"] [txt "System packages changed"]; - code (key_value_changes changed_pkgs); + H.h3 ~a:H.[a_id "opam-packages-unchanged"] + [H.txt "Unchanged opam packages"]; + H.code (packages same); + H.h3 ~a:H.[a_id "env-added"] [H.txt "Environment variables added"]; + H.code (key_values added_env); + H.h3 ~a:H.[a_id "env-removed"] [H.txt "Environment variables removed"]; + H.code (key_values removed_env); + H.h3 ~a:H.[a_id "env-changed"] [H.txt "Environment variables changed"]; + H.code (key_value_changes changed_env); + H.h3 ~a:H.[a_id "pkgs-added"] [H.txt "System packages added"]; + H.code (key_values added_pkgs); + H.h3 ~a:H.[a_id "pkgs-removed"] [H.txt "System packages removed"]; + H.code (key_values removed_pkgs); + H.h3 ~a:H.[a_id "pkgs-changed"] [H.txt "System packages changed"]; + H.code (key_value_changes changed_pkgs); ]) let failed_builds ~start ~count builds = let build (job_name, build) = - li [ + H.li [ check_icon build.Builder_db.Build.result; txtf " %s %a " job_name pp_platform (Some build.platform); - a ~a:[Fmt.kstr a_href "/job/%s/build/%a/" job_name Uuidm.pp build.uuid] + H.a ~a:H.[Fmt.kstr a_href "/job/%s/build/%a/" job_name Uuidm.pp build.uuid] [txtf "%a" pp_ptime build.start]; txtf " %a" Builder.pp_execution_result build.result; ] in layout ~title:"Failed builds" ([ - h1 [txt "Failed builds"]; - ul (List.map build builds); - p [ txtf "View the next %d failed builds " count; - a ~a:[ + H.h1 [H.txt "Failed builds"]; + H.ul (List.map build builds); + H.p [ txtf "View the next %d failed builds " count; + H.a ~a:H.[ Fmt.kstr a_href "/failed-builds/?count=%d&start=%d" count (start + count) ] - [ txt "here"]; - txt "."; + [ H.txt "here"]; + H.txt "."; ] ])