···2233let run ~repo ~missing ~tools ~package () =
44 let root = match repo with Some r -> r | None -> Sys.getcwd () in
55+ Eio_main.run @@ fun env ->
66+ let fs = Eio.Stdenv.cwd env in
77+ Eio.Switch.run @@ fun sw ->
88+ let monorepo = Fpath.v root in
99+ let repo = Git.Repository.open_repo ~sw ~fs monorepo in
510 match package with
611 | Some name ->
712 let pkg_dir = Filename.concat root name in
88- let r = Monopam.Quality.check ~tools pkg_dir name in
1313+ let r = Monopam.Quality.check ~repo ~tools pkg_dir name in
914 Fmt.pr "%a\n" Monopam.Quality.pp_entry r;
1015 if r.missing <> [] then (
1116 Fmt.pr "\nFAILED: %d required quality check(s) missing\n"
···1318 `Error (false, "quality policy violated"))
1419 else `Ok ()
1520 | None ->
1616- let results = Monopam.Quality.check_all ~tools root in
2121+ let results = Monopam.Quality.check_all ~repo ~tools root in
1722 (match missing with
1823 | Some feature ->
1924 let mrs = Monopam.Quality.query_missing feature results in
···4651 Arg.(value & opt (some string) None & info [ "r"; "repo" ] ~docv:"DIR" ~doc)
47524853let tools_arg =
4949- let doc = "Also run merlint, prune, and dupfind checks (slower)." in
5454+ let doc = "Also run merlint, prune, and dupfind checks." in
5055 Arg.(value & flag & info [ "tools" ] ~doc)
51565257let man =
5358 [
5459 `S Manpage.s_description;
5560 `P
5656- "Check packages against their quality policy. Each package declares \
5757- required quality features in its dune-project via $(b,(x-quality build \
5858- test fuzz ...)). This command checks whether the package actually has \
5959- those features and fails if any are missing.";
6060- `P "Without $(b,--tools), only checks directory structure.";
6161- `P
6262- "With $(b,--tools), also runs merlint, prune, and dupfind (uses $(b,dune \
6363- exec -B) to reuse existing build artifacts).";
6161+ "Check packages against their quality policy. Policy is declared in \
6262+ *.opam.template via $(b,x-quality: [\"build\" \"test\" ...]). Fails if \
6363+ any declared feature is missing.";
6464 `S Manpage.s_examples;
6565 `Pre " monopam quality # check all against policy";
6666 `Pre " monopam quality crdt # check one package";
6767 `Pre " monopam quality --missing fuzz # packages without fuzz";
6868- `Pre " monopam quality --tools crdt # include tool checks";
6968 ]
70697170let cmd =
+24-6
lib/quality.ml
···161161162162type result = {
163163 package : string;
164164+ tree_hash : string; (** Subtree hash of the package directory. *)
164165 policy : string list;
165166 present : string list;
166167 missing : string list; (** In policy but not present. *)
···169170170171let tool_features = [ "merlint"; "prune"; "dupfind" ]
171172172172-let check ?(tools = false) pkg_dir name =
173173+(** Compute the subtree tree hash for a package prefix using ocaml-git. *)
174174+let subtree_hash repo prefix =
175175+ Log.debug (fun m -> m "Looking up subtree hash for %s" prefix);
176176+ match Git.Repository.tree_hash_at_path repo ~rev:"HEAD" ~path:prefix with
177177+ | Some h ->
178178+ let hex = Git.Hash.to_hex h in
179179+ Log.debug (fun m -> m "subtree_hash %s = %s" prefix hex);
180180+ hex
181181+ | None ->
182182+ Log.debug (fun m -> m "subtree_hash %s = None" prefix);
183183+ ""
184184+185185+let check ~repo ?(tools = false) pkg_dir name =
173186 let policy = read_policy pkg_dir in
174174- (* Run tools if explicitly requested OR if policy requires any tool feature *)
175187 let need_tools =
176188 tools || List.exists (fun f -> List.mem f policy) tool_features
177189 in
178190 let present = detect ~tools:need_tools pkg_dir in
179191 let missing = List.filter (fun f -> not (List.mem f present)) policy in
180192 let extra = List.filter (fun f -> not (List.mem f policy)) present in
181181- { package = name; policy; present; missing; extra }
193193+ let tree_hash = subtree_hash repo name in
194194+ { package = name; tree_hash; policy; present; missing; extra }
182195183183-let check_all ?(tools = false) root =
196196+let check_all ~repo ?(tools = false) root =
184197 let packages =
185198 try
186199 Sys.readdir root |> Array.to_list
···195208 List.map
196209 (fun name ->
197210 let pkg_dir = Filename.concat root name in
198198- check ~tools pkg_dir name)
211211+ check ~repo ~tools pkg_dir name)
199212 packages
200213201214(* ===== Output ===== *)
···211224 Fmt.pf ppf " [suggest: x-quality %s]"
212225 (String.concat " " (r.policy @ r.extra))
213226214214-let pp_entry ppf r = Fmt.pf ppf "%-30s%a" r.package pp_result r
227227+let pp_entry ppf r =
228228+ let short_hash =
229229+ if String.length r.tree_hash >= 8 then String.sub r.tree_hash 0 8
230230+ else r.tree_hash
231231+ in
232232+ Fmt.pf ppf "%-30s %s%a" r.package short_hash pp_result r
215233216234let pp_summary ppf results =
217235 let total = List.length results in
+4-2
lib/quality.mli
···23232424type result = {
2525 package : string;
2626+ tree_hash : string;
2627 policy : string list;
2728 present : string list;
2829 missing : string list;
2930 extra : string list;
3031}
31323232-val check : ?tools:bool -> string -> string -> result
3333-val check_all : ?tools:bool -> string -> result list
3333+val subtree_hash : Git.Repository.t -> string -> string
3434+val check : repo:Git.Repository.t -> ?tools:bool -> string -> string -> result
3535+val check_all : repo:Git.Repository.t -> ?tools:bool -> string -> result list
3436val pp_entry : result Fmt.t
3537val pp_summary : result list Fmt.t
3638val has_failures : result list -> bool