Skip to content

Commit 3667138

Browse files
alireza78ateknium1
authored andcommitted
fix(config): atomic write for .env to prevent API key loss on crash
save_env_value() used bare open('w') which truncates .env immediately. A crash or OOM kill between truncation and completed write silently wipes every credential in the file. Write now goes to a temp file first, then os.replace() swaps it atomically. Either the old .env exists or the new one does — never a truncated half-write. Same pattern used in cron/jobs.py. Cherry-picked from PR #842 by alireza78a, rebased onto current main with conflict resolution (_secure_file refactor). Co-authored-by: alireza78a <alireza78a@users.noreply.github.com>
1 parent 66c0b71 commit 3667138

File tree

1 file changed

+14
-2
lines changed

1 file changed

+14
-2
lines changed

hermes_cli/config.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import stat
1818
import subprocess
1919
import sys
20+
import tempfile
2021
from pathlib import Path
2122
from typing import Dict, Any, Optional, List, Tuple
2223

@@ -958,8 +959,19 @@ def save_env_value(key: str, value: str):
958959
lines[-1] += "\n"
959960
lines.append(f"{key}={value}\n")
960961

961-
with open(env_path, 'w', **write_kw) as f:
962-
f.writelines(lines)
962+
fd, tmp_path = tempfile.mkstemp(dir=str(env_path.parent), suffix='.tmp', prefix='.env_')
963+
try:
964+
with os.fdopen(fd, 'w', **write_kw) as f:
965+
f.writelines(lines)
966+
f.flush()
967+
os.fsync(f.fileno())
968+
os.replace(tmp_path, env_path)
969+
except BaseException:
970+
try:
971+
os.unlink(tmp_path)
972+
except OSError:
973+
pass
974+
raise
963975
_secure_file(env_path)
964976

965977
# Restrict .env permissions to owner-only (contains API keys)

0 commit comments

Comments
 (0)