(installer) initial work on the installer

This commit is contained in:
Eugene Brodsky 2023-01-08 03:09:04 -05:00
parent c18db4e47b
commit 2296f5449e
4 changed files with 218 additions and 0 deletions

0
installer/__init__.py Normal file
View File

116
installer/installer.py Normal file
View File

@ -0,0 +1,116 @@
"""
InvokeAI installer script
"""
import subprocess
import sys
import venv
from pathlib import Path
from tempfile import TemporaryDirectory
SUPPORTED_PYTHON = ">=3.9.0,<3.11"
INSTALLER_REQS = ["rich", "semver"]
PLATFORM = sys.platform
### Feature flags
# Place the virtualenv inside the runtime dir
# (default for 2.2.5) == True
VENV_IN_RUNTIME = True
# Install the wheel from pypi
# (default for 2.2.5) == False
USE_WHEEL = False
class Installer:
"""
Deploys an InvokeAI installation into a given path
"""
def __init__(self) -> None:
self.reqs = INSTALLER_REQS
self.preflight()
self.bootstrap()
def preflight(self) -> None:
"""
Preflight checks
"""
# TODO
# verify python version
# on macOS verify XCode tools are present
# verify libmesa, libglx on linux
# check that the system arch is not i386 (?)
# check that the system has a GPU, and the type of GPU
pass
def inst_venv(self) -> TemporaryDirectory:
"""
Creates a temporary virtual environment for the installer itself
:return: path to the created virtual environment directory
:rtype: TemporaryDirectory
"""
venv_dir = TemporaryDirectory(prefix="invokeai-installer-")
venv.create(venv_dir.name, with_pip=True)
sys.path.append(f"{venv_dir.name}/lib/python{sys.version_info.major}.{sys.version_info.minor}/site-packages")
self.venv_dir = venv_dir
return venv_dir
def bootstrap(self, verbose: bool = False) -> TemporaryDirectory:
"""
Bootstrap the installer venv with packages required at install time
:return: path to the virtual environment directory that was bootstrapped
:rtype: TemporaryDirectory
"""
print("Initializing the installer, please wait...")
venv_dir = self.inst_venv()
pip = str(Path(venv_dir.name).absolute() / "bin/pip")
cmd = [pip, "install", "--require-virtualenv"]
cmd.extend(self.reqs)
try:
res = subprocess.check_output(cmd).decode()
if verbose:
print(res)
return venv_dir
except subprocess.CalledProcessError as e:
print(e)
def install(self, path: str = "~/invokeai", version: str = "latest") -> None:
"""
Install the InvokeAI application into the given runtime path
:param path: Destination path for the installation
:type path: str
:param version: InvokeAI version to install
:type version: str
"""
from messages import dest_path, welcome
welcome()
self.dest = dest_path(path)
def application_venv():
"""
Create a virtualenv for the InvokeAI installation
"""
pass
class InvokeAiDeployment:
"""
Manages an installed instance of InvokeAI
"""
def __init__(self, path) -> None:
pass

14
installer/main.py Normal file
View File

@ -0,0 +1,14 @@
"""
InvokeAI Installer
"""
from installer import Installer
if __name__ == "__main__":
inst = Installer()
try:
inst.install()
except KeyboardInterrupt as exc:
print("\n")
print("Ctrl-C pressed. Aborting.")
print("See you again soon!")

88
installer/messages.py Normal file
View File

@ -0,0 +1,88 @@
"""
Installer user interaction
"""
import platform
from pathlib import Path
from tkinter.filedialog import askdirectory
from rich import box, print
from rich.console import Console
from rich.panel import Panel
from rich.prompt import Confirm
from rich.style import Style
from rich.text import Text
console = Console(width=80)
OS = platform.uname().system
ARCH = platform.uname().machine
def welcome():
console.rule()
print(
Panel(
title="[bold wheat1]Welcome to the InvokeAI Installer!",
renderable=Text(
"Some of the installation steps take a long time to run. Please be patient. If the script appears to hang for more than 10 minutes, please interrupt with control-C and retry.",
justify="center",
),
box=box.DOUBLE,
width=80,
expand=False,
padding=(1, 2),
style=Style(bgcolor="grey23", color="orange1"),
subtitle=f"[wheat1] Installing for [bold]{OS}-{ARCH}",
)
)
console.line()
def dest_path(init_path=None) -> Path:
"""
Prompt the user for the destination path and create the path
:param init_path: a filesystem path, defaults to None
:type init_path: str, optional
:return: absolute path to the created installation directory
:rtype: Path
"""
dest = init_path
dest_confirmed = False
while not dest_confirmed:
console.line()
if dest is not None:
dest = Path(dest).expanduser().resolve()
print(f"InvokeAI will be installed at {dest}")
dest_confirmed = Confirm.ask(f"Continue?")
if not dest_confirmed:
print(f"Please select the destination directory for the installation")
resp = askdirectory(initialdir=dest)
if resp == ():
continue
dest = Path(resp).expanduser().resolve()
if dest.exists():
print(f":exclamation: Directory {dest} already exists.")
dest_confirmed = Confirm.ask(
":question: Are you sure you want to (re)install in this location?", default="y"
)
try:
dest.mkdir(exist_ok=True, parents=True)
return dest
except PermissionError as exc:
print(
f"Failed to create directory {dest} due to insufficient permissions",
style=Style(color="red"),
highlight=True,
)
except OSError as exc:
console.print_exception(exc)
if Confirm.ask("Would you like to try again?"):
dest_path(init_path)
else:
console.rule("Goodbye!")