-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🔧 Implement configuration management system
Introduce ConfigManager for centralized config handling - Add ConfigManager class in config.py for managing AeonSync settings - Implement TOML-based configuration file storage - Update CLI to use new configuration system - Add unit tests for configuration management - Integrate appdirs for cross-platform config directory handling - Update dependencies in pyproject.toml This change improves configuration management, allowing for easier user customization and more robust handling of settings across different environments.
- Loading branch information
1 parent
212045b
commit f73637d
Showing
5 changed files
with
321 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,116 @@ | ||
"""Configuration module for AeonSync.""" | ||
|
||
import socket | ||
from typing import List, Optional, Union, NamedTuple | ||
from typing import List, NamedTuple, Optional, Union, Dict, Any | ||
from pathlib import Path | ||
|
||
# Configuration | ||
HOSTNAME = socket.gethostname() | ||
DEFAULT_REMOTE = "bliss@cloudless:/volume1/rsync_backups/aeonsync" | ||
DEFAULT_RETENTION_PERIOD = 7 # Default number of days to keep backups | ||
METADATA_FILE_NAME = "backup_metadata.json" | ||
import toml | ||
from appdirs import user_config_dir | ||
|
||
|
||
class ConfigManager: | ||
"""Manages AeonSync configuration.""" | ||
|
||
APP_NAME = "aeonsync" | ||
CONFIG_FILE_NAME = "config.toml" | ||
|
||
def __init__(self, config_dir: Optional[Path] = None): | ||
self.config_dir = config_dir or Path(user_config_dir(self.APP_NAME)) | ||
self.config_file_path = self.config_dir / self.CONFIG_FILE_NAME | ||
self.config: Dict[str, Any] = {} # Initialize config as an empty dict | ||
self.load_config() # Load the configuration | ||
|
||
@property | ||
def default_config(self) -> Dict[str, Any]: | ||
return { | ||
"hostname": socket.gethostname(), | ||
"remote_address": "[email protected]", | ||
"remote_path": "/path/to/backups", | ||
"remote_port": 22, | ||
"retention_period": 7, | ||
"source_dirs": [str(Path.home())], | ||
"exclusions": [ | ||
".cache", | ||
"*/caches/*", | ||
".local/share/Trash", | ||
"*/node_modules", | ||
"*/.venv", | ||
"*/venv", | ||
"*/__pycache__", | ||
"*/.gradle", | ||
"*/build", | ||
"*/target", | ||
"*/.cargo", | ||
"*/dist", | ||
"*/.npm", | ||
"*/.yarn", | ||
"*/.pub-cache", | ||
], | ||
"ssh_key": str(Path.home() / ".ssh" / "id_rsa"), | ||
"verbose": False, | ||
"log_file": str( | ||
Path.home() / ".local" / "share" / self.APP_NAME / "aeonsync.log" | ||
), | ||
} | ||
|
||
def load_config(self) -> None: | ||
"""Load the configuration from file or create with default values if it doesn't exist.""" | ||
self.config_dir.mkdir(parents=True, exist_ok=True) | ||
if self.config_file_path.exists(): | ||
with open(self.config_file_path, "r", encoding="utf-8") as config_file: | ||
self.config = toml.load(config_file) | ||
else: | ||
# If the config file doesn't exist, use default values | ||
self.config = self.default_config.copy() | ||
self.save_config(self.config) | ||
|
||
def save_config(self, new_config: Dict[str, Any]) -> None: | ||
"""Save the configuration to file.""" | ||
self.config_dir.mkdir(parents=True, exist_ok=True) | ||
with open(self.config_file_path, "w", encoding="utf-8") as config_file: | ||
toml.dump(new_config, config_file) | ||
self.config = new_config | ||
|
||
# Default source directory | ||
DEFAULT_SOURCE_DIRS: List[str] = ["/home/bliss"] | ||
|
||
# Exclusions | ||
EXCLUSIONS: List[str] = [ | ||
".cache", | ||
"*/caches/*", | ||
".local/share/Trash", | ||
"*/node_modules", | ||
"*/.venv", | ||
"*/venv", | ||
"*/__pycache__", | ||
"*/.gradle", | ||
"*/build", | ||
"*/target", | ||
"*/.cargo", | ||
"*/dist", | ||
"*/.npm", | ||
"*/.yarn", | ||
"*/.pub-cache", | ||
] | ||
def get(self, key: str, default: Any = None) -> Any: | ||
"""Get a configuration value.""" | ||
return self.config.get(key, default) | ||
|
||
def set(self, key: str, value: Any) -> None: | ||
"""Set a configuration value and save the configuration.""" | ||
self.config[key] = value | ||
self.save_config(self.config) | ||
|
||
def add_to_list(self, key: str, value: Any) -> None: | ||
"""Add a value to a list configuration item.""" | ||
if key not in self.config or not isinstance(self.config[key], list): | ||
self.config[key] = [] | ||
if value not in self.config[key]: | ||
self.config[key].append(value) | ||
self.save_config(self.config) | ||
|
||
def remove_from_list(self, key: str, value: Any) -> None: | ||
"""Remove a value from a list configuration item.""" | ||
if key in self.config and isinstance(self.config[key], list): | ||
if value in self.config[key]: | ||
self.config[key].remove(value) | ||
self.save_config(self.config) | ||
|
||
|
||
config_manager = ConfigManager() | ||
|
||
# Expose configuration values as module-level variables | ||
HOSTNAME = config_manager.get("hostname") | ||
DEFAULT_REMOTE = ( | ||
f"{config_manager.get('remote_address')}:{config_manager.get('remote_path')}" | ||
) | ||
DEFAULT_RETENTION_PERIOD = config_manager.get("retention_period") | ||
METADATA_FILE_NAME = "backup_metadata.json" | ||
DEFAULT_SOURCE_DIRS: List[str] = config_manager.get("source_dirs") | ||
EXCLUSIONS: List[str] = config_manager.get("exclusions") | ||
DEFAULT_SSH_KEY = config_manager.get("ssh_key") | ||
DEFAULT_REMOTE_PORT = config_manager.get("remote_port") | ||
VERBOSE = config_manager.get("verbose") | ||
LOG_FILE = config_manager.get("log_file") | ||
|
||
|
||
class BackupConfig(NamedTuple): | ||
|
@@ -40,8 +120,8 @@ class BackupConfig(NamedTuple): | |
sources: List[Union[str, Path]] | ||
full: bool = False | ||
dry_run: bool = False | ||
ssh_key: Optional[str] = None | ||
remote_port: Optional[int] = None | ||
verbose: bool = False | ||
ssh_key: Optional[str] = DEFAULT_SSH_KEY | ||
remote_port: Optional[int] = DEFAULT_REMOTE_PORT | ||
verbose: bool = VERBOSE | ||
retention_period: int = DEFAULT_RETENTION_PERIOD | ||
log_file: Optional[str] = None | ||
log_file: Optional[str] = LOG_FILE |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.