···
{ store; tool_dir = tools }
+
let exec (config : config) ~stdout fs proc
+
((H.Store ((module S), _) : entry H.t), (ctx : ctx)) (entry : entry) =
+
let build, env, (uid, gid) =
+
match entry.pre.build with
+
| Store.Build.Image img ->
+
let build, env, user = Store.fetch ctx.store img in
+
(build, env, Option.value ~default:(0, 0) user)
+
| Store.Build.Build cid -> (cid, entry.pre.env, entry.pre.user)
+
let command = entry.pre.args in
+
{ entry with pre = { entry.pre with build = Build build } }
+
(* Store things under History.pre, this makes it possible to rediscover
+
the hash for something purely from the arguments needed to execute something
+
rather than needing, for example, the time it took to execute! *)
+
let new_cid = Store.cid (Repr.to_string History.pre_t hash_entry.pre) in
+
if entry.pre.mode = R then (Store.Run.with_build ctx.store build fn, [])
+
else Store.Run.with_clone ctx.store ~src:build new_cid fn
+
with_rootfs @@ function
+
(* Copy the stdout log to stdout *)
+
Eio.Path.(with_open_in (fs / (path :> string) / "log")) @@ fun ic ->
+
Eio.Flow.copy ic stdout
+
let c = Eio.Path.(load (fs / (path :> string) / "hash")) in
+
let rootfs = Filename.concat rootfs "rootfs" in
+
|> Void.rootfs ~mode:entry.pre.mode rootfs
+
|> Void.cwd entry.pre.cwd
+
(* TODO: Support UIDs |> Void.uid 1000 *)
+
String.concat " " command ^ " && env > /tmp/shelter-env";
+
`Void (Void.spawn ~sw void |> Void.exit_status)
+
let tool_mount : Runc.Json_config.mount =
+
dst = "/shelter-tools";
+
String.concat " " command ^ " && env > /tmp/shelter-env";
+
mounts = [ tool_mount ];
+
`Runc (Runc.spawn ~sw log env config rootfs)
+
Switch.run @@ fun sw ->
+
Eio.Path.open_out ~sw ~create:(`If_missing 0o644)
+
Eio.Path.(fs / rootfs / "log")
+
let res = spawn sw log in
+
let start = Mtime_clock.now () in
+
| `Runc r -> Eio.Process.await r
+
| `Void v -> Void.to_eio_status (Eio.Promise.await v)
+
let stop = Mtime_clock.now () in
+
let span = Mtime.span start stop in
+
let time = Mtime.Span.to_uint64_ns span in
+
(* Add command to history regardless of exit status *)
+
let _ : (unit, string) result =
+
LNoise.history_add (String.concat " " command)
+
if res = `Exited 0 then (
+
Eio.Path.(fs / rootfs / "rootfs" / "tmp" / "shelter-env")
+
Eio.Path.(load env_path)
+
|> String.split_on_char '\n'
+
|> List.filter (fun s -> not (String.equal "" s))
+
Eio.Path.unlink env_path;
+
match Astring.String.cut ~sep:"=" v with
+
| Some ("PWD", dir) -> Some dir
+
|> Option.value ~default:hash_entry.pre.cwd
+
if entry.pre.mode = RW then
+
pre = { hash_entry.pre with cwd; env; user = (uid, gid) };
+
post = { hash_entry.post with time };
+
else Error (Eio.Process.Child_error res)
let run (config : config) ~stdout fs clock proc
(((H.Store ((module S), store) : entry H.t) as s), (ctx : ctx)) = function
···
post = { diff = []; time = 0L };
+
let entry = { entry with pre = { entry.pre with args = command } } in
+
let new_entry, diff = exec config ~stdout fs proc (s, ctx) entry in
+
S.Hash.unsafe_of_raw_string c |> S.Commit.of_hash (S.repo store)
+
Fmt.epr "Resetting to existing entry failed...\n%!";
| Ok (`Entry (entry, path)) ->
let entry = { entry with post = { entry.post with diff } } in