Summary
The per-slug preamble at .ecluse/preambles/<slug>.sh (introduced in 0.3.0) preserves the quote characters from the source .env file when re-emitting them as shell export statements. The result is that the runtime env var contains literal quote characters as part of its value, breaking any consumer that expects the unquoted value.
This is a separate bug from #26 / #27 but compounds with #27: zsh users hit #27 first (no env loads at all), bash users would hit this quote bug as soon as the preamble actually loads.
Version / environment
- ecluse 0.3.0
process_manager = "tmux"
- macOS 14, zsh and bash both affected
- mode = "hybrid"
Reproduction
Given a .env file with quoted values (the most common dotenv style):
# .env
ONYX_ENVIRONMENT="development"
E2E_TEST_EMAIL="e2e-test@example.com"
API_BASE_URL=http://localhost:4444
After ecluse up feat-foo, inspect the generated preamble:
grep -E "ONYX_ENVIRONMENT |E2E_TEST_EMAIL|API_BASE_URL" .ecluse/preambles/feat-foo.sh
# Output:
# export ONYX_ENVIRONMENT='"development"'
# export E2E_TEST_EMAIL='"e2e-test@example.com"'
# export API_BASE_URL='http://localhost:4444'
Note the literal "..." double quotes inside the single quotes on the first two lines — those quotes are part of the value, not syntax. The third line is correct because the original .env line had no quotes.
When the preamble is sourced and a downstream app reads the var:
source .ecluse/preambles/feat-foo.sh
echo "ONYX_ENVIRONMENT is [$ONYX_ENVIRONMENT]"
# Output: ONYX_ENVIRONMENT is ["development"] ← includes the quotes!
For example a vite plugin that validates ONYX_ENVIRONMENT against z.literal("development") will fail with:
{
"received": "\"development\"",
"code": "invalid_literal",
"expected": "development",
"message": "Invalid literal value, expected \"development\""
}
…because the value is the 13-character string "development" (with quotes), not the 11-character string development.
Root cause
The preamble generator appears to be reading the raw bytes of each line in .env, finding the =, taking everything after it as the value (including any surrounding quotes), and then wrapping that whole substring in single quotes for the export statement. Dotenv parsers normally strip the outer quotes during parse; this code path is treating dotenv syntax as if it were shell syntax (where FOO="bar" is identical to FOO=bar).
A correct implementation would either:
- Parse the
.env file with a dotenv-compatible parser that strips outer matching quote pairs ("..." and '...') before re-serialising
- Re-emit values with shell-correct quoting via
printf %q (bash) or equivalent, working from the parsed (unquoted) value, not the raw source bytes
Right now it's the worst of both worlds: values that happen to have no quotes in .env come through fine, but values that have quotes round-trip with the quotes embedded.
Suggested fix
Use a dotenv parser when reading .env / .env.local, and re-emit with shell-safe quoting from the parsed value:
// pseudocode
for (key, value) in dotenv_parse(env_file_path) {
// value already has outer quotes stripped by the parser
writeln!(preamble, "export {}={}", key, shell_escape(&value))?;
}
shell_escape should wrap the value in single quotes and escape any embedded single quotes. The current logic seems to be doing roughly the second half (single-quote wrapping) but skipping the dotenv parse step.
Impact
Followup to #26, related to #27
This issue was masked in 0.2.16 because the cd-ordering bug from #26 meant the per-slot env never loaded at all — so nobody noticed the preamble itself was malformed. Now that the preamble is the primary env source for spawned services (per the 0.3.0 design), the malformation matters.
Summary
The per-slug preamble at
.ecluse/preambles/<slug>.sh(introduced in 0.3.0) preserves the quote characters from the source.envfile when re-emitting them as shellexportstatements. The result is that the runtime env var contains literal quote characters as part of its value, breaking any consumer that expects the unquoted value.This is a separate bug from #26 / #27 but compounds with #27: zsh users hit #27 first (no env loads at all), bash users would hit this quote bug as soon as the preamble actually loads.
Version / environment
process_manager = "tmux"Reproduction
Given a
.envfile with quoted values (the most common dotenv style):After
ecluse up feat-foo, inspect the generated preamble:Note the literal
"..."double quotes inside the single quotes on the first two lines — those quotes are part of the value, not syntax. The third line is correct because the original.envline had no quotes.When the preamble is sourced and a downstream app reads the var:
For example a vite plugin that validates
ONYX_ENVIRONMENTagainstz.literal("development")will fail with:…because the value is the 13-character string
"development"(with quotes), not the 11-character stringdevelopment.Root cause
The preamble generator appears to be reading the raw bytes of each line in
.env, finding the=, taking everything after it as the value (including any surrounding quotes), and then wrapping that whole substring in single quotes for theexportstatement. Dotenv parsers normally strip the outer quotes during parse; this code path is treating dotenv syntax as if it were shell syntax (whereFOO="bar"is identical toFOO=bar).A correct implementation would either:
.envfile with a dotenv-compatible parser that strips outer matching quote pairs ("..."and'...') before re-serialisingprintf %q(bash) or equivalent, working from the parsed (unquoted) value, not the raw source bytesRight now it's the worst of both worlds: values that happen to have no quotes in
.envcome through fine, but values that have quotes round-trip with the quotes embedded.Suggested fix
Use a dotenv parser when reading
.env/.env.local, and re-emit with shell-safe quoting from the parsed value:shell_escapeshould wrap the value in single quotes and escape any embedded single quotes. The current logic seems to be doing roughly the second half (single-quote wrapping) but skipping the dotenv parse step.Impact
.envchain on zsh, so this quote bug doesn't surface (apps fail earlier). After zsh: tmux startup '. <file>' fails — POSIX dot builtin doesn't search cwd in zsh, need './<file>' or 'source' #27 fixes, this becomes the blocker..env/.env.localthat uses the conventionalKEY="value"quoting. That's the default style in most.env.tpltemplates and the recommended way to write strings with spaces or special chars in dotenv.if "$FOO" = "expected"checks) will fail with mysteriously quoted values.Followup to #26, related to #27
This issue was masked in 0.2.16 because the cd-ordering bug from #26 meant the per-slot env never loaded at all — so nobody noticed the preamble itself was malformed. Now that the preamble is the primary env source for spawned services (per the 0.3.0 design), the malformation matters.