# Implementation Workflow (Copier + Jinja Template Repository) Follow this sequence when creating a new template-engine repository. ## Phase 1 — Define rendering contract 1. Define template scope (what downstream repository you generate). 2. Identify stable input schema sections (e.g. tenant/project/environments/resources). 3. Decide what is: - static files, - conditional files/folders, - repeatable per-instance blocks. Deliverable: first draft of `copier-answers.example.yaml`. ## Phase 2 — Bootstrap Copier skeleton Create: - `.copier/copier.yaml` - `.copier/questionnaire/*.yml` - `{{ _copier_conf.answers_file }}.jinja` Use baseline: - `_template_suffix: ".jinja"` - modular `!include .copier/questionnaire/*.yml` - explicit `_exclude` list - `_answers_file: copier-answers.yml` ## Phase 3 — Create operational wrappers Implement wrapper chain: 1. root `Taskfile.yml` includes `.scripts/copier/Taskfile.yml` 2. `.scripts/copier/Taskfile.yml` exposes `bootstrap-one` 3. `.scripts/copier/api/bootstrap-one.sh` remains thin 4. **confirm ENV var prefix with user** (or propose one) before implementing lib internals 5. `.scripts/copier/lib/` contains logic and answers merge helpers Minimum behavior in lib: - parse CLI args with both styles: `--arg value` and `--arg=value` (for `--copier-target-dir`, `--copier-force-copy`, `--copier-params-file`), - prepare merged answers file, - run `copier copy --trust --data-file ...` from a sibling temporary copy of the template repo, not from the live template repo folder, - post-render sanitization/renames if needed. Mandatory temp-copy bootstrap pattern from `iac-tmpl-aws-projects`: - In `api/bootstrap-one.sh`: 1. derive the live template folder name from `basename "$PWD"`, 2. delete any old sibling `../tmp-${TEMPLATE_FOLDER}`, 3. copy the whole template repo into `../tmp-${TEMPLATE_FOLDER}`, 4. `cd` into the temp folder, 5. source `.scripts/loggers/lib/--index.sh` and `.scripts/copier/lib/--index.sh`, 6. execute `_copier_bootstrap_one`, 7. return to the original live template folder. - In `lib/-bootstrap-one.sh`, before `copier copy`: 1. copy `.copier/copier.yaml` to root `copier.yaml` if the repo uses `.copier/copier.yaml`, 2. remove `.git`, `.devbox`, and `.venv` from the temp copy, 3. run Copier from the temp folder into the real target. Do **not** create Git tags merely to silence Copier's local template version warning. The preferred local bootstrap hygiene is rendering from the sanitized temp copy. Use `--vcs-ref=HEAD` only when there is an explicit reason to render directly from a Git-backed live folder. ## Phase 4 — Add Jinja template layers 1. Add top-level templates (`iac/main.tf.jinja`, providers, backend, etc.). 2. Gate directories via folder-name expressions: - `{{ 'dev' if flat.dev_is_enabled }}` 3. Gate module calls inside files via `{% if ... %}`. Pattern: - folder condition controls existence, - inner condition controls file content clarity. **Mandatory wrapped render-path rule:** - For every optional module/resource, apply conditions at the **path level** (folder/file names), not only inside file contents. - Preferred pattern (from `iac-tmpl-aws-projects`): - `iac/{{ 'dev' if flat.dev_is_enabled }}/{{ 'compute' if flat.dev_compute_is_enabled }}/main.tf.jinja` - Apply this systematically to all optional Jinja outputs so disabled features do not produce placeholder files/directories. **Mandatory safe-variable rule:** - Do not embed repeated normalization/sanitization logic in template paths or file bodies, e.g. avoid: - `{{ project.name | lower | replace(' ', '-') | replace('_', '-') | replace('.', '-') }}` - If a safe name is needed, calculate it once in the context hook and expose a named variable, e.g.: - `flat.project_safe` - `flat.environment_safe` - `flat.component_safe_names` - Use the named variable in templates: - `applications/{{ flat.project_safe }}/main.yaml.jinja` - If a one-off transformation is genuinely needed, prefer a small deterministic resolver over duplicating filter chains. ## Phase 5 — Add custom extensions (if complexity requires) When logic gets repetitive, move it into extensions. ### Resolver extension examples - normalize names: `resolve_safe_tf_module_name(name)` - derive tags / regions / zones ### Context hook extension examples - calculate `flat.*` shortcuts, - calculate safe/normalized names such as `flat.project_safe` and `flat.environment_safe`, - expand `[INDEX]` directories into per-instance folders. Register in `.copier/copier.yaml` under `_jinja_extensions`. ## Phase 6 — Implement answers merge strategy Use this exact order to avoid destructive updates: 1. `copier-answers-new.yml` create if missing, 2. copy `/copier-answers.yml` if present, 3. merge params file into working answers file (`yq eval-all ...`). Then run copier using merged file. ## Phase 7 — Handle template/self-repo conflicts If template repo contains files that should not be copied 1:1 (e.g. self CI): 1. render as temp name (`.tmp-gitlab-ci.yml`), 2. rename in target during bootstrap. ## Phase 8 — Add docs and examples Add/update: - `README.md` with local setup and bootstrap command, - `copier-answers.example.yaml` with realistic schema examples, - optional `bootstrap-one-test` task for local maintainer testing. ## Phase 9 — Validate end-to-end Run two critical scenarios: 1. fresh render into empty target, 2. re-render into existing target with existing `copier-answers.yml`. Both must succeed without manual edits. ## Quick-start from this skill example If you want to accelerate implementation, copy `example/` and then: 1. expand questionnaire files, 2. expand `flat.*` derivation logic in context hook, 3. add domain-specific resolver extensions, 4. extend `iac/**/*.jinja` resource tree, 5. keep bootstrap/answers flow unchanged unless you have a strong reason.