#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Генерация docker-compose-prod.yaml из шаблона и env.example. Автор: Сергей Антропов Сайт: https://devops.org.ru Поведение: - Читает env.example и подставляет значения в docker-compose-prod.tmpl.yaml - Запрашивает (или берет из ENV/CLI) REGISTRY_HOST, IMAGE_NAME_FULL, IMAGE_TAG - Заменяет плейсхолдеры REGISTRY_PLACEHOLDER/IMAGE_NAME_PLACEHOLDER/IMAGE_TAG_PLACEHOLDER """ from __future__ import annotations import argparse import io import os import re from typing import Dict ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) def parse_env_file(env_path: str) -> Dict[str, str]: variables: Dict[str, str] = {} with io.open(env_path, "r", encoding="utf-8") as fh: for raw in fh: line = raw.strip() if not line or line.startswith("#"): continue if "=" not in line: continue key, val = line.split("=", 1) key = key.strip() val = val.strip() if (val.startswith('"') and val.endswith('"')) or ( val.startswith("'") and val.endswith("'") ): val = val[1:-1] variables[key] = val return variables def substitute_placeholders(text: str, env: Dict[str, str]) -> str: pattern = re.compile(r"\$\{([A-Za-z_][A-Za-z0-9_]*)\}") return pattern.sub(lambda m: env.get(m.group(1), m.group(0)), text) def main() -> int: parser = argparse.ArgumentParser(description="Генерация docker-compose-prod.yaml") parser.add_argument("--template", default=os.path.join(ROOT, "release", "docker-compose-prod.tmpl.yaml")) parser.add_argument("--output", default=os.path.join(ROOT, "docker-compose-prod.yaml")) parser.add_argument("--env", dest="env_path", default=os.path.join(ROOT, "env.example")) parser.add_argument("--registry", default=os.getenv("REGISTRY_HOST", "")) parser.add_argument("--image", dest="image", default=os.getenv("IMAGE_NAME_FULL", "")) parser.add_argument("--tag", dest="tag", default=os.getenv("IMAGE_TAG", "")) args = parser.parse_args() env = parse_env_file(args.env_path) # Подстановка ${VAR} из env.example with io.open(args.template, "r", encoding="utf-8") as fh: content = fh.read() content = substitute_placeholders(content, env) # Комментирование строк, где остались неразрешенные ${VAR} # Ищем строки формата 'KEY: "${VAR}"' или 'KEY: ${VAR}' и комментируем их lines = content.splitlines() commented: list[str] = [] unresolved_pattern = re.compile(r"^([ \t-]*[^:#\n]+:\s*)(\"?\$\{[A-Za-z_][A-Za-z0-9_]*\}\"?)\s*$") for line in lines: if unresolved_pattern.match(line): commented.append("# " + line) else: commented.append(line) content = "\n".join(commented) + ("\n" if content.endswith("\n") else "") # Плейсхолдеры реестра/имени/тега registry = args.registry.strip() or input("Введите Docker Registry (например, ghcr.io или docker.io): ").strip() or "docker.io" if registry == "registry.hub.docker.com": registry = "docker.io" image = args.image.strip() or input(f"Введите имя образа (например, inecs/logboard) (по умолчанию: logboard): ").strip() or "logboard" tag = args.tag.strip() or input("Введите тег образа (по умолчанию: latest): ").strip() or "latest" content = content.replace("REGISTRY_PLACEHOLDER", registry) content = content.replace("IMAGE_NAME_PLACEHOLDER", image) content = content.replace("IMAGE_TAG_PLACEHOLDER", tag) with io.open(args.output, "w", encoding="utf-8") as fh: fh.write(content) print(f"Файл {os.path.relpath(args.output, ROOT)} сгенерирован") return 0 if __name__ == "__main__": raise SystemExit(main())