this repo has no description

Git-store and more

Changed files
+155 -52
src
+9 -3
src/bin/main.ml
···
Eio.Path.mkdirs ~exists_ok:true ~perm:0o755 path;
path
+
module Eventloop = struct
+
let run fn =
+
Eio_posix.run @@ fun env ->
+
Lwt_eio.with_event_loop ~clock:env#clock @@ fun _token -> fn env
+
end
+
(* Command Line *)
open Cmdliner
···
let main =
let run config cmd_file =
-
Eio_posix.run @@ fun env ->
+
Eventloop.run @@ fun env ->
let cmd_file = Option.map (Eio.Path.( / ) env#fs) cmd_file in
let dir = state_dir env#fs "shelter" in
let stdout = (env#stdout :> Eio.Flow.sink_ty Eio.Flow.sink) in
···
let passthrough =
let run config cmd_file =
-
Eio_posix.run @@ fun env ->
+
Eventloop.run @@ fun env ->
let cmd_file = Option.map (Eio.Path.( / ) env#fs) cmd_file in
let dir = state_dir env#fs "passthrough" in
let stdout = (env#stdout :> Eio.Flow.sink_ty Eio.Flow.sink) in
···
let extract_commands =
let run cmd_file =
-
Eio_posix.run @@ fun env ->
+
Eventloop.run @@ fun env ->
let cmd_file = Eio.Path.( / ) env#fs (Option.get cmd_file) in
Shelter.Script.to_commands cmd_file |> List.iter (Fmt.pr "%s\n")
in
+10 -1
src/lib/dune
···
(library
(name shelter)
(public_name shelter)
-
(libraries cmdliner irmin-fs.unix eio.unix eio linenoise void repr morbig))
+
(libraries
+
cmdliner
+
irmin-fs.unix
+
irmin-git.unix
+
eio.unix
+
eio
+
linenoise
+
void
+
repr
+
morbig))
+8 -1
src/lib/engine.ml
···
(** A context that is not persisted, but is passed through each loop of the
shell *)
+
type error
+
(** Shell specific errors *)
+
+
val pp_error : error Fmt.t
+
val init :
_ Eio.Path.t ->
Eio_unix.Process.mgr_ty Eio_unix.Process.mgr ->
···
Eio_unix.Process.mgr_ty Eio_unix.Process.mgr ->
entry History.t * ctx ->
action ->
-
(entry History.t * ctx, Eio.Process.error) result
+
( entry History.t * ctx,
+
[ `Process of Eio.Process.error | `Shell of error ] )
+
result
(** [run history action] runs the action in [history]. Return a new [history]
that can be persisted *)
+6 -2
src/lib/passthrough/shelter_passthrough.ml
···
open Eio
+
type error = string
+
+
let pp_error = Fmt.string
+
type config = unit
let config_term = Cmdliner.Term.const ()
···
S.set_exn ~info store (key ()) command;
let _ : (unit, string) result = LNoise.history_add command in
Ok (full_store, ()))
-
else Error (Eio.Process.Child_error res)
-
with Eio.Exn.Io (Eio.Process.E e, _) -> Error e
+
else Shelter.process_error (Eio.Process.Child_error res)
+
with Eio.Exn.Io (Eio.Process.E e, _) -> Shelter.process_error e
+16 -7
src/lib/shelter.ml
···
module Engine = Engine
module Script = Script
+
let process_error e = Error (`Process e)
+
let shell_error e = Error (`Shell e)
+
module Make (H : History.S) (Engine : Engine.S with type entry = H.t) = struct
-
module Store = Irmin_fs_unix.KV.Make (H)
+
module Store = Irmin_git_unix.FS.KV (H)
let run config ~stdout fs clock proc store =
let store = History.Store ((module Store), store) in
···
| Some input -> (
let action = Engine.action_of_command input in
match Engine.run config ~stdout fs clock proc (store, ctx) action with
-
| Error (Eio.Process.Child_error exit_code) ->
+
| Error (`Process (Eio.Process.Child_error exit_code)) ->
Fmt.epr "%a\n%!" Eio.Process.pp_status exit_code;
loop store ctx exit_code
-
| Error (Eio.Process.Executable_not_found m) ->
-
Fmt.epr "cshell: excutable not found %s\n%!" m;
+
| Error (`Process (Eio.Process.Executable_not_found m)) ->
+
Fmt.epr "shelter: excutable not found %s\n%!" m;
loop store ctx (`Exited 127)
+
| Error (`Shell e) ->
+
Fmt.epr "shelter: %a\n%!" Engine.pp_error e;
+
loop store ctx (`Exited 255)
| Ok (store, ctx) -> loop store ctx (`Exited 0))
in
loop store initial_ctx (`Exited 0)
···
match
Engine.run config ~stdout fs clock proc (store, ctx) action
with
-
| Error (Eio.Process.Child_error exit_code) ->
+
| Error (`Process (Eio.Process.Child_error exit_code)) ->
Fmt.epr "%a\n%!" Eio.Process.pp_status exit_code;
(store, ctx, exit_code)
-
| Error (Eio.Process.Executable_not_found m) ->
-
Fmt.epr "cshell: excutable not found %s\n%!" m;
+
| Error (`Process (Eio.Process.Executable_not_found m)) ->
+
Fmt.epr "shelter: excutable not found %s\n%!" m;
(store, ctx, `Exited 127)
+
| Error (`Shell e) ->
+
Fmt.epr "shelter: %a\n%!" Engine.pp_error e;
+
(store, ctx, `Exited 255)
| Ok (store, ctx) -> (store, ctx, `Exited 0)
in
let _store, _ctx, exit_code =
+106 -38
src/lib/shelter/shelter_main.ml
···
module Store = Store
module H = Shelter.History
+
type error = string
+
+
let pp_error = Fmt.string
+
module History = struct
type mode = Void.mode
···
in
{ store; tool_dir = tools }
-
(* Run a command *)
+
(* Run a command:
+
+
- TODO: pretty confusing that we `entry` to build from and also as the
+
thing we are building (e.g. the build field and the args field... *)
let exec (config : config) ~stdout fs proc
((H.Store ((module S), _) : entry H.t), (ctx : ctx)) (entry : entry) =
let build, env, (uid, gid) =
···
{ entry with pre = { entry.pre with build = Build build } }
in
(* 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
+
the hash for something purely from the arguments needed to execute something
+
rather than needing, for example, the time it took to execute!
+
+
Also, combine it with previous build step. *)
+
let new_cid =
+
Store.cid (Cid.to_string build ^ Repr.to_string History.pre_t hash_entry.pre)
+
in
let with_rootfs fn =
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
···
| `Build rootfs ->
let spawn sw log =
if config.no_runc then
+
(* Experiment Void Process *)
let rootfs = Filename.concat rootfs "rootfs" in
let void =
Void.empty
···
post = { hash_entry.post with time };
},
rootfs )))
-
else Error (Eio.Process.Child_error res)
+
else Shelter.process_error (Eio.Process.Child_error res)
+
+
let complete_exec ((H.Store ((module S), store) as s : entry H.t), ctx) clock fs
+
new_entry diff =
+
match new_entry with
+
| Error e -> Error e
+
| Ok (`Reset c) -> (
+
match
+
S.Hash.unsafe_of_raw_string c |> S.Commit.of_hash (S.repo store)
+
with
+
| None ->
+
Fmt.epr "Resetting to existing entry failed...\n%!";
+
Ok (s, ctx)
+
| Some c ->
+
S.Head.set store c;
+
Ok (s, ctx))
+
| Ok (`Entry (entry, path)) ->
+
(* Set diff *)
+
let entry = History.{ entry with post = { entry.post with diff } } in
+
(* Commit if RW *)
+
if entry.pre.mode = RW then (
+
commit
+
~message:("exec " ^ String.concat " " entry.pre.args)
+
clock s entry;
+
(* Save the commit hash for easy restoring later *)
+
let hash = S.Head.get store |> S.Commit.hash |> S.Hash.to_raw_string in
+
Eio.Path.save ~create:(`If_missing 0o644)
+
Eio.Path.(fs / path / "hash")
+
hash);
+
Ok (s, ctx)
+
+
let replay config (H.Store ((module S), s) as store : entry H.t) ctx fs clock
+
proc stdout existing_branch =
+
let seshes = sessions store in
+
if not (List.exists (String.equal existing_branch) seshes) then (
+
Fmt.epr "%s does not exist!" existing_branch;
+
Ok (store, ctx))
+
else
+
let repo = S.repo s in
+
let onto = S.of_branch repo existing_branch in
+
match S.lcas ~n:1 s onto with
+
| Error lcas_error ->
+
Fmt.epr "Replay LCAS: %a" (Repr.pp S.lca_error_t) lcas_error;
+
Ok (store, ctx)
+
| Ok [ lcas ] -> (
+
let all_commits = history store in
+
let lcas_hash = S.Commit.hash lcas |> S.Hash.to_raw_string in
+
let rec collect = function
+
| [] -> []
+
| (x, _) :: _ when String.equal lcas_hash x -> []
+
| v :: vs -> v :: collect vs
+
in
+
let commits_to_apply = collect all_commits in
+
match commits_to_apply with
+
| [] -> Shelter.shell_error ""
+
| (h, first) :: rest ->
+
let _, last_other =
+
history (H.Store ((module S), onto)) |> List.hd
+
in
+
let new_first =
+
{
+
first with
+
pre = { first.pre with build = last_other.pre.build };
+
}
+
in
+
let commits_to_apply = (h, new_first) :: rest in
+
(* Now we reset our head to point to the other store's head
+
and replay our commits onto it *)
+
let other_head = S.Head.get onto in
+
S.Head.set s other_head;
+
let res =
+
List.fold_left
+
(fun last (_, (entry : entry)) ->
+
match last with
+
| Error _ as e -> e
+
| Ok (new_store, new_ctx) ->
+
let new_entry, diff =
+
exec config ~stdout fs proc (new_store, new_ctx) entry
+
in
+
complete_exec (new_store, new_ctx) clock fs new_entry diff)
+
(Ok (H.Store ((module S), s), ctx))
+
commits_to_apply
+
in
+
res)
+
| _ -> assert false (* Because n = 1 *)
let run (config : config) ~stdout fs clock proc
(((H.Store ((module S), store) : entry H.t) as s), (ctx : ctx)) = function
···
Ok (s, ctx)
| Ok store -> Ok (store, ctx)))
| Unknown args ->
-
Fmt.epr "%a: %s\n%!" (text `Red) "Unknown Shelter Action"
-
(String.concat " " args);
-
Ok (s, ctx)
+
Fmt.epr "%a" (text `Red) "Unknown Shelter Action\n";
+
Shelter.shell_error (String.concat " " args)
| Info `Current ->
let sessions = sessions s in
let sesh = Option.value ~default:"main" (snd (which_branch s)) in
···
Ok (s, ctx)
| Exec [] -> Ok (s, ctx)
| Undo -> Ok (reset_hard s, ctx)
-
| Replay _ -> Ok (s, ctx)
+
| Replay branch -> replay config s ctx fs clock proc stdout branch
| Info `History ->
display_history s;
Ok (s, ctx)
···
let entry = { entry with pre = { entry.pre with args = command } } in
try
let new_entry, diff = exec config ~stdout fs proc (s, ctx) entry in
-
match new_entry with
-
| Error e -> Error e
-
| Ok (`Reset c) -> (
-
match
-
S.Hash.unsafe_of_raw_string c |> S.Commit.of_hash (S.repo store)
-
with
-
| None ->
-
Fmt.epr "Resetting to existing entry failed...\n%!";
-
Ok (s, ctx)
-
| Some c ->
-
S.Head.set store c;
-
Ok (s, ctx))
-
| Ok (`Entry (entry, path)) ->
-
(* Set diff *)
-
let entry = { entry with post = { entry.post with diff } } in
-
(* Commit if RW *)
-
if entry.pre.mode = RW then (
-
commit
-
~message:("exec " ^ String.concat " " command)
-
clock s entry;
-
(* Save the commit hash for easy restoring later *)
-
let hash =
-
S.Head.get store |> S.Commit.hash |> S.Hash.to_raw_string
-
in
-
Eio.Path.save ~create:(`If_missing 0o644)
-
Eio.Path.(fs / path / "hash")
-
hash);
-
Ok (s, ctx)
-
with Eio.Exn.Io (Eio.Process.E e, _) -> Error e)
+
complete_exec (s, ctx) clock fs new_entry diff
+
with Eio.Exn.Io (Eio.Process.E e, _) -> Shelter.process_error e)