11import os
2+ import shutil
3+ from typing import Any , Dict , List , Optional
24
35import questionary
46import yaml
911from commitizen .config import BaseConfig , JsonConfig , TomlConfig , YAMLConfig
1012from commitizen .cz import registry
1113from commitizen .defaults import config_files
12- from commitizen .exceptions import NoAnswersError
14+ from commitizen .exceptions import InitFailedError , NoAnswersError
1315from commitizen .git import get_latest_tag_name , get_tag_names , smart_open
1416
1517
@@ -19,34 +21,43 @@ def __init__(self, config: BaseConfig, *args):
1921 self .cz = factory .commiter_factory (self .config )
2022
2123 def __call__ (self ):
22- values_to_add = {}
24+ if self .config .path :
25+ out .line (f"Config file { self .config .path } already exists" )
26+ return
2327
2428 # No config for commitizen exist
25- if not self .config .path :
26- config_path = self ._ask_config_path ()
27- if "toml" in config_path :
28- self .config = TomlConfig (data = "" , path = config_path )
29- elif "json" in config_path :
30- self .config = JsonConfig (data = "{}" , path = config_path )
31- elif "yaml" in config_path :
32- self .config = YAMLConfig (data = "" , path = config_path )
33-
34- self .config .init_empty_config_content ()
35-
36- values_to_add ["name" ] = self ._ask_name ()
37- tag = self ._ask_tag ()
38- values_to_add ["version" ] = Version (tag ).public
39- values_to_add ["tag_format" ] = self ._ask_tag_format (tag )
40- self ._update_config_file (values_to_add )
41-
42- if questionary .confirm ("Do you want to install pre-commit hook?" ).ask ():
43- self ._install_pre_commit_hook ()
44-
45- out .write ("You can bump the version and create changelog running:\n " )
46- out .info ("cz bump --changelog" )
47- out .success ("The configuration are all set." )
48- else :
49- out .line (f"Config file { self .config .path } already exists" )
29+ config_path = self ._ask_config_path ()
30+ if "toml" in config_path :
31+ self .config = TomlConfig (data = "" , path = config_path )
32+ elif "json" in config_path :
33+ self .config = JsonConfig (data = "{}" , path = config_path )
34+ elif "yaml" in config_path :
35+ self .config = YAMLConfig (data = "" , path = config_path )
36+ self .config .init_empty_config_content ()
37+
38+ values_to_add = {}
39+ values_to_add ["name" ] = self ._ask_name ()
40+ tag = self ._ask_tag ()
41+ values_to_add ["version" ] = Version (tag ).public
42+ values_to_add ["tag_format" ] = self ._ask_tag_format (tag )
43+ self ._update_config_file (values_to_add )
44+
45+ hook_types = questionary .checkbox (
46+ "What types of pre-commit hook you want to install? (Leave blank if you don't want to install)" ,
47+ choices = [
48+ questionary .Choice ("commit-msg" , checked = True ),
49+ questionary .Choice ("pre-push" , checked = True ),
50+ ],
51+ ).ask ()
52+ if hook_types :
53+ try :
54+ self ._install_pre_commit_hook (hook_types )
55+ except InitFailedError as e :
56+ raise InitFailedError (f"Failed to install pre-commit hook.\n { e } " )
57+
58+ out .write ("You can bump the version and create changelog running:\n " )
59+ out .info ("cz bump --changelog" )
60+ out .success ("The configuration are all set." )
5061
5162 def _ask_config_path (self ) -> str :
5263 name : str = questionary .select (
@@ -109,17 +120,45 @@ def _ask_tag_format(self, latest_tag) -> str:
109120 tag_format = "$version"
110121 return tag_format
111122
112- def _install_pre_commit_hook (self ):
123+ def _search_pre_commit (self ) -> bool :
124+ """Check whether pre-commit is installed"""
125+ return shutil .which ("pre-commit" ) is not None
126+
127+ def _exec_install_pre_commit_hook (self , hook_types : List [str ]):
128+ cmd_str = self ._gen_pre_commit_cmd (hook_types )
129+ c = cmd .run (cmd_str )
130+ if c .return_code != 0 :
131+ err_msg = (
132+ f"Error running { cmd_str } ."
133+ "Outputs are attached below:\n "
134+ f"stdout: { c .out } \n "
135+ f"stderr: { c .err } "
136+ )
137+ raise InitFailedError (err_msg )
138+
139+ def _gen_pre_commit_cmd (self , hook_types : List [str ]) -> str :
140+ """Generate pre-commit command according to given hook types"""
141+ if not hook_types :
142+ raise ValueError ("At least 1 hook type should be provided." )
143+ cmd_str = "pre-commit install " + " " .join (
144+ f"--hook-type { ty } " for ty in hook_types
145+ )
146+ return cmd_str
147+
148+ def _install_pre_commit_hook (self , hook_types : Optional [List [str ]] = None ):
113149 pre_commit_config_filename = ".pre-commit-config.yaml"
114150 cz_hook_config = {
115151 "repo" : "https://github.com/commitizen-tools/commitizen" ,
116152 "rev" : f"v{ __version__ } " ,
117- "hooks" : [{"id" : "commitizen" }],
153+ "hooks" : [
154+ {"id" : "commitizen" },
155+ {"id" : "commitizen-branch" , "stages" : ["push" ]},
156+ ],
118157 }
119158
120159 config_data = {}
121160 if not os .path .isfile (pre_commit_config_filename ):
122- # .pre-commit-config does not exist
161+ # .pre-commit-config.yaml does not exist
123162 config_data ["repos" ] = [cz_hook_config ]
124163 else :
125164 with open (pre_commit_config_filename ) as config_file :
@@ -135,23 +174,21 @@ def _install_pre_commit_hook(self):
135174 else :
136175 config_data ["repos" ].append (cz_hook_config )
137176 else :
138- # .pre-commit-config exists but there's no "repos" key
177+ # .pre-commit-config.yaml exists but there's no "repos" key
139178 config_data ["repos" ] = [cz_hook_config ]
140179
141180 with smart_open (pre_commit_config_filename , "w" ) as config_file :
142181 yaml .safe_dump (config_data , stream = config_file )
143182
144- c = cmd .run ("pre-commit install --hook-type commit-msg" )
145- if c .return_code == 127 :
146- out .error (
147- "pre-commit is not installed in current environement.\n "
148- "Run 'pre-commit install --hook-type commit-msg' again after it's installed"
183+ if not self ._search_pre_commit ():
184+ raise InitFailedError (
185+ "pre-commit is not installed in current environement."
149186 )
150- elif c . return_code != 0 :
151- out . error ( c . err )
152- else :
153- out .write ("commitizen pre-commit hook is now installed in your '.git'\n " )
187+ if hook_types is None :
188+ hook_types = [ "commit-msg" , "pre-push" ]
189+ self . _exec_install_pre_commit_hook ( hook_types )
190+ out .write ("commitizen pre-commit hook is now installed in your '.git'\n " )
154191
155- def _update_config_file (self , values ):
192+ def _update_config_file (self , values : Dict [ str , Any ] ):
156193 for key , value in values .items ():
157194 self .config .set_key (key , value )
0 commit comments