intermediate10 minutesevnx v0.2.2+

evnx template

Reference documentation for the evnx template command: generate configuration files from templates with variable substitution and filters.

evnx template — Command Reference

Generate configuration files from templates using .env variables, with support for flexible substitution syntax and powerful filter transformations.

When to use this command: Use evnx template when you need to generate environment-specific configuration files (YAML, JSON, TOML, etc.) from a template, injecting values from your .env file. Ideal for CI/CD pipelines, Docker builds, or multi-environment deployments.


Command signature

Bash
evnx template --input <TEMPLATE> --output <OUTPUT> [--env <ENV_FILE>] [--verbose]

Required arguments

ArgumentTypeDescription
--input, -ipathPath to the template file (e.g., config.yaml.template)
--output, -opathPath where the generated configuration file will be written

Optional options

FlagTypeDefaultDescription
--env, -epath.envPath to the environment file containing variable definitions
--gitignoreboolfalseAutomatically add the output file to .gitignore. No prompt. Mutually exclusive with --no-gitignore
--no-gitignoreboolfalseSkip all .gitignore checks and warnings. Mutually exclusive with --gitignore
--verbose, -vboolfalseEnable detailed progress output to stderr
--help, -hboolfalseDisplay help information

Gitignore protection

Generated config files contain real values injected from .env and must never be committed. After writing the output file, evnx template can automatically add it to .gitignore.

Behaviour by flag and context

Context--gitignore--no-gitignoreNo flag
Interactive terminalAuto-write, no promptSkip, no warningPrompt: Add config.toml to .gitignore? [Y/n]
CI / non-interactiveAuto-write, no promptSkip, no warningWarn only — does not prompt, does not write
Already in .gitignorePrint "already in .gitignore"Skip silentlySkip silently
No git repository foundWarn "not a git repository"Skip silentlySkip silently

The default (--no-gitignore absent) is safe in both contexts: interactive users get a one-keystroke prompt, CI pipelines get a visible warning without a blocking prompt.

What gets written

Entries are grouped under a labelled section so they are easy to identify:

gitignore
# ── evnx generated ─────────────────────────────────────────
# Files generated by `evnx template` may contain secrets — do not commit.
config.toml
k8s/configmap.yaml

Running evnx template again with the same --output detects the existing entry and prints "already in .gitignore" without adding a duplicate.

Git root detection

evnx walks ancestor directories from the current working directory looking for a .git entry (directory or file, so worktrees are supported). The .gitignore is located at that root. If no .git ancestor is found, the check is skipped with a warning when --gitignore is explicitly set.

The entry written to .gitignore is always relative to the git root, regardless of where you run the command from.

Output files outside the repository root cannot be protected. If --output points to a path outside the directory containing .git/, evnx warns and skips the .gitignore update — gitignore patterns cannot cover paths outside the repository.


Variable substitution syntax

The template engine supports three styles for referencing variables from your .env file:

SyntaxExampleDescription
\${VAR}\${DATABASE_URL}Shell-style (bash-like); most portable
{{VAR}}{{DATABASE_URL}}Template-style (Jinja-like); recommended for filters
$VAR$DATABASE_URLSimple prefix style; use with word boundaries

Pattern precedence: Filters are processed before simple substitution. This ensures is not accidentally replaced by a plain pattern.


Filter transformations

Apply transformations to variable values using the pipe (|) operator within double braces:

Text
{{VAR|filter}}

Supported filters

FilterDescriptionExample
|upperConvert to uppercaseInput: production → Output: PRODUCTION
|lowerConvert to lowercaseInput: PRODUCTION → Output: production
|titleCapitalize first letterInput: hello world → Output: Hello world
|boolConvert to boolean stringInput: yes or 1 → Output: true; others → false
|intParse as 64-bit integerInput: 8000 → Output: 8000; errors on invalid input
|jsonJSON-escape for safe embeddingSee example below
|default:valueFallback if variable is empty or undefinedInput: "" → Output: value; Input: actual → Output: actual

Filters are not chainable. Each filter is applied independently — and cannot be combined on the same variable reference. Use separate template variables or validate values in your .env file before templating.

JSON filter example

The |json filter properly escapes values for embedding in JSON structures:

Text
# If VALUE = hello "world"
# Template:  "desc": {{VALUE|json}}
# Output:    "desc": "hello \"world\""

Integer filter errors: The filter will fail if the value cannot be parsed as an integer. This prevents silent configuration errors. Use the filter on a separate variable reference as a fallback if the variable might be missing.


Basic usage examples

