diff --git a/cli50/__main__.py b/cli50/__main__.py index ee300c5..46587d5 100644 --- a/cli50/__main__.py +++ b/cli50/__main__.py @@ -221,6 +221,13 @@ def main(): if lang := os.getenv("LANG"): options += ["--env", f"LANG={lang}"] + # Check for .env + try: + # Source and export any variables from DIRECTORY/.env. + options += env_options(directory) + except RuntimeError as e: + sys.exit(_("{}: unable to source").format(e)) + # Validate ports if not args["port"]: args["port"] = PORTS @@ -321,6 +328,34 @@ def ports(container): return ", ".join(mappings) +def env_options(directory): + """Return Docker options for .env in directory.""" + dotenv = os.path.join(directory, ".env") + if not os.path.isfile(dotenv): + return [] + try: + # Capture bash's baseline environment so shell defaults like PWD + # are not forwarded unless DIRECTORY/.env actually changes them. + before = subprocess.check_output([ + "bash", "-c", "env -0" + ], env={}, cwd=directory).decode("utf-8") + + # Source and export DIRECTORY/.env, then emit the resulting environment. + after = subprocess.check_output([ + "bash", "-c", 'set -a && source "$1" && env -0', "_", dotenv + ], env={}, cwd=directory).decode("utf-8") + except subprocess.CalledProcessError: + raise RuntimeError(dotenv) from None + + before = dict(entry.split("=", 1) for entry in before.rstrip("\0").split("\0") if entry) + after = dict(entry.split("=", 1) for entry in after.rstrip("\0").split("\0") if entry) + options = [] + for key, value in after.items(): + if before.get(key) != value: + options += ["--env", f"{key}={value}"] + return options + + def pull(image, tag): """Pull image as needed.""" import json