Skip to content

Commit cdfa023

Browse files
authored
Merge pull request #116 from plone/feature/address-scaffolding-evals
Fix scaffolding evaluation failures
2 parents 8fe410d + bb03d3e commit cdfa023

21 files changed

Lines changed: 1907 additions & 57 deletions

.github/workflows/python-package.yml

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
uses: actions/checkout@v2
2424
with:
2525
repository: plone/copier-templates
26-
path: copier-templates
26+
path: develop/plone/src/copier-templates
2727
- name: Set up Python ${{ matrix.python-version }}
2828
uses: actions/setup-python@v2
2929
with:
@@ -44,6 +44,31 @@ jobs:
4444
flake8 . --exclude=copier-templates --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
4545
- name: Test with pytest
4646
env:
47-
PLONECLI_TEMPLATES_DIR: ${{ github.workspace }}/copier-templates
47+
PLONECLI_TEMPLATES_DIR: ${{ github.workspace }}/develop/plone/src/copier-templates
4848
run: |
4949
pytest
50+
51+
scaffolding:
52+
runs-on: ubuntu-latest
53+
timeout-minutes: 20
54+
steps:
55+
- uses: actions/checkout@v4
56+
- name: Checkout copier-templates
57+
uses: actions/checkout@v4
58+
with:
59+
repository: plone/copier-templates
60+
path: develop/plone/src/copier-templates
61+
- name: Set up uv
62+
uses: astral-sh/setup-uv@v6
63+
with:
64+
python-version: "3.12"
65+
- name: Install dependencies
66+
run: uv sync --extra test
67+
- name: Validate generated scaffolding
68+
run: uv run python evals/scaffolding/run_evals.py --ci-validation
69+
- name: Upload scaffolding report
70+
if: always()
71+
uses: actions/upload-artifact@v4
72+
with:
73+
name: scaffolding-evaluation
74+
path: evals/scaffolding/results/

CHANGES.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@
33
## 7.0.0b15 (unreleased)
44

55

6-
- Nothing changed yet.
6+
- Allow the plonecli skill to start the development server when the user
7+
explicitly requests it.
8+
[MrTango]
9+
10+
- Harden scaffolding evaluation and remove false template Git warnings.
11+
[MrTango]
712

813

914
## 7.0.0b14 (2026-08-13)

evals/scaffolding/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
workspaces/
2+
results/
3+
__pycache__/
4+
*.py[cod]

