forked from Github-Mirrors/canaille
feat: env_prefix
create_app variable can select the environment var prefix
This commit is contained in:
parent
b8645ce1a3
commit
afa0a6ff1e
4 changed files with 43 additions and 15 deletions
|
@ -1,9 +1,13 @@
|
||||||
|
Added
|
||||||
|
^^^^^
|
||||||
|
- `env_prefix` create_app variable can select the environment var prefix.
|
||||||
|
|
||||||
[0.0.52] - 2024-04-22
|
[0.0.52] - 2024-04-22
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
Added
|
Added
|
||||||
^^^^^
|
^^^^^
|
||||||
- `ENV_FILE` environment variable can customize/disable the .env file
|
- `env_file` create_app variable can customize/disable the .env file
|
||||||
|
|
||||||
Changed
|
Changed
|
||||||
^^^^^^^
|
^^^^^^^
|
||||||
|
|
|
@ -124,7 +124,9 @@ def setup_flask_converters(app):
|
||||||
app.url_map.converters[model_name.lower()] = model_converter(model_class)
|
app.url_map.converters[model_name.lower()] = model_converter(model_class)
|
||||||
|
|
||||||
|
|
||||||
def create_app(config=None, validate=True, backend=None):
|
def create_app(
|
||||||
|
config=None, validate=True, backend=None, env_file=".env", env_prefix=""
|
||||||
|
):
|
||||||
from .app.configuration import setup_config
|
from .app.configuration import setup_config
|
||||||
from .app.i18n import setup_i18n
|
from .app.i18n import setup_i18n
|
||||||
from .app.themes import setup_themer
|
from .app.themes import setup_themer
|
||||||
|
@ -132,7 +134,13 @@ def create_app(config=None, validate=True, backend=None):
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
if not setup_config(app, config, validate): # pragma: no cover
|
if not setup_config(
|
||||||
|
app=app,
|
||||||
|
config=config,
|
||||||
|
test_config=validate,
|
||||||
|
env_file=env_file,
|
||||||
|
env_prefix=env_prefix,
|
||||||
|
): # pragma: no cover
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
sentry_sdk = setup_sentry(app)
|
sentry_sdk = setup_sentry(app)
|
||||||
|
|
|
@ -13,7 +13,6 @@ from pydantic_settings import SettingsConfigDict
|
||||||
from canaille.core.configuration import CoreSettings
|
from canaille.core.configuration import CoreSettings
|
||||||
|
|
||||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
DEFAULT_ENV_FILE = ".env"
|
|
||||||
|
|
||||||
|
|
||||||
class RootSettings(BaseSettings):
|
class RootSettings(BaseSettings):
|
||||||
|
@ -60,10 +59,10 @@ class RootSettings(BaseSettings):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def settings_factory(config):
|
def settings_factory(config, env_file=".env", env_prefix=""):
|
||||||
"""Overly complicated function that pushes the backend specific
|
"""Pushes the backend specific configuration into CoreSettings, in the
|
||||||
configuration into CoreSettings, in the purpose break dependency against
|
purpose break dependency against backends libraries like python-ldap or
|
||||||
backends libraries like python-ldap or sqlalchemy."""
|
sqlalchemy."""
|
||||||
attributes = {"CANAILLE": (CoreSettings, CoreSettings())}
|
attributes = {"CANAILLE": (CoreSettings, CoreSettings())}
|
||||||
|
|
||||||
if "CANAILLE_SQL" in config or any(
|
if "CANAILLE_SQL" in config or any(
|
||||||
|
@ -93,11 +92,11 @@ def settings_factory(config):
|
||||||
**attributes,
|
**attributes,
|
||||||
)
|
)
|
||||||
|
|
||||||
env_file = os.getenv("ENV_FILE", config.get("ENV_FILE", DEFAULT_ENV_FILE))
|
|
||||||
return Settings(
|
return Settings(
|
||||||
**config,
|
**config,
|
||||||
_secrets_dir=os.environ.get("SECRETS_DIR"),
|
_secrets_dir=os.environ.get("SECRETS_DIR"),
|
||||||
_env_file=env_file,
|
_env_file=env_file,
|
||||||
|
_env_prefix=env_prefix,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -121,7 +120,7 @@ def toml_content(file_path):
|
||||||
raise Exception("toml library not installed. Cannot load configuration.")
|
raise Exception("toml library not installed. Cannot load configuration.")
|
||||||
|
|
||||||
|
|
||||||
def setup_config(app, config=None, test_config=True):
|
def setup_config(app, config=None, test_config=True, env_file=".env", env_prefix=""):
|
||||||
from canaille.oidc.installation import install
|
from canaille.oidc.installation import install
|
||||||
|
|
||||||
app.config.from_mapping(
|
app.config.from_mapping(
|
||||||
|
@ -135,7 +134,9 @@ def setup_config(app, config=None, test_config=True):
|
||||||
config = toml_content(os.environ.get("CONFIG"))
|
config = toml_content(os.environ.get("CONFIG"))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
config_obj = settings_factory(config or {})
|
config_obj = settings_factory(
|
||||||
|
config or {}, env_file=env_file, env_prefix=env_prefix
|
||||||
|
)
|
||||||
except ValidationError as exc: # pragma: no cover
|
except ValidationError as exc: # pragma: no cover
|
||||||
app.logger.critical(str(exc))
|
app.logger.critical(str(exc))
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -41,6 +41,7 @@ def test_configuration_nestedsecrets_directory(tmp_path, backend, configuration)
|
||||||
|
|
||||||
|
|
||||||
def test_configuration_from_environment_vars():
|
def test_configuration_from_environment_vars():
|
||||||
|
"""Canaille should read configuration from environment vars."""
|
||||||
os.environ["SECRET_KEY"] = "very-very-secret"
|
os.environ["SECRET_KEY"] = "very-very-secret"
|
||||||
os.environ["CANAILLE__SMTP__FROM_ADDR"] = "user@mydomain.tld"
|
os.environ["CANAILLE__SMTP__FROM_ADDR"] = "user@mydomain.tld"
|
||||||
os.environ["CANAILLE_SQL__DATABASE_URI"] = "sqlite:///anything.db"
|
os.environ["CANAILLE_SQL__DATABASE_URI"] = "sqlite:///anything.db"
|
||||||
|
@ -60,6 +61,22 @@ def test_configuration_from_environment_vars():
|
||||||
del os.environ["CANAILLE_SQL__DATABASE_URI"]
|
del os.environ["CANAILLE_SQL__DATABASE_URI"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_disable_env_var_loading(tmp_path, configuration):
|
||||||
|
"""Canaille should not read configuration from environment vars when
|
||||||
|
env_prefix is False."""
|
||||||
|
del configuration["SERVER_NAME"]
|
||||||
|
os.environ["SERVER_NAME"] = "example.com"
|
||||||
|
os.environ["FOOBAR_SERVER_NAME"] = "foobar.example.com"
|
||||||
|
|
||||||
|
app = create_app(configuration, env_prefix="")
|
||||||
|
assert app.config["SERVER_NAME"] == "example.com"
|
||||||
|
|
||||||
|
app = create_app(configuration, env_prefix="FOOBAR_")
|
||||||
|
assert app.config["SERVER_NAME"] == "foobar.example.com"
|
||||||
|
|
||||||
|
del os.environ["SERVER_NAME"]
|
||||||
|
|
||||||
|
|
||||||
def test_dotenv_file(tmp_path, configuration):
|
def test_dotenv_file(tmp_path, configuration):
|
||||||
"""Canaille should read configuration from .env files."""
|
"""Canaille should read configuration from .env files."""
|
||||||
oldcwd = os.getcwd()
|
oldcwd = os.getcwd()
|
||||||
|
@ -81,8 +98,7 @@ def test_custom_dotenv_file(tmp_path, configuration):
|
||||||
with open(dotenv, "w") as fd:
|
with open(dotenv, "w") as fd:
|
||||||
fd.write("FOOBAR=other-custom-value")
|
fd.write("FOOBAR=other-custom-value")
|
||||||
|
|
||||||
configuration["ENV_FILE"] = dotenv
|
app = create_app(configuration, env_file=dotenv)
|
||||||
app = create_app(configuration)
|
|
||||||
assert app.config["FOOBAR"] == "other-custom-value"
|
assert app.config["FOOBAR"] == "other-custom-value"
|
||||||
|
|
||||||
|
|
||||||
|
@ -95,8 +111,7 @@ def test_disable_dotenv_file(tmp_path, configuration):
|
||||||
with open(dotenv, "w") as fd:
|
with open(dotenv, "w") as fd:
|
||||||
fd.write("FOOBAR=custom-value")
|
fd.write("FOOBAR=custom-value")
|
||||||
|
|
||||||
configuration["ENV_FILE"] = None
|
app = create_app(configuration, env_file=None)
|
||||||
app = create_app(configuration)
|
|
||||||
assert "FOOBAR" not in app.config
|
assert "FOOBAR" not in app.config
|
||||||
os.chdir(oldcwd)
|
os.chdir(oldcwd)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue