copier icon indicating copy to clipboard operation
copier copied to clipboard

Conditional directory generated as empty when non-conditional directory of same name exists and is excluded

Open Virtlink opened this issue 3 months ago • 2 comments

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

copier-problem.zip

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

Virtlink avatar Nov 18 '25 12:11 Virtlink

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.

sisp avatar Nov 18 '25 12:11 sisp

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:

  1. The output of Copier is confusing, as it states that the .github directory is both created and identical. Note that the created .github is empty, thus not identical to anything, and the conditional .github directory and the unconditional .github directories in the template repo were also different.
  2. As far as I can tell, when you have a file (instead of a directory) myfile.txt that is in _exclude and a conditional file {% if true %}myfile.txt{% endif %}, it does work as expected. Just for directories it doesn't.

Virtlink avatar Nov 18 '25 13:11 Virtlink