Skip to content

feat: add -prefix flag for variable filtering#73

Open
nicholasklem wants to merge 3 commits into
a8m:masterfrom
nicholasklem:feat/prefix-filter
Open

feat: add -prefix flag for variable filtering#73
nicholasklem wants to merge 3 commits into
a8m:masterfrom
nicholasklem:feat/prefix-filter

Conversation

@nicholasklem

@nicholasklem nicholasklem commented Jan 16, 2026

Copy link
Copy Markdown

Summary

Add -prefix flag to substitute only variables matching a given prefix, leaving other variable references as literal text.

This is a simpler alternative to #72 (GNU SHELL-FORMAT compatibility), which I've converted to draft. The -prefix approach is easier to use in practice.

Use Case

When templating files that contain variable references you want to preserve, you can use a prefix convention to distinguish which variables should be substituted:

export MY_VERSION=1.0.0
envsubst -prefix MY_ < deployment.yaml

Input:

image: myapp:$MY_VERSION
command: ["sh", "-c", "echo $HOME"]

Output:

image: myapp:1.0.0
command: ["sh", "-c", "echo $HOME"]

Behavior

  • Variables matching the prefix are substituted normally
  • Variables not matching the prefix are preserved as literal text
  • All substitution operators work (:-, -, :=, :+)
  • Defaults with different prefixes work: ${MY_X:-$OTHER} → substitutes MY_X, preserves $OTHER if used as default

Test Plan

  • Comprehensive unit tests added (223 new lines in parse_test.go)
  • All existing tests pass
  • Tested manually

🤖 Generated with Claude Code

Add --prefix flag to substitute only variables matching a given prefix.
Variables not matching the prefix are left as literal text in the output.

This enables use cases like:
  envsubst --prefix ARGOCD_ENV_ < template.yaml

Which will only substitute $ARGOCD_ENV_* variables, leaving other
variable references (like $OTHER_VAR) intact.

Key behavior:
- Variables matching the prefix are processed normally
- Unset variables matching prefix use defaults (${VAR:-default})
- Variables not matching prefix are preserved as literal text
- Level 1 variable nesting in defaults works (${VAR:-$VAR2})

Known limitation:
- Deeply nested defaults like ${VAR:-${VAR2:-default}} have issues
  with extra closing braces (pre-existing parser limitation)

Comprehensive tests added for:
- VarFilter prefix matching
- All substitution operators (:-  -  :=  :+)
- Mixed allowed/disallowed variables
- Variable defaults with different prefixes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@nicholasklem nicholasklem changed the title feat: add --prefix flag for variable filtering feat: add -prefix flag for variable filtering Jan 16, 2026
Upstream a8m/envsubst treats $$ as a shell-style escape that collapses to
$ (since 2018, commit e89c09c "parse: add support for escaping
expressions"). That silently mangles inputs containing literal $$ — most
notably the KEDA Helm chart, which embeds Kubernetes' own env-var
expansion docs in CRD schema descriptions: "Double $$ are reduced to a
single $". Result: ArgoCD shows perpetual $$ → $ drift on KEDA resources,
since rendered manifests have $ but live cluster has $$ (or vice versa,
depending on which side of the apply you read).

This fork is scoped to ArgoCD CMP rendering with --prefix filtering, not
generic shell templating. There is no use case here for shell-style $$
escapes; the only effect is unintended mangling of literal text. Drop
the l.ignore() that throws away the first $ in the lexer's $$ branch, so
$$ emits as literal text instead of collapsing.

Also reframes the README: this fork is now documented as an envsubst
variant tuned for ArgoCD CMP rendering, with two deliberate deviations
from upstream (the existing --prefix flag and this $$ change). It is not
a generic shell-templating tool; users who want shell-style escapes
should use upstream a8m/envsubst.

Tests: 4 existing escape tests in parse_test.go + 2 in lex_test.go
updated to assert new semantics. Added 4 KEDA-flavored regression cases
covering CRD descriptions, kubelet $$(VAR_NAME) escapes, and $$ mixed
with prefix-allowed substitutions.

Verified end-to-end against actual kustomize build --enable-helm output
of keda/environments/production: 18 $$ occurrences in rendered chart,
18 preserved after envsubst -prefix ARGOCD_ENV_, IAM annotation
substitution still works correctly.
…on tests

Address self-review feedback on PR #1:

- README "About this fork": clarify which shell operators are preserved
  vs dropped. List all the kept operators explicitly so readers don't
  have to guess. Only the $$ escape is removed.

- README new section "Combining literal $$ with a substitution": document
  the four forms users are likely to try, with verified behavior. The
  only working forms are $$$VAR (triple-dollar) and $$ ${VAR} (space-
  separated). $$VAR and $${VAR} both fail to substitute because the $$
  consumes both dollars, leaving the variable name as plain text.

  Initial draft of this section claimed $${VAR} would substitute — that
  was wrong, verified empirically before commit.

- Tests: pin the four documented forms as regression cases in
  parseTests, so README and behavior can't drift apart silently. Also
  add two cases for $$ inside substitution operands (${VAR:-pre$$post})
  — that path is unchanged by this fork but worth pinning so a future
  refactor of lexSubstitution doesn't regress it.

All 6 new test cases pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant