54 lines
1.8 KiB
Python
54 lines
1.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Run a command with one or more Docker-style env files.
|
|
|
|
Values are parsed literally rather than sourced by a shell. Tokens containing
|
|
`$`, `#`, spaces or shell metacharacters therefore remain unchanged.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import pathlib
|
|
import sys
|
|
|
|
|
|
def load_env(path: pathlib.Path) -> None:
|
|
if not path.is_file():
|
|
raise SystemExit(f"secret environment file is missing: {path}")
|
|
for raw in path.read_text(encoding="utf-8").splitlines():
|
|
line = raw.strip()
|
|
if not line or line.startswith("#"):
|
|
continue
|
|
if line.startswith("export "):
|
|
line = line[7:].lstrip()
|
|
if "=" not in line:
|
|
raise SystemExit(f"invalid environment line in {path}: {raw!r}")
|
|
key, value = line.split("=", 1)
|
|
key = key.strip()
|
|
if not key or not key.replace("_", "A").isalnum() or key[0].isdigit():
|
|
raise SystemExit(f"invalid environment variable in {path}: {key!r}")
|
|
value = value.strip()
|
|
if len(value) >= 2 and value[0] == value[-1] and value[0] in "\"'":
|
|
value = value[1:-1]
|
|
os.environ[key] = value
|
|
|
|
|
|
def main() -> None:
|
|
if len(sys.argv) < 3:
|
|
raise SystemExit("usage: run-with-env ENV_FILE [ENV_FILE ...] -- COMMAND [ARG ...]")
|
|
try:
|
|
separator = sys.argv.index("--")
|
|
except ValueError as exc:
|
|
raise SystemExit("missing -- before command") from exc
|
|
env_files = [pathlib.Path(item) for item in sys.argv[1:separator]]
|
|
command = sys.argv[separator + 1 :]
|
|
if not env_files or not command:
|
|
raise SystemExit("at least one env file and a command are required")
|
|
for env_file in env_files:
|
|
load_env(env_file)
|
|
os.execvp(command[0], command)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|