AgentSkillsCN

env-config

面向生产级 Tauri 桌面应用,掌握 CI/CD 意识与交付规范的权威,确保构建、发布与更新的可靠性

SKILL.md
--- frontmatter
name: env-config
description: Configuration via dataclass frozen + os.getenv + python-dotenv. Validation au démarrage. Triggers: config, settings, environnement, .env, configuration
version: 1.0.0

Environment Configuration

Overview

Pattern de configuration utilisant une dataclass frozen avec valeurs par défaut issues des variables d'environnement. Chargement automatique du fichier .env et validation au démarrage.

File Structure

code
src/cuve-api/
├── app/
│   ├── config.py        # Settings dataclass
├── .env                  # Variables locales (non versionné)
├── .env.example          # Template des variables

Implementation Pattern

Dataclass Settings

python
from dataclasses import dataclass
import os
from dotenv import load_dotenv

# Charge .env si présent (local), sinon pas grave (prod via variables d'env)
load_dotenv()

@dataclass(frozen=True)
class Settings:
    # Mode : real = appelle le capteur ESP ; sim = capteur simulé
    mode: str = os.getenv("CUVE_MODE", "real").strip().lower()

    # URL du capteur (uniquement nécessaire en mode real)
    sensor_url: str | None = os.getenv("CUVE_SENSOR_URL")

    # Paramètres client HTTP
    cache_ttl_seconds: int = int(os.getenv("CUVE_CACHE_TTL_SECONDS", "10"))
    http_timeout_seconds: float = float(os.getenv("CUVE_HTTP_TIMEOUT_SECONDS", "2.0"))

    # Paramètres DB et collecte
    db_path: str = os.getenv("CUVE_DB_PATH", "cuve.sqlite3")
    collect_interval_seconds: int = int(os.getenv("CUVE_COLLECT_INTERVAL_SECONDS", "60"))

    # Paramètres cuve (géométrie)
    tank_total_volume_liters: float = float(os.getenv("CUVE_TANK_TOTAL_LITERS", "10000"))
    tank_diameter_cm: float = float(os.getenv("CUVE_TANK_DIAMETER_CM", "184.5"))
    tank_length_cm: float = float(os.getenv("CUVE_TANK_LENGTH_CM", "436.4"))
    tank_full_air_gap_cm: float = float(os.getenv("CUVE_TANK_FULL_AIR_GAP_CM", "20"))


settings = Settings()

Validation au démarrage

python
if settings.mode not in ("real", "sim"):
    raise RuntimeError(f"CUVE_MODE invalide: {settings.mode} (attendu: real ou sim)")

if settings.mode == "real" and not settings.sensor_url:
    raise RuntimeError("CUVE_SENSOR_URL est requis quand CUVE_MODE=real")

Exemple .env.example

bash
# Mode: real ou sim
CUVE_MODE=sim

# URL du capteur ESP8266 (requis si CUVE_MODE=real)
# CUVE_SENSOR_URL=http://192.168.1.50/json

# Cache et timeout HTTP
CUVE_CACHE_TTL_SECONDS=10
CUVE_HTTP_TIMEOUT_SECONDS=2.0

# Base de données
CUVE_DB_PATH=cuve.sqlite3
CUVE_COLLECT_INTERVAL_SECONDS=60

# Géométrie de la cuve
CUVE_TANK_TOTAL_LITERS=10000
CUVE_TANK_DIAMETER_CM=184.5
CUVE_TANK_LENGTH_CM=436.4
CUVE_TANK_FULL_AIR_GAP_CM=20

Utilisation dans le code

python
from app.config import settings

# Accès direct aux propriétés
if settings.mode == "sim":
    cuve = SimCuveClient()
else:
    cuve = RealCuveClient(sensor_url=settings.sensor_url)

Rules

Do

  • Utiliser dataclass(frozen=True) pour l'immutabilité
  • Préfixer toutes les variables par le nom du projet (ex: CUVE_)
  • Fournir des valeurs par défaut sensées
  • Valider les valeurs critiques au démarrage avec des messages clairs
  • Créer un .env.example documenté
  • Utiliser load_dotenv() en début de module

Don't

  • Ne pas versionner le fichier .env (ajouter à .gitignore)
  • Ne pas oublier de convertir les types (int(), float())
  • Ne pas accéder directement à os.getenv() ailleurs que dans config.py
  • Ne pas utiliser de valeurs par défaut qui fonctionnent en production

File Location

  • Configuration : src/cuve-api/app/config.py
  • Template : src/cuve-api/.env.example
<!-- Generated by skill-master command Version: 1.0.0 Sources: - src/cuve-api/app/config.py - src/cuve-api/.env.example Last updated: 2026-02-02 -->