Conditional directory generated as empty when non-conditional directory of same name exists and is excluded
Describe the problem
I have a template project that has its own .github/workflows/workflow.yaml file while also conditionally providing one for the project generated from the template. Of course, I exclude the repository's own .github/ directory, as I only want the generated one in new projects.
This is the structure:
📁 your_template
├── 📄 copier.yml
├── 📁 .github
│ └── 📁 workflows
│ └── 📄 deploy-docs.yml
└── 📁 {% if ci == 'GitHub' %}.github{% endif %}
└── 📁 workflows
└── 📄 ci.yml
Generating a project from my template gives the following output. Note that the output states identical .github for the directory it just created.
🎤 Which Git platform will you use?
GitHub
Copying from template version None
create .github
identical .github
And creates this project structure:
📁 showproblem
└── 📁 .github
Note the absence of any contents in the .github directory, it is completely empty.
Template
To Reproduce
My directory structure is as follows:
📁 your_template
├── 📄 copier.yml
├── 📁 .github
│ └── 📁 workflows
│ └── 📄 deploy-docs.yml
└── 📁 {% if ci == 'GitHub' %}.github{% endif %}
└── 📁 workflows
└── 📄 ci.yml
The contents of the workflow YAML files doesn't matter and can be different, and their filenames can also be different. The copier.yml contains:
---
_exclude:
- .github/
- copier.yml
- .git
ci:
type: str
help: "Which Git platform will you use?"
choices:
- GitHub
- GitLab
- Other
default: GitHub
Note that I added .github/ to _exclude: to exclude the repository's own .github/ from ending up in the generated project. Invoking this command and answering using the defaults:
rm -rf ../showproblem/ && copier copy . ../showproblem/
Outputs:
Copying from template version None
create .github
identical .github
Logs
Expected behavior
I expected the contents of the conditional .github directory to appear in the generated project.
Screenshots/screencasts/logs
No response
Operating system
Linux
Operating system distribution and version
Ubuntu 24.04
Copier version
copier 9.10.3
Python version
Python 3.12.11
Installation method
uvx+pypi
Additional context
No response
The items in the _exclude list are destination paths, not source paths. Thus, you can't exclude your .github directory in the template repo this way.
Perhaps we should document this: my general recommendation is to move the template content in a subdirectory like template and set _subdirectory accordingly (e.g., _subdirectory: template). This solves any file or directory conflicts between the template repo and the template itself. It's also much cleaner; think about a README.md file in your template repo and a README.md.jinja file in your template, for example. Note that the copier.yml file still belongs in the repo root.
Thanks for pointing that out, I didn't know this. As you guessed, I interpreted _exclude as excluding source paths. Not in the least because one can specify the conditional name of a file to be excluded:
_exclude:
- "{% if _copier_operation == 'update' -%}src/*_example.py{% endif %}"
My use case was to have a repository for a documentation website template that also builds and demo's itself. So apart from a handful of files that are generated using Copier (for which demo-defaults exist next to the Jinja templates), the rest of the content is the same.
Indeed, copying all template files and content into a subdirectory template and setting _subdirectory: template fixed the issue for me.
However, I would like to note two things:
- The output of Copier is confusing, as it states that the
.githubdirectory is both created and identical. Note that the created.githubis empty, thus not identical to anything, and the conditional.githubdirectory and the unconditional.githubdirectories in the template repo were also different. - As far as I can tell, when you have a file (instead of a directory)
myfile.txtthat is in_excludeand a conditional file{% if true %}myfile.txt{% endif %}, it does work as expected. Just for directories it doesn't.