Skip to content

Copier compatibility

deckctl can apply an existing Copier template without converting it. Point it at a template whose root contains a copier.yml and it parses the questions, translates them into its own model, and renders.

Terminal window
deckctl template apply my-copier-template ./out --source work --var project_name=billing

Under the default --sidecar-format auto:

  1. An explicit --sidecar-format deckctl or --sidecar-format copier wins.
  2. A --sidecar <filename> override implies the native format.
  3. A deckctl.yaml at the template root → native.
  4. Otherwise a copier.yml at the template root → Copier.
  5. Otherwise native, with built-in defaults.

Native wins when both files are present. Force the other one explicitly:

Terminal window
deckctl template apply my-template ./out --source work --sidecar-format copier

Both template apply and template variables accept --sidecar-format.

Both the shorthand and the full mapping form parse, and document order is preserved — which matters because computed defaults reference earlier answers.

copier.yml
project_name: myproject # shorthand: the value is the default
http_port: # full form
type: int
help: "Port to listen on"
default: 8080
runtime:
type: str
help: "Runtime to target"
choices: ["go", "python", "node"]
default: "go"
Copier typedeckctl type
str, or omittedstr
intint
boolbool
anything elsestr, with a warning

A question with choices becomes a choice variable regardless of its declared type — matching Copier’s own behaviour. List-form choices are supported; dict-form choices are reduced to their values with a warning.

A default containing the variable-start delimiter is rendered as a Jinja expression against the answers resolved so far:

project_name: myproject
package_name:
type: str
default: "{{ project_name|lower|replace('-', '_') }}"
module_path:
type: str
default: "gitlab.com/acme/{{ project_name }}"

Rendered values are coerced back to the question’s declared type, so a computed int default reaches the template as an integer and a computed bool as a boolean.

use_database:
type: bool
default: false
database_url:
type: str
when: "{{ use_database }}"

A question whose when evaluates falsy is never prompted for. If it still has a default, that default is recorded so later expressions can reference it. A when that fails to evaluate produces a warning and the question is treated as active.

Truthiness follows Copier/Jinja conventions: "", false, 0, none and no (case-insensitive) are false; everything else is true.

Copier keyMapped to
_subdirectorySidecar subdirectory — the template tree is rendered from there
_templates_suffixSidecar template_suffixes (replaces the whole list)
_envopsSidecar delimiters — see Custom delimiters

Any other _-prefixed key produces a warning and is ignored — including _exclude, _skip_if_exists, _tasks, _migrations and _answers_file.

These produce a warning on stderr and are a no-op. Nothing blocks rendering.

Copier featureBehaviour
validatorIgnored — no validation is performed
secretIgnored — input is not masked
multiselectIgnored
placeholderIgnored
Dict-form choicesValues used, keys dropped
_exclude, _tasks, _migrations, …Ignored
Answers file / template updatesNot implemented — deckctl has no copier update equivalent

Slightly different from the native path, because Copier defaults can compute:

  1. --var key=value — always wins, even over a falsy when
  2. The default — evaluated as a Jinja expression if it looks like one
  3. Interactive prompt — only if the question is active (when truthy or absent)

A question with a default is therefore never prompted for, even when you wanted to be asked. That is Copier’s behaviour too.

Terminal window
deckctl template variables my-copier-template --source work

This shows the translated variables and prints every translation warning to stderr — the fastest way to see what deckctl will and won’t honour before you apply anything.

copier.yml
_subdirectory: template
_envops:
variable_start_string: "[["
variable_end_string: "]]"
project_name:
type: str
help: "Human-readable project name"
default: "My Project"
package_name:
type: str
default: "[[ project_name|lower|replace(' ', '_') ]]"
use_ci:
type: bool
default: true
ci_image:
type: str
default: "golang:1.26"
when: "[[ use_ci ]]"
Terminal window
deckctl template apply acme-service ./out --source work --var project_name="Billing Service"
  • project_name"Billing Service" (from --var)
  • package_name"billing_service" (computed)
  • use_citrue (default)
  • ci_image"golang:1.26" (active, default applies)
  • Everything under template/ is rendered with [[ ]] delimiters