You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Surfaced by the high-effort retrospective /review of PR #14 (post_create .env.local env injection).
Symptom (two faces of the same root cause)
Multi-line values truncate.parseEnvLocal (internal/cli/create.go, added by fix(create): inject .env.local into post_create child env (bypass bash source) #14) splits the whole file on every \n and treats each resulting fragment as an independent KEY=VALUE line. envwriter.Write (internal/envwriter/envwriter.go) has no line-count guard and serializes any value verbatim via fmt.Fprintf("%s=%s\n", k, v). If a .bough.yamlenv_local: template renders a genuinely multi-line value (e.g. via a Sprig nindent/toYaml call), the file itself becomes structurally ambiguous the moment it's written — parseEnvLocal then reads only the first physical line of that value, silently drops continuation lines lacking =, or worse, promotes a continuation line that coincidentally contains = into a bogus unrelated entry.
Quoted values can't be safely unquoted. A post_create script that hand-appends to .env.local using ordinary dotenv authoring convention (KEY="value") gets the literal quote characters passed into the child process's env, because parseEnvLocal intentionally does no shell-style unquoting (see its doc comment, and TestParseEnvLocal_ValuesWithShellMetachars's WITH_QUOTES case, which asserts quotes survive unchanged — this is PR fix(create): inject .env.local into post_create child env (bypass bash source) #14's own tested, deliberate contract, not an oversight).
Why this needs a coordinated fix, not a parseEnvLocal-only patch
Both symptoms trace back to the same design gap: .env.local's wire format (KEY=value\n, no escaping, no quoting) cannot round-trip a value containing a literal newline, and cannot distinguish "the value is "abc" with literal quote characters" from "the value is abc and the quotes are just dotenv-convention decoration." Fixing the read side (parseEnvLocal) alone would require guessing at a quoting/escaping convention the write side (envwriter.Write) never established — and stripping quotes unconditionally risks silently corrupting a value that genuinely needs a literal leading/trailing quote character (a different, arguably worse bug than the one it fixes).
Options (needs a product decision, not a unilateral pick)
Adopt a real quoting/escaping format for .env.local on both envwriter.Write and parseEnvLocal (e.g. Go-string-literal-style escaping, or reuse an existing dotenv-parsing library on read plus matching writer discipline) — most correct, touches both the render and post_create paths.
Leave hand-appended quoting as "the operator's problem" (document it) and only guard against multi-line values by having envwriter.Write reject/error on a value containing \n rather than silently emitting a corrupt file — cheaper, narrower, still leaves quote-stripping unaddressed.
Status quo (do nothing) — both are edge cases (no current bough-shipped template produces multi-line values or relies on quoted post_create appends), but silent data corruption rather than a loud error either way if someone does hit it.
Surfaced by the high-effort retrospective
/reviewof PR #14 (post_create .env.local env injection).Symptom (two faces of the same root cause)
Multi-line values truncate.
parseEnvLocal(internal/cli/create.go, added by fix(create): inject .env.local into post_create child env (bypass bash source) #14) splits the whole file on every\nand treats each resulting fragment as an independentKEY=VALUEline.envwriter.Write(internal/envwriter/envwriter.go) has no line-count guard and serializes any value verbatim viafmt.Fprintf("%s=%s\n", k, v). If a.bough.yamlenv_local:template renders a genuinely multi-line value (e.g. via a Sprignindent/toYamlcall), the file itself becomes structurally ambiguous the moment it's written —parseEnvLocalthen reads only the first physical line of that value, silently drops continuation lines lacking=, or worse, promotes a continuation line that coincidentally contains=into a bogus unrelated entry.Quoted values can't be safely unquoted. A post_create script that hand-appends to
.env.localusing ordinary dotenv authoring convention (KEY="value") gets the literal quote characters passed into the child process's env, becauseparseEnvLocalintentionally does no shell-style unquoting (see its doc comment, andTestParseEnvLocal_ValuesWithShellMetachars'sWITH_QUOTEScase, which asserts quotes survive unchanged — this is PR fix(create): inject .env.local into post_create child env (bypass bash source) #14's own tested, deliberate contract, not an oversight).Why this needs a coordinated fix, not a parseEnvLocal-only patch
Both symptoms trace back to the same design gap:
.env.local's wire format (KEY=value\n, no escaping, no quoting) cannot round-trip a value containing a literal newline, and cannot distinguish "the value is"abc"with literal quote characters" from "the value isabcand the quotes are just dotenv-convention decoration." Fixing the read side (parseEnvLocal) alone would require guessing at a quoting/escaping convention the write side (envwriter.Write) never established — and stripping quotes unconditionally risks silently corrupting a value that genuinely needs a literal leading/trailing quote character (a different, arguably worse bug than the one it fixes).Options (needs a product decision, not a unilateral pick)
.env.localon bothenvwriter.WriteandparseEnvLocal(e.g. Go-string-literal-style escaping, or reuse an existing dotenv-parsing library on read plus matching writer discipline) — most correct, touches both the render and post_create paths.envwriter.Writereject/error on a value containing\nrather than silently emitting a corrupt file — cheaper, narrower, still leaves quote-stripping unaddressed.