Config Refactor: unify config handling
Build Library / Build Library (push) Failing after 5m55s

- Consolidated all system/user config logic into configmanager.py
- Removed duplicate loaders from setup.py and TUI.py
- Eliminated .env redundancy; now only stores WORKING_DIR
- Clarified boundaries: system config immutable, user config mutable
- Updated TUI to use save_user_config()
- Removed all deprecated/legacy config functions and aliases
This commit is contained in:
2025-11-17 12:00:32 -05:00
parent b198362ac8
commit 89654d3a8c
13 changed files with 432 additions and 417 deletions
+15 -42
View File
@@ -4,7 +4,6 @@ import sys
from typing import Optional
import dotenv
from dotenv import set_key
from textual.app import App, ComposeResult
from textual.containers import Vertical
from textual.message import Message
@@ -38,8 +37,8 @@ from TUI.resultsdisplay import ResultsDisplay
from TUI.theme_amber_terminal import get_amber_terminal_theme
from TUI.theme_retro_terminal import get_retro_terminal_theme
from TUI.themeselector import ThemeSelector
from utils.configmanager import load_env
from utils.setup import get_base_directory, load_user_config
from utils.configmanager import get_user_value, load_env, save_user_config
from utils.setup import get_base_directory
from utils.utils import open_directory
dotenv.load_dotenv()
@@ -58,47 +57,17 @@ logger = logging.getLogger(__name__)
# ---------------------------------------------------------------------------
def _persist_user_theme(theme_name: str) -> None:
"""
Store the chosen Textual theme in the user's config:
<base>/config/user_config.json
and also mirror to <base>/.env so load_env(...) sees it.
Store the chosen Textual theme in the user's config using the config manager.
No need to touch .env - config manager handles everything.
"""
base_dir = get_base_directory()
config_dir = base_dir / "config"
user_config_path = config_dir / "user_config.json"
env_path = base_dir / ".env"
# ensure dirs / files exist similarly to setup()
config_dir.mkdir(parents=True, exist_ok=True)
if not user_config_path.exists():
# minimal default like your load_user_config does
user_config_path.write_text(
'{"URL": "", "LOG_LEVEL": "INFO"}\n', encoding="utf-8"
)
# load existing user config
user_conf = load_user_config(config_dir)
user_conf["TEXTUAL_THEME"] = theme_name
# write it back
user_config_path.write_text(
# pretty print so it stays human-readable
__import__("json").dumps(user_conf, indent=4),
encoding="utf-8",
)
logger.debug("Updated user_config.json with TEXTUAL_THEME=%s", theme_name)
# mirror to .env (like setup.write_config_to_env does)
env_path.parent.mkdir(parents=True, exist_ok=True)
if not env_path.exists():
env_path.touch()
try:
set_key(str(env_path), "TEXTUAL_THEME", theme_name)
except Exception as exc: # keep going even if .env write fails
logger.warning("Failed to mirror TEXTUAL_THEME to .env: %s", exc)
# reload so load_env(...) sees the new value right now
dotenv.load_dotenv(dotenv_path=env_path, override=True)
logger.debug("Reloaded .env from %s", env_path)
save_user_config(config_dir, {"TEXTUAL_THEME": theme_name})
logger.debug("Updated user config with TEXTUAL_THEME=%s", theme_name)
except Exception as exc:
logger.error("Failed to save TEXTUAL_THEME: %s", exc)
# ---------------------------------------------------------------------------
@@ -115,7 +84,7 @@ class MainMenuScreen(Screen):
"move_agent_workflow_button",
),
("📊 - Review and appove OTP Activities", "otp_activities_button"),
("🔇 - Find and Move Quiet Hosts to Enforcement", "find_quiet_button"),
("📇 - Find and Move Quiet Hosts to Enforcement", "find_quiet_button"),
],
"policy": [
("🔒 - Prepare Policy For Enforcement", "policy_prep_button"),
@@ -126,7 +95,7 @@ class MainMenuScreen(Screen):
def __init__(self) -> None:
super().__init__()
self.extras = load_env("EXTRAS")
self.extras = get_user_value("EXTRAS", str, "NOTTODAY")
wd = load_env("WORKING_DIR") or os.getcwd()
if not os.path.isdir(wd):
wd = os.getcwd()
@@ -380,10 +349,11 @@ class Loxide(App[Message]):
BINDINGS = [
("q", "quit", "Quit"),
("f", "open_fe", "Launch Explorer"),
("r", "refresh", "Refresh"),
]
def __init__(self, api: AirlockAPIWrapper):
self._textual_theme = load_env("TEXTUAL_THEME") or "nord"
self._textual_theme = get_user_value("TEXTUAL_THEME", str, "nord")
super().__init__()
self.api = api
wd = load_env("WORKING_DIR") or os.getcwd()
@@ -421,6 +391,9 @@ class Loxide(App[Message]):
self.theme = self._textual_theme
self.push_screen(MainMenuScreen())
def action_refresh(self) -> None:
self.refresh_data()
def action_quit(self) -> None:
global _PENDING_JOB
_PENDING_JOB = None