diff --git a/.gitignore b/.gitignore index c033b54a..6d2b7c50 100644 --- a/.gitignore +++ b/.gitignore @@ -48,4 +48,6 @@ yalc.lock #prettier config .prettierrc.json -.idea \ No newline at end of file +.idea + +package/.eggs \ No newline at end of file diff --git a/package/.linux_installer.sh.swp b/package/.linux_installer.sh.swp new file mode 100644 index 00000000..730b11d1 Binary files /dev/null and b/package/.linux_installer.sh.swp differ diff --git a/package/README.md b/package/README.md index a31dd27b..bbf53043 100644 --- a/package/README.md +++ b/package/README.md @@ -3,35 +3,72 @@ Download bash script to install [here](https://raw.githubusercontent.com/MetaCell/PsyNeuLinkView/feature/PSYNEU-140/package/linux_installer.sh). It's recommended to download it in your home directory. -On terminal, go to your root directory +On terminal, go to your home directory ``` cd ~ ``` +Change file permissions to allow user to run it +``` +chmod + x linux_installer.sh +``` + And run installer ``` -bash -i linux_installer.sh +./linux_installer.sh +``` + +After installation is done: + +If conda environment is already active, run +``` +psyneulinkviewer +``` + +If conda environment is not active, then run : +``` +source ~/.profile +conda activate psyneulinkview +psyneulinkviewer ``` -The application should open up after succesfully installing psyneulinkviewer. +The application should open up succesfully after this. An icon will be visible on the Desktop as well. # Installing on MAC Download bash script to install [here](https://raw.githubusercontent.com/MetaCell/PsyNeuLinkView/feature/PSYNEU-140/package/mac_installer.sh). It's recommended to download it in your home directory. -On terminal, go to your root directory +On terminal, go to your home directory ``` cd ~ ``` +Give user permissions to file +``` +chmod +x mac_installer.sh +``` + And run installer ``` -bash -i mac_installer.sh +./mac_installer.sh +``` + +After installation is done: + +If conda environment is already active, run +``` +open ~/psyneulinkviewer-darwin-x64/psyneulinkviewer.app/ ``` -The application should open up after succesfully installing psyneulinkviewer. +If conda environment is not active, then run : +``` +source ~/.bash_profile +conda activate psyneulinkview +open ~/psyneulinkviewer-darwin-x64/psyneulinkviewer.app/ +``` +The application should open up succesfully after this. A desktop icon should be visible as well. # Installation process inside script @@ -54,27 +91,8 @@ source ~/.profile source ~/.bash_profile ``` -Then, the conda environment created is activated with command below: -``` -conda activate psyneulinkview -``` - -Finally, the last step of the script opens the psyneulinkviewer application: +Then, a desktop file is created on the Desktop which allows users to open the application this way -On linux: -``` -psyneulinkviewer -``` - -On Mac: -``` -open /usr/local/bin/psyneulinkviewer-darwin-x64/psyneulinkviewer.app -``` - -The psyneulinkviewer application is installed on user directory: -``` -/usr/local/bin/ -``` # Psyneulinkviewer Requirements diff --git a/package/dist/psyneulinkviewer-0.3.0.tar.gz b/package/dist/psyneulinkviewer-0.3.0.tar.gz deleted file mode 100644 index 86ef671c..00000000 Binary files a/package/dist/psyneulinkviewer-0.3.0.tar.gz and /dev/null differ diff --git a/package/dist/psyneulinkviewer-0.4.8.tar.gz b/package/dist/psyneulinkviewer-0.4.8.tar.gz new file mode 100644 index 00000000..b04a8eb0 Binary files /dev/null and b/package/dist/psyneulinkviewer-0.4.8.tar.gz differ diff --git a/package/linux_installer.sh b/package/linux_installer.sh index 25577f82..796c1f51 100755 --- a/package/linux_installer.sh +++ b/package/linux_installer.sh @@ -1,2 +1,46 @@ -#!/usr/bin/env bash -l -pip install -vv psyneulinkviewer && source ~/.profile && conda activate psyneulinkview && psyneulinkviewer +#!/bin/bash +pip install -vv psyneulinkviewer --break-system-packages --use-pep517 && . ~/.profile && sudo chown root:root /usr/local/bin/psyneulinkviewer-linux-x64/chrome-sandbox && sudo chmod 4755 /usr/local/bin/psyneulinkviewer-linux-x64/chrome-sandbox + +# Variables +APP_NAME="PsyneulinkViewer" # Name of the application +SYMLINK_PATH="psyneulinkviewer" # Symlink to the application you want to launch +CONDA_ENV=$PSYNEULINK_ENV # Conda environment to activate if none is active +ICON_PATH="/usr/local/bin/psyneulinkviewer-linux-x64/resources/app/build/logo.png" # Path to the custom icon +DESKTOP_FILE="$HOME/Desktop/$APP_NAME.desktop" # Path where the desktop shortcut will be created + +# Function to check if a conda environment is already active +is_conda_active() { + if [[ -n "$CONDA_DEFAULT_ENV" ]]; then + echo "Conda environment '$CONDA_DEFAULT_ENV' is already active." + return 0 + else + echo "No active conda environment found. Activating '$CONDA_ENV'..." + return 1 + fi +} + +# Creating the .desktop file for the application +echo "[Desktop Entry]" > "$DESKTOP_FILE" +echo "Version=1.0" >> "$DESKTOP_FILE" +echo "Name=$APP_NAME" >> "$DESKTOP_FILE" + +# Create the Exec command: Check if conda environment is active, if not activate the specified one +echo "Exec=bash -c '. ~/miniconda3/etc/profile.d/conda.sh && \ +conda activate $CONDA_ENV && \ +$SYMLINK_PATH'" >> "$DESKTOP_FILE" + +# Set the custom icon +echo "Icon=$ICON_PATH" >> "$DESKTOP_FILE" +echo "Type=Application" >> "$DESKTOP_FILE" +echo "Terminal=false" >> "$DESKTOP_FILE" +echo "Categories=Application;" >> "$DESKTOP_FILE" + +APP_DESKTOP="$HOME/.local/share/applications/$APP_NAME.desktop" + +cp "$DESKTOP_FILE" "$APP_DESKTOP" +# Make the .desktop file executable +chmod +x "$DESKTOP_FILE" +rm "$DESKTOP_FILE" +chmod +x "$APP_DESKTOP" + +echo "Application shortcut created at $APP_DESKTOP" diff --git a/package/mac_installer.sh b/package/mac_installer.sh index 5cfe27ac..44ce4728 100755 --- a/package/mac_installer.sh +++ b/package/mac_installer.sh @@ -1,2 +1,74 @@ -#!/usr/bin/env bash -l -pip install -vv psyneulinkviewer --break-system-packages && source ~/.bash_profile && conda activate psyneulinkview && open /usr/local/bin/psyneulinkviewer-darwin-x64/psyneulinkviewer.app/ +#!/bin/bash +pip install -vv psyneulinkviewer --break-system-packages --use-pep517 && source ~/.bashrc_profile + +# Variables - adjust these for your setup +APP_PATH="$HOME/psyneulinkviewer-darwin-x64/psyneulinkviewer.app/" # Replace with the full path to the application +CONDA_ENV=$PSYNEULINK_ENV # Replace with your Conda environment name +ICON_PATH="$HOME/psyneulinkviewer-darwin-x64/psyneulinkviewer.app/Contents/Resources/electron.icns" # Replace with the full path to your custom icon (should be in .icns format) +SHORTCUT_NAME="PsyneulinkViewer" # Name for the desktop shortcut + +# Define paths +DESKTOP_PATH="$HOME/Desktop" +APP_SHORTCUT_PATH="$DESKTOP_PATH/$SHORTCUT_NAME.app" +COMMAND_FILE_PATH="$APP_SHORTCUT_PATH/Contents/MacOS/$SHORTCUT_NAME" +ICON_FILE_PATH="$APP_SHORTCUT_PATH/Contents/Resources/$SHORTCUT_NAME.icns" + +# Create .app structure +mkdir -p "$APP_SHORTCUT_PATH/Contents/MacOS" +mkdir -p "$APP_SHORTCUT_PATH/Contents/Resources" + +# Function to check if a conda environment is active +is_conda_active() { + # Check if the CONDA_DEFAULT_ENV variable is set, meaning a conda environment is active + if [ -z "$CONDA_DEFAULT_ENV" ]; then + return 1 # No active conda environment + else + return 0 # A conda environment is already active + fi +} + +# Write the .command file that launches the app with conda environment +cat < "$COMMAND_FILE_PATH" +#!/bin/bash +source ~/.bashrc_profile +source ~/miniconda3/etc/profile.d/conda.sh +conda activate $CONDA_ENV +open "$APP_PATH" +EOL + +# Make the script executable +chmod +x "$COMMAND_FILE_PATH" + +# Copy the custom icon +cp "$ICON_PATH" "$ICON_FILE_PATH" + +# Create Info.plist for the app +cat < "$APP_SHORTCUT_PATH/Contents/Info.plist" + + + + + CFBundleExecutable + $SHORTCUT_NAME + CFBundleIconFile + $SHORTCUT_NAME + CFBundleIdentifier + com.example.$SHORTCUT_NAME + CFBundleName + $SHORTCUT_NAME + CFBundleVersion + 1.0 + CFBundlePackageType + APPL + + +EOL + +# Set the correct file permissions for the .app +chmod -R 755 "$APP_SHORTCUT_PATH" + +# Touch the app to refresh Finder so it displays the correct icon +touch "$APP_SHORTCUT_PATH" + +echo "Shortcut created at $DESKTOP_PATH/$SHORTCUT_NAME.app" + diff --git a/package/psyneulinkviewer.egg-info/PKG-INFO b/package/psyneulinkviewer.egg-info/PKG-INFO new file mode 100644 index 00000000..f9bfd786 --- /dev/null +++ b/package/psyneulinkviewer.egg-info/PKG-INFO @@ -0,0 +1,11 @@ +Metadata-Version: 2.1 +Name: psyneulinkviewer +Version: 0.4.8 +Home-page: https://github.com/metacell/psyneulinkviewer +Author: metacell +Author-email: dev@metacell.us +Requires-Python: >=3.7 +License-File: LICENSE +Requires-Dist: requests +Requires-Dist: wget +Requires-Dist: packaging<=24.0 diff --git a/package/psyneulinkviewer.egg-info/SOURCES.txt b/package/psyneulinkviewer.egg-info/SOURCES.txt new file mode 100644 index 00000000..478c163b --- /dev/null +++ b/package/psyneulinkviewer.egg-info/SOURCES.txt @@ -0,0 +1,14 @@ +LICENSE +README.md +setup.py +psyneulinkviewer/__init__.py +psyneulinkviewer/conda.py +psyneulinkviewer/configuration.py +psyneulinkviewer/node.py +psyneulinkviewer/rosetta.py +psyneulinkviewer/start.py +psyneulinkviewer.egg-info/PKG-INFO +psyneulinkviewer.egg-info/SOURCES.txt +psyneulinkviewer.egg-info/dependency_links.txt +psyneulinkviewer.egg-info/requires.txt +psyneulinkviewer.egg-info/top_level.txt \ No newline at end of file diff --git a/package/psyneulinkviewer.egg-info/dependency_links.txt b/package/psyneulinkviewer.egg-info/dependency_links.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/package/psyneulinkviewer.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/package/psyneulinkviewer.egg-info/requires.txt b/package/psyneulinkviewer.egg-info/requires.txt new file mode 100644 index 00000000..3f10f0cf --- /dev/null +++ b/package/psyneulinkviewer.egg-info/requires.txt @@ -0,0 +1,3 @@ +requests +wget +packaging<=24.0 diff --git a/package/psyneulinkviewer.egg-info/top_level.txt b/package/psyneulinkviewer.egg-info/top_level.txt new file mode 100644 index 00000000..7c1a94eb --- /dev/null +++ b/package/psyneulinkviewer.egg-info/top_level.txt @@ -0,0 +1 @@ +psyneulinkviewer diff --git a/package/psyneulinkviewer/conda.py b/package/psyneulinkviewer/conda.py index cc3f8ec2..d1522995 100644 --- a/package/psyneulinkviewer/conda.py +++ b/package/psyneulinkviewer/conda.py @@ -31,6 +31,7 @@ def create_env(): logging.info("Conda environment found %s", env_name) except Exception as error: logging.info("Conda environment not found") + env_name = None if env_name is None: command = get_conda_installed_path() + configuration.create_env @@ -51,7 +52,7 @@ def shell_source(script): def install_conda(): import wget if platform.system() == 'Linux': - bash_file = wget.download(configuration.linux_conda_bash, out="tmp") + bash_file = wget.download(configuration.linux_conda_bash) elif platform.system() == 'Darwin': bash_file = wget.download(configuration.mac_conda_bash) @@ -138,11 +139,7 @@ def detect_activated_conda() : logging.info("Conda environment not detected active : %s", env_name) env_name = None else: - if env_name == configuration.env_name: - logging.info("Conda environment detected active : %s", env_name) - else: - logging.info("Active environment not matching : %s", configuration.env_name) - env_name = None + logging.info("Conda environment detected active : %s", env_name) except Exception as error: logging.info("Environment not found active: %s ", error) @@ -159,6 +156,7 @@ def detect_activated_conda_location() : if env_location: env_location = re.search('(?<=base environment : )(/[a-zA-Z0-9\./]*[\s]?)', env_location) env_location = env_location.group(0) + env_location = env_location.strip() except Exception as error: logging.info("Environment not found active: %s ", error) diff --git a/package/psyneulinkviewer/configuration.py b/package/psyneulinkviewer/configuration.py index b8466866..796716ea 100644 --- a/package/psyneulinkviewer/configuration.py +++ b/package/psyneulinkviewer/configuration.py @@ -4,8 +4,9 @@ releases_url = 'https://api.github.com/repos/MetaCell/PsyNeuLinkView/releases' application_url = "psyneulinkviewer-linux-x64/psyneulinkviewer" -application_url_mac = "psyneulinkviewer-linux-x64/psyneulinkviewer.app" - +application_url_mac = "psyneulinkviewer-darwin-x64/psyneulinkviewer.app" +installation_folder_name = "/psyneulinkviewer-linux-x64" +installation_folder_name_mac = "/psyneulinkviewer-darwin-x64" #Symlink symlink = "/usr/local/bin/psyneulinkviewer" diff --git a/package/psyneulinkviewer/node.py b/package/psyneulinkviewer/node.py index 2fd1f1a3..7fed5836 100644 --- a/package/psyneulinkviewer/node.py +++ b/package/psyneulinkviewer/node.py @@ -51,8 +51,12 @@ def check_node_installation(): logging.error("Exiting, node must be installed to continue...") sys.exit() from packaging.version import Version - if Version(node_version) > Version(configuration.node_required_version): - logging.info("Node version exists and valid, %s", node_version) - else: + if node_version is None: logging.error("Node version not up to date, update required") - install_node() \ No newline at end of file + install_node() + else: + if Version(node_version) > Version(configuration.node_required_version): + logging.info("Node version exists and valid, %s", node_version) + else: + logging.error("Node version not up to date, update required") + install_node() \ No newline at end of file diff --git a/package/psyneulinkviewer/start.py b/package/psyneulinkviewer/start.py index e8434cc7..8548f740 100644 --- a/package/psyneulinkviewer/start.py +++ b/package/psyneulinkviewer/start.py @@ -2,10 +2,10 @@ import json import platform import os +import shutil import sys import subprocess import logging -import importlib.util import tarfile import atexit from psyneulinkviewer import configuration @@ -18,6 +18,8 @@ logger = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO) +in_conda = 'CONDA_PREFIX' in os.environ + def check_os(): if os.name == 'nt': sys.exit('Windows is not supported') @@ -25,11 +27,12 @@ def check_os(): logging.info("OS version supported") def check_python(): - if not sys.version_info.major == 3 and not sys.version_info.minor == 7 : - logging.error('Python version not supported, 3.11 is required. %f' , sys.version_info) - sys.exit('Python version not supported, 3.11 is required.') - else: - logging.info("Python version is supported") + if in_conda: + if not (3, 7) <= sys.version_info < (3, 12): + logging.error('Python version not supported, python 3.7 to 3.11 is required. %f' , sys.version_info) + sys.exit('Python version not supported, 3.11 is required.') + else: + logging.info("Python version is supported") def check_graphviz(): logging.info(configuration.graphviz +" is not installed, installing") @@ -46,12 +49,8 @@ def check_graphviz(): def check_psyneulink(): logging.info(configuration.psyneulink +" installing") try: - result = subprocess.run( - ["pip", "install", "psyneulink"], - capture_output = True, - text = True - ).stdout - logger.info("Success installing psyneulink %s ", result) + subprocess.check_call([sys.executable, "-m", "pip", "install", "psyneulink"]) + logger.info("Success installing psyneulink") except Exception as error: logger.info("Error installing psyneulink") @@ -95,8 +94,20 @@ def get_latest_release(installation_path): logging.info("Opening compressed file %s", filename) tar = tarfile.open(tar_location) - + extract_location = configuration.extract_location + + psyneulink_location = extract_location + configuration.installation_folder_name + if platform.system() == "Darwin": + extract_location = os.path.expanduser("~") + psyneulink_location = extract_location + configuration.installation_folder_name_mac + + logging.info("Removing %s", psyneulink_location) + # Remove the folder if it exists + if os.path.exists(psyneulink_location): + logging.info("Removing %s", psyneulink_location) + shutil.rmtree(psyneulink_location) + permissions = os.access(extract_location, os.W_OK) logging.info("Extract location permissions : %s", permissions) @@ -109,6 +120,8 @@ def get_latest_release(installation_path): application = os.path.join(extract_location, configuration.application_url_mac) symlink = configuration.symlink + if platform.system() == "Darwin": + symlink = os.path.join(extract_location, "psyneulinkviewer") permissions = os.access(symlink, os.W_OK) logging.info("Symlink path permission : %s", permissions) @@ -134,6 +147,37 @@ def continue_on_conda(): check_psyneulink() get_latest_release(os.path.dirname(os.path.realpath(__file__))) +def update_env_variable(var_name, var_value): + # Determine the appropriate profile file based on the OS + profile_file = os.path.expanduser('~/.profile') + if platform.system() == 'Darwin': + # For macOS and Linux + profile_file = os.path.expanduser('~/.bashrc_profile') + + # Read the current content of the profile file + try: + with open(profile_file, 'r') as file: + lines = file.readlines() + except FileNotFoundError: + lines = [] + + # Update or add the environment variable + var_found = False + for i, line in enumerate(lines): + if line.startswith(f'export {var_name}='): + lines[i] = f'export {var_name}="{var_value}"\n' + var_found = True + break + + if not var_found: + lines.append(f'export {var_name}="{var_value}"\n') + + # Write the updated content back to the profile file + with open(profile_file, 'w') as file: + file.writelines(lines) + + logging.info(f"Updated {var_name} in {profile_file}.") + def prerequisites(): check_os() check_python() @@ -141,6 +185,9 @@ def prerequisites(): #Install package requirements on conda env_name = detect_activated_conda() env_location = detect_activated_conda_location() + env_var_name = 'PSYNEULINK_ENV' + env_var_value = configuration.env_name + if env_name is None or env_location is None: conda_command_binary = configuration.conda_installation_path + configuration.continue_on_conda_new_env if platform.system() == 'Darwin': @@ -148,10 +195,13 @@ def prerequisites(): logging.info("Binary command %s ", conda_command_binary) subprocess.run(conda_command_binary, shell=True) else: + env_var_value = env_name command = env_location + "/bin/conda run -n " + env_name + configuration.binary_commands logging.info("Binary command %s ", command) subprocess.run(command, shell=True) + update_env_variable(env_var_name, env_var_value) + def main(): prerequisites() diff --git a/package/setup.py b/package/setup.py index 9bff3fc0..a88d7741 100644 --- a/package/setup.py +++ b/package/setup.py @@ -8,6 +8,29 @@ logger = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO) +# Check if running in a conda environment +in_conda = 'CONDA_PREFIX' in os.environ + +if in_conda: + if not (3, 7) <= sys.version_info < (3, 12): + logging.error('Python version not supported, python 3.7 to 3.11 is required. %f' , sys.version_info) + sys.exit('Python version not supported, 3.7 to 3.11 is required.') + +# Define common dependencies +common_requires = [ + 'requests', + 'wget', + 'packaging<=24.0' +] + +# Define extra dependencies for conda environments +conda_requires = ['psyneulink'] + +non_conda_requires = [] + +# Combine dependencies based on environment +install_requires = common_requires + (conda_requires if in_conda else non_conda_requires) + class Install(install): user_options = install.user_options + [ ('path=', None, 'an option that takes a value') @@ -26,19 +49,33 @@ def finalize_options(self): def run(self): global path path = self.path # will be 1 or None + + package_name = 'psyneulinkviewer' + + # Try to uninstall the package using pip + try: + result = subprocess.run(['pip', 'uninstall', '-y', package_name, "--break-system-packages"], + capture_output = True, + text = True + ).stdout + subprocess.run(['pip', 'cache', 'purge']) + logging.info(f"Previous version of {package_name} uninstalled {result}.") + except subprocess.CalledProcessError: + logging.info(f"No previous version of {package_name} installed or uninstall failed.") from psyneulinkviewer.start import prerequisites prerequisites() install.run(self) setup( name="psyneulinkviewer", - version="0.3.0", + version="0.4.8", url='https://github.com/metacell/psyneulinkviewer', author='metacell', author_email='dev@metacell.us', setup_requires=['requests', 'wget', - 'packaging'], + 'packaging<=24.0'], + install_requires=install_requires, packages=find_packages(), cmdclass={ 'install': Install