Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server-side command: upload-pack #618

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
29 changes: 29 additions & 0 deletions bin/guit/dune
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,35 @@
mimic
git-unix))

(executable
(name upload_pack)
(modules upload_pack)
(package git-unix)
(public_name guit.upload_pack)
(libraries
happy-eyeballs-lwt
git
git.nss.git
logs
logs.fmt
fmt
mtime
mtime.clock.os
lwt
lwt.unix
fmt.cli
logs.cli
cstruct
domain-name
mirage-flow
fmt.tty
fpath
result
cmdliner
rresult
mimic
git-unix))

(executable
(name v)
(modules v)
Expand Down
142 changes: 142 additions & 0 deletions bin/guit/upload_pack.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
let () = Random.self_init ()

open Git_unix
module Sync = Sync (Store)

let src = Logs.Src.create "guit-upload-pack" ~doc:"logs binary event"

module Log = (val Logs.src_log src : Logs.LOG)

let pad n x =
if String.length x > n then x else x ^ String.make (n - String.length x) ' '

let pp_header ppf (level, header) =
let level_style =
match level with
| Logs.App -> Logs_fmt.app_style
| Logs.Debug -> Logs_fmt.debug_style
| Logs.Warning -> Logs_fmt.warn_style
| Logs.Error -> Logs_fmt.err_style
| Logs.Info -> Logs_fmt.info_style
in
let level = Logs.level_to_string (Some level) in
Fmt.pf ppf "[%a][%a]"
(Fmt.styled level_style Fmt.string)
level (Fmt.option Fmt.string)
(Option.map (pad 10) header)

let reporter ppf =
let report src level ~over k msgf =
let k _ =
over ();
k ()
in
let with_src_and_stamp h _ k fmt =
let dt_us = 1e-3 *. Int64.to_float (Mtime_clock.elapsed_ns ()) in
Fmt.kpf k ppf
("%s %a %a: @[" ^^ fmt ^^ "@]@.")
(pad 10 (Fmt.str "%+04.0fus" dt_us))
pp_header (level, h)
Fmt.(styled `Magenta string)
(pad 10 @@ Logs.Src.name src)
in
msgf @@ fun ?header ?tags fmt -> with_src_and_stamp header tags k fmt
in
{ Logs.report }

let setup_logs style_renderer level ppf =
Fmt_tty.setup_std_outputs ?style_renderer ();
Logs.set_level level;
Logs.set_reporter (reporter ppf);
let quiet = match level with Some _ -> false | None -> true in
quiet, ppf

type error = [ `Store of Store.error | `Sync of Sync.error ]

let store_err err = `Store err
let sync_err err = `Sync err

let pp_error ppf = function
| `Store err -> Fmt.pf ppf "(`Store %a)" Store.pp_error err
| `Sync err -> Fmt.pf ppf "(`Sync %a)" Sync.pp_error err

let main quiet (directory : string) : (unit, 'error) Lwt_result.t =
let root =
(match directory with "" -> Sys.getcwd () | _ -> directory) |> Fpath.v
in
let ( >>? ) = Lwt_result.bind in
let ( >>?? ) = Lwt.bind in
let ( >>! ) v f = Lwt_result.map_error f v in
Store.v root >>! store_err >>? fun store ->
let _push_stdout, _push_stderr =
match quiet with
| true -> ignore, ignore
| false -> print_string, prerr_string
in
Git_unix.std_in_out_ctx () >>?? fun ctx ->
Mimic.resolve ctx >>? fun flow ->
Sync.upload_pack ~flow store >>?? Lwt.return_ok
(* >>! sync_err *)
(* >>? fun _ -> Lwt.return (Ok ()) *)

open Cmdliner

(* XXX(ulugbekna): We want ogit-fetch to have the following interface:
* ogit-fetch [-r <path> | --root <path>] [--output <output_channel>]
* [--progress] <repository> <refspec>... *)

let output =
let converter =
let parse str =
match str with
| "stdout" -> Ok Fmt.stdout
| "stderr" -> Ok Fmt.stderr
| s -> Error (`Msg (Fmt.str "%s is not an output." s))
in
let print ppf v =
Fmt.pf ppf "%s" (if v == Fmt.stdout then "stdout" else "stderr")
in
Arg.conv ~docv:"<output>" (parse, print)
in
let doc =
"Output of the progress status. Can take values 'stdout' (default) or \
'stderr'."
in
Arg.(
value & opt converter Fmt.stdout & info [ "output" ] ~doc ~docv:"<output>")

let directory =
let doc = "Indicate path to repository root containing '.git' folder" in
Arg.(value & opt string "" & info [ "r"; "root" ] ~doc ~docv:"<directory>")

(* XXX(ulugbekna): passed argument needs to be a URI of the repository *)
let repository =
let endpoint =
let parse = Smart_git.Endpoint.of_string in
let print = Smart_git.Endpoint.pp in
Arg.conv ~docv:"<uri>" (parse, print)
in
let doc = "URI leading to repository" in
Arg.(
required & pos 0 (some endpoint) None & info [] ~docv:"<repository>" ~doc)

let setup_logs =
let docs = Manpage.s_common_options in
Term.(
const setup_logs
$ Fmt_cli.style_renderer ~docs ()
$ Logs_cli.level ~docs ()
$ output)

let main (quiet, _) directory =
match Lwt_main.run (main quiet directory) with
| Ok () -> Ok ()
| Error (#error as err) -> Error (Fmt.str "%a." pp_error err)
| Error _ -> Error "other"

let command =
let doc = "Answer to a fetch." in
let info = Cmd.info "upload-pack" ~doc in
Cmd.v info Term.(const main $ setup_logs $ directory)

let () = exit @@ Cmd.eval_result command
2 changes: 2 additions & 0 deletions src/git-unix/git_unix.ml
Original file line number Diff line number Diff line change
Expand Up @@ -747,3 +747,5 @@ module Sync (Git_store : Git.S) = struct
let push ~ctx edn store ?version ?capabilities cmds =
push ~ctx edn store ?version ?capabilities cmds
end

let std_in_out_ctx = Git_unix_mimic.std_in_out_ctx
2 changes: 2 additions & 0 deletions src/git-unix/git_unix.mli
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,5 @@ module Store : sig
function should be registered with [at_exit] to clean pending
file-descriptors. *)
end

val std_in_out_ctx : unit -> Mimic.ctx Lwt.t
20 changes: 18 additions & 2 deletions src/git-unix/git_unix_mimic.ml
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,12 @@ module TCP = struct
let unlisten _ = assert false
end

module FIFO = struct
module Lwt_unix_file_descr_flow = struct
open Lwt.Infix

let ( >>? ) = Lwt_result.bind

type flow = Lwt_unix.file_descr * Lwt_unix.file_descr
type endpoint = Fpath.t
type error = [ `Error of Unix.error * string * string ]
type write_error = [ `Closed | `Error of Unix.error * string * string ]

Expand Down Expand Up @@ -132,6 +131,12 @@ module FIFO = struct
| x :: r -> write fd x >>? fun () -> writev fd r

let close (ic, oc) = Lwt_unix.close ic >>= fun () -> Lwt_unix.close oc
end

module FIFO = struct
include Lwt_unix_file_descr_flow

type endpoint = Fpath.t

let connect fpath =
let process () =
Expand Down Expand Up @@ -184,3 +189,14 @@ let ctx happy_eyeballs =
~k:k2 ctx
in
C.with_optional_tls_config_and_headers ctx

module Std_in_out = struct
include Lwt_unix_file_descr_flow

type endpoint = unit

let connect () = Lwt.return_ok Lwt_unix.(stdin, stdout)
end

let std_endpoint, _ = Mimic.register ~name:"std" (module Std_in_out)
let std_in_out_ctx () = Lwt.return (Mimic.add std_endpoint () Mimic.empty)
2 changes: 2 additions & 0 deletions src/git/dune
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
hxd.core
hxd.string
mimic
mirage-flow
git.nss.unixiz
rresult
git.nss.sigs
git.nss.pck
Expand Down
30 changes: 29 additions & 1 deletion src/git/sync.ml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ module type S = sig
| `Update of Reference.t * Reference.t ]
list ->
(unit, error) result Lwt.t

val upload_pack : flow:Mimic.flow -> store -> unit Lwt.t
end

module Make
Expand Down Expand Up @@ -181,7 +183,7 @@ struct
Lwt.return (Carton.Dec.v ~kind raw)
| None -> Lwt.fail Not_found

include Smart_git.Make (Scheduler) (Pack) (Index) (Hash) (Reference)
include Smart_git.Make_client (Scheduler) (Pack) (Index) (Hash) (Reference)

let ( >>? ) x f =
x >>= function Ok x -> f x | Error err -> Lwt.return_error err
Expand Down Expand Up @@ -293,4 +295,30 @@ struct
push ~ctx
(access, lightly_load t, heavily_load t)
ministore endpoint ?version ?capabilities cmds

module Flow = Unixiz.Make (Mimic)

include
Smart_git.Make_server (Scheduler) (Flow) (Pack) (Index) (Hash) (Reference)

let access =
Sigs.
{
get =
(fun uid t ->
Scheduler.inj (get_object_for_packer (Ministore.prj t) uid));
parents = (fun _ _ -> assert false);
deref =
(fun t refname -> Scheduler.inj (deref (Ministore.prj t) refname));
locals = (fun t -> Scheduler.inj (locals (Ministore.prj t)));
shallowed = (fun _ -> assert false);
shallow = (fun _ -> assert false);
unshallow = (fun _ -> assert false);
}

let upload_pack ~flow t =
let ministore = Ministore.inj (t, Hashtbl.create 0x100) in
upload_pack (Flow.make flow)
(access, lightly_load t, heavily_load t)
ministore
end
5 changes: 5 additions & 0 deletions src/git/sync.mli
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ module type S = sig
| `Update of Reference.t * Reference.t ]
list ->
(unit, error) result Lwt.t

val upload_pack : flow:Mimic.flow -> store -> unit Lwt.t
end

(** Creates a lower-level [Sync] functions [fetch] and [push] that are then
Expand Down Expand Up @@ -106,4 +108,7 @@ module Make
| `Update of Reference.t * Reference.t ]
list ->
(unit, ([> error ] as 'err)) result Lwt.t

val upload_pack : flow:Mimic.flow -> store -> unit Lwt.t
(** Answers a [git fetch] *)
end
2 changes: 1 addition & 1 deletion src/not-so-smart/dune
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
(library
(name nss)
(public_name git.nss)
(modules nss fetch push)
(modules nss fetch push upload_pack)
(libraries
fmt
result
Expand Down
Loading