mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 02:54:26 +00:00
fix(env): break NIX_CONFIG bootstrap-loop
Two independent bugs were compounding into a corrupted NIX_CONFIG that broke every local image rebuild: 1. scripts/tests/code/run.sh non-idempotently appended "accept-flake-config = true" to whatever NIX_CONFIG it inherited. On the next source of .env (which carried the previous value back), the line accumulated -- "true\naccept-flake-config = true\n..." -- until Nix rejected the Boolean. Fix: pipe through `awk '\!seen[$0]++'` to dedup lines while preserving order. 2. utils/env/writer.py silently re-encoded literal newlines as `\n` escape sequences inside double-quoted values. docker-compose's --env-file loader does not decode them and neither does bash `source`, so any multi-line value round-tripped into garbage on the next read. Fix: raise ValueError instead -- producers must collapse to a single line before handing off to the env-builder. Test fixtures for the writer adjusted to assert the new raise semantics. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
5e0036dfa9
commit
a3782bf8d3
3 files changed
+19
-25
No files matched your search
@@ -12,7 +12,8 @@ NIX_CONFIG_EFFECTIVE="$(
|
||||
printf "%s\n%s\n" \
|
||||
"${NIX_CONFIG:-}" \
|
||||
"accept-flake-config = true" |
|
||||
sed -e "s/[[:space:]]\+$//" -e "/^$/d"
|
||||
sed -e "s/[[:space:]]\+$//" -e "/^$/d" |
|
||||
awk '!seen[$0]++'
|
||||
)"
|
||||
export NIX_CONFIG="${NIX_CONFIG_EFFECTIVE}"
|
||||
|
||||
|
||||
Vendored
+9
-14
@@ -41,11 +41,13 @@ class TestQuoteForDotenv(unittest.TestCase):
|
||||
def test_backslash_then_quote(self) -> None:
|
||||
self.assertEqual(quote_for_dotenv('a\\"b'), '"a\\\\\\"b"')
|
||||
|
||||
def test_newline_is_escaped(self) -> None:
|
||||
self.assertEqual(quote_for_dotenv("line1\nline2"), '"line1\\nline2"')
|
||||
def test_newline_raises(self) -> None:
|
||||
with self.assertRaises(ValueError):
|
||||
quote_for_dotenv("line1\nline2")
|
||||
|
||||
def test_carriage_return_is_escaped(self) -> None:
|
||||
self.assertEqual(quote_for_dotenv("a\rb"), '"a\\rb"')
|
||||
def test_carriage_return_raises(self) -> None:
|
||||
with self.assertRaises(ValueError):
|
||||
quote_for_dotenv("a\rb")
|
||||
|
||||
def test_unicode_passes_through_quoted(self) -> None:
|
||||
self.assertEqual(quote_for_dotenv("naïve"), '"naïve"')
|
||||
@@ -117,18 +119,11 @@ class TestWriteDotenv(unittest.TestCase):
|
||||
text = read_text(str(dest))
|
||||
self.assertIn('K="with space"', text)
|
||||
|
||||
def test_multiline_value_escaped_to_single_line(self) -> None:
|
||||
"""docker-compose's .env loader cannot read multi-line values;
|
||||
the writer collapses \\n into a literal escape."""
|
||||
def test_multiline_value_raises(self) -> None:
|
||||
with TemporaryDirectory() as td:
|
||||
dest = Path(td) / ".env"
|
||||
write_dotenv(self._build({"K": "a\nb"}), dest)
|
||||
text = read_text(str(dest))
|
||||
# Verify no literal newline inside the K=... entry.
|
||||
entry_line = next(
|
||||
line for line in text.splitlines() if line.startswith("K=")
|
||||
)
|
||||
self.assertEqual(entry_line, 'K="a\\nb"')
|
||||
with self.assertRaises(ValueError):
|
||||
write_dotenv(self._build({"K": "a\nb"}), dest)
|
||||
|
||||
def test_trailing_newline(self) -> None:
|
||||
with TemporaryDirectory() as td:
|
||||
|
||||
Vendored
+8
-10
@@ -25,16 +25,14 @@ _SAFE_VALUE_RE = re.compile(r"^[A-Za-z0-9_/.@:%+=-]+$")
|
||||
def quote_for_dotenv(value: str) -> str:
|
||||
if value != "" and _SAFE_VALUE_RE.match(value):
|
||||
return value
|
||||
escaped = (
|
||||
value.replace("\\", "\\\\")
|
||||
.replace('"', '\\"')
|
||||
# Escape newlines so multi-line values (e.g. NIX_CONFIG) collapse
|
||||
# into a single env-file line. docker-compose's .env loader does
|
||||
# not support multi-line values, and the env-file schema lint
|
||||
# would otherwise see each continuation line as a separate entry.
|
||||
.replace("\r", "\\r")
|
||||
.replace("\n", "\\n")
|
||||
)
|
||||
if "\n" in value or "\r" in value:
|
||||
raise ValueError(
|
||||
"Multi-line env values cannot be written to .env "
|
||||
"(bash `source` and docker-compose `--env-file` both fail "
|
||||
"to round-trip them). Collapse to a single line at the "
|
||||
f"producer. Offending value: {value!r}"
|
||||
)
|
||||
escaped = value.replace("\\", "\\\\").replace('"', '\\"')
|
||||
return f'"{escaped}"'
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user