Skip to content

Frequently Asked Questions

Can Copier be applied over a preexisting project?

Yes, of course. Copier understands this use case out of the box. That's actually what powers features such as updating or the ability of applying multiple templates to the same subproject.

Example

copier copy https://github.com/me/my-template.git ./my-preexisting-git-project

How to use Copier from Docker or Podman?

Copier doesn't provide an image by default. However, it does provide a nix package, so you can use Nix to run Copier reproducibly from within a container:

# Change for docker if needed
engine=podman

# You can pin the version; example: github:copier-org/copier/v8.0.0
copier=github:copier-org/copier

$engine container run --rm -it docker.io/nixos/nix \
    nix --extra-experimental-features 'nix-command flakes' --accept-flake-config \
    run $copier -- --help

You can even generate a reproducible minimal docker image with just Copier inside, with:

nix bundle --bundler github:NixOS/bundlers#toDockerImage \
    github:copier-org/copier#packages.x86_64-linux.default
docker load < python*copier*.tar.gz

How to create computed values?

Combine default and when: false.

Example

copier.yaml
copyright_year:
    type: int
    default: 2024

next_year:
    type: int
    default: "{{ copyright_year + 1 }}" # This computes the value
    when: false # This makes sure it isn't asked nor stored

See advanced prompt formatting docs. If you need more power, see below.

How can I alter the context before rendering the project?

Use the ContextHook extension. It lets you modify the context used to render templates, so that you can add, change or remove variables. Since it is a Python extension, you have the full power of Python at your fingertips, at the cost of having to mark the template as unsafe.

In order for Copier to be able to load and use the extension when generating a project, it must be installed alongside Copier itself. More details in the jinja_extensions docs.

You can then configure your Jinja extensions in Copier's configuration file:

copier.yaml
_jinja_extensions:
    - copier_templates_extensions.TemplateExtensionLoader
    - extensions/context.py:ContextUpdater

Following this example, you are supposed to provide a context.py file in the extensions folder at the root of your template to modify the context. If for example your copier.yaml contains a multiple-choice variable like this:

copier.yaml
flavor:
    type: str
    choices:
        - docker
        - instances
        - kubernetes
        - none

The context.py file contains your context hook which could look like:

extensions/context.py
from copier_templates_extensions import ContextHook


class ContextUpdater(ContextHook):
    def hook(self, context):
        flavor = context["flavor"]  # user's answer to the "flavor" question
        return {
            "isDocker": flavor == "docker"
            "isK8s": flavor == "kubernetes"
            "isInstances": flavor == "instances"
            "isLite": flavor == "none"
            "isNotDocker": flavor != "docker"
            "isNotK8s": flavor != "kubernetes"
            "isNotInstances": flavor != "instances"
            "isNotLite": flavor != "none"
            "hasContainers": flavor in {"docker", "kubernetes"}
        }

Before rendering each templated file/folder, the context will be updated with this new context object that you return from the hook. If you wish to update the context in-place rather than update it, set the update class attribute to false:

extensions/context.py
from copier_templates_extensions import ContextHook


class ContextUpdater(ContextHook):
    update = False

    def hook(self, context):
        flavor = context["flavor"]  # user's answer to the "flavor" question

        context["isDocker"] = flavor == "docker"
        context["isK8s"] = flavor == "kubernetes"
        context["isInstances"] = flavor == "instances"
        context["isLite"] = flavor == "none"

        context["isNotDocker"] = flavor != "docker"
        context["isNotK8s"] = flavor != "kubernetes"
        context["isNotInstances"] = flavor != "instances"
        context["isNotLite"] = flavor != "none"

        context["hasContainers"] = context["isDocker"] or context["isK8s"]

        # you can now actually remove items from the context
        del context["flavor"]

Now you can use these added variables in your Jinja templates, and in files and folders names!

Why Copier consumes a lot of resources?

If the repository containing the template is a shallow clone, the git process called by Copier might consume unusually high resources. To avoid that, use a fully-cloned repository.

While developing, why the template doesn't include dirty changes?

Copier follows a specific algorithm to choose what reference to use from the template. It also includes dirty changes in the HEAD ref while developing locally.

However, did you make sure you are selecting the HEAD ref for copying?

Imagine this is the status of your dirty template in ./src:

$ git -C ./src status --porcelain=v1
?? new-file.txt

$ git -C ./src tag
v1.0.0
v2.0.0

Now, if you copy that template into a folder like this:

$ copier copy ./src ./dst

... you'll notice there's no new-file.txt. Why?

Well, Copier indeed included that into the HEAD ref. However, it still selected v2.0.0 as the ref to copy, because that's what Copier does.

However, if you do this:

$ copier copy -r HEAD ./src ./dst

... then you'll notice new-file.txt does exist. You passed a specific ref to copy, so Copier skips its autodetection and just goes for the HEAD you already chose.