Change engine/.env to be committed and have .env.local override (#6150)

# Description of Changes
We keep adding stuff to `engine/config/.env.example` and have to
manually update `.env` because of it, which is really clunky, especially
when working on multiple worktrees at once. This PR changes it so that
we just have a committed `.env` file and have an `.env.local` override
to put the actual private keys into, which should make it a bit easier
to manage.

> [!warning]
>
> After this goes in, be very careful for a little while not to
accidentally commit any keys that you've got inside your `.env` file!
This commit is contained in:
James Brunton
2026-04-21 16:18:25 +01:00
committed by GitHub
parent 2a856fbc19
commit 3b2afe0deb
9 changed files with 34 additions and 44 deletions
+13 -37
View File
@@ -1,48 +1,24 @@
"""
Copies .env from .env.example if missing, and errors if any keys from the example
are absent from the actual .env file.
Ensures `.env.local` exists so developers have a place to put overrides
(API keys, local model choices, etc.) without touching the committed `.env`.
Usage:
uv run scripts/setup_env.py
"""
import os
import shutil
import sys
from pathlib import Path
from dotenv import dotenv_values
ROOT = Path(__file__).parent.parent
EXAMPLE_FILE = ROOT / "config" / ".env.example"
ENV_FILE = ROOT / ".env"
ENV_LOCAL_FILE = ROOT / ".env.local"
print("setup-env: see engine/config/.env.example for documentation")
TEMPLATE = """\
###############################################################################
# Local overrides for `engine/.env`
# Put API keys and machine-specific settings here. Any variable defined here
# takes precedence over the committed `.env`
###############################################################################
"""
if not EXAMPLE_FILE.exists():
print(f"setup-env: {EXAMPLE_FILE.name} not found, skipping", file=sys.stderr)
sys.exit(0)
if not ENV_FILE.exists():
shutil.copy(EXAMPLE_FILE, ENV_FILE)
print("setup-env: created .env from .env.example")
env_keys = set(dotenv_values(ENV_FILE).keys()) | set(os.environ.keys())
example_keys = set(dotenv_values(EXAMPLE_FILE).keys())
missing = sorted(example_keys - env_keys)
if missing:
sys.exit(
"setup-env: .env is missing keys from .env.example:\n"
+ "\n".join(f" {k}" for k in missing)
+ "\n Add them manually or delete your local .env to re-copy from config/.env.example."
)
extra = sorted(k for k in dotenv_values(ENV_FILE) if k.startswith("STIRLING_") and k not in example_keys)
if extra:
print(
"setup-env: .env contains STIRLING_ keys not in config/.env.example:\n"
+ "\n".join(f" {k}" for k in extra)
+ "\n Add them to config/.env.example if they are intentional.",
file=sys.stderr,
)
if not ENV_LOCAL_FILE.exists():
ENV_LOCAL_FILE.write_text(TEMPLATE)
print("setup-env: created empty .env.local for local overrides")