Installs pre-commit hooks for OCaml projects that run dune fmt automatically
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

irmin: Simplify Link API, remove Effects and Obj.magic

+82 -16
+82 -16
bin/main.ml
··· 2 2 3 3 open Cmdliner 4 4 5 + let log_src = Logs.Src.create "precommit" 6 + 7 + module Log = (val Logs.src_log log_src : Logs.LOG) 8 + 9 + (* {1 Styled output} *) 10 + 11 + let success fmt = 12 + Fmt.pf Fmt.stdout ("%a " ^^ fmt ^^ "@.") 13 + Fmt.(styled (`Fg `Green) string) 14 + "✓" 15 + 16 + let error fmt = 17 + Fmt.pf Fmt.stderr ("%a " ^^ fmt ^^ "@.") Fmt.(styled (`Fg `Red) string) "✗" 18 + 19 + let warning fmt = 20 + Fmt.pf Fmt.stdout ("%a " ^^ fmt ^^ "@.") 21 + Fmt.(styled (`Fg `Yellow) string) 22 + "⚠" 23 + 24 + let info fmt = 25 + Fmt.pf Fmt.stdout ("%a " ^^ fmt ^^ "@.") Fmt.(styled (`Fg `Cyan) string) "ℹ" 26 + 5 27 (* {1 Common arguments} *) 6 28 7 29 let dirs = ··· 53 75 let or_die = function 54 76 | Ok () -> () 55 77 | Error msg -> 56 - Printf.eprintf "precommit: %s\n" msg; 78 + error "%s" msg; 57 79 exit 1 58 80 59 81 let collect_dirs ~fs ~recursive dirs = ··· 69 91 70 92 let init_impl ~fs dry_run force hooks recursive dirs = 71 93 let dirs = collect_dirs ~fs ~recursive dirs in 94 + let count = ref 0 in 72 95 List.iter 73 96 (fun d -> 74 97 let s = Precommit.status_in_dir ~fs d in ··· 77 100 let needs_ai = hooks.Precommit.ai && not s.has_commit_msg in 78 101 if needs_fmt || needs_ai then begin 79 102 or_die (Precommit.init_in_dir ~fs ~dry_run ~force ~hooks d); 80 - let verb = if dry_run then "Would initialise" else "Initialised" in 81 - Printf.printf "%s: %s\n" d verb 103 + incr count; 104 + if dry_run then info "Would initialise %a" Fmt.(styled `Bold string) d 105 + else success "Initialised %a" Fmt.(styled `Bold string) d 82 106 end) 83 - dirs 107 + dirs; 108 + if !count = 0 then info "All directories already have hooks installed" 109 + else 110 + Log.info (fun m -> m "Processed %d director%s" !count (if !count = 1 then "y" else "ies")) 84 111 85 112 let init dry_run force hooks recursive dirs = 86 113 Eio_main.run @@ fun env -> ··· 120 147 121 148 let status_impl ~fs recursive dirs = 122 149 let dirs = collect_dirs ~fs ~recursive dirs in 123 - let missing = ref false in 150 + let missing = ref 0 in 151 + let ok = ref 0 in 124 152 let rows = 125 153 List.map 126 154 (fun d -> 127 155 let s = Precommit.status_in_dir ~fs d in 128 156 if s.is_ocaml_project && s.is_git_repo then begin 129 157 if not (s.has_pre_commit && s.has_commit_msg && s.has_ocamlformat) 130 - then missing := true; 131 - if s.formatting_disabled then missing := true 158 + then incr missing 159 + else if s.formatting_disabled then incr missing 160 + else incr ok 132 161 end; 133 162 [ 134 163 Tty.Span.text d; ··· 153 182 in 154 183 Tty.Table.pp Format.std_formatter table; 155 184 Format.pp_print_newline Format.std_formatter (); 156 - if !missing then exit 1 185 + (* Summary *) 186 + if !missing > 0 then begin 187 + Fmt.pf Fmt.stdout "%a %d project%s with missing hooks@." 188 + Fmt.(styled (`Fg `Red) string) 189 + "✗" !missing 190 + (if !missing = 1 then "" else "s"); 191 + exit 1 192 + end 193 + else if !ok > 0 then 194 + success "%d project%s properly configured" !ok (if !ok = 1 then "" else "s") 157 195 158 196 let status recursive dirs = 159 197 Eio_main.run @@ fun env -> ··· 182 220 183 221 let check_impl ~process_mgr ~fs recursive dirs = 184 222 let dirs = collect_dirs ~fs ~recursive dirs in 185 - let found = ref false in 223 + let total_commits = ref 0 in 224 + let repos_with_issues = ref 0 in 186 225 List.iter 187 226 (fun d -> 188 227 let commits = Precommit.check_ai_attribution ~process_mgr ~fs d in 189 228 if commits <> [] then begin 190 - found := true; 191 - Printf.printf "%s:\n" d; 192 - List.iter 193 - (fun (c : Precommit.ai_commit) -> 194 - Printf.printf " %s %s\n" c.hash c.subject) 195 - commits 229 + incr repos_with_issues; 230 + total_commits := !total_commits + List.length commits; 231 + warning "%a" Fmt.(styled `Bold string) d; 232 + let rows = 233 + List.map 234 + (fun (c : Precommit.ai_commit) -> 235 + [ 236 + Tty.Span.styled Tty.Style.(fg Tty.Color.yellow) c.hash; 237 + Tty.Span.text c.subject; 238 + ]) 239 + commits 240 + in 241 + let table = 242 + Tty.Table.( 243 + of_rows ~border:Tty.Border.rounded 244 + [ column ~align:`Left "hash"; column ~align:`Left "subject" ] 245 + rows) 246 + in 247 + Tty.Table.pp Format.std_formatter table; 248 + Format.pp_print_newline Format.std_formatter () 196 249 end) 197 250 dirs; 198 - if !found then exit 1 251 + (* Summary *) 252 + if !total_commits > 0 then begin 253 + Fmt.pf Fmt.stdout "@.%a@." 254 + Fmt.(styled `Bold string) 255 + "Summary:"; 256 + Fmt.pf Fmt.stdout " %a %d commit%s with AI attribution in %d repo%s@." 257 + Fmt.(styled (`Fg `Red) string) 258 + "✗" !total_commits 259 + (if !total_commits = 1 then "" else "s") 260 + !repos_with_issues 261 + (if !repos_with_issues = 1 then "" else "s"); 262 + exit 1 263 + end 264 + else success "No AI attribution found in commit history" 199 265 200 266 let check recursive dirs = 201 267 Eio_main.run @@ fun env ->