fix: emit_app_env writes single-quote values compose cannot parse #60

Closed
opened 2026-09-08 06:15:14 +00:00 by ahmad · 1 comment
Owner

Context

deploy-vps's emit_app_env escapes an embedded ' the POSIX way ('\''), which the sourcing shell reads back byte-exactly — but docker compose's dotenv parser cannot read at all: a line KEY='ab'\''cd' makes docker compose config exit 1 with unexpected character "\" in variable name and abandon the whole file, so every later line is lost and the deploy fails at compose pointing at the wrong place (measured on suhba/suhba-docs#11's review). Every other awkward byte round-trips through both parsers. Separately, now that every consumer passes raw values (census closed 2026-09-08), the consumer-quoting pass-through branch has no legitimate use left, and a raw value that happens to begin and end with a matching quote would be written unquoted.

Scope

  • In: emit_app_env refuses a value containing ', failing the deploy naming only the key (like the malformed-line guard); decide-and-do on the pass-through branch — either delete it or leave it with the reason recorded in the action.
  • Out: supporting ' in values (would require compose-side quoting no shell can also read); consumer changes.

Acceptance criteria

  • Given an app-env value containing ', when the env file is written, then the step fails naming the key and writes no partial file compose would reject.
  • Given every current consumer's raw values, when the deploy runs, then behaviour is unchanged.
  • Given the pass-through decision, when the PR lands, then the branch is deleted or its retention reason is in the code.
### Context deploy-vps's `emit_app_env` escapes an embedded `'` the POSIX way (`'\''`), which the sourcing shell reads back byte-exactly — but docker compose's dotenv parser cannot read at all: a line `KEY='ab'\''cd'` makes `docker compose config` exit 1 with `unexpected character "\" in variable name` and abandon the whole file, so every later line is lost and the deploy fails at compose pointing at the wrong place (measured on suhba/suhba-docs#11's review). Every other awkward byte round-trips through both parsers. Separately, now that every consumer passes raw values (census closed 2026-09-08), the consumer-quoting pass-through branch has no legitimate use left, and a raw value that happens to begin and end with a matching quote would be written unquoted. ### Scope - In: `emit_app_env` refuses a value containing `'`, failing the deploy naming only the key (like the malformed-line guard); decide-and-do on the pass-through branch — either delete it or leave it with the reason recorded in the action. - Out: supporting `'` in values (would require compose-side quoting no shell can also read); consumer changes. ### Acceptance criteria - [ ] Given an app-env value containing `'`, when the env file is written, then the step fails naming the key and writes no partial file compose would reject. - [ ] Given every current consumer's raw values, when the deploy runs, then behaviour is unchanged. - [ ] Given the pass-through decision, when the PR lands, then the branch is deleted or its retention reason is in the code.
ahmad self-assigned this 2026-09-08 06:16:00 +00:00
Author
Owner

Plan. One step in deploy-vps/action.yml plus its input description and the README paragraph. emit_app_env becomes two passes: validate every line first (valid key, and now: no ' anywhere in the value — the one byte the two parsers cannot agree on), then emit; a refusal names only the key and, being pre-emit, adds nothing from app-env to the file — and the failed step means nothing ships regardless. Pass-through decision: deleted. The census is closed, no consumer pre-quotes, the README has taught raw since #56, and the branch's raw-value-that-looks-quoted edge is a footgun with no remaining benefit; every value now goes through sq() uniformly. The local matrix updates with the semantics: pre-quoted values are no longer honoured (a single-quoted one is refused — it contains '; a double-quoted one round-trips WITH its quotes as literal bytes), and two new kill cases assert the refusal names the key and never leaks the value. Residual noted for the PR: the action's own inputs (grafana token et al.) share the compose-side ' exposure through sq(); out of this issue's scope, will file separately if the review agrees it is real.

Plan. One step in deploy-vps/action.yml plus its input description and the README paragraph. `emit_app_env` becomes two passes: validate every line first (valid key, and now: no `'` anywhere in the value — the one byte the two parsers cannot agree on), then emit; a refusal names only the key and, being pre-emit, adds nothing from app-env to the file — and the failed step means nothing ships regardless. **Pass-through decision: deleted.** The census is closed, no consumer pre-quotes, the README has taught raw since #56, and the branch's raw-value-that-looks-quoted edge is a footgun with no remaining benefit; every value now goes through `sq()` uniformly. The local matrix updates with the semantics: pre-quoted values are no longer honoured (a single-quoted one is refused — it contains `'`; a double-quoted one round-trips WITH its quotes as literal bytes), and two new kill cases assert the refusal names the key and never leaks the value. Residual noted for the PR: the action's own inputs (grafana token et al.) share the compose-side `'` exposure through `sq()`; out of this issue's scope, will file separately if the review agrees it is real.
ahmad 2026-09-08 08:02:57 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
amtronics/platform-actions#60
No description provided.