Skip to content
13 changes: 11 additions & 2 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,17 @@ jobs:

steps:
- uses: actions/checkout@v2
- name: Checkout copier-templates
uses: actions/checkout@v2
with:
repository: plone/copier-templates
path: copier-templates
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Set up uv
uses: astral-sh/setup-uv@v5
- name: Install dependencies
run: |
python -m pip install --upgrade pip
Expand All @@ -32,9 +39,11 @@ jobs:
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
flake8 . --exclude=copier-templates --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
flake8 . --exclude=copier-templates --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
env:
PLONECLI_TEMPLATES_DIR: ${{ github.workspace }}/copier-templates
run: |
pytest
5 changes: 5 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
## 7.0.0b10 (unreleased)


- Fix non-interactive composite `create`/`setup`: layered steps now overwrite
files generated by earlier steps instead of raising a copier conflict in
`--defaults` mode.
[MrTango]

- Skill: list field names reserved by Plone's default behaviors so naming
conflicts are caught early and an alternative is recommended; keeping a
conflicting name is allowed when the type does not enable that behavior.
Expand Down
6 changes: 3 additions & 3 deletions plonecli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,11 +287,11 @@ def create(context, template, name, data, data_file, defaults, no_git):
steps = reg.get_composite_steps(resolved)
if steps:
echo(f"\nCreating {resolved} project: {name}", fg="green", reverse=True)
for step in steps:
for index, step in enumerate(steps):
echo(f"\n Applying template: {step}", fg="green")
committed = run_create(
step, name, config, data=answers, defaults=defaults,
git_commit=git_commit,
git_commit=git_commit, overwrite=index > 0,
)
if committed:
echo(f" Committed: {committed}", fg="green")
Expand Down Expand Up @@ -386,7 +386,7 @@ def setup(context):

config = context.obj["config"]
echo("\nRunning zope-setup...", fg="green", reverse=True)
run_create("zope-setup", str(project.root_folder), config)
run_create("zope-setup", str(project.root_folder), config, overwrite=True)


@cli.command("serve")
Expand Down
6 changes: 6 additions & 0 deletions plonecli/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ def run_create(
data: dict | None = None,
defaults: bool = False,
git_commit: bool = True,
overwrite: bool = False,
) -> str | None:
"""Run copier to create a new project from a main template.

Expand All @@ -148,6 +149,10 @@ def run_create(
defaults: Use template defaults for unanswered questions instead of
prompting (non-interactive mode).
git_commit: Initialise git (if needed) and commit the result.
overwrite: Overwrite existing files without prompting. Needed when a
template is layered onto an existing project (composite steps after
the first, ``plonecli setup``); copier otherwise raises an
interactive-conflict error in non-interactive mode.

Returns:
The commit message if a commit was made, otherwise ``None``.
Expand All @@ -161,6 +166,7 @@ def run_create(
data=data or {},
user_defaults=_build_user_defaults(config),
defaults=defaults,
overwrite=overwrite,
unsafe=True,
)

Expand Down
4 changes: 4 additions & 0 deletions tests/test_all_templates_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

from __future__ import annotations

import os
import shutil
from pathlib import Path

Expand Down Expand Up @@ -46,6 +47,9 @@


def _find_templates_dir() -> Path | None:
env_dir = os.environ.get("PLONECLI_TEMPLATES_DIR")
if env_dir and Path(env_dir).exists():
return Path(env_dir)
if DEV_TEMPLATES_DIR.exists():
return DEV_TEMPLATES_DIR
if FALLBACK_TEMPLATES_DIR.exists():
Expand Down
1 change: 1 addition & 0 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ def test_templates_path_reloads_under_different_home(tmp_path, monkeypatch):
config_file = config_dir / "config.toml"
monkeypatch.setattr("plonecli.config.CONFIG_DIR", config_dir)
monkeypatch.setattr("plonecli.config.CONFIG_FILE", config_file)
monkeypatch.delenv("PLONECLI_TEMPLATES_DIR", raising=False)

home_a = tmp_path / "home_a"
monkeypatch.setenv("HOME", str(home_a))
Expand Down
3 changes: 3 additions & 0 deletions tests/test_plonecli.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ def test_create_composite_template(mock_ensure, mock_run_create, mock_config, mo
assert calls[0][0][1] == "my.addon"
assert calls[1][0][0] == "zope-setup"
assert calls[1][0][1] == "my.addon"
# First layer creates fresh files; later layers overlay and must overwrite.
assert calls[0].kwargs["overwrite"] is False
assert calls[1].kwargs["overwrite"] is True


@patch("plonecli.cli.find_project_root", return_value=None)
Expand Down
24 changes: 24 additions & 0 deletions tests/test_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
ensure_templates_cloned,
get_template_path,
get_templates_info,
run_create,
update_templates_clone,
)

Expand Down Expand Up @@ -130,3 +131,26 @@ def test_get_templates_info_not_cloned(tmp_path):
config = PlonecliConfig(templates_dir=str(tmp_path / "nonexistent"))
info = get_templates_info(config)
assert info == "not cloned"


@patch("plonecli.templates.run_copy")
def test_run_create_overwrite_default_false(mock_run_copy, tmp_path):
"""run_create does not overwrite by default."""
(tmp_path / ".git").mkdir()
(tmp_path / "backend_addon").mkdir()
config = PlonecliConfig(templates_dir=str(tmp_path))
run_create("backend_addon", "out", config, defaults=True, git_commit=False)
assert mock_run_copy.call_args.kwargs["overwrite"] is False


@patch("plonecli.templates.run_copy")
def test_run_create_overwrite_forwarded(mock_run_copy, tmp_path):
"""overwrite=True is forwarded to copier (layering onto existing files)."""
(tmp_path / ".git").mkdir()
(tmp_path / "zope-setup").mkdir()
config = PlonecliConfig(templates_dir=str(tmp_path))
run_create(
"zope-setup", "out", config, defaults=True, git_commit=False,
overwrite=True,
)
assert mock_run_copy.call_args.kwargs["overwrite"] is True
3 changes: 3 additions & 0 deletions tests/test_theme_barceloneta_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@


def _templates_dir() -> Path:
env_dir = os.environ.get("PLONECLI_TEMPLATES_DIR")
if env_dir and Path(env_dir).exists():
return Path(env_dir)
if DEV_TEMPLATES_DIR.exists():
return DEV_TEMPLATES_DIR
if FALLBACK_TEMPLATES_DIR.exists():
Expand Down
Loading