diff --git a/installer/install.sh.in b/installer/install.sh.in index 997145c8dd..19d0d2f322 100644 --- a/installer/install.sh.in +++ b/installer/install.sh.in @@ -1,234 +1,9 @@ -#!/usr/bin/env bash - -# ensure we're in the correct folder in case user's CWD is somewhere else -scriptdir=$(dirname "$0") -cd "$scriptdir" - -# This version number will be replaced by the one supplied to create_installers.sh. -# Do not change it here - change it in create_installers.sh. -INVOKEAI_VERSION="latest" +#!/bin/bash # make sure we are not already in a venv # (don't need to check status) deactivate >/dev/null 2>&1 -INVOKE_AI_SRC=https://github.com/invoke-ai/InvokeAI/archive/refs/tags/${INVOKEAI_VERSION}.zip -INSTRUCTIONS=https://invoke-ai.github.io/InvokeAI/installation/INSTALL_AUTOMATED/ -TROUBLESHOOTING=https://invoke-ai.github.io/InvokeAI/installation/INSTALL_AUTOMATED/#troubleshooting -MINIMUM_PYTHON_VERSION=3.9.0 - set -euo pipefail -IFS=$'\n\t' -function _err_exit { - if test "$1" -ne 0 - then - echo -e "Error code $1; Error caught was '$2'" - if [ "$OS_NAME" == "osx" ]; then - echo "Something went wrong while installing InvokeAI and/or its requirements." - echo "You may need to use the Xcode command line tools to proceed. See step number 3 of" - echo "https://invoke-ai.github.io/InvokeAI/INSTALL_SOURCE#walk_through for" - echo "installation instructions and then run this script again." - else - echo "Something went wrong while installing InvokeAI and/or its requirements." - echo "See https://invoke-ai.github.io/InvokeAI/INSTALL_SOURCE#troubleshooting for troubleshooting" - echo "tips, or visit https://invoke-ai.github.io/InvokeAI/#installation for alternative" - echo "installation methods" - fi - read -p "Press any key to exit..." - exit - fi -} - -function readinput() { - local CLEAN_ARGS="" - while [[ $# -gt 0 ]]; do - local i="$1" - case "$i" in - "-i") - if read -i "default" 2>/dev/null <<< "test"; then - CLEAN_ARGS="$CLEAN_ARGS -i \"$2\"" - fi - shift - shift - ;; - "-p") - CLEAN_ARGS="$CLEAN_ARGS -p \"$2\"" - shift - shift - ;; - *) - CLEAN_ARGS="$CLEAN_ARGS $1" - shift - ;; - esac - done - eval read $CLEAN_ARGS -} - - -function version { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; } - -echo "InvokeAI simple installer..." -echo "" -echo "Some of the installation steps take a long time to run. Please be patient." -echo "If the script appears to hang for more than 10 minutes, please interrupt with control-C and retry." -read -n 1 -s -r -p "" -echo "" - -OS_NAME=$(uname -s) -case "${OS_NAME}" in - Linux*) OS_NAME="linux";; - Darwin*) OS_NAME="osx";; - *) echo "Unknown OS: $OS_NAME! This script runs only on Linux or Mac" && exit -esac - -OS_ARCH=$(uname -m) -case "${OS_ARCH}" in - x86_64*) OS_ARCH="64";; - arm64*) OS_ARCH="arm64";; - *) echo "Unknown system architecture: $OS_ARCH! This script runs only on x86_64 or arm64" && exit -esac - -echo "Installing for $OS_NAME-$OS_ARCH" -# confirm that python is installed and is up to date - -PYTHON="" -for candidate in python3.10 python3.9 python3 python python3.11 ; do - if ppath=`which $candidate`; then - python_version=$($ppath -V | awk '{ print $2 }') - if [ $(version $python_version) -ge $(version "$MINIMUM_PYTHON_VERSION") ]; then - PYTHON=$ppath - echo Python $python_version found at $PYTHON - break - fi - fi -done - -if [ -z "$PYTHON" ]; then - echo "A suitable Python interpreter could not be found" - echo "Please install Python 3.9 or higher before running this script. See instructions at $INSTRUCTIONS for help." - read -p "Press any key to exit" - exit -1 -fi - - -ROOTDIR="" -while [ "$ROOTDIR" == "" ] -do - echo - readinput -e -p "Select your preferred location for the 'invokeai' directory [$HOME]: " -i $HOME input - ROOTDIR=${input:=$HOME}/invokeai - - # This is surprisingly hard to do in plain Bash; easier in ZSH. Just running python in subshell is easiest. - ROOTDIR=$($PYTHON -c "from pathlib import Path; print(Path('${ROOTDIR}').expanduser().resolve())") - - read -e -p "InvokeAI will be installed into $ROOTDIR. OK? [y]: " input - RESPONSE=${input:='y'} - if [ "$RESPONSE" == 'y' ]; then - if [ -e "$ROOTDIR" ]; then - echo - read -e -p "Directory "$ROOTDIR" already exists. Do you want to resume an interrupted install? [y]: " input - RESPONSE=${input:='y'} - if [ "$RESPONSE" != 'y' ]; then - ROOTDIR="" - fi - else - mkdir -p "$ROOTDIR" - if [ $? -ne 0 ]; then - echo "Could not create "$ROOTDIR". Try again with a different install location." - ROOTDIR="" - fi - fi - else - ROOTDIR="" - fi -done - -#-------------------------------------------------------------------------------- -echo -echo "** Creating Virtual Environment for InvokeAI **" - -$PYTHON -mvenv "$ROOTDIR"/.venv -_err_exit $? "Python failed to create virtual environment "$ROOTDIR"/.venv. Please see $TROUBLESHOOTING for help." - -#-------------------------------------------------------------------------------- -echo -echo "** Activating Virtual Environment for InvokeAI **" - -source "$ROOTDIR"/.venv/bin/activate -_err_exit $? "Failed to activate virtual evironment "$ROOTDIR"/.venv. Please see $TROUBLESHOOTING for help." - -PYTHON="$ROOTDIR"/.venv/bin/python -$PYTHON -mensurepip --upgrade -$PYTHON -mpip install --upgrade pip - -#-------------------------------------------------------------------------------- -echo -echo "*** Installing InvokeAI Dependencies ***" - -if [ "$OS_NAME" == "osx" ]; then - echo "macOS detected. Installing MPS and CPU support." - egrep -v '^-e .' environments-and-requirements/requirements-mac-mps-cpu.txt >requirements.txt -else - if (lsmod | grep amdgpu) &>/dev/null ; then - readinput -e -p "Linux system with AMD GPU driver detected. Install ROCm AMD accelerated support? (Otherwise, CUDA support will be installed) [n]: " input - RESPONSE=${input:='n'} - if [ "$RESPONSE" != "n" ]; then - echo "Installing ROCm (AMD) support" - egrep -v '^-e .' environments-and-requirements/requirements-lin-amd.txt >requirements.txt - else - echo "Installing CUDA support" - egrep -v '^-e .' environments-and-requirements/requirements-lin-cuda.txt >requirements.txt - fi - else - echo "Linux system detected. Installing CUDA and CPU support." - egrep -v '^-e .' environments-and-requirements/requirements-lin-cuda.txt >requirements.txt - fi -fi - -$PYTHON -mpip install --prefer-binary -r requirements.txt -_err_exit $? "Failed to install InvokeAI's dependencies." - -#-------------------------------------------------------------------------------- -echo -echo "*** Installing InvokeAI Modules and Executables ***" -$PYTHON -mpip install $INVOKE_AI_SRC -_err_exit $? "Installation of InvokeAI failed." - -#-------------------------------------------------------------------------------- -echo " *** Setting Up Root Directory "$ROOTDIR" *** " -cp -pr templates/rootdir/* "$ROOTDIR"/ -cp templates/invoke.sh.in "$ROOTDIR"/invoke.sh -chmod a+rx "$ROOTDIR"/invoke.sh -cp templates/update.sh.in "$ROOTDIR"/update.sh -chmod a+rx "$ROOTDIR"/update.sh - -# This allows the updater to work! -cp -pr environments-and-requirements requirements.txt "$ROOTDIR/" - -#-------------------------------------------------------------------------------- -echo -echo "*** Confguring InvokeAI ***" -pushd "$ROOTDIR" >/dev/null -$PYTHON ./.venv/bin/configure_invokeai.py --root="$ROOTDIR" -_err_exit $? "Initial configuration failed. Please see above error messages and $TROUBLESHOOTING for help." - -#-------------------------------------------------------------------------------- -popd -cp templates/invoke.sh.in "$ROOTDIR"/invoke.sh -chmod a+rx "$ROOTDIR"/invoke.sh - -cp templates/update.sh.in "$ROOTDIR"/update.sh -chmod a+rx "$ROOTDIR"/update.sh - -echo "You may now run InvokeAI by entering the directory $ROOTDIR and running invoke.sh:" -echo -echo " ${ROOTDIR}/invoke.sh" -echo - -read -e -p "Run InvokeAI now? [y]:" input -RESPONSE=${input:='y'} -if [ "$RESPONSE" == 'y' ]; then - exec ${ROOTDIR}/invoke.sh -fi +exec python3 $(dirname $0)/main.py ${@} diff --git a/installer/installer.py b/installer/installer.py index 7d6ec0669f..569ecead73 100644 --- a/installer/installer.py +++ b/installer/installer.py @@ -4,6 +4,7 @@ InvokeAI installer script import os import platform +import shutil import subprocess import sys import venv @@ -160,10 +161,15 @@ class Installer: self.instance = InvokeAiInstance(runtime=self.dest, venv=self.venv) + # create the venv, install dependencies and the application self.instance.deploy(extra_index_url=get_torch_source()) + # run the configuration flow self.instance.configure() + # install the launch/update scritps into the runtime directory + self.instance.install_user_scripts() + class InvokeAiInstance: """ @@ -206,7 +212,7 @@ class InvokeAiInstance: ### until we continuously build wheels import messages - from plumbum import local, FG + from plumbum import FG, local # pre-installing Torch because this is the most reliable way to ensure # the correct version gets installed. @@ -253,7 +259,7 @@ class InvokeAiInstance: Install PyTorch """ - from plumbum import local, FG + from plumbum import FG, local extra_index_url_arg = "--extra-index-url" if extra_index_url is not None else None @@ -284,6 +290,19 @@ class InvokeAiInstance: configure_invokeai.main() + def install_user_scripts(self): + """ + Copy the launch and update scripts to the runtime dir + """ + + ext = 'bat' if OS == 'Windows' else 'sh' + + for script in ["invoke", "update"]: + src = Path(__file__).parent / "templates" / f"{script}.{ext}.in" + dest = self.runtime / f"{script}.{ext}" + shutil.copy (src, dest) + os.chmod(dest, 0o0755) + def update(self): pass diff --git a/installer/templates/invoke.sh.in b/installer/templates/invoke.sh.in index 5ec2e195ca..edb2f49bcb 100644 --- a/installer/templates/invoke.sh.in +++ b/installer/templates/invoke.sh.in @@ -1,5 +1,14 @@ #!/bin/bash +#### +# This launch script assumes that: +# 1. it is located in the runtime directory, +# 2. the .venv is also located in the runtime directory and is named exactly that +# +# If both of the above are not true, this script will likely not work as intended. +# Activate the virtual environment and run `invoke.py` directly. +#### + set -eu # ensure we're in the correct folder in case user's CWD is somewhere else @@ -23,16 +32,41 @@ if [ "$0" != "bash" ]; then echo "4. merge models (diffusers type only)" echo "5. open the developer console" echo "6. re-run the configure script to download new models" - read -p "Please enter 1, 2, 3, 4 or 5: [1] " yn + echo "7. command-line help " + echo "" + read -p "Please enter 1, 2, 3, 4, 5, 6 or 7: [2] " yn choice=${yn:='2'} case $choice in - 1 ) printf "\nStarting the InvokeAI command-line..\n"; invoke $*;; - 2 ) printf "\nStarting the InvokeAI browser-based UI..\n"; invoke --web $*;; - 3 ) printf "\nStarting Textual Inversion:\n"; textual_inversion --gui $*;; - 4 ) printf "\nMerging Models:\n"; merge_models --gui $*;; - 5 ) printf "\nDeveloper Console:\n"; file_name=$(basename "${BASH_SOURCE[0]}"); bash --init-file "$file_name";; - 6 ) printf "\nRunning configure_invokeai.py:\n"; configure_invokeai $*;; - * ) echo "Invalid selection"; exit;; + 1) + echo "Starting the InvokeAI command-line..." + exec invokeai $@ + ;; + 2) + echo "Starting the InvokeAI browser-based UI..." + exec invokeai --web $@ + ;; + 3) + echo "Starting Textual Inversion:" + exec textual_inversion --gui $@ + ;; + 4) + echo "Merging Models:" + exec merge_models --gui $@ + ;; + 5) + echo "Developer Console:" + file_name=$(basename "${BASH_SOURCE[0]}") + bash --init-file "$file_name" + ;; + 6) + exec invokeai-config --root ${INVOKEAI_ROOT} + ;; + 7) + exec invokeai --help + ;; + *) + echo "Invalid selection" + exit;; esac else # in developer console python --version diff --git a/ldm/invoke/config/configure_invokeai.py b/ldm/invoke/config/configure_invokeai.py index d4d2da099c..3de82d8962 100755 --- a/ldm/invoke/config/configure_invokeai.py +++ b/ldm/invoke/config/configure_invokeai.py @@ -71,23 +71,30 @@ Config_preamble = '''# This file describes the alternative machine learning mode #-------------------------------------------- def postscript(errors: None): if not any(errors): - message=''' + message=f''' ** Model Installation Successful ** You're all set! -If you installed using one of the automated installation scripts, -execute 'invoke.sh' (Linux/macOS) or 'invoke.bat' (Windows) to -start InvokeAI. +--- -If you installed manually, activate the 'invokeai' environment -(e.g. 'conda activate invokeai'), then run one of the following -commands to start InvokeAI. +If you installed manually from source or with 'pip install': activate the virtual environment +then run one of the following commands to start InvokeAI. Web UI: - python scripts/invoke.py --web # (connect to http://localhost:9090) + invoke.py --web # (connect to http://localhost:9090) + invoke.py --web --host 0.0.0.0 # (connect to http://your-lan-ip:9090 from another computer on the local network) + Command-line interface: - python scripts/invoke.py + invoke.py + +--- + +If you installed using an installation script, run: + +{Globals.root}/invoke.{"bat" if sys.platform == "win32" else "sh"} + +Add the '--help' argument to see all of the command-line switches available for use. Have fun! ''' @@ -277,7 +284,7 @@ The license terms are located here: print(f"Login failed due to invalid token found in cache") if not (yes_to_all or token_found): - print(''' You may optionally enter your Huggingface token now. InvokeAI + print(f''' You may optionally enter your Huggingface token now. InvokeAI *will* work without it but you will not be able to automatically download some of the Hugging Face style concepts. See https://invoke-ai.github.io/InvokeAI/features/CONCEPTS/#using-a-hugging-face-concept @@ -285,8 +292,10 @@ for more information. Visit https://huggingface.co/settings/tokens to generate a token. (Sign up for an account if needed). -Paste the token below using Ctrl-V on macOS/Linux, or Ctrl-Shift-V or right-click on Windows. -Alternatively press 'Enter' to skip this step and continue. +Paste the token below using {"Ctrl+Shift+V" if sys.platform == "linux" else "Command+V" if sys.platform == "darwin" else "Ctrl+V, right-click, or Edit>Paste"}. + +Alternatively, press 'Enter' to skip this step and continue. + You may re-run the configuration script again in the future if you do not wish to set the token right now. ''') again = True