summaryrefslogtreecommitdiff
path: root/dots.py
diff options
context:
space:
mode:
Diffstat (limited to 'dots.py')
-rw-r--r--dots.py190
1 files changed, 190 insertions, 0 deletions
diff --git a/dots.py b/dots.py
new file mode 100644
index 0000000..ed0af77
--- /dev/null
+++ b/dots.py
@@ -0,0 +1,190 @@
+#!/usr/bin/env python3
+
+import argparse
+import json
+import os
+import shutil
+import subprocess
+import sys
+from pathlib import Path
+import jinja2
+
+def get_platform():
+ """Get the platform using the platform.sh script"""
+ try:
+ script_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "home/scripts/platform.sh")
+ result = subprocess.run([script_path], capture_output=True, text=True, check=True)
+ return result.stdout.strip()
+ except subprocess.CalledProcessError as e:
+ print(f"Error getting platform: {e}")
+ sys.exit(1)
+
+def get_device_name():
+ """Get the device name using the system_name.sh script"""
+ try:
+ script_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "home/scripts/system_name.sh")
+ result = subprocess.run([script_path], capture_output=True, text=True, check=True)
+ return result.stdout.strip()
+ except subprocess.CalledProcessError as e:
+ print(f"Error getting device name: {e}")
+ sys.exit(1)
+
+def load_context(platform, device_name):
+ """Load the appropriate context from contexts.json"""
+ try:
+ with open('contexts.json', 'r') as f:
+ contexts = json.load(f)
+
+ if platform not in contexts:
+ print(f"Warning: Platform '{platform}' not found in contexts.json")
+ return {}
+
+ if device_name not in contexts[platform]:
+ print(f"Warning: Device '{device_name}' not found for platform '{platform}' in contexts.json")
+ # Try to use 'default' for the platform if device not found
+ if 'default' in contexts[platform]:
+ print(f"Using default context for platform '{platform}'")
+ return contexts[platform]['default']
+ return {}
+
+ return contexts[platform][device_name]
+ except FileNotFoundError:
+ print("Warning: contexts.json not found")
+ return {}
+ except json.JSONDecodeError as e:
+ print(f"Error parsing contexts.json: {e}")
+ sys.exit(1)
+
+def compile_dotfiles(source_dir, target_dir, context):
+ """Compile dotfiles, processing Jinja templates with context"""
+ # Create jinja environment
+ env = jinja2.Environment(
+ undefined=jinja2.StrictUndefined,
+ trim_blocks=True,
+ lstrip_blocks=True
+ )
+
+ # Ensure target directory exists
+ target_path = Path(target_dir)
+ target_path.mkdir(exist_ok=True, parents=True)
+
+ # Process each file in source directory
+ for root, dirs, files in os.walk(source_dir):
+ # Skip .git directories
+ if '.git' in dirs:
+ dirs.remove('.git')
+
+ # Create relative path from source_dir
+ rel_path = os.path.relpath(root, source_dir)
+ if rel_path == '.':
+ rel_path = ''
+
+ # Create target directory
+ if rel_path:
+ target_subdir = target_path / rel_path
+ target_subdir.mkdir(exist_ok=True, parents=True)
+ else:
+ target_subdir = target_path
+
+ for file in files:
+ source_file = os.path.join(root, file)
+
+ # Determine target filename (remove .j2 extension for templates)
+ target_file_name = file[:-3] if file.endswith('.j2') else file
+ target_file = target_subdir / target_file_name
+
+ print(f"Processing: {source_file} -> {target_file}")
+
+ if file.endswith('.j2'):
+ # Render Jinja2 template
+ try:
+ with open(source_file, 'r') as f:
+ template_content = f.read()
+
+ # Use the environment to create templates
+ template = env.from_string(template_content)
+ rendered_content = template.render(**context)
+
+ # Write rendered content to target file
+ with open(target_file, 'w') as f:
+ f.write(rendered_content)
+
+ # Make executable if source is executable
+ if os.access(source_file, os.X_OK):
+ os.chmod(target_file, 0o755)
+
+ except Exception as e:
+ print(f"Error rendering template {source_file}: {e}")
+ else:
+ # Copy file as-is
+ shutil.copy2(source_file, target_file)
+
+def stow_dotfiles(dotfiles_dir, target_dir=None):
+ """Use GNU Stow to symlink the compiled dotfiles"""
+ if target_dir is None:
+ target_dir = os.path.expanduser("~")
+
+ # Check if stow is installed
+ try:
+ subprocess.run(["stow", "--version"], capture_output=True, check=True)
+ except (subprocess.CalledProcessError, FileNotFoundError):
+ print("Error: GNU Stow not found. Please install it before using --stow.")
+ sys.exit(1)
+
+ # Get list of directories in dotfiles_dir (each is a stow package)
+ packages = [d for d in os.listdir(dotfiles_dir) if os.path.isdir(os.path.join(dotfiles_dir, d))]
+
+ for package in packages:
+ print(f"Stowing package: {package}")
+ try:
+ # Use --adopt to replace existing files
+ # Use --no-folding to enable leaf mode (each file individually linked)
+ subprocess.run([
+ "stow",
+ "--dir=" + dotfiles_dir,
+ "--target=" + target_dir,
+ "--adopt",
+ "--no-folding",
+ package
+ ], check=True)
+ print(f"Successfully stowed {package}")
+ except subprocess.CalledProcessError as e:
+ print(f"Error stowing {package}: {e}")
+
+def main():
+ parser = argparse.ArgumentParser(description="Dotfiles manager")
+ parser.add_argument("--compile", action="store_true", help="Compile dotfiles")
+ parser.add_argument("--stow", action="store_true", help="Stow compiled dotfiles")
+ parser.add_argument("--target", help="Target directory for stow (default: $HOME)")
+ args = parser.parse_args()
+
+ if not args.compile and not args.stow:
+ parser.print_help()
+ sys.exit(1)
+
+ platform = get_platform()
+ device_name = get_device_name()
+ print(f"Platform: {platform}, Device: {device_name}")
+
+ context = load_context(platform, device_name)
+ print(f"Loaded context: {context}")
+
+ # Add platform and device_name to context
+ context['platform'] = platform
+ context['device_name'] = device_name
+
+ compiled_dir = ".compiled_dotfiles"
+
+ if args.compile:
+ print(f"Compiling dotfiles from 'dotfiles' to '{compiled_dir}'...")
+ compile_dotfiles("dotfiles", compiled_dir, context)
+ print("Compilation complete.")
+
+ if args.stow:
+ target_dir = args.target if args.target else None
+ print(f"Stowing dotfiles from '{compiled_dir}'...")
+ stow_dotfiles(compiled_dir, target_dir)
+ print("Stowing complete.")
+
+if __name__ == "__main__":
+ main()