66import shlex
77import sys
88from typing import Any
9+ from typing import NamedTuple
910from typing import Sequence
1011
1112import cfgv
2021
2122check_string_regex = cfgv .check_and (cfgv .check_string , cfgv .check_regex )
2223
24+ HOOK_TYPES = (
25+ 'commit-msg' ,
26+ 'post-checkout' ,
27+ 'post-commit' ,
28+ 'post-merge' ,
29+ 'post-rewrite' ,
30+ 'pre-commit' ,
31+ 'pre-merge-commit' ,
32+ 'pre-push' ,
33+ 'prepare-commit-msg' ,
34+ )
35+ # `manual` is not invoked by any installed git hook. See #719
36+ STAGES = (* HOOK_TYPES , 'manual' )
37+
2338
2439def check_type_tag (tag : str ) -> None :
2540 if tag not in ALL_TAGS :
@@ -43,6 +58,46 @@ def check_min_version(version: str) -> None:
4358 )
4459
4560
61+ _STAGES = {
62+ 'commit' : 'pre-commit' ,
63+ 'merge-commit' : 'pre-merge-commit' ,
64+ 'push' : 'pre-push' ,
65+ }
66+
67+
68+ def transform_stage (stage : str ) -> str :
69+ return _STAGES .get (stage , stage )
70+
71+
72+ class StagesMigrationNoDefault (NamedTuple ):
73+ key : str
74+ default : Sequence [str ]
75+
76+ def check (self , dct : dict [str , Any ]) -> None :
77+ if self .key not in dct :
78+ return
79+
80+ val = dct [self .key ]
81+ cfgv .check_array (cfgv .check_any )(val )
82+
83+ val = [transform_stage (v ) for v in val ]
84+ cfgv .check_array (cfgv .check_one_of (STAGES ))(val )
85+
86+ def apply_default (self , dct : dict [str , Any ]) -> None :
87+ if self .key not in dct :
88+ return
89+ dct [self .key ] = [transform_stage (v ) for v in dct [self .key ]]
90+
91+ def remove_default (self , dct : dict [str , Any ]) -> None :
92+ raise NotImplementedError
93+
94+
95+ class StagesMigration (StagesMigrationNoDefault ):
96+ def apply_default (self , dct : dict [str , Any ]) -> None :
97+ dct .setdefault (self .key , self .default )
98+ super ().apply_default (dct )
99+
100+
46101MANIFEST_HOOK_DICT = cfgv .Map (
47102 'Hook' , 'id' ,
48103
@@ -70,7 +125,7 @@ def check_min_version(version: str) -> None:
70125 cfgv .Optional ('log_file' , cfgv .check_string , '' ),
71126 cfgv .Optional ('minimum_pre_commit_version' , cfgv .check_string , '0' ),
72127 cfgv .Optional ('require_serial' , cfgv .check_bool , False ),
73- cfgv . Optional ('stages' , cfgv . check_array ( cfgv . check_one_of ( C . STAGES )) , []),
128+ StagesMigration ('stages' , []),
74129 cfgv .Optional ('verbose' , cfgv .check_bool , False ),
75130)
76131MANIFEST_SCHEMA = cfgv .Array (MANIFEST_HOOK_DICT )
@@ -241,7 +296,9 @@ def check(self, dct: dict[str, Any]) -> None:
241296 cfgv .OptionalNoDefault (item .key , item .check_fn )
242297 for item in MANIFEST_HOOK_DICT .items
243298 if item .key != 'id'
299+ if item .key != 'stages'
244300 ),
301+ StagesMigrationNoDefault ('stages' , []),
245302 OptionalSensibleRegexAtHook ('files' , cfgv .check_string ),
246303 OptionalSensibleRegexAtHook ('exclude' , cfgv .check_string ),
247304)
@@ -290,17 +347,13 @@ def check(self, dct: dict[str, Any]) -> None:
290347 cfgv .RequiredRecurse ('repos' , cfgv .Array (CONFIG_REPO_DICT )),
291348 cfgv .Optional (
292349 'default_install_hook_types' ,
293- cfgv .check_array (cfgv .check_one_of (C . HOOK_TYPES )),
350+ cfgv .check_array (cfgv .check_one_of (HOOK_TYPES )),
294351 ['pre-commit' ],
295352 ),
296353 cfgv .OptionalRecurse (
297354 'default_language_version' , DEFAULT_LANGUAGE_VERSION , {},
298355 ),
299- cfgv .Optional (
300- 'default_stages' ,
301- cfgv .check_array (cfgv .check_one_of (C .STAGES )),
302- C .STAGES ,
303- ),
356+ StagesMigration ('default_stages' , STAGES ),
304357 cfgv .Optional ('files' , check_string_regex , '' ),
305358 cfgv .Optional ('exclude' , check_string_regex , '^$' ),
306359 cfgv .Optional ('fail_fast' , cfgv .check_bool , False ),
0 commit comments