diff --git a/README.md b/README.md index 171a9ba..dde80a5 100644 --- a/README.md +++ b/README.md @@ -60,8 +60,17 @@ rostr add # Generate configs rostr sync +# The base directory (BASE_DIR) is determined as: +# - If ROSTR_HOME is set: $ROSTR_HOME +# - If XDG_CONFIG_HOME is set: $XDG_CONFIG_HOME/rostr +# - Otherwise: ~/.rostr + +# Set BASE_DIR for use in commands below +BASE_DIR="${ROSTR_HOME:-${XDG_CONFIG_HOME:+$XDG_CONFIG_HOME/rostr}}" +BASE_DIR="${BASE_DIR:-$HOME/.rostr}" + # Include in your SSH config -echo "Include ~/.rostr/generated/ssh_config" >> ~/.ssh/config +echo "Include $BASE_DIR/generated/ssh_config" >> ~/.ssh/config ``` ## Usage @@ -108,7 +117,11 @@ rostr setup automation # Configure cloud-init registration ## Directory Structure ``` -~/.rostr/ +# Set BASE_DIR for reference +BASE_DIR="${ROSTR_HOME:-${XDG_CONFIG_HOME:+$XDG_CONFIG_HOME/rostr}}" +BASE_DIR="${BASE_DIR:-$HOME/.rostr}" + +$BASE_DIR/ ├── config.yaml # Configuration ├── inventory/ │ ├── _defaults.yaml # Default values @@ -150,7 +163,11 @@ servers: ### SSH Config ``` -# ~/.rostr/generated/ssh_config +# Set BASE_DIR for reference +BASE_DIR="${ROSTR_HOME:-${XDG_CONFIG_HOME:+$XDG_CONFIG_HOME/rostr}}" +BASE_DIR="${BASE_DIR:-$HOME/.rostr}" + +# $BASE_DIR/generated/ssh_config Host web-prod-01 HostName 10.0.1.10 User admin @@ -165,7 +182,11 @@ Groups are auto-generated by: - Notes: `notes_primary` ```bash -ansible -i ~/.rostr/generated/ansible-inventory.yaml tag_webserver -m ping +# Set BASE_DIR for reference +BASE_DIR="${ROSTR_HOME:-${XDG_CONFIG_HOME:+$XDG_CONFIG_HOME/rostr}}" +BASE_DIR="${BASE_DIR:-$HOME/.rostr}" + +ansible -i $BASE_DIR/generated/ansible-inventory.yaml tag_webserver -m ping ``` ## Configuration diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 4c35553..4ff65b5 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -65,7 +65,8 @@ These are applied when adding servers if not explicitly specified. | Variable | Description | Default | |----------|-------------|---------| -| `ROSTR_HOME` | Base directory | `~/.rostr` | +| `ROSTR_HOME` | Base directory | `$XDG_CONFIG_HOME/rostr` or `~/.rostr` | +| `XDG_CONFIG_HOME` | XDG config directory | `~/.config` | | `ROSTR_UPTIMEKUMA_SSH_HOST` | SSH host override | From config | | `ROSTR_UPTIMEKUMA_DOCKER_CMD` | Docker command path | `docker` | | `ROSTR_UPTIMEKUMA_DB_USER` | Database username | `kuma` | diff --git a/src/rostr/cli.py b/src/rostr/cli.py index 0aa4f35..b241300 100644 --- a/src/rostr/cli.py +++ b/src/rostr/cli.py @@ -10,6 +10,7 @@ import logging import os import re +import shutil import subprocess import sys from concurrent.futures import ThreadPoolExecutor, as_completed @@ -43,12 +44,15 @@ # Initialize Rich console with theme console = Console(theme=CATPPUCCIN) -# Determine base directory from environment or default to ~/.rostr +# Determine base directory from environment or default def get_base_dir() -> Path: - """Get base directory from ROSTR_HOME environment variable or default.""" + """Get base directory from environment or default.""" env_home = os.environ.get("ROSTR_HOME") if env_home: return Path(env_home).expanduser() + xdg_config = os.environ.get("XDG_CONFIG_HOME") + if xdg_config: + return Path(xdg_config) / "rostr" return Path.home() / ".rostr" BASE_DIR = get_base_dir() @@ -517,6 +521,14 @@ def init(ctx): """Initialize rostr in the current or default directory.""" console.print("[primary]Welcome to rostr![/primary]\n") + # Check for migration from old .rostr location + old_base = Path.home() / ".rostr" + if old_base.exists() and not BASE_DIR.exists() and os.environ.get("XDG_CONFIG_HOME"): + if Confirm.ask(f"Found existing rostr config at {old_base}. Would you like to migrate to the new XDG location {BASE_DIR}?", default=True): + shutil.copytree(old_base, BASE_DIR) + console.print(f"[success]✓[/success] Migrated from {old_base} to {BASE_DIR}") + console.print("You can now remove the old directory if desired.\n") + # Check if already initialized if CONFIG_FILE.exists(): if not Confirm.ask(f"rostr is already initialized at {BASE_DIR}. Reinitialize?", default=False): @@ -553,7 +565,7 @@ def init(ctx): console.print(f" [success]✓[/success] {DEFAULTS_FILE}") # Create config file - config_content = """# rostr configuration + config_content = f"""# rostr configuration # See docs/CONFIGURATION.md for all options defaults: @@ -585,10 +597,10 @@ def init(ctx): enabled: false paths: - inventory: ~/.rostr/inventory - generated: ~/.rostr/generated - backups: ~/.rostr/backups - logs: ~/.rostr/logs + inventory: {BASE_DIR}/inventory + generated: {BASE_DIR}/generated + backups: {BASE_DIR}/backups + logs: {BASE_DIR}/logs """ if not CONFIG_FILE.exists(): with open(CONFIG_FILE, "w") as f: @@ -669,7 +681,7 @@ def init(ctx): "1. Edit config.yaml with your settings", "2. Add your first server: [cyan]rostr add[/cyan]", "3. Generate configs: [cyan]rostr sync[/cyan]", - "4. Include in SSH: [dim]echo \"Include ~/.rostr/generated/ssh_config\" >> ~/.ssh/config[/dim]", + f"4. Include in SSH: [dim]echo \"Include {BASE_DIR}/generated/ssh_config\" >> ~/.ssh/config[/dim]", ]) console.print("\n[primary]Next steps:[/primary]") diff --git a/src/rostr/setup.py b/src/rostr/setup.py index f03eda7..517e430 100644 --- a/src/rostr/setup.py +++ b/src/rostr/setup.py @@ -738,7 +738,7 @@ def generate_cloud_init_template(webhook_url: str, token: str, default_group: st def save_cloud_init_templates(webhook_url: str, token: str, groups: list[str], ssh_pubkey: str, username: str) -> list[Path]: """Generate and save cloud-init template for each group.""" - from rostr.cli import GENERATED_DIR + from rostr.cli import BASE_DIR, GENERATED_DIR output_dir = Path(GENERATED_DIR) / "cloud-init" output_dir.mkdir(parents=True, exist_ok=True) @@ -881,7 +881,7 @@ def generate_n8n_workflow( def save_n8n_workflow(workflow: dict) -> Path: """Save n8n workflow JSON to generated directory.""" - from rostr.cli import GENERATED_DIR + from rostr.cli import BASE_DIR, GENERATED_DIR output_dir = Path(GENERATED_DIR) / "n8n" output_dir.mkdir(parents=True, exist_ok=True) @@ -1179,8 +1179,8 @@ def automation(): console.print() console.print("[bold]Quick Reference:[/bold]") - console.print(" Cloud-init templates: [cyan]~/.rostr/generated/cloud-init/[/cyan]") - console.print(" n8n workflow: [cyan]~/.rostr/generated/n8n/server-registration-workflow.json[/cyan]") + console.print(f" Cloud-init templates: [cyan]{BASE_DIR}/generated/cloud-init/[/cyan]") + console.print(f" n8n workflow: [cyan]{BASE_DIR}/generated/n8n/server-registration-workflow.json[/cyan]") console.print(f" Webhook URL: [cyan]{webhook_url}[/cyan]") console.print(f" Groups: [cyan]{', '.join(groups)}[/cyan]") console.print(f" SSH user: [cyan]{cloud_username}[/cyan]")