Initial commit
This commit is contained in:
commit
0a92bdae65
9 changed files with 163 additions and 0 deletions
11
bin/builder_web_app.ml
Normal file
11
bin/builder_web_app.ml
Normal file
|
@ -0,0 +1,11 @@
|
|||
open Opium
|
||||
|
||||
let t = { Builder_web.Model.dir = Fpath.v "sample" }
|
||||
|
||||
let app =
|
||||
App.empty
|
||||
|> App.cmd_name "Builder Web"
|
||||
|> Builder_web.add_routes t
|
||||
|
||||
let () =
|
||||
App.run_command app
|
4
bin/dune
Normal file
4
bin/dune
Normal file
|
@ -0,0 +1,4 @@
|
|||
(executable
|
||||
(public_name builder_web)
|
||||
(name builder_web_app)
|
||||
(libraries builder_web))
|
23
builder-web.opam
Normal file
23
builder-web.opam
Normal file
|
@ -0,0 +1,23 @@
|
|||
opam-version: "2.0"
|
||||
maintainer: "Reynir Björnsson <reynir@reynir.dk>"
|
||||
authors: ["Reynir Björnsson <reynir@reynir.dk>"]
|
||||
homepage: "https://github.com/roburio/builder-web"
|
||||
dev-repo: "git+https://github.com/roburio/builder-web.git"
|
||||
bug-reports: "https://github.com/roburio/builder-web/issues"
|
||||
license: "ISC"
|
||||
|
||||
build: [
|
||||
["dune" "subst"] {pinned}
|
||||
["dune" "build" "-p" name "-j" jobs]
|
||||
]
|
||||
|
||||
depends: [
|
||||
"builder"
|
||||
"opium"
|
||||
]
|
||||
|
||||
synopsis: "Web interface for builder"
|
||||
|
||||
pin-depends: [
|
||||
["builder.dev" "git+https://git.data.coop/reynir/builder.git"]
|
||||
]
|
2
dune-project
Normal file
2
dune-project
Normal file
|
@ -0,0 +1,2 @@
|
|||
(lang dune 2.7)
|
||||
(name builder-web)
|
25
lib/builder_web.ml
Normal file
25
lib/builder_web.ml
Normal file
|
@ -0,0 +1,25 @@
|
|||
let src = Logs.Src.create "builder-web" ~doc:"Builder_web"
|
||||
module Log = (val Logs.src_log src : Logs.LOG)
|
||||
|
||||
open Opium
|
||||
|
||||
module Model = Model
|
||||
|
||||
let routes (t : Model.t) =
|
||||
let builder _req =
|
||||
match Model.jobs t with
|
||||
| Error (`Msg e) ->
|
||||
Log.warn (fun f -> f "Error getting jobs: %s" e);
|
||||
Response.of_plain_text ~status:`Internal_server_error
|
||||
"Error getting jobs" |> Lwt.return
|
||||
| Ok jobs ->
|
||||
Views.builder jobs |> Response.of_html |> Lwt.return
|
||||
in
|
||||
|
||||
[ App.get "/" builder ]
|
||||
|
||||
let add_routes t (app : App.t) =
|
||||
List.fold_right
|
||||
(fun route app -> route app)
|
||||
(routes t)
|
||||
app
|
3
lib/dune
Normal file
3
lib/dune
Normal file
|
@ -0,0 +1,3 @@
|
|||
(library
|
||||
(name builder_web)
|
||||
(libraries builder opium bos rresult))
|
51
lib/model.ml
Normal file
51
lib/model.ml
Normal file
|
@ -0,0 +1,51 @@
|
|||
let src = Logs.Src.create "builder-web.model" ~doc:"Builder_web model"
|
||||
module Log = (val Logs.src_log src : Logs.LOG)
|
||||
|
||||
open Rresult.R.Infix
|
||||
|
||||
type t = {
|
||||
dir : Fpath.t;
|
||||
}
|
||||
|
||||
type job_run = Fpath.t
|
||||
|
||||
type job = {
|
||||
name : Fpath.t;
|
||||
runs : job_run list;
|
||||
}
|
||||
|
||||
let job_name { name; _ } = Fpath.to_string name
|
||||
(* TODO: ensure invariant: jobs are always valid UUIDs *)
|
||||
let job_run_uuid f = Option.get (Uuidm.of_string (Fpath.to_string f))
|
||||
|
||||
type job_run_info = {
|
||||
job_info : Builder.job;
|
||||
uuid : Uuidm.t;
|
||||
out : (int * string) list;
|
||||
start : Ptime.t;
|
||||
finish : Ptime.t;
|
||||
result : Builder.execution_result;
|
||||
data : (Fpath.t * string) list
|
||||
}
|
||||
|
||||
let read_full t job run =
|
||||
let f = Fpath.(t.dir // job.name // run / "full") in
|
||||
Bos.OS.File.read f >>= fun s ->
|
||||
Builder.Asn.exec_of_cs (Cstruct.of_string s)
|
||||
>>| fun (job_info, uuid, out, start, finish, result, data) ->
|
||||
{ job_info; uuid; out; start; finish; result; data }
|
||||
|
||||
let job_runs t job =
|
||||
Bos.OS.Dir.contents ~rel:true Fpath.(t.dir // job) >>= fun job_runs ->
|
||||
Ok { name = job; runs = job_runs }
|
||||
|
||||
let jobs t =
|
||||
Bos.OS.Dir.contents ~rel:true t.dir >>|
|
||||
List.filter (fun f -> not (Fpath.equal (Fpath.v "state") f)) >>|
|
||||
List.filter_map (fun job ->
|
||||
match job_runs t job with
|
||||
| Ok job -> Some job
|
||||
| Error (`Msg e) ->
|
||||
Log.warn (fun f -> f "Error reading job run dir %a: %s" Fpath.pp
|
||||
Fpath.(t.dir // job) e);
|
||||
None)
|
24
lib/model.mli
Normal file
24
lib/model.mli
Normal file
|
@ -0,0 +1,24 @@
|
|||
type t = {
|
||||
dir : Fpath.t
|
||||
}
|
||||
|
||||
type job
|
||||
|
||||
type job_run
|
||||
|
||||
type job_run_info = {
|
||||
job_info : Builder.job;
|
||||
uuid : Uuidm.t;
|
||||
out : (int * string) list;
|
||||
start : Ptime.t;
|
||||
finish : Ptime.t;
|
||||
result : Builder.execution_result;
|
||||
data : (Fpath.t * string) list
|
||||
}
|
||||
|
||||
val job_name : job -> string
|
||||
val job_run_uuid : job_run -> Uuidm.t
|
||||
|
||||
val read_full : t -> job -> job_run -> (job_run_info, [> `Msg of string ]) result
|
||||
|
||||
val jobs : t -> (job list, [> `Msg of string ]) result
|
20
lib/views.ml
Normal file
20
lib/views.ml
Normal file
|
@ -0,0 +1,20 @@
|
|||
open Tyxml.Html
|
||||
|
||||
let layout ~title:title_ body_ =
|
||||
html
|
||||
(head (title (txt title_))
|
||||
[])
|
||||
(body body_)
|
||||
|
||||
let builder jobs =
|
||||
layout ~title:"Builder Web"
|
||||
[ h1 [txt "Builder web"];
|
||||
p [
|
||||
txt "We have currently ";
|
||||
txt (string_of_int (List.length jobs));
|
||||
txt " jobs.";
|
||||
];
|
||||
ul (List.map (fun job ->
|
||||
li [txt (Model.job_name job)])
|
||||
jobs);
|
||||
]
|
Loading…
Reference in a new issue