#!/usr/bin/env python3 """将 dotenv 转成 docker run --env-file 的字面值格式,不打印凭证。""" import argparse import os from pathlib import Path import re import tempfile from dotenv import dotenv_values def export(source, target): source, target = Path(source).resolve(strict=True), Path(target).absolute() if source == target.resolve(): raise ValueError('导出文件不能覆盖原 .env') rows = [] for key, value in dotenv_values(source).items(): if not re.fullmatch(r'[A-Za-z_][A-Za-z0-9_]*', key): raise ValueError('存在 Docker 不支持的环境变量名称') if value is None: continue if any(char in value for char in '\r\n\x00'): raise ValueError(f'{key} 含有 Docker env-file 不支持的换行或空字符') rows.append(key + '=' + value + '\n') target.parent.mkdir(parents=True, exist_ok=True) partial = None try: # 临时文件权限0600;写完后原子替换,保留原 .env,不输出任何配置值。 with tempfile.NamedTemporaryFile(mode='w', encoding='utf-8', dir=target.parent, prefix='.docker-env-', delete=False) as stream: partial = Path(stream.name) stream.writelines(rows) os.replace(partial, target) finally: if partial is not None: partial.unlink(missing_ok=True) return len(rows) if __name__ == '__main__': parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('source', nargs='?', default='.env') parser.add_argument('target', nargs='?', default='.env.docker') args = parser.parse_args() try: count = export(args.source, args.target) except (OSError, ValueError) as exc: parser.exit(1, f'转换失败:{exc}\n') print(f'已导出 {count} 个配置项,文件权限0600;未输出配置值。')