Source file template.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
let parse s =
  let lexbuf = Template_lexer.Encoding.from_string s in
  let lexer = Sedlexing.with_tokenizer Template_lexer.token lexbuf in
  let parser = MenhirLib.Convert.Simplified.traditional2revised Template_parser.template in
  parser lexer

let parse_file file =
  In_channel.with_open_bin (Path.project file) @@ fun ic ->
  let s = In_channel.input_all ic in
  parse s

let substitute_iden node =
  match node with
  | Template_ast.Text _ -> Lwt.return node
  | Template_ast.Iden name ->
    let secret_name = Storage.Secret_name.inject name in
    (try%lwt
       let%lwt plaintext = Storage.Secrets.decrypt_exn ~silence_stderr:true secret_name in
       let secret = Secret.Validation.parse_exn plaintext in
       Lwt.return @@ Template_ast.Text secret.text
     with
    | Failure s -> failwith ("unable to decrypt secret: " ^ s)
    | exn ->
      let%lwt () = Lwt_io.eprintlf "E: could not decrypt secret %s" (Storage.Secret_name.project secret_name) in
      Lwt.reraise exn)

let build_text_from_ast ast =
  List.map
    (fun node ->
      match node with
      | Template_ast.Text s -> s
      | Template_ast.Iden secret_name -> Devkit.Exn.fail "found unsubstituted secret %s" secret_name)
    ast
  |> String.concat ""

let substitute ~template ?(file_out = None) () =
  let%lwt substituted_ast = Lwt_list.map_p substitute_iden template in
  let contents = build_text_from_ast substituted_ast in
  match file_out with
  | None ->
    print_string contents;
    Lwt.return_unit
  | Some target_file ->
    Devkit.Files.save_as (Path.project target_file) ~mode:0o600 (fun oc -> Out_channel.output_string oc contents);
    Lwt.return_unit

let substitute_file ~template_file ~target_file =
  let template = parse_file template_file in
  substitute ~template ~file_out:target_file ()