[feat] Docker-first configuration
This commit is contained in:
parent
0819d4bb85
commit
b920ee56fb
4 changed files with 810 additions and 858 deletions
27
Dockerfile
27
Dockerfile
|
@ -1,8 +1,27 @@
|
||||||
FROM python:3.10
|
FROM python:3.12-bookworm as builder
|
||||||
|
|
||||||
|
RUN pip install poetry==1.7.1
|
||||||
|
|
||||||
|
ENV POETRY_NO_INTERACTION=1 \
|
||||||
|
POETRY_VIRTUALENVS_IN_PROJECT=1 \
|
||||||
|
POETRY_VIRTUALENVS_CREATE=1 \
|
||||||
|
POETRY_CACHE_DIR=/tmp/poetry_cache
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY . /app
|
COPY pyproject.toml poetry.lock README.md ./
|
||||||
RUN pip install --no-cache-dir .
|
|
||||||
|
|
||||||
CMD [ "python", "run.py" ]
|
RUN poetry install --without dev --no-root && rm -rf $POETRY_CACHE_DIR
|
||||||
|
|
||||||
|
FROM python:3.12-slim-bookworm as runtime
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ENV VIRTUAL_ENV=/app/.venv \
|
||||||
|
PATH="/app/.venv/bin:$PATH"
|
||||||
|
|
||||||
|
COPY --from=builder ${VIRTUAL_ENV} ${VIRTUAL_ENV}
|
||||||
|
|
||||||
|
COPY . /app/
|
||||||
|
|
||||||
|
CMD [ "python", "run.py" ]
|
||||||
|
|
|
@ -1,19 +1,9 @@
|
||||||
"""Task config."""
|
"""Task config."""
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from os import environ
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import orjson as json
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
import yaml
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
from jarvis_core.util import find_all
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
try:
|
|
||||||
from yaml import CLoader as Loader
|
|
||||||
except ImportError:
|
|
||||||
from yaml import Loader
|
|
||||||
|
|
||||||
|
|
||||||
class Environment(Enum):
|
class Environment(Enum):
|
||||||
|
@ -23,7 +13,7 @@ class Environment(Enum):
|
||||||
develop = "develop"
|
develop = "develop"
|
||||||
|
|
||||||
|
|
||||||
class Mongo(BaseModel):
|
class Mongo(BaseSettings):
|
||||||
"""MongoDB config."""
|
"""MongoDB config."""
|
||||||
|
|
||||||
host: list[str] | str = "localhost"
|
host: list[str] | str = "localhost"
|
||||||
|
@ -32,76 +22,19 @@ class Mongo(BaseModel):
|
||||||
port: int = 27017
|
port: int = 27017
|
||||||
|
|
||||||
|
|
||||||
class Config(BaseModel):
|
class Config(BaseSettings, case_sensitive=False):
|
||||||
"""Tasks config model."""
|
"""Tasks config model."""
|
||||||
|
|
||||||
token: str
|
token: str
|
||||||
mongo: Mongo
|
mongo: Mongo = Mongo()
|
||||||
log_level: str = "INFO"
|
log_level: str = "INFO"
|
||||||
environment: Environment = Environment.develop
|
environment: Environment = Environment.develop
|
||||||
|
|
||||||
|
model_config = SettingsConfigDict(
|
||||||
_config: Config = None
|
env_file=".env", env_file_encoding="utf-8", env_nested_delimiter="."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _load_json() -> Config | None:
|
def load_config() -> Config:
|
||||||
path = Path("config.json")
|
"""Load the config using the specified method first"""
|
||||||
if path.exists():
|
return Config()
|
||||||
with path.open() as f:
|
|
||||||
j = json.loads(f.read())
|
|
||||||
return Config(**j)
|
|
||||||
|
|
||||||
|
|
||||||
def _load_yaml() -> Config | None:
|
|
||||||
path = Path("config.yaml")
|
|
||||||
if path.exists():
|
|
||||||
with path.open() as f:
|
|
||||||
y = yaml.load(f.read(), Loader=Loader)
|
|
||||||
return Config(**y)
|
|
||||||
|
|
||||||
|
|
||||||
def _load_env() -> Config | None:
|
|
||||||
load_dotenv()
|
|
||||||
data = {}
|
|
||||||
mongo = {}
|
|
||||||
mongo_keys = find_all(lambda x: x.upper().startswith("MONGO"), environ.keys())
|
|
||||||
|
|
||||||
config_keys = mongo_keys + ["TOKEN", "LOG_LEVEL", "ENVIRONMENT"]
|
|
||||||
|
|
||||||
for item, value in environ.items():
|
|
||||||
if item not in config_keys:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if item in mongo_keys:
|
|
||||||
key = "_".join(item.split("_")[1:]).lower()
|
|
||||||
mongo[key] = value
|
|
||||||
else:
|
|
||||||
data[item.lower()] = value
|
|
||||||
|
|
||||||
data["mongo"] = mongo
|
|
||||||
|
|
||||||
return Config(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def load_config(method: Optional[str] = None) -> Config:
|
|
||||||
"""
|
|
||||||
Load the config using the specified method first
|
|
||||||
|
|
||||||
Args:
|
|
||||||
method: Method to use first
|
|
||||||
"""
|
|
||||||
global _config
|
|
||||||
if _config is not None:
|
|
||||||
return _config
|
|
||||||
|
|
||||||
methods = {"yaml": _load_yaml, "json": _load_json, "env": _load_env}
|
|
||||||
method_names = list(methods.keys())
|
|
||||||
if method and method in method_names:
|
|
||||||
method_names.remove(method)
|
|
||||||
method_names.insert(0, method)
|
|
||||||
|
|
||||||
for method in method_names:
|
|
||||||
if _config := methods[method]():
|
|
||||||
return _config
|
|
||||||
|
|
||||||
raise FileNotFoundError("Missing one of: config.yaml, config.json, .env")
|
|
||||||
|
|
1551
poetry.lock
generated
1551
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -17,6 +17,7 @@ pydantic = ">=2.3.0,<3"
|
||||||
# rocketry = "^2.5.1"
|
# rocketry = "^2.5.1"
|
||||||
croniter = "^1.4.1"
|
croniter = "^1.4.1"
|
||||||
beanie = ">=1.21.0"
|
beanie = ">=1.21.0"
|
||||||
|
pydantic-settings = "^2.2.1"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
pytest = "^7.1"
|
pytest = "^7.1"
|
||||||
|
|
Loading…
Add table
Reference in a new issue