Summary
The action renders the stack/services/volumes YAML input as a Go text/template before parsing it as YAML (cmd/action/main.go, loadYamlOptional(): template.Parse at line 201, renderConfigTemplate at line 207, yaml.Unmarshal only afterwards at line 214). Since text/template has no notion of YAML, # comments are treated like any other text — template expressions inside comments are executed.
Failing scenarios (empirically confirmed)
-
Commented-out line referencing an unset variable — the most common case, e.g. commenting out a service option:
services:
app:
image: nginx
# db_url: {{ .Env.DATABASE_URL }}
With DATABASE_URL unset, the deploy hard-fails because of missingkey=error:
failure while rendering template: template: :1:13: executing "" at <.Env.DATABASE_URL>: map has no entry for key "DATABASE_URL"
Trailing comments (image: nginx # {{ .Env.UNSET }}) fail the same way.
-
Comment containing invalid/partial template syntax, e.g. # see {{ or # see {{ foo }}:
failure while creating template from input: template: :2: function "services" not defined
The error is actively misleading — the parser runs past the line end, consumes the next YAML line as template code, and reports a wrong line number and an invented function name.
-
Plain comments without {{ are harmless (confirmed), which is why this only bites once someone comments out a templated line — the most common YAML edit there is.
Both input paths are affected equally (stack_yaml/stack_file and the services/volumes variants).
Proposed fix
Strip YAML comments from the raw input before template.Parse. Comments are semantically void in YAML, so this is loss-free — but the stripper must be quote- and block-scalar-aware, because # is literal content:
- inside single- and double-quoted scalars (
"a # b", 'a # b', incl. multi-line double-quoted scalars),
- when not preceded by whitespace (
image: nginx#latest is not a comment),
- inside block scalars (
command: | followed by #!/bin/sh).
Replacing the comment text in place (keeping the newline) keeps template/YAML error line numbers accurate.
Rejected alternatives:
- Drop
missingkey=error — doesn't fix scenario 2 (parse-time failure) and removes a valuable safety net against typo'd variable names.
- Parse YAML first, strip comments via
yaml.Node — impossible; un-rendered templated input is not valid YAML (yaml: invalid map key).
- Naive regex
# strip — corrupts nginx#latest, "a # b", and shebangs in block scalars.
- Change template delimiters — breaking change, and the new delimiters would still execute inside comments.
A note in the README ("comments are stripped before templating, {{ }} inside comments is inert") should accompany the fix.
Summary
The action renders the stack/services/volumes YAML input as a Go
text/templatebefore parsing it as YAML (cmd/action/main.go,loadYamlOptional():template.Parseat line 201,renderConfigTemplateat line 207,yaml.Unmarshalonly afterwards at line 214). Sincetext/templatehas no notion of YAML,#comments are treated like any other text — template expressions inside comments are executed.Failing scenarios (empirically confirmed)
Commented-out line referencing an unset variable — the most common case, e.g. commenting out a service option:
With
DATABASE_URLunset, the deploy hard-fails because ofmissingkey=error:Trailing comments (
image: nginx # {{ .Env.UNSET }}) fail the same way.Comment containing invalid/partial template syntax, e.g.
# see {{or# see {{ foo }}:The error is actively misleading — the parser runs past the line end, consumes the next YAML line as template code, and reports a wrong line number and an invented function name.
Plain comments without
{{are harmless (confirmed), which is why this only bites once someone comments out a templated line — the most common YAML edit there is.Both input paths are affected equally (
stack_yaml/stack_fileand the services/volumes variants).Proposed fix
Strip YAML comments from the raw input before
template.Parse. Comments are semantically void in YAML, so this is loss-free — but the stripper must be quote- and block-scalar-aware, because#is literal content:"a # b",'a # b', incl. multi-line double-quoted scalars),image: nginx#latestis not a comment),command: |followed by#!/bin/sh).Replacing the comment text in place (keeping the newline) keeps template/YAML error line numbers accurate.
Rejected alternatives:
missingkey=error— doesn't fix scenario 2 (parse-time failure) and removes a valuable safety net against typo'd variable names.yaml.Node— impossible; un-rendered templated input is not valid YAML (yaml: invalid map key).#strip — corruptsnginx#latest,"a # b", and shebangs in block scalars.A note in the README ("comments are stripped before templating,
{{ }}inside comments is inert") should accompany the fix.