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.

precommit: add .ocamlformat check and creation

- Add has_ocamlformat field to hook_status type
- init creates .ocamlformat (version 0.28.1) if missing
- status displays .ocamlformat column
- Exit code 1 if any OCaml project missing .ocamlformat

+27 -8
+12 -8
bin/main.ml
··· 41 41 (fun d -> 42 42 let s = Precommit.status_in_dir d in 43 43 if s.is_ocaml_project && s.is_git_repo then 44 - if not (s.has_pre_commit && s.has_commit_msg) then begin 44 + if not (s.has_pre_commit && s.has_commit_msg && s.has_ocamlformat) then begin 45 45 or_die (Precommit.init_in_dir ~dry_run d); 46 46 let verb = if dry_run then "Would initialise" else "Initialised" in 47 47 Printf.printf "%s: %s\n" d verb ··· 55 55 `S Manpage.s_description; 56 56 `P 57 57 "Install git hooks that run $(b,dune fmt) before commit and remove \ 58 - Claude attribution from commit messages."; 58 + Claude attribution from commit messages. Also creates \ 59 + $(b,.ocamlformat) if missing."; 59 60 `S Manpage.s_examples; 60 61 `P "Initialise hooks in the current directory:"; 61 62 `Pre " precommit init"; ··· 73 74 let pp_check b = if b then "\027[32m✓\027[0m" else "\027[31m✗\027[0m" 74 75 75 76 let pp_status name (s : Precommit.hook_status) = 76 - Printf.printf "%s %s %s %s\n" 77 + Printf.printf "%s %s %s %s %s\n" 77 78 (pp_check s.has_pre_commit) 78 79 (pp_check s.has_commit_msg) 80 + (pp_check s.has_ocamlformat) 79 81 (pp_check s.is_ocaml_project) 80 82 name 81 83 82 84 let status recursive dirs = 83 85 let dirs = collect_dirs ~recursive dirs in 84 - Printf.printf "pre-commit commit-msg ocaml directory\n"; 85 - Printf.printf "---------- ---------- ----- ---------\n"; 86 + Printf.printf "pre-commit commit-msg ocamlformat ocaml directory\n"; 87 + Printf.printf "---------- ---------- ----------- ----- ---------\n"; 86 88 let missing = ref false in 87 89 List.iter 88 90 (fun d -> 89 91 let s = Precommit.status_in_dir d in 90 92 pp_status d s; 91 93 if s.is_ocaml_project && s.is_git_repo then 92 - if not (s.has_pre_commit && s.has_commit_msg) then missing := true) 94 + if not (s.has_pre_commit && s.has_commit_msg && s.has_ocamlformat) then 95 + missing := true) 93 96 dirs; 94 97 if !missing then exit 1 95 98 ··· 100 103 `S Manpage.s_description; 101 104 `P "Show which directories have hooks installed."; 102 105 `P 103 - "Columns show: pre-commit hook, commit-msg hook, is OCaml project. \ 104 - Exit code is 1 if any OCaml project is missing hooks."; 106 + "Columns show: pre-commit hook, commit-msg hook, .ocamlformat file, is \ 107 + OCaml project. Exit code is 1 if any OCaml project is missing hooks \ 108 + or .ocamlformat."; 105 109 `S Manpage.s_examples; 106 110 `P "Check status of all projects under src/:"; 107 111 `Pre " precommit status -r src/";
+13
lib/precommit.ml
··· 55 55 exit 0 56 56 |} 57 57 58 + let default_ocamlformat = {|version = 0.28.1 59 + |} 60 + 58 61 let file_exists path = Sys.file_exists path 59 62 60 63 let mkdir_p path = ··· 82 85 let init_in_dir ~dry_run dir = 83 86 let dune_project = Filename.concat dir "dune-project" in 84 87 let git_dir_path = Filename.concat dir ".git" in 88 + let ocamlformat_path = Filename.concat dir ".ocamlformat" in 85 89 if not (file_exists dune_project) then 86 90 Error (Printf.sprintf "%s: No dune-project found" dir) 87 91 else if not (file_exists git_dir_path) then ··· 115 119 write_file ~dry_run commit_msg_path commit_msg_hook; 116 120 chmod_exec ~dry_run commit_msg_path; 117 121 122 + (* Create .ocamlformat if missing *) 123 + if not (file_exists ocamlformat_path) then 124 + write_file ~dry_run ocamlformat_path default_ocamlformat; 125 + 118 126 Ok () 119 127 120 128 let init ~dry_run () = init_in_dir ~dry_run "." ··· 122 130 type hook_status = { 123 131 has_pre_commit : bool; 124 132 has_commit_msg : bool; 133 + has_ocamlformat : bool; 125 134 is_ocaml_project : bool; 126 135 is_git_repo : bool; 127 136 } ··· 129 138 let status_in_dir dir = 130 139 let dune_project = Filename.concat dir "dune-project" in 131 140 let git_dir_path = Filename.concat dir ".git" in 141 + let ocamlformat_path = Filename.concat dir ".ocamlformat" in 132 142 let is_ocaml_project = file_exists dune_project in 133 143 let is_git_repo = file_exists git_dir_path in 144 + let has_ocamlformat = file_exists ocamlformat_path in 134 145 if not is_git_repo then 135 146 { 136 147 has_pre_commit = false; 137 148 has_commit_msg = false; 149 + has_ocamlformat; 138 150 is_ocaml_project; 139 151 is_git_repo; 140 152 } ··· 156 168 { 157 169 has_pre_commit = file_exists pre_commit_path; 158 170 has_commit_msg = file_exists commit_msg_path; 171 + has_ocamlformat; 159 172 is_ocaml_project; 160 173 is_git_repo; 161 174 }
+2
lib/precommit.mli
··· 17 17 type hook_status = { 18 18 has_pre_commit : bool; 19 19 has_commit_msg : bool; 20 + has_ocamlformat : bool; 20 21 is_ocaml_project : bool; 21 22 is_git_repo : bool; 22 23 } ··· 30 31 Creates: 31 32 - [.git/hooks/pre-commit] - runs [dune fmt] on staged OCaml files 32 33 - [.git/hooks/commit-msg] - checks for emojis and removes Claude attribution 34 + - [.ocamlformat] - if missing, creates with default version 33 35 34 36 If [dry_run] is [true], prints what would be done without making changes. 35 37