1 import Base 2 import ./type.bend as Sv 3 4 def container(s: Sv.Service) -> String: 5 match s: 6 case Sv.Service{c, _, _, _, _, _, _, _, _}: 7 c 8 9 def label(s: Sv.Service) -> String: 10 match s: 11 case Sv.Service{_, l, _, _, _, _, _, _, _}: 12 l 13 14 def port(s: Sv.Service) -> U32: 15 match s: 16 case Sv.Service{_, _, _, _, _, _, _, _, p}: 17 p 18 19 def start(s: Sv.Service) -> Sv.Start: 20 match s: 21 case Sv.Service{_, _, _, _, _, _, _, st, _}: 22 st 23 24 def labels(xs: List<&2, Sv.Service>) -> List<&2, String>: 25 match xs: 26 case []: 27 [] 28 case s <> rest: 29 label(s) <> labels(rest) 30 31 def at_login(s: Sv.Start) -> Bool: 32 match s: 33 case Sv.AtLogin{}: 34 True{} 35 case Sv.AtBoot{}: 36 False{} 37 38 # Whether any service needs a user session to start. 39 def needs_login(xs: List<&2, Sv.Service>) -> Bool: 40 match xs: 41 case []: 42 False{} 43 case s <> rest: 44 at_login(start(s)) || needs_login(rest) 45 46 # ---- a service as a nix-darwin launchd job ---- 47 # The program's path in the Nix store. 48 def program(p: Sv.Program) -> String: 49 match p: 50 case Sv.RunNode{}: 51 "\"${pkgs.nodejs_24}/bin/node\"" 52 case Sv.RunPython{}: 53 "\"${pkgs.python3.withPackages (ps: [ ps.watchdog ])}/bin/python3\"" 54 case Sv.RunBash{}: 55 "\"${pkgs.bash}/bin/bash\"" 56 57 def quoted(xs: List<&2, String>) -> String: 58 match xs: 59 case []: 60 "" 61 case x <> rest: 62 " \"" ++ x ++ "\"" ++ quoted(rest) 63 64 def env(e: Sv.Env) -> String: 65 match e: 66 case Sv.Plain{k, v}: 67 " " ++ k ++ " = \"" ++ v ++ "\";" 68 case Sv.Expr{k, x}: 69 " " ++ k ++ " = " ++ x ++ ";" 70 71 def envs(xs: List<&2, Sv.Env>) -> String: 72 match xs: 73 case []: 74 "" 75 case e <> rest: 76 env(e) ++ envs(rest) 77 78 # At login: a launch agent in the user's session. At boot: a launch daemon that runs as the user. 79 def job_kind(s: Sv.Start) -> String: 80 match s: 81 case Sv.AtLogin{}: 82 "launchd.user.agents" 83 case Sv.AtBoot{}: 84 "launchd.daemons" 85 86 def user_name(s: Sv.Start, +user: String) -> String: 87 match s: 88 case Sv.AtLogin{}: 89 "" 90 case Sv.AtBoot{}: 91 " UserName = \"" ++ user ++ "\";" 92 93 def job(s: Sv.Service, +user: String, +logs: String) -> String: 94 match s: 95 case Sv.Service{_, +l, p, args, dir, e, +log, +st, _}: 96 " " ++ job_kind(st) ++ ".\"" ++ l ++ "\".serviceConfig = {\n" ++ 97 " Label = \"" ++ l ++ "\";" ++ user_name(st, user) ++ "\n" ++ 98 " ProgramArguments = [ " ++ program(p) ++ quoted(args) ++ " ];\n" ++ 99 " WorkingDirectory = \"" ++ dir ++ "\";\n" ++ 100 " EnvironmentVariables = {" ++ envs(e) ++ " };\n" ++ 101 " RunAtLoad = true; KeepAlive = true; ThrottleInterval = 10;\n" ++ 102 " StandardOutPath = \"" ++ logs ++ "/" ++ log ++ ".log\";\n" ++ 103 " StandardErrorPath = \"" ++ logs ++ "/" ++ log ++ ".log\";\n" ++ 104 " };\n" 105 106 def jobs(xs: List<&2, Sv.Service>, +user: String, +logs: String) -> String: 107 match xs: 108 case []: 109 "" 110 case s <> rest: 111 job(s, user, logs) ++ jobs(rest, user, logs)