evals/scaffolding/EVALUATION.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Scaffolding evaluation findings
2+
3+
> Historical baseline from before the fixes. A full verification run passed
4+
> 245/245 cases with no warnings on 2026-08-13. The ignored
5+
> `results/report.md` is mutable and may instead contain the latest quick or
6+
> CI-validation run.
7+
8+
## Scope
9+
10+
The full run executed 245 cases against the development templates checkout:
11+
12+
- all 3 project templates;
13+
- all 20 feature templates individually;
14+
- explicit finite high-interaction matrices for backend/Svelte booleans, behavior booleans, REST booleans crossed with target mode, reachable content-type states, view choices/booleans, vocabulary types, all viewlet managers and template states, and Zope distribution/storage choices;
15+
- combined, reversed-order, repeated-application, hostile-input, and command-chain cases;
16+
- harmless root commands and the CLI command unit suite.
17+
18+
Open-ended strings, integers, and environment-discovered choices cannot have a literal exhaustive Cartesian product. They are covered with defaults, non-default valid values, manual-choice paths, and hostile quote/newline/backslash partitions. Finite domains are exhaustive only in the explicitly named high-interaction matrices; other templates receive individual non-default cases plus their focused unit tests.
19+
20+
Result: **237 passed, 8 failed**. See the ignored runtime report at `results/report.md` and per-case logs under `results/logs/`.
21+
22+
## Problems
23+
24+
### High: free text can generate invalid TOML
25+
26+
`backend_addon` and `zope-setup` interpolate title, description, and author values directly into quoted TOML. Quotes, newlines, and backslashes can produce an invalid `pyproject.toml`.
27+
28+
Evidence:
29+
30+
- `hostile-backend-toml-strings`: generation returned success, but TOML validation failed.
31+
- `hostile-zope-toml-strings`: the generated TOML was invalid and the post-copy hook aborted while parsing it.
32+
33+
Use a TOML-safe Jinja filter or generate these values through `tomlkit` instead of interpolating raw strings.
34+
35+
### High: `zope_instance` cannot be added through plonecli
36+
37+
All four `zope_instance` CLI cases failed because `plonecli add` did not list `zope_instance` in a standalone `zope-setup` project. The generated project contains both `[tool.plone.project.settings]` and `[tool.plone.backend_addon.settings]`. Project detection checks backend settings first, classifies the project as `backend_addon`, and exposes the wrong subtemplate set.
38+
39+
Either avoid writing backend-addon settings for standalone Zope projects or make project detection prefer the substantive project settings in this mixed layout.
40+
41+
### High: chained `create` then `setup` fails
42+
43+
The CLI declares `chain=True`, but `chain-create-then-setup` failed after successfully creating the backend add-on. The group retains the project context detected before `create`, so `setup` still reports that it is outside a package.
44+
45+
Refresh project context after creation, or remove command chaining if cross-context chains are not supported.
46+
47+
### Medium: theme variants conflict without non-interactive resolution
48+
49+
The all-feature sequence failed when `theme_barceloneta` followed `theme`: both own `profiles/default/theme.xml` and related theme paths. Copier requested an interactive overwrite despite `--defaults`, then aborted in the non-TTY evaluation.
50+
51+
Treat theme templates as explicit alternatives and reject a second theme with a clear message, or add a documented overwrite/replacement flow.
52+
53+
### Medium: Barceloneta integration test uses a stale path
54+
55+
The root integration suite generated the test at `src/collective/mythemetest/tests/test_theme_my_test_theme.py`, but `tests/test_theme_barceloneta_integration.py` expects it under top-level `tests/`. Result: 23 integration cases passed and 1 failed.
56+
57+
Update the assertion and pytest target to the generated `src/<package>/tests/` layout.
58+
59+
## Optimization opportunities
60+
61+
- Copier template extensions emitted hundreds of deprecation warnings because `ContextHook.update` is deprecated. Migrate hooks to modify context in `hook`.
62+
- `click_aliases` reads deprecated `click.__version__`; update or replace the dependency before Click 9.1.
63+
- Feature generation inside these nested, `--no-git` workspaces reports the outer plonecli repository as dirty. Git cleanliness checks should be scoped to the detected generated project rather than walking into an unrelated parent repository.
64+
- Keep the generated TOML/XML/Python validators as CI checks. They found failures that successful Copier exit codes did not detect.
65+
66+
## Resolution
67+
68+
All findings above have been addressed:
69+
70+
- free-text TOML values use serialization filters;
71+
- standalone Zope projects are detected correctly;
72+
- chained `create``setup` refreshes project context;
73+
- theme variants reject conflicting overlays;
74+
- the Barceloneta integration test uses the generated package test path;
75+
- context hooks use the current in-place API;
76+
- the deprecated command-alias dependency was removed;
77+
- Git checks are scoped to the generated project;
78+
- subtemplate validation tasks use Copier's `_copier_operation` value and no
79+
longer report files generated earlier in the same copy as pre-existing
80+
changes;
81+
- generated TOML/XML/Python validation runs in CI.
82+
83+
## Baseline test receipts
84+
85+
- Root unit suite: **209 passed, 16 skipped**.
86+
- Copier-template unit suite: **386 passed, 2 integration tests deselected**.
87+
- Copier-template integration suite: **2 passed**.
88+
- Root integration suite: **23 passed, 1 failed** (stale Barceloneta test path above).
89+
- Full scaffolding matrix: **199 passed, 8 failed** (the eight cases map to four product problems above).

