#!/usr/bin/env python3 # safe-deploy.sh — Safe Docker Stack deployer with .env support # Usage: python3 deploy/safe-deploy.sh [compose-dir] # Parses .env safely — no shell interpretation of special chars import subprocess, sys, os def parse_env_file(path): env = {} with open(path) as f: for line in f: line = line.rstrip('\n') if not line.strip() or line.strip().startswith('#'): continue if '=' not in line: continue key, _, value = line.partition('=') key = key.strip(); value = value.strip() if len(value) >= 2: if (value[0]=="'" and value[-1]=="'") or (value[0]=='"' and value[-1]=='"'): value = value[1:-1] env[key] = value return env def main(): if len(sys.argv) < 2: print("Usage: safe-deploy.sh [dir]"); sys.exit(1) stack = sys.argv[1] d = sys.argv[2] if len(sys.argv)>2 else "/volume1/docker/compose-files" yp = os.path.join(d, stack+".yaml") ep = os.path.join(d, stack+".env") if not os.path.exists(yp): print(f"ERROR: {yp} not found"); sys.exit(1) env = dict(os.environ) if os.path.exists(ep): parsed=parse_env_file(ep); print(f" Loaded {len(parsed)} vars"); env.update(parsed) else: print(f" No {stack}.env - deploying without overlay") cmd=["docker","stack","deploy","-c",yp,stack] print(f" Running: {chr(32).join(cmd)}") sys.exit(subprocess.run(cmd,env=env,cwd=d).returncode) if __name__=="__main__": main()