docs: update README and docs with strict Quick Start (docker-compose-prod), fix WebSocket paths, enforce strict tone
This commit is contained in:
84
app/scripts/substitute_env.py
Normal file
84
app/scripts/substitute_env.py
Normal file
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Скрипт подстановки переменных окружения из .env/env.example в файл шаблона.
|
||||
|
||||
Автор: Сергей Антропов
|
||||
Сайт: https://devops.org.ru
|
||||
|
||||
Использование:
|
||||
python3 app/scripts/substitute_env.py PATH_TO_ENV PATH_TO_COMPOSE_FILE
|
||||
|
||||
Логика:
|
||||
- Читает пары KEY=VALUE из указанного env-файла (игнорирует комментарии и пустые строки)
|
||||
- Не выполняет файл как shell (безопасно парсит)
|
||||
- Заменяет плейсхолдеры вида ${KEY} в целевом файле
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from typing import Dict
|
||||
|
||||
|
||||
def parse_env_file(env_path: str) -> Dict[str, str]:
|
||||
"""Простой парсер .env файла: KEY=VALUE, без исполнения shell."""
|
||||
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:
|
||||
"""Заменяет ${VAR} из словаря env, оставляет плейсхолдер, если VAR не найден."""
|
||||
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:
|
||||
if len(sys.argv) != 3:
|
||||
sys.stderr.write(
|
||||
"Usage: substitute_env.py <env_path> <compose_path>\n"
|
||||
)
|
||||
return 2
|
||||
|
||||
env_path, compose_path = sys.argv[1], sys.argv[2]
|
||||
|
||||
if not os.path.exists(env_path):
|
||||
sys.stderr.write(f"Env file not found: {env_path}\n")
|
||||
return 3
|
||||
if not os.path.exists(compose_path):
|
||||
sys.stderr.write(f"Compose file not found: {compose_path}\n")
|
||||
return 4
|
||||
|
||||
env = parse_env_file(env_path)
|
||||
|
||||
with io.open(compose_path, "r", encoding="utf-8") as fh:
|
||||
content = fh.read()
|
||||
new_content = substitute_placeholders(content, env)
|
||||
with io.open(compose_path, "w", encoding="utf-8") as fh:
|
||||
fh.write(new_content)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
||||
|
||||
Reference in New Issue
Block a user