Source file docker.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
open Dockerfile

type ctx = {
  user : Spec.user;
}

let default_ctx = {
  user = Spec.root;
}

(* Note: could do with some escaping here, but the rules are not clear. *)
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 "@[<h>%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 { src; dst; exclude = _ } ->
    if ctx.user = Spec.root then copy ~src ~dst () :: acc, ctx
    else (
      let { Spec.uid; gid } = ctx.user in
      let chown = Printf.sprintf "%d:%d" uid gid in
      copy ~chown ~src ~dst () :: acc, ctx
    )
  | `User ({ uid; gid } as u) -> user "%d:%d" uid gid :: acc, { user = u }
  | `Env b -> env [b] :: acc, ctx

let dockerfile_of_spec ~buildkit { Spec.from; ops } =
  let ops', _ctx = List.fold_left (of_op ~buildkit) ([], default_ctx) ops in
  Dockerfile.from from @@@ List.rev ops'