forked from Shinonome/caelestia-cli
174 lines
6.7 KiB
Python
174 lines
6.7 KiB
Python
import shutil
|
|
import textwrap
|
|
from argparse import Namespace
|
|
from pathlib import Path
|
|
|
|
from caelestia.utils.dots.deployer import Deployer
|
|
from caelestia.utils.dots.manifest import ComponentError, Manifest, ManifestError
|
|
from caelestia.utils.dots.misc import build_local_packages, run_hooks
|
|
from caelestia.utils.dots.packages import DEFAULT_AUR_HELPER, PackageInstaller
|
|
from caelestia.utils.dots.source import DotsSource, SourceError
|
|
from caelestia.utils.dots.state import DotsState
|
|
from caelestia.utils.io import confirm, disable_input, fatal, info, log, pause, prompt_selection, warn
|
|
from caelestia.utils.paths import (
|
|
config_backup_dir,
|
|
config_dir,
|
|
)
|
|
|
|
|
|
def _parse_list_arg(value: str | None) -> list[str] | None:
|
|
if value is None:
|
|
return None
|
|
return [item.strip() for item in value.split(",") if item.strip()]
|
|
|
|
|
|
class Command:
|
|
args: Namespace
|
|
|
|
def __init__(self, args: Namespace) -> None:
|
|
self.args = args
|
|
|
|
def run(self) -> None:
|
|
if self.args.noconfirm:
|
|
disable_input()
|
|
|
|
self.print_greeting()
|
|
self.create_backup()
|
|
|
|
source, tip, manifest = self.fetch_manifest()
|
|
deployed = self.deploy_configs(source, manifest)
|
|
helper, packages, local_packages = self.install_packages(source, manifest)
|
|
run_hooks(manifest, "post_install")
|
|
|
|
DotsState(
|
|
aur_helper=helper,
|
|
applied_rev=tip,
|
|
enabled_components=manifest.enabled_components,
|
|
packages=packages,
|
|
local_packages=local_packages,
|
|
deployed_files=deployed,
|
|
).save()
|
|
|
|
self.print_done()
|
|
|
|
def print_greeting(self) -> None:
|
|
print(
|
|
"\033[38;2;150;241;241m" # Caelestia colour
|
|
+ textwrap.dedent(
|
|
r"""
|
|
╭─────────────────────────────────────────────────╮
|
|
│ ______ __ __ _ │
|
|
│ / ____/___ ____ / /__ _____/ /_(_)___ _ │
|
|
│ / / / __ `/ _ \/ / _ \/ ___/ __/ / __ `/ │
|
|
│ / /___/ /_/ / __/ / __(__ ) /_/ / /_/ / │
|
|
│ \____/\__,_/\___/_/\___/____/\__/_/\__,_/ │
|
|
│ │
|
|
╰─────────────────────────────────────────────────╯
|
|
"""
|
|
)
|
|
+ "\033[0m"
|
|
)
|
|
info("Welcome to the Caelestia dotfiles installer!")
|
|
info("Here's a quick overview on what this command is going to do:")
|
|
info(" - Install dependencies")
|
|
info(" - Install config files")
|
|
info("The installer does NOT set up hardware/system level configs (e.g. drivers). Please do this yourself.")
|
|
pause()
|
|
print()
|
|
|
|
def create_backup(self) -> None:
|
|
if config_dir.exists():
|
|
if not confirm("Back up the config directory?", default=True):
|
|
return
|
|
|
|
log(f"Creating a backup of {config_dir}...")
|
|
if config_backup_dir.exists():
|
|
if not confirm("A backup already exists, overwrite?", default=False):
|
|
info("Not creating backup.")
|
|
return
|
|
|
|
log("Deleting old backup...")
|
|
shutil.rmtree(config_backup_dir)
|
|
|
|
shutil.copytree(config_dir, config_backup_dir, symlinks=True)
|
|
info(f"Created backup at {config_backup_dir}")
|
|
|
|
def fetch_manifest(self) -> tuple[DotsSource, str, Manifest]:
|
|
print()
|
|
log("Fetching dots repo...")
|
|
source = DotsSource()
|
|
try:
|
|
source.ensure()
|
|
tip = source.checkout_tip()
|
|
except SourceError as e:
|
|
fatal(e)
|
|
|
|
enable = _parse_list_arg(self.args.enable_components)
|
|
disable = _parse_list_arg(self.args.disable_components)
|
|
try:
|
|
manifest = source.manifest_at(tip)
|
|
|
|
# No flags given, prompt user for non-default components
|
|
if enable is None and disable is None:
|
|
optional = [name for name, comp in manifest.components.items() if not comp.default]
|
|
if optional:
|
|
enable = prompt_selection(optional, "Components to enable?")
|
|
|
|
manifest.resolve_components(enable=enable, disable=disable)
|
|
except (SourceError, ManifestError, ComponentError) as e:
|
|
fatal(e)
|
|
|
|
names = ", ".join(manifest.enabled_components) or "none"
|
|
info(f"Enabled components: {names}")
|
|
|
|
return source, tip, manifest
|
|
|
|
def deploy_configs(self, source: DotsSource, manifest: Manifest) -> dict[str, str]:
|
|
print()
|
|
log("Installing configs...")
|
|
deployer = Deployer()
|
|
for entry in manifest.enabled_entries():
|
|
src = source.working_path(entry.expanded_src())
|
|
if not src.exists():
|
|
warn(f"missing in source, skipping: {entry.src}")
|
|
continue
|
|
|
|
dests = entry.expanded_dests()
|
|
if not dests:
|
|
warn(f"dest glob matched nothing, skipping: {entry.dest}")
|
|
continue
|
|
|
|
for dest in dests:
|
|
deployer.place(src, Path(dest))
|
|
info(f"{entry.src} -> {dest}")
|
|
|
|
return deployer.deployed_files
|
|
|
|
def install_packages(self, source: DotsSource, manifest: Manifest) -> tuple[str, list[str], dict[str, list[str]]]:
|
|
installer = PackageInstaller.get(self.args.aur_helper, self.args.noconfirm)
|
|
|
|
packages = manifest.enabled_packages()
|
|
if packages:
|
|
print()
|
|
log("Installing packages...")
|
|
installer.install(packages)
|
|
|
|
local_packages = {}
|
|
local_dirs = manifest.enabled_local_packages()
|
|
if local_dirs:
|
|
print()
|
|
log("Building local packages...")
|
|
local_packages = build_local_packages(installer, source, local_dirs)
|
|
|
|
return getattr(installer, "helper", DEFAULT_AUR_HELPER), packages, local_packages
|
|
|
|
def print_done(self) -> None:
|
|
print()
|
|
info("All done! Caelestia has been installed.")
|
|
info("A few things to finish up:")
|
|
info(" - A reboot is recommended for all changes take effect")
|
|
info(" - Edit `~/.config/caelestia/hypr-vars.conf` to set default apps, keybinds and much more")
|
|
info(" - Edit `~/.config/caelestia/hypr-user.conf` to set your monitor layout and other Hyprland configs")
|
|
info(" - Run `caelestia update` later to pull in the latest changes")
|
|
info("Enjoy! For support (or to just hang out), join our Discord server: https://discord.gg/BGDCFCmMBk")
|