@@ -26,6 +26,52 @@ def echo(msg, fg="green", reverse=False):
2626 click .echo (click .style (msg , fg = fg , reverse = reverse ))
2727
2828
29+ def ensure_templates (config ):
30+ """Clone the copier-templates on first use.
31+
32+ Template discovery, resolution and listing all read from the local clone, so
33+ they must run *after* it exists. Calling this before using the registry means
34+ a freshly installed plonecli works without a manual ``plonecli update``.
35+ Idempotent: a no-op once the clone is present.
36+ """
37+ from pathlib import Path
38+
39+ templates_dir = Path (config .templates_dir )
40+ if not (templates_dir / ".git" ).exists ():
41+ echo ("\n Fetching copier-templates (first run)..." , fg = "green" )
42+ ensure_templates_cloned (config )
43+
44+
45+ def _is_interactive ():
46+ """Whether we can prompt the user (stdin is a terminal)."""
47+ return sys .stdin .isatty ()
48+
49+
50+ def confirm_clean_git (target_dir , defaults : bool ) -> bool :
51+ """Warn and ask to continue if the target repo has uncommitted changes.
52+
53+ Returns True to proceed, False if the user cancels. The prompt defaults to
54+ *cancel* so an accidental Enter is safe. In non-interactive mode (``--defaults``
55+ or no tty) the warning is printed but the run proceeds without prompting.
56+ """
57+ from plonecli .git import dirty_files
58+
59+ modified , untracked = dirty_files (target_dir )
60+ if not modified and not untracked :
61+ return True
62+
63+ echo ("\n WARNING: the git repository has uncommitted changes:" , fg = "yellow" )
64+ for f in modified :
65+ echo (f" modified: { f } " , fg = "yellow" )
66+ for f in untracked :
67+ echo (f" untracked: { f } " , fg = "yellow" )
68+ echo ("" , fg = "yellow" )
69+
70+ if defaults or not _is_interactive ():
71+ return True
72+ return click .confirm ("Continue anyway?" , default = False )
73+
74+
2975def _parse_data (pairs ):
3076 """Parse ``KEY=VALUE`` strings into a dict of copier answers.
3177
@@ -130,6 +176,7 @@ def cli(context, list_templates, versions):
130176 }
131177
132178 if list_templates :
179+ ensure_templates (config )
133180 reg = TemplateRegistry (config , project )
134181 click .echo (reg .list_templates ())
135182
@@ -210,10 +257,17 @@ def format_help(self, ctx, formatter):
210257 help = "Use template defaults for unanswered questions instead of prompting "
211258 "(non-interactive)." ,
212259)
260+ @click .option (
261+ "--no-git" ,
262+ "no_git" ,
263+ is_flag = True ,
264+ help = "Do not initialise git or auto-commit the generated package." ,
265+ )
213266@click .pass_context
214- def create (context , template , name , data , data_file , defaults ):
267+ def create (context , template , name , data , data_file , defaults , no_git ):
215268 """Create a new Plone package"""
216269 config = context .obj ["config" ]
270+ ensure_templates (config )
217271 reg = TemplateRegistry (config )
218272
219273 resolved = reg .resolve_template_name (template )
@@ -224,16 +278,31 @@ def create(context, template, name, data, data_file, defaults):
224278 possibilities = reg .get_main_templates (),
225279 )
226280
281+ if not confirm_clean_git (name , defaults ):
282+ echo ("Aborted." , fg = "yellow" )
283+ return
284+
285+ git_commit = config .auto_commit and not no_git
227286 answers = _collect_data (data_file , data )
228287 steps = reg .get_composite_steps (resolved )
229288 if steps :
230289 echo (f"\n Creating { resolved } project: { name } " , fg = "green" , reverse = True )
231290 for step in steps :
232291 echo (f"\n Applying template: { step } " , fg = "green" )
233- run_create (step , name , config , data = answers , defaults = defaults )
292+ committed = run_create (
293+ step , name , config , data = answers , defaults = defaults ,
294+ git_commit = git_commit ,
295+ )
296+ if committed :
297+ echo (f" Committed: { committed } " , fg = "green" )
234298 else :
235299 echo (f"\n Creating { resolved } project: { name } " , fg = "green" , reverse = True )
236- run_create (resolved , name , config , data = answers , defaults = defaults )
300+ committed = run_create (
301+ resolved , name , config , data = answers , defaults = defaults ,
302+ git_commit = git_commit ,
303+ )
304+ if committed :
305+ echo (f" Committed: { committed } " , fg = "green" )
237306 context .obj ["target_dir" ] = name
238307
239308
@@ -259,14 +328,21 @@ def create(context, template, name, data, data_file, defaults):
259328 help = "Use template defaults for unanswered questions instead of prompting "
260329 "(non-interactive)." ,
261330)
331+ @click .option (
332+ "--no-git" ,
333+ "no_git" ,
334+ is_flag = True ,
335+ help = "Do not auto-commit the changes made by this subtemplate." ,
336+ )
262337@click .pass_context
263- def add (context , template , data , data_file , defaults ):
338+ def add (context , template , data , data_file , defaults , no_git ):
264339 """Add features to your existing Plone package"""
265340 project = context .obj .get ("project" )
266341 if project is None :
267342 raise NotInPackageError (context .command .name )
268343
269344 config = context .obj ["config" ]
345+ ensure_templates (config )
270346 reg = TemplateRegistry (config , project )
271347
272348 resolved = reg .resolve_template_name (template )
@@ -277,9 +353,19 @@ def add(context, template, data, data_file, defaults):
277353 possibilities = reg .get_subtemplates (),
278354 )
279355
356+ if not confirm_clean_git (project .root_folder , defaults ):
357+ echo ("Aborted." , fg = "yellow" )
358+ return
359+
360+ git_commit = config .auto_commit and not no_git
280361 answers = _collect_data (data_file , data )
281362 echo (f"\n Adding { resolved } to { project .root_folder .name } " , fg = "green" , reverse = True )
282- run_add (resolved , project , config , data = answers , defaults = defaults )
363+ committed = run_add (
364+ resolved , project , config , data = answers , defaults = defaults ,
365+ git_commit = git_commit ,
366+ )
367+ if committed :
368+ echo (f" Committed: { committed } " , fg = "green" )
283369
284370
285371@cli .command ()
@@ -294,6 +380,10 @@ def setup(context):
294380 "The 'setup' command can only be run inside a backend_addon project."
295381 )
296382
383+ if not confirm_clean_git (project .root_folder , defaults = False ):
384+ echo ("Aborted." , fg = "yellow" )
385+ return
386+
297387 config = context .obj ["config" ]
298388 echo ("\n Running zope-setup..." , fg = "green" , reverse = True )
299389 run_create ("zope-setup" , str (project .root_folder ), config )
0 commit comments