diff --git a/backend/config/config_manager.py b/backend/config/config_manager.py index 92f89b6bc..4655f1774 100644 --- a/backend/config/config_manager.py +++ b/backend/config/config_manager.py @@ -4,6 +4,7 @@ import glob import json import os import sys +from pathlib import Path from typing import Final, NotRequired, TypedDict import pydash @@ -178,9 +179,7 @@ class ConfigManager: # Also check if the config file is writable self._config_file_writable = os.access(self.config_file, os.W_OK) except FileNotFoundError: - log.critical( - "Config file not found! Any changes made to the configuration will not persist after the application restarts." - ) + self._create_missing_config_file() except PermissionError: log.warning( "Config file not writable! Any changes made to the configuration will not persist after the application restarts." @@ -190,6 +189,28 @@ class ConfigManager: self._parse_config() self._validate_config() + def _create_missing_config_file(self) -> None: + log.warning( + f"Config file not found, creating an empty config at {hl(self.config_file, BLUE)}" + ) + + try: + config_file = Path(self.config_file) + config_file.parent.mkdir(parents=True, exist_ok=True) + config_file.touch(exist_ok=True) + + # Reset any previously loaded singleton state so parsing reflects + # the newly created empty config file. + self._raw_config = {} + self._config_file_mounted = True + self._config_file_writable = os.access(self.config_file, os.W_OK) + except PermissionError: + self._config_file_mounted = False + self._config_file_writable = False + log.critical( + "Config file not found and could not be created! Any changes made to the configuration will not persist after the application restarts." + ) + @staticmethod def get_db_engine() -> URL: """Builds the database connection string using environment variables diff --git a/backend/tests/config/test_config_loader.py b/backend/tests/config/test_config_loader.py index 37c29cfa4..72b57f940 100644 --- a/backend/tests/config/test_config_loader.py +++ b/backend/tests/config/test_config_loader.py @@ -113,3 +113,15 @@ def test_empty_config_loader(): assert loader.config.EJS_CONTROLS == {} assert loader.config.GAMELIST_MEDIA_THUMBNAIL == "box2d" assert loader.config.GAMELIST_MEDIA_IMAGE == "screenshot" + + +def test_missing_config_file_is_created(tmp_path): + config_file = tmp_path / "config" / "config.yml" + + loader = ConfigManager(str(config_file)) + + assert config_file.parent.exists() + assert config_file.exists() + assert config_file.read_text() == "" + assert loader.config.CONFIG_FILE_MOUNTED + assert loader.config.CONFIG_FILE_WRITABLE