mirror of
https://github.com/checktheroads/hyperglass
synced 2024-05-11 05:55:08 +00:00
115 lines
3.2 KiB
Python
115 lines
3.2 KiB
Python
"""Install hyperglass."""
|
|
|
|
# Standard Library
|
|
import os
|
|
import shutil
|
|
from filecmp import dircmp
|
|
from pathlib import Path
|
|
|
|
# Third Party
|
|
import inquirer
|
|
|
|
# Local
|
|
from .echo import error, success, warning
|
|
from .util import create_dir
|
|
|
|
USER_PATH = Path.home() / "hyperglass"
|
|
ROOT_PATH = Path("/etc/hyperglass/")
|
|
ASSET_DIR = Path(__file__).parent.parent / "images"
|
|
IGNORED_FILES = [".DS_Store"]
|
|
|
|
INSTALL_PATHS = [
|
|
inquirer.List(
|
|
"install_path",
|
|
message="Choose a directory for hyperglass",
|
|
choices=[USER_PATH, ROOT_PATH],
|
|
)
|
|
]
|
|
|
|
|
|
def prompt_for_path() -> str:
|
|
"""Recursively prompt the user for an app path until one is provided."""
|
|
|
|
answer = inquirer.prompt(INSTALL_PATHS)
|
|
|
|
if answer is None:
|
|
warning("A directory for hyperglass is required")
|
|
answer = prompt_for_path()
|
|
|
|
return answer["install_path"]
|
|
|
|
|
|
class Installer:
|
|
"""Install hyperglass."""
|
|
|
|
def __init__(self, unattended: bool):
|
|
"""Initialize installer."""
|
|
|
|
self.unattended = unattended
|
|
|
|
def install(self) -> None:
|
|
"""Complete the installation."""
|
|
|
|
self.app_path = self._get_app_path()
|
|
self._scaffold()
|
|
self._migrate_static_assets()
|
|
|
|
def _get_app_path(self) -> Path:
|
|
"""Find the app path from env variables or a prompt."""
|
|
|
|
if self.unattended:
|
|
return USER_PATH
|
|
|
|
app_path = os.environ.get("HYPERGLASS_PATH", None)
|
|
|
|
if app_path is None:
|
|
app_path = prompt_for_path()
|
|
|
|
return app_path
|
|
|
|
def _scaffold(self) -> None:
|
|
"""Create the file structure necessary for hyperglass to run."""
|
|
|
|
ui_dir = self.app_path / "static" / "ui"
|
|
images_dir = self.app_path / "static" / "images"
|
|
favicon_dir = images_dir / "favicons"
|
|
custom_dir = self.app_path / "static" / "custom"
|
|
|
|
create_dir(self.app_path)
|
|
|
|
for path in (ui_dir, images_dir, favicon_dir, custom_dir):
|
|
create_dir(path, parents=True)
|
|
|
|
def _migrate_static_assets(self) -> bool:
|
|
"""Synchronize the project assets with the installation assets."""
|
|
|
|
target_dir = self.app_path / "static" / "images"
|
|
|
|
if not target_dir.exists():
|
|
shutil.copytree(ASSET_DIR, target_dir)
|
|
|
|
# Compare the contents of the project's asset directory (considered
|
|
# the source of truth) with the installation directory. If they do
|
|
# not match, delete the installation directory's asset directory and
|
|
# re-copy it.
|
|
compare_initial = dircmp(ASSET_DIR, target_dir, ignore=IGNORED_FILES)
|
|
|
|
if not compare_initial.left_list == compare_initial.right_list:
|
|
shutil.rmtree(target_dir)
|
|
shutil.copytree(ASSET_DIR, target_dir)
|
|
|
|
# Re-compare the source and destination directory contents to
|
|
# ensure they match.
|
|
compare_post = dircmp(ASSET_DIR, target_dir, ignore=IGNORED_FILES)
|
|
|
|
if not compare_post.left_list == compare_post.right_list:
|
|
error(
|
|
"Files in {a} do not match files in {b}",
|
|
a=str(ASSET_DIR),
|
|
b=str(target_dir),
|
|
)
|
|
return False
|
|
|
|
success("Migrated assets from {a} to {b}", a=str(ASSET_DIR), b=str(target_dir))
|
|
return True
|