diff --git a/dune-project b/dune-project index cbf2f44e..3340ac75 100644 --- a/dune-project +++ b/dune-project @@ -1,19 +1,16 @@ (lang dune 2.7) - (name obuilder) (formatting disabled) - (generate_opam_files true) - (source (github ocurrent/obuilder)) (authors "talex5@gmail.com") (maintainers "talex5@gmail.com") (documentation "https://ocurrent.github.io/obuilder/") - (package (name obuilder) (synopsis "Run build scripts for CI") - (description "OBuilder takes a build script (similar to a Dockerfile) and performs the steps in it in a sandboxed environment.") + (description + "OBuilder takes a build script (similar to a Dockerfile) and performs the steps in it in a sandboxed environment.") (depends lwt astring @@ -29,18 +26,16 @@ sqlite3 obuilder-spec (ocaml (>= 4.10.0)) - (alcotest-lwt :with-test) -)) - + (alcotest-lwt :with-test))) (package (name obuilder-spec) (synopsis "Build specification format") - (description "A library for constructing, reading and writing OBuilder build specification files.") + (description + "A library for constructing, reading and writing OBuilder build specification files.") (depends + (fmt (>= 0.8.9)) sexplib astring ppx_deriving ppx_sexp_conv - (dockerfile (>= 6.6.0)) - (ocaml (>= 4.10.0)) -)) + (ocaml (>= 4.10.0)))) diff --git a/lib_spec/docker.ml b/lib_spec/docker.ml index acf14ac7..2122c2b2 100644 --- a/lib_spec/docker.ml +++ b/lib_spec/docker.ml @@ -1,5 +1,3 @@ -open Dockerfile - type ctx = { user : Spec.user; } @@ -12,49 +10,65 @@ let default_ctx = { let pp_pair f (k, v) = Fmt.pf f "%s=%s" k v -let wrap x = - x - |> String.split_on_char '\n' - |> List.map String.trim - |> String.concat " \\\n " - -let of_op ~buildkit (acc, ctx) : Spec.op -> Dockerfile.t list * ctx = function - | `Comment x -> comment "%s" x :: acc, ctx - | `Workdir x -> workdir "%s" x :: acc, ctx - | `Shell xs -> shell xs :: acc, ctx - | `Run { cache = (_ :: _) as cache; shell; network = _ } when buildkit -> - let mounts = - cache |> List.map (fun { Cache.id; target; buildkit_options } -> - let buildkit_options = - ("--mount=type", "cache") :: - ("id", id) :: - ("target", target) :: - ("uid", string_of_int ctx.user.uid) :: - buildkit_options - in - Fmt.strf "@[%a@]" Fmt.(list ~sep:(unit ",") pp_pair) buildkit_options - ) - in - run "%s %s" (String.concat " " mounts) (wrap shell) :: acc, ctx - | `Run { cache = _; network = _; shell } -> run "%s" (wrap shell) :: acc, ctx - | `Copy { from; src; dst; exclude = _ } -> - let from = match from with - | `Build name -> Some name - | `Context -> None - in - if ctx.user = Spec.root then copy ?from ~src ~dst () :: acc, ctx +let pp_wrap = + Fmt.using (String.split_on_char '\n') + Fmt.(list ~sep:(unit " \\@\n ") (using String.trim string)) + +let pp_cache ~ctx f { Cache.id; target; buildkit_options } = + let buildkit_options = + ("--mount=type", "cache") :: + ("id", id) :: + ("target", target) :: + ("uid", string_of_int ctx.user.uid) :: + buildkit_options + in + Fmt.pf f "%a" Fmt.(list ~sep:(unit ",") pp_pair) buildkit_options + +let pp_run ~ctx f { Spec.cache; shell; network = _ } = + Fmt.pf f "RUN %a%a" Fmt.(list (pp_cache ~ctx ++ const string " ")) cache pp_wrap shell + +let pp_copy ~ctx f { Spec.from; src; dst; exclude = _ } = + let from = match from with + | `Build name -> Some name + | `Context -> None + in + let chown = + if ctx.user = Spec.root then None else ( let { Spec.uid; gid } = ctx.user in - let chown = Printf.sprintf "%d:%d" uid gid in - copy ?from ~chown ~src ~dst () :: acc, ctx + Some (Printf.sprintf "%d:%d" uid gid) ) - | `User ({ uid; gid } as u) -> user "%d:%d" uid gid :: acc, { user = u } - | `Env b -> env [b] :: acc, ctx + in + Fmt.pf f "COPY %a%a%a %s" + Fmt.(option (fmt "--chown=%s ")) chown + Fmt.(option (fmt "--from=%s ")) from + Fmt.(list ~sep:sp string) src + dst + +let pp_op ~buildkit ctx f : Spec.op -> ctx = function + | `Comment x -> Fmt.pf f "# %s" x; ctx + | `Workdir x -> Fmt.pf f "WORKDIR %s" x; ctx + | `Shell xs -> Fmt.pf f "SHELL [ %a ]" Fmt.(list ~sep:comma (quote string)) xs; ctx + | `Run x when buildkit -> pp_run ~ctx f x; ctx + | `Run x -> pp_run ~ctx f { x with cache = [] }; ctx + | `Copy x -> pp_copy ~ctx f x; ctx + | `User ({ uid; gid } as u) -> Fmt.pf f "USER %d:%d" uid gid; { user = u } + | `Env (k, v) -> Fmt.pf f "ENV %s %s" k v; ctx -let rec convert ?name ~buildkit { Spec.child_builds; from; ops } = - let stages = child_builds |> List.map (fun (name, spec) -> convert ~name ~buildkit spec) |> List.flatten in - let ops', _ctx = List.fold_left (of_op ~buildkit) ([], default_ctx) ops in - stages @ [Dockerfile.from ?alias:name from @@@ List.rev ops'] +let rec convert ~buildkit f (name, { Spec.child_builds; from; ops }) = + child_builds |> List.iter (fun (name, spec) -> + convert ~buildkit f (Some name, spec); + Format.pp_print_newline f (); + ); + Fmt.pf f "@[FROM %s%a@]@." from Fmt.(option (const string " as " ++ string)) name; + let (_ : ctx) = List.fold_left (fun ctx op -> + Format.pp_open_hbox f (); + let ctx = pp_op ~buildkit ctx f op in + Format.pp_close_box f (); + Format.pp_print_newline f (); + ctx + ) default_ctx ops + in () let dockerfile_of_spec ~buildkit t = - Dockerfile.empty @@@ convert ~buildkit t + Fmt.strf "%a" (convert ~buildkit) (None, t) diff --git a/lib_spec/docker.mli b/lib_spec/docker.mli index 572e8468..83fe91e4 100644 --- a/lib_spec/docker.mli +++ b/lib_spec/docker.mli @@ -1,4 +1,4 @@ -val dockerfile_of_spec : buildkit:bool -> Spec.t -> Dockerfile.t +val dockerfile_of_spec : buildkit:bool -> Spec.t -> string (** [dockerfile_of_spec x] produces a Dockerfile that aims to be equivalent to [x]. However, note that: diff --git a/lib_spec/dune b/lib_spec/dune index 3b746f43..d7ecfb47 100644 --- a/lib_spec/dune +++ b/lib_spec/dune @@ -2,4 +2,4 @@ (name obuilder_spec) (public_name obuilder-spec) (preprocess (pps ppx_sexp_conv)) - (libraries astring sexplib dockerfile)) + (libraries astring sexplib fmt)) diff --git a/main.ml b/main.ml index 60fb5b96..1d6b056b 100644 --- a/main.ml +++ b/main.ml @@ -59,7 +59,6 @@ let dockerfile buildkit spec = Sexplib.Sexp.load_sexp spec |> Obuilder_spec.t_of_sexp |> Obuilder_spec.Docker.dockerfile_of_spec ~buildkit - |> Dockerfile.string_of_t |> print_endline open Cmdliner diff --git a/obuilder-spec.opam b/obuilder-spec.opam index 6a966a51..aac0d75b 100644 --- a/obuilder-spec.opam +++ b/obuilder-spec.opam @@ -10,11 +10,11 @@ doc: "https://ocurrent.github.io/obuilder/" bug-reports: "https://github.com/ocurrent/obuilder/issues" depends: [ "dune" {>= "2.7"} + "fmt" {>= "0.8.9"} "sexplib" "astring" "ppx_deriving" "ppx_sexp_conv" - "dockerfile" {>= "6.6.0"} "ocaml" {>= "4.10.0"} "odoc" {with-doc} ] diff --git a/test/test.ml b/test/test.ml index 749a9e6e..0558377d 100644 --- a/test/test.ml +++ b/test/test.ml @@ -429,14 +429,13 @@ let remove_line_indents = function let remove_indent s = String.split_on_char '\n' s |> remove_line_indents - |> List.filter ((<>) "") |> String.concat "\n" (* Check that parsing an S-expression and then serialising it again gets the same result. *) let test_sexp () = let test name s = - let s = remove_indent s in + let s = String.trim (remove_indent s) in let s1 = Sexplib.Sexp.of_string s in let spec = Spec.t_of_sexp s1 in let s2 = Spec.sexp_of_t spec in @@ -463,7 +462,7 @@ let test_sexp () = let test_docker () = let test ~buildkit name expect sexp = let spec = Spec.t_of_sexp (Sexplib.Sexp.of_string sexp) in - let got = Obuilder_spec.Docker.dockerfile_of_spec ~buildkit spec |> Dockerfile.string_of_t in + let got = Obuilder_spec.Docker.dockerfile_of_spec ~buildkit spec in let expect = remove_indent expect in Alcotest.(check string) name expect got in @@ -528,6 +527,7 @@ let test_docker () = test ~buildkit:false "Multi-stage" {| FROM base as tools RUN make tools + FROM base COPY --from=tools binary /usr/local/bin/ |} {|