mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
(installer) initial work on the installer
This commit is contained in:
parent
c18db4e47b
commit
2296f5449e
0
installer/__init__.py
Normal file
0
installer/__init__.py
Normal file
116
installer/installer.py
Normal file
116
installer/installer.py
Normal 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
14
installer/main.py
Normal 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
88
installer/messages.py
Normal 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!")
|
Loading…
Reference in New Issue
Block a user