evals/scaffolding/README.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# plonecli scaffolding evaluations
2+
3+
This directory contains a reusable, real-CLI evaluation of plonecli commands and
4+
all copier templates in the development checkout. It is intentionally separate
5+
from product and template source.
6+
7+
## Run
8+
9+
From the repository root:
10+
11+
```sh
12+
uv run python evals/scaffolding/run_evals.py --quick
13+
uv run python evals/scaffolding/run_evals.py --ci-validation
14+
uv run python evals/scaffolding/run_evals.py
15+
```
16+
17+
`--quick` runs a reduced smoke subset. `--ci-validation` runs every template
18+
once plus hostile-input and command checks. With no flag, the runner executes
19+
the explicit finite high-interaction matrices described below. It audits the
20+
repository template inventory and fails when a new template has no lane. The runner itself invokes plonecli only as
21+
`uv run --project /workspaces/plonecli plonecli`, so copied generated projects cannot shadow the checkout with their own environment. It sets:
22+
23+
```text
24+
PLONECLI_TEMPLATES_DIR=/workspaces/plonecli/develop/plone/src/copier-templates
25+
```
26+
27+
Generated trees are disposable and always live beneath `workspaces/`. Reports
28+
and per-case command logs are written beneath `results/`:
29+
30+
- `results/report.json` — machine-readable case inventory, commands, coverage,
31+
validation results, counts, and failures.
32+
- `results/report.md` — human-readable coverage table and problem summary.
33+
- `results/logs/*.log` — captured stdout/stderr for every case.
34+
35+
Both output directories are ignored by git and replaced at the start of a run.
36+
37+
## Coverage
38+
39+
The full run covers:
40+
41+
- harmless root commands: help, template list, versions, and bash/zsh/fish
42+
completion output;
43+
- real non-default creation of `backend_addon`, `zope-setup`, and the `addon`
44+
composite, plus a real standalone `setup` application;
45+
- every currently shipped backend subtemplate individually against a copied
46+
clean parent, plus `zope_instance` against a copied Zope parent;
47+
- both backend headless states and both Svelte custom-element states;
48+
- the complete behavior boolean matrix (4 cases);
49+
- all REST boolean states crossed with normal/manual registration targets
50+
(64 cases);
51+
- all reachable content-type gated boolean/choice states;
52+
- every view base class × template × marker × normal/manual target state;
53+
- both vocabulary implementation choices;
54+
- all 26 viewlet managers with both template values (52 cases);
55+
- both Zope distributions × all three storage modes, and all three
56+
`zope_instance` storage modes;
57+
- one all-backend-subtemplates project, reversed-order pairs, and representative
58+
repeated-application/idempotency cases;
59+
- TOML-hostile quote/newline/backslash partitions and a real chained
60+
`create``setup` command.
61+
62+
Names are unique per isolated project to make collisions deterministic. The
63+
report records the full planned and actually executed counts by category, and
64+
each matrix case records its parameter values.
65+
66+
## Validation and safety
67+
68+
Every generated project receives deterministic syntax and duplicate-registration
69+
checks without installing Plone. The template unit suite supplies
70+
feature-specific semantic assertions:
71+
72+
- every TOML file is parsed with `tomllib`;
73+
- every XML and ZCML file is parsed;
74+
- every Python file is compiled;
75+
- exact duplicate direct-child XML registrations are reported where practical;
76+
- every subprocess exit code is checked;
77+
- stdin is disabled and every command has a configurable timeout.
78+
79+
The harness does **not** run `serve` or `debug`, and does not run a generated
80+
project's `test` task because those branches can start services or resolve a
81+
full Plone environment. Instead it runs the repository's root CLI command unit
82+
test suite, which covers `serve`, `debug`, and `test` dispatch and error paths
83+
with mocked subprocesses. Template hooks may still ask native `uv` to resolve
84+
small hook-only tools (`tomlkit`, Copier extensions); use a warmed uv cache for
85+
the most network-independent run.
86+
87+
## Reading failures
88+
89+
A nonzero runner exit means at least one case failed or was blocked. Start with
90+
`results/report.md`, then inspect the referenced log. Failures are retained as
91+
evaluation findings rather than hidden or retried with defaults. Reports include
92+
repository commits, dirty state, Python, and uv provenance.

0 commit comments

Comments
 (0)