Repository configuration (cookieplone-config.json)#
The cookieplone-config.json file lives at the root of a template repository.
It declares the available templates, organizes them into groups for display, and provides global configuration shared across all templates.
Top-level structure#
{
"version": "1.0",
"title": "Plone Community Templates",
"description": "Official Cookieplone templates for the Plone community",
"extends": "",
"groups": { },
"templates": { },
"config": { }
}
Key |
Type |
Required |
Description |
|---|---|---|---|
|
string |
yes |
Schema version. Must be |
|
string |
yes |
Human-readable name for the template repository. |
|
string |
no |
Short description of the repository. |
|
string | object |
no |
Repository to inherit templates from (see extends). |
|
object |
no |
Template groups for the selection menu (see groups). |
|
object |
yes¹ |
Template definitions (see templates). |
¹ templates is optional when extends is set—see extends.
| config | object | no | Global configuration shared by all templates (see config). |
templates#
A mapping of template IDs to template entry objects. Each key is a unique identifier used to reference the template from the CLI, groups, and sub-template hooks.
{
"templates": {
"project": {
"path": "./templates/projects/monorepo",
"title": "Volto Project",
"description": "Create a new Plone project that uses the Volto frontend",
"hidden": false
},
"sub/cache": {
"path": "./templates/sub/cache",
"title": "Cache settings",
"description": "Sub-template with cache settings.",
"hidden": true
}
}
}
Template entry fields#
Key |
Type |
Required |
Default |
Description |
|---|---|---|---|---|
|
string |
yes |
Relative path from the repository root to the template directory. |
|
|
string |
yes |
Human-readable label shown in the selection menu. |
|
|
string |
yes |
Short description of what the template generates. |
|
|
boolean |
no |
|
When |
Hidden templates can still be invoked directly by name:
cookieplone sub/cache
Or made visible with --all:
cookieplone --all
groups#
Groups organize templates into categories for the selection menu. Each key is a unique group identifier.
{
"groups": {
"projects": {
"title": "Projects",
"description": "Generators that create a new Plone project",
"templates": ["project", "classic_project"],
"hidden": false
},
"sub": {
"title": "Sub-Templates",
"description": "Templates used only for internal purposes",
"templates": ["sub/cache", "sub/frontend_project"],
"hidden": true
}
}
}
Group entry fields#
Key |
Type |
Required |
Default |
Description |
|---|---|---|---|---|
|
string |
yes |
Human-readable label for the group category. |
|
|
string |
yes |
Short description of the group. |
|
|
array of strings |
yes |
Ordered list of template IDs. Must not be empty. |
|
|
boolean |
no |
|
When |
Validation constraints#
Every template ID in a group must match a key in the top-level
templatesmapping.A template must not appear in more than one group.
Every template must be assigned to at least one group.
config#
Global configuration shared across all templates in the repository.
{
"config": {
"min_version": "2.0.0a2",
"versions": {
"gha_version_checkout": "v6",
"gha_version_setup_node": "v4",
"frontend_pnpm": "10.20.0"
},
"renderer": "rich"
}
}
config.versions#
A flat mapping of version identifiers to version strings.
These values are available in all templates via the versions Jinja2 namespace:
{{ versions.gha_version_checkout }}
Individual templates can override specific version values through their own config.versions in cookieplone.json.
Template-level values take precedence over repository-level values for the same key.
See Schema v2 reference (cookieplone.json) for details on the per-template config.versions.
config.renderer#
Selects which tui_forms renderer is used by the wizard for interactive runs in this repository.
Valid values are the renderer names registered with tui_forms: cookiecutter (default), rich, and stdlib.
{
"config": {
"renderer": "rich"
}
}
The COOKIEPLONE_RENDERER environment variable takes precedence over this field, so end-users can override the repository preference on a per-run basis.
The --no-input flag always forces the noinput renderer regardless of either setting.
If the configured renderer name is not registered with tui_forms, Cookieplone aborts with an InvalidConfiguration error listing the available renderers.
See Environment variables reference for the corresponding environment variable.
config.min_version#
Declares the minimum Cookieplone version required by the templates in this repository.
The value must be a valid PEP 440 version string, including pre-release versions such as 2.0.0a1.
{
"config": {
"min_version": "2.0.0a2"
}
}
When present, Cookieplone compares the installed version against this value before any generation begins. If the installed version is older, generation stops with an actionable error message:
This template requires cookieplone >= 2.0.0a2, but you have 1.3.0 installed.
Please upgrade: uvx --no-cache cookieplone@2.0.0a2
When the key is absent or empty, no version check is performed (backwards compatible with existing repositories).
extends#
The extends field declares that this repository builds on top of another template repository.
It lets organizations keep a minimal repository with only their local templates and overrides while transparently consuming an upstream, mirroring how extends works in GitLab CI and similar inheritance systems.
A downstream repository can:
Inherit every template and group from upstream by default.
Override a specific template by redeclaring it under the same
id.Add brand-new templates on top of the upstream set.
Hide an upstream template by redeclaring it with
"hidden": true.
When extends is set, the templates field is optional—a "pure-extension" downstream that only inherits is valid.
Forms#
extends accepts either a string or an object:
{
"extends": "gh:plone/cookieplone-templates"
}
{
"extends": {
"url": "gh:plone/cookieplone-templates",
"tag": "2.1.0"
}
}
The object form pins the upstream to a specific tag or branch for reproducibility. The string form uses the upstream default branch.
Merge rules#
When a downstream declares extends, Cookieplone clones the upstream and merges its config underneath the downstream with the following rules.
Field |
Rule |
|---|---|
|
Downstream wins (the merged result must satisfy the downstream schema version). |
|
Downstream wins. |
|
Stripped from the merged result. |
|
Keyed union; a downstream entry merges per-field with the upstream entry of the same |
|
Keyed union; a downstream group entry replaces the upstream entry with the same |
|
Shallow merge, downstream wins per key. |
|
Downstream wins if set; otherwise falls back to upstream. |
|
Stricter wins: |
Note
Group-level merging is currently replace-or-nothing: a downstream that redeclares a group inherits no entries from the upstream group. To add a single template to an upstream group, the downstream must re-list every upstream template ID in that group.
An opt-in append mode is tracked in issue #185.
Partial redeclares#
When a downstream redeclares a template that exists upstream, the entry is merged per field. The downstream may declare just the fields it wants to change:
"templates": {
"project": {"hidden": true}
}
This hides the upstream project template without supplying a local path / title / description: the missing fields are filled from upstream after merge.
When the downstream supplies a path, the downstream entry's directory is overlaid on top of upstream's at render time. The generator walks the upstream template first, then copies the downstream template directory on top, so a downstream may override individual files (for example README.md) while inheriting the upstream cookieplone.json, hooks, and everything else:
"templates": {
"project": {
"path": "./templates/project",
"title": "Override of upstream"
}
}
templates/project/
└── {{ cookiecutter.__folder_name }}/
└── README.md # overrides upstream's README
The overlay is materialised in a temporary directory and cleaned up after the run.
Transitive inheritance#
extends follows chains. If A extends B and B extends C, then A resolves to C ∪ B ∪ A with each downstream layer winning over the layers below it.
To prevent runaway resolution, the chain is bounded by MAX_EXTENDS_DEPTH = 5.
A circular chain (A → B → A) is detected and reported with the full cycle.
Errors#
Failure |
Behaviour |
|---|---|
Upstream URL unreachable |
|
Schema validation fails on a loaded link |
|
Cycle detected |
|
Depth limit exceeded |
|
Cross-referential check fails on the merged result |
|
See Extend an upstream template repository for a worked walkthrough.
Backward compatibility#
Repositories that use the legacy cookiecutter.json format (a flat templates mapping without groups or versioning) continue to work.
Cookieplone checks for cookieplone-config.json first, then falls back to cookiecutter.json.
Full example#
{
"version": "1.0",
"title": "Plone Community Templates",
"description": "Official Cookieplone templates for the Plone community",
"extends": "",
"groups": {
"projects": {
"title": "Projects",
"description": "Generators that create a new Plone project",
"templates": ["project", "classic_project"],
"hidden": false
},
"add-ons": {
"title": "Add-ons",
"description": "Extend Plone with add-ons",
"templates": ["backend_addon", "frontend_addon"],
"hidden": false
},
"sub": {
"title": "Sub-Templates",
"description": "Templates used only for internal purposes",
"templates": ["sub/cache"],
"hidden": true
}
},
"templates": {
"project": {
"path": "./templates/projects/monorepo",
"title": "Volto Project",
"description": "Create a new Plone project that uses the Volto frontend",
"hidden": false
},
"classic_project": {
"path": "./templates/projects/classic",
"title": "Classic UI Project",
"description": "Create a new Plone project that uses Classic UI",
"hidden": false
},
"backend_addon": {
"path": "./templates/add-ons/backend",
"title": "Backend Add-on",
"description": "Create a new Python package to be used with Plone",
"hidden": false
},
"frontend_addon": {
"path": "./templates/add-ons/frontend",
"title": "Frontend Add-on",
"description": "Create a new Node package to be used with Volto",
"hidden": false
},
"sub/cache": {
"path": "./templates/sub/cache",
"title": "Cache settings",
"description": "Sub-template with cache settings.",
"hidden": true
}
},
"config": {
"min_version": "2.0.0a2",
"versions": {
"gha_version_checkout": "v6",
"frontend_pnpm": "10.20.0"
}
}
}
Related pages#
Template repositories: how template repositories are structured.
Schema v2 reference (cookieplone.json): the per-template schema format.
Sub-templates: how sub-templates compose.