Generate a config file from template

Bash
# Simple substitution: replace ${VAR} patterns with .env values
evnx template --input config.yaml.template --output config.yaml

Use a custom environment file

Bash
# Generate production config using .env.production
evnx template -i deploy.yaml.tpl -o deploy.yaml --env .env.production

Enable verbose output for debugging

Bash
# See detailed progress and variable loading info
evnx template -i template.json -o output.json --verbose

Generate multiple configs in a script

Bash
#!/bin/bash
# Generate configs for multiple environments — auto-protect output files in CI
for env in dev staging prod; do
  evnx template \
    --input config.yaml.template \
    --output "config.${env}.yaml" \
    --env ".env.${env}" \
    --gitignore \
    --verbose
done

Opt out of gitignore management

Bash
# You manage .gitignore externally (e.g. via a generator or monorepo tooling)
evnx template \
  --input config.yaml.template \
  --output config.yaml \
  --env .env.production \
  --no-gitignore

When to use evnx template

ScenarioWhy use this command
CI/CD pipeline config generationGenerate environment-specific configs at build time without committing secrets
Docker/Kubernetes deploymentsCreate docker-compose.yml or k8s/ manifests from templates using runtime env vars
Multi-environment setupsMaintain one template file, generate config.dev.yaml, config.prod.yaml, etc.
Dynamic configuration injectionInject values from .env into config formats that don't natively support env vars (JSON, TOML)
Safe secret handlingUse |json filter to properly escape values; avoid manual string concatenation bugs

Pro tip: Store your template files with a .template or .tpl extension and commit them to version control. Generated output files contain real secrets — use --gitignore to let evnx add them to .gitignore automatically, or add them manually. Never commit generated output.


Exit codes

CodeMeaningUse case
0✅ Template processed successfullySuccess in scripts/CI
1🚨 Processing failedMissing files, parse errors, or invalid filter values (e.g. non-integer for |int)
2+⚠️ Runtime errorI/O failures, file permission issues, or unexpected OS-level errors

Troubleshooting

"Failed to parse environment file" error

✗ Failed to parse environment file: .env.production

Solution: Validate your .env file syntax or run evnx validate --env .env.production to check for issues. Ensure the file uses valid KEY=VALUE syntax:

  • One variable per line
  • No spaces around = (KEY=value, not KEY = value)
  • Quote values with spaces: KEY="value with spaces"
  • Use # for comments

"Variable 'X' referenced but not defined" warning

⚠️ Variable 'MISSING_VAR' referenced in template but not defined in .env
ℹ️ Tip: Define 1 undefined variable to avoid runtime issues

Solution: Add the missing variable to your .env file, or use the |default:value filter to provide a fallback:

Text
{{MISSING_VAR|default:fallback_value}}

Filter transformation errors

✗ Failed to process template substitutions
Caused by: Variable 'PORT' value 'not-a-number' is not a valid integer for |int filter

Solution: Ensure the variable value in your .env file is a valid integer. If the variable might be absent or malformed, provide a validated default in .env rather than relying on the template:

Bash
# In .env — validate and set a safe default before templating
PORT=${PORT:-8000}

Or use the |default filter on a separate variable:

Text
# Only use |default — filters cannot be chained
{{PORT|default:8000}}

"Not a git repository" warning with --gitignore

⚠️  Not a git repository — cannot update .gitignore

Cause: evnx walks ancestor directories looking for .git/ and reached the filesystem root without finding one. This happens when running outside a git repository or from a temp directory in CI.

Solutions:

  • Run the command from within your repository root
  • If you are not using git, add --no-gitignore to silence the warning
  • If using git worktrees, the .git file (not directory) is also detected correctly

Output file already in .gitignore

✓ 'config.toml' is already in .gitignore

This is correct behavior — idempotent. evnx checks for an exact line match before writing. Running evnx template multiple times with the same --output and --gitignore will never produce duplicate entries.

JSON filter output has unexpected quotes

Text
# Template: key={{VALUE|json}}
# Output:   key="hello \"world\""

This is correct behavior. The |json filter returns a properly quoted JSON string literal, including the surrounding double quotes. Use it when embedding values directly inside JSON structures:

JSON
{
  "description": {{DESCRIPTION|json}}
}

If DESCRIPTION = hello "world", the output will be:

JSON
{
  "description": "hello \"world\""
}

Environment variables

VariableValuesDefaultDescription
NO_COLOR1, truefalseDisable colored output for terminals or log files
CI1, truefalseAuto-enable non-interactive mode (affects verbose output formatting)

See also