Add CLI support for user-provided project scaffold templates#5406
Add CLI support for user-provided project scaffold templates#5406huyhoang171106 wants to merge 1 commit intoplatformio:developfrom
Conversation
Signed-off-by: Nguyen Huy Hoang <181364121+huyhoang171106@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds a new CLI option to platformio project init to allow seeding a newly-created project with additional user-provided scaffold files (e.g., mbed_app.json), by copying a template directory into the project during initialization.
Changes:
- Added
--template-diroption with validation for a user-provided template directory. - Extended
init_base_project()to accepttemplate_dirand copy template contents into the new project. - Implemented
copy_project_template()to walk the template directory and copy missing files into the project.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if not os.access(value, os.R_OK): | ||
| raise click.BadParameter("`%s` is not readable" % value) |
There was a problem hiding this comment.
validate_template_dir() only checks os.R_OK. For directories on POSIX, read permission alone isn’t sufficient to traverse/copy contents (needs execute/search permission too), so os.walk()/copy2() can still fail with PermissionError after validation passes. Consider validating os.X_OK as well (and/or handle os.walk(..., onerror=...) to surface a clear Click error).
| if not os.access(value, os.R_OK): | |
| raise click.BadParameter("`%s` is not readable" % value) | |
| if not os.access(value, os.R_OK | os.X_OK): | |
| raise click.BadParameter("`%s` is not readable or traversable" % value) |
| "--template-dir", | ||
| type=click.Path(exists=True, file_okay=False, dir_okay=True, readable=True), | ||
| callback=validate_template_dir, | ||
| help="Copy files from this directory into a new project", |
There was a problem hiding this comment.
The --template-dir help text says it will “Copy files … into a new project”, but the implementation (a) only applies when is_new_project is true and (b) silently skips any destination files that already exist (including the default scaffold files created earlier). Please either document these constraints in the option help (e.g., “only for new projects; does not overwrite existing files”) or adjust behavior to match the description.
| help="Copy files from this directory into a new project", | |
| help=( | |
| "Copy files from this directory into a new project only; " | |
| "existing files are not overwritten" | |
| ), |
| dst_file = os.path.join(dst_root, fname) | ||
| if os.path.exists(dst_file): | ||
| continue | ||
| shutil.copy2(src_file, dst_file) |
There was a problem hiding this comment.
shutil.copy2() follows symlinks by default, so a template containing symlinked files will copy the target contents into the project (and will not preserve the symlink). That’s surprising behavior for a “template copy”, and it can also end up copying special/unexpected files. Consider explicitly choosing a symlink policy (skip symlinks, preserve them with follow_symlinks=False, and/or validate file types before copying).
| shutil.copy2(src_file, dst_file) | |
| shutil.copy2(src_file, dst_file, follow_symlinks=False) |
| if template_dir: | ||
| copy_project_template(template_dir, project_dir) |
There was a problem hiding this comment.
There are existing CLI tests for project_init_cmd (see tests/commands/test_init.py), but this new --template-dir behavior isn’t covered. Please add tests that verify template files are copied into a new project and that existing scaffold files aren’t overwritten (or whatever the intended overwrite policy is).
Summary
Extend the
project initcommand to accept a new option for custom scaffold content (for example, addingmbed_app.jsonautomatically). This is the entry point where project creation arguments are parsed and forwarded to project-generation logic, so it should expose a user-facing argument such as--template-dir(or--project-template-dir) and pass it through to the initializer.Files changed
platformio/project/commands/init.py(modified)Testing
Closes #5264