mirror of
https://github.com/dartdavros/chatballs.git
synced 2026-10-05 09:14:58 +03:00
Переход на запуск без .env (41a4b0d) не был доведён: в дереве остались файл
`env.example`, чтение instance .env во всём deploy/cli и CI, который этот .env
сам же и создавал из `env.example`. Из-за остатков ломались две вещи.
`chatballs deploy` не работал на установке без .env: _normalize_schema_ownership
брал POSTGRES_USER и POSTGRES_DB из файла и падал с «POSTGRES_USER not set»,
хотя установка исправна. Теперь берёт те же значения по умолчанию, что compose.
`chatballs doctor` выдавал пять ложных ошибок подряд: искал в .env домены,
ACME-почту, POSTGRES_PASSWORD и CHATBALLS_SECRET_KEY. Первое задаёт владелец
в «Настройках», второе генерирует в том с секретами первый старт стека —
снаружи, с хоста, этого не видно, и проверки убраны.
Убрано: env.example (из репозитория, release bundle и README); instance_env_file()
и все его чтения в common/compose/deploy/doctor/status; --env-file instance .env
из вызова compose — остаётся только release.env с digest-пинами образов от CI;
создание .env в gateway:validate. Профиль calls и его адреса читаются из
переменных окружения — оттуда же, откуда их берёт сам compose.
Тесты CLI переведены с фиктивного .env на переменные окружения; три проверки
удалённого поведения doctor убраны. Девять оставшихся проходят вообще без .env
— это и есть проверка, что установка теперь обслуживается. Заодно у
test_custocrm_cli.py выправлены окончания строк: в файле были одиночные CR
внутри кода, отчего diff по нему больше содержательной правки.
Проверено: pytest tests/cli — 9 passed; docker compose config валиден с пустым
каталогом инстанса; bash -n чист по всем скриптам CLI.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
43 lines
1.7 KiB
Python
43 lines
1.7 KiB
Python
import os
|
||
from pathlib import Path
|
||
|
||
# Секреты инстанса генерируются при первом старте и лежат файлами в томе
|
||
# (deploy/secrets/generate-instance-secrets.sh). В .env их нет — установка не
|
||
# требует от человека ни одной переменной.
|
||
SECRETS_DIR = Path(os.environ.get("CHATBALLS_SECRETS_DIR", "/run/chatballs/secrets"))
|
||
|
||
|
||
def env_secret(name: str, secret_file: str, default: str = "") -> str:
|
||
"""Значение из переменной окружения, иначе из файла секрета, иначе default.
|
||
|
||
Штатный источник — файл в томе: файла с переменными у продукта нет.
|
||
Переменная окружения остаётся выше по приоритету для контуров, которые
|
||
подставляют значения средствами оркестратора (облако, CI).
|
||
"""
|
||
value = os.environ.get(name)
|
||
if value:
|
||
return value
|
||
path_override = os.environ.get(f"{name}_FILE")
|
||
path = Path(path_override) if path_override else SECRETS_DIR / secret_file
|
||
try:
|
||
content = path.read_text(encoding="utf-8").strip()
|
||
except OSError:
|
||
return default
|
||
return content or default
|
||
|
||
|
||
|
||
|
||
def env_bool(name: str, default: bool = False) -> bool:
|
||
value = os.environ.get(name)
|
||
if value is None:
|
||
return default
|
||
return value.lower() in {"1", "true", "yes", "on"}
|
||
|
||
|
||
def env_list(name: str, default: list[str]) -> list[str]:
|
||
value = os.environ.get(name)
|
||
if value is None:
|
||
return default
|
||
return [item.strip() for item in value.split(",") if item.strip()]
|