Maintaining Projects with Copier Templates
Table of Contents
Copier enables you to continuously update software projects from sets of templates, so that you can maintain consistent configurations across many projects.
How It Works #
A Copier template is a Git repository that contains a configuration file and template files. Each Git repository should contain one Copier template, because Copier uses Git tags for version information. You can use Jinja for templating files and directories, with extensions.
The first time that you run Copier, you must specify the address of the repository for a template, like this:
copier copy git+https://github.com/my-username/copier-mynamespace-mytemplate my-project
It then prompts you for answers to the questions that are defined in the template, and then creates the files and
directories that are included in the template in the my-project/ directory. It also creates an answers file in the
project directory to store the address of the template, the version of the template that you used, and your responses to
the questions.
You can also define options on the command-line or in a data file.
You can update a project at any time. Specify the answers file that was created by the first run of Copier. Copier then uses Git to fetch either the latest version of the template, or the version of the template that you specify:
# Update the current project with the latest version of the template
copier update -a .copier-answers-my-template.yaml
# Update the current project with version v1.2.3 of the template
copier update -a .copier-answers-my-template.yaml -r v1.2.3
By default, Copier prompts you to answer the questions again. You can tell it to use the same answers. It then performs an update. The update can also include running migrations or defined tasks.
Add the -A option to use the same responses from the answers file without prompting you:
copier update -A -a .copier-answers-my-template.yaml
You can safely use multiple Copier templates on the same project. Templates can uses the answers that users have provided to other templates, as explained in a later section.
These features mean that you can maintain large numbers of projects that are composed from sets of Copier templates. The developers that work on these projects can update them as needed, or you can use automation to run template updates and commit the changes. If you use Renovate it updates projects with the latest versions of Copier templates, in the same way that it updates dependencies.
Use SSH authentication for Git operations with Copier. You can pass credentials, if you do not use an SSH agent.
Versioning Your Copier Templates #
Copier treats each Git tag on a template repository as a version. Use Semantic Versioning for template repositories.
By default, Copier will copy from the last release found in template Git tags, sorted as a Python version specifier, regardless of whether the template is from a URL or a local clone of a Git repository.
Version tags may have a prefix of v.
Tools like Python Semantic Release create Git tags that
have a v prefix, e.g. v1.2.3.
By default, Copier excludes pre-releases.
Running Copier #
Copier requires Git for all operations.
To run Copier on a development system, use a tool like pipx or
uv. If you are using uv, call Copier with uvx.
This command uses pipx to run copier copy:
pipx run copier==9.17.0 copy git+https://github.com/my-username/copier-mynamespace-mytemplate my-project
Both
pipx runanduvxdownload Copier to a cache, so that you do not need to manage a Python virtual environment.
If you use extra Jinja filters in a Copier template, the clients must have the Python packages for these. You will need
to inject additional these packages into the virtual environment that pipx or uvx maintains for Copier.
By default, Copier disables features that allow arbitrary code execution, including migrations and tasks. You must use the —trust flag to enable these to run.
Creating a Copier Template #
This process does not require the Copier tool.
- First, create a Git repository to hold the template. By convention, the name of this template repository should start
with
copier-. Add a namespace and a name for the specific template to the full name of the repository. For example:copier-mynamespace-mytemplate. - Create a
copier.yamlconfiguration file in the root of the template repository. To avoid conflicts with other Copier templates that are in use, the_answers_filemust specify a unique name. See below for an example. - Create a directory called
template/in the repository to hold the files and directories that make up the template. - Create a directory called
template/.copier/in the repository to hold the template answers file. - Create a template answers file called
[[_copier_conf.answers_file]].jinjain the directorytemplate/.copier/. See below for an example. - Set the template delimiters. By default, Copier uses curly braces to denote templated values, but these may cause issues with some types of files, such as Jinja templates in projects. Specify square brackets as delimiters.
- Optional: Set up a project release tool for the template repository, such as Python Semantic Release.
- Optional: Add metadata to the project for the template repository. For example, if it is hosted on GitHub, add the GitHub Topic copier-template.
Example Configuration File for the Template #
This is an example of a copier.yaml file:
---
# Configuration for Copier Template
#
# See:
#
# https://copier.readthedocs.io/en/stable/
# This template uses the configuration format introduced in Copier version 9.
_min_copier_version: "9"
# Use this subdirectory of the template repository as the root directory of the template.
_subdirectory: template
# Name of the answers file.
# This file name must be unique to avoid conflicts with other Copier templates.
# Start the name of the file with .copier-answers so that Renovate can detect it.
# By default, Copier uses the root directory of the project for answers files,
# but this example uses a .copier/ directory.
_answers_file: .copier/.copier-answers-mynamespace-mytemplate.yaml
# Use alternate template delimiters
# This avoids conflicts with templating that is defined in the managed files.
_envops:
block_end_string: "%]"
block_start_string: "[%"
comment_end_string: "#]"
comment_start_string: "[#"
variable_end_string: "]]"
variable_start_string: "[["
# Create these files from the template, if they are not already present.
# If one of these files exists, it will not be updated from the template.
_skip_if_exists:
- README.md
Set defaults for answers as much as possible. They minimise user effort and increase consistency. You can also use the well-known variables to get information.
Example Template Answers File #
Always create a template answers file. It must render the answers that are provided to YAML:
---
# Maintained by Copier: NEVER EDIT THIS FILE
#
# See:
#
# https://copier.readthedocs.io/en/stable/updating/#never-change-the-answers-file-manually
[[ _copier_answers|to_nice_yaml -]]
Copier must template the name of the answers file from _copier_conf.answers_file. This means that if you use square
brackets as delimiters, it will be called [[_copier_conf.answers_file]].jinja.
Automating Version Tags #
Automate your release process, to ensure that every version of a Copier template has a version tag. Popular tools for release automation include:
I provide an article on using Python Semantic Release with GitLab.
Referencing Other Copier Templates #
A Copier template can reference other templates that have already been applied to the project:
# Template loads answers from the previous template into the _external_data object
_external_data:
# A dynamic path. Make sure you answer that question
# before the first access to the data (with `_external_data.parent_tpl`)
parent_tpl: "[[ parent_tpl_answers_file ]]"
# Ask the user where the answers file for the previous template is located
parent_tpl_answers_file:
help: Where did you store answers for the parent template?
default: .copier-answers-example-parent.yaml
# Answers can then reference answers in the other template through _external_data
project_name:
type: str
help: Your project name
default: "[[ _external_data.parent_tpl.project_name ]]"
Updating Specific Files #
Use –exclude to update a single file in a project from a template:
copier copy --exclude '*' --exclude '!file-i-want' ./template ./destination
For example:
copier copy -a .copier/.copier-answers-my-template.yaml --exclude '*' --exclude '!.pre-commit-config.yaml' git+https://github.com/my-account/copier-my-template .
Resources #
Here are some useful articles and tutorials about Copier.
Copier #
Example Templates #
- Example Copier template for Python projects, by Timothée Mazzucotelli
- Example Copier baseline template, an example of a general-purpose template