Create a template#
This tutorial walks you through building a minimal Cookieplone template repository. By the end, you will have a working template that generates a small project on your machine.
Prerequisites:
uv installed.
Basic familiarity with Jinja2 templating syntax.
Step 1: Create the repository structure#
A template repository needs a repository configuration file at its root and at least one template directory. You will build this structure:
my-template/
├── cookieplone-config.json ← repository configuration: lists the templates
└── templates/
└── myproject/ ← one template
├── cookieplone.json ← the questions this template asks
└── {{ cookiecutter.project_slug }}/
├── README.md
└── pyproject.toml
Create the directories:
mkdir -p "my-template/templates/myproject/{{ cookiecutter.project_slug }}"
Cookieplone renders directory names too: in the generated output, {{ cookiecutter.project_slug }} takes the value of the project_slug answer.
Note
A plain directory is enough for this tutorial.
If you keep your template repository in git, commit your files before you run Cookieplone.
A git repository without any commit fails with an error such as ValueError: Reference at 'refs/heads/main' does not exist.
Step 2: Write the repository configuration#
The cookieplone-config.json file tells Cookieplone which templates the repository provides and how to group them in the selection menu.
Create my-template/cookieplone-config.json:
{
"version": "1.0",
"title": "My templates",
"description": "Templates for my organization.",
"groups": {
"projects": {
"title": "Projects",
"description": "Generators that create a new project.",
"templates": ["myproject"]
}
},
"templates": {
"myproject": {
"path": "./templates/myproject",
"title": "My project",
"description": "A minimal example project."
}
}
}
versionis the version of the repository configuration format and must be"1.0".templatesmaps each template ID to the directory that contains it.groupsorganizes templates into categories. Each template must belong to exactly one group.
See Repository configuration (cookieplone-config.json) for every available key.
Step 3: Write the template questions#
Each template has a cookieplone.json file that defines the questions asked during generation.
Create my-template/templates/myproject/cookieplone.json:
{
"id": "myproject",
"schema": {
"title": "My project",
"description": "A minimal example project.",
"version": "2.0",
"properties": {
"project_title": {
"type": "string",
"title": "Project title",
"description": "The human-readable name of your project.",
"default": "My Project"
},
"project_slug": {
"type": "string",
"title": "Project slug",
"description": "Identifier used for the directory name.",
"default": "my-project"
},
"author_name": {
"type": "string",
"title": "Author name",
"default": "Jane Developer"
},
"author_email": {
"type": "string",
"title": "Author email",
"default": "jane@example.com"
}
}
},
"config": {
"versions": {
"python": "3.12"
}
}
}
ididentifies the template.schemaholds the form: itsversionmust be"2.0", and each entry inpropertiesbecomes a question, asked in the order it appears.configholds generator settings that the user doesn't see. Here,versionspins a Python version that the template files can use.
See Schema v2 reference (cookieplone.json) for every field type and setting.
Step 4: Write the template files#
Cookieplone renders the files inside {{ cookiecutter.project_slug }} with Jinja2.
Answers are available as {{ cookiecutter.<question> }}, and version pins as {{ versions.<key> }}.
Create my-template/templates/myproject/{{ cookiecutter.project_slug }}/README.md:
# {{ cookiecutter.project_title }}
Created by {{ cookiecutter.author_name }} <{{ cookiecutter.author_email }}>.
Create my-template/templates/myproject/{{ cookiecutter.project_slug }}/pyproject.toml:
[project]
name = "{{ cookiecutter.project_slug }}"
requires-python = ">={{ versions.python }}"
authors = [
{name = "{{ cookiecutter.author_name }}", email = "{{ cookiecutter.author_email }}"},
]
Step 5: Run your template#
Cookieplone reads the template repository from the COOKIEPLONE_REPOSITORY environment variable.
From the directory that contains my-template, run:
COOKIEPLONE_REPOSITORY=./my-template uvx cookieplone
Cookieplone shows a numbered list of categories, then a numbered list of the templates in the chosen category. Your repository has one of each, so press Enter twice to select the only category, then the only template. Answer the questions, or press Enter to accept each default. After the last question, Cookieplone shows your answers and asks you to confirm them.
To skip the prompts and accept every default, pass the template ID and --no-input:
COOKIEPLONE_REPOSITORY=./my-template uvx cookieplone myproject --no-input
Step 6: Inspect the output#
Cookieplone generates the project in the current directory:
my-project/
├── .cookieplone.json
├── pyproject.toml
└── README.md
With the default answers, my-project/README.md contains:
# My Project
Created by Jane Developer <jane@example.com>.
And my-project/pyproject.toml contains:
[project]
name = "my-project"
requires-python = ">=3.12"
authors = [
{name = "Jane Developer", email = "jane@example.com"},
]
.cookieplone.json records your answers, so you can generate the project again with the same values.
See Use an answers file.
What's next?#
Add validators to your template: validate user input on specific fields.
Add computed fields: derive field values automatically from other fields.
Use built-in filters: use Cookieplone's built-in Jinja2 filters.
Repository configuration (cookieplone-config.json): the complete
cookieplone-config.jsonreference.Schema v2 reference (cookieplone.json): the complete
cookieplone.jsonreference.