diff --git a/.github/scripts/installer_test.sh b/.github/scripts/installer_test.sh new file mode 100755 index 00000000000..4473cdf5f3d --- /dev/null +++ b/.github/scripts/installer_test.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash + +# This script tests the installer for macOS and Linux on CI +# and will only install for the local user. + +exit_status=0 + +install() { + if [[ "$OSTYPE" == "darwin"* ]]; then + # Stream install.log to stdout to view all log messages. + tail -F /var/log/install.log & tail_id=$! + trap "kill -s TERM $tail_id" EXIT + + installer -pkg $PKG_PATH -target CurrentUserHomeDirectory >/dev/null + elif [[ "$OSTYPE" == "linux"* ]]; then + $PKG_PATH -b + fi +} + +check_prefix() { + if [[ "$OSTYPE" == "darwin"* ]]; then + base_prefix=$(compgen -G $HOME/Library/spyder-*) + elif [[ "$OSTYPE" == "linux"* ]]; then + base_prefix=$(compgen -G $HOME/.local/spyder-*) + fi + + if [[ -d "$base_prefix" ]]; then + echo "\nContents of ${base_prefix}:" + ls -al $base_prefix + else + echo "$base_prefix does not exist!" + exit 1 + fi +} + +check_uninstall() { + if [[ -e "${base_prefix}/uninstall-spyder.sh" ]]; then + echo -e "\nContents of ${base_prefix}/uninstall-spyder.sh:" + cat $base_prefix/uninstall-spyder.sh + else + echo "${base_prefix}/uninstall-spyder.sh does not exist!" + exit_status=1 + fi +} + +check_shortcut() { + pythonexe=${base_prefix}/bin/python + menuinst=${base_prefix}/bin/menuinst_cli.py + shortcut=$($pythonexe $menuinst shortcut --mode=user) + if [[ -e "${shortcut}" ]]; then + if [[ "$OSTYPE" == "darwin"* ]]; then + echo "\n${shortcut}/Contents/MacOS contents:" + ls -al "${shortcut}/Contents/MacOS" + echo -e "\n$shortcut/Contents/Info.plist contents:" + cat "${shortcut}/Contents/Info.plist" + script=$(compgen -G "${shortcut}/Contents/MacOS/spyder"*-script) + echo -e "\n${script} contents:" + cat "${script}" + elif [[ "$OSTYPE" == "linux"* ]]; then + echo -e "\n${shortcut} contents:" + cat $shortcut + fi + else + echo "$shortcut does not exist" + exit_status=1 + fi +} + +install +echo "Install info:" +check_prefix +check_uninstall +check_shortcut + +exit $exit_status diff --git a/.github/workflows/build-subrepos.yml b/.github/workflows/build-subrepos.yml index 4440a77866c..d681ba7cb85 100644 --- a/.github/workflows/build-subrepos.yml +++ b/.github/workflows/build-subrepos.yml @@ -64,7 +64,7 @@ jobs: environment-file: installers-conda/build-environment.yml environment-name: spy-inst create-args: >- - --channel-priority=strict + --channel-priority=flexible python=${{ matrix.python-version }} cache-downloads: true cache-environment: true diff --git a/.github/workflows/installers-conda.yml b/.github/workflows/installers-conda.yml index 5df327d5263..2aae6637bbb 100644 --- a/.github/workflows/installers-conda.yml +++ b/.github/workflows/installers-conda.yml @@ -115,6 +115,7 @@ jobs: MACOS_INSTALLER_CERTIFICATE: ${{ secrets.MACOS_INSTALLER_CERTIFICATE }} APPLICATION_PWD: ${{ secrets.APPLICATION_PWD }} CONSTRUCTOR_TARGET_PLATFORM: ${{ matrix.target-platform }} + NSIS_USING_LOG_BUILD: 1 steps: - name: Checkout Code @@ -156,13 +157,27 @@ jobs: enableCrossOsArchive: true fail-on-cache-miss: true - - name: Setup Build Environment + - name: Setup Build Environment (Windows) + if: runner.os == 'Windows' uses: mamba-org/setup-micromamba@v1 with: environment-file: installers-conda/build-environment.yml environment-name: spy-inst create-args: >- - --channel-priority=strict + --channel-priority=flexible + python=${{ matrix.python-version }} + nsis>=3.08=*_log_* + cache-downloads: true + cache-environment: true + + - name: Setup Build Environment (macOS & Linux) + if: runner.os != 'Windows' + uses: mamba-org/setup-micromamba@v1 + with: + environment-file: installers-conda/build-environment.yml + environment-name: spy-inst + create-args: >- + --channel-priority=flexible python=${{ matrix.python-version }} cache-downloads: true cache-environment: true @@ -185,7 +200,7 @@ jobs: CONDA_BLD_PATH: ${{ runner.temp }}/conda-bld run: | conda config --set bld_path $CONDA_BLD_PATH - mamba index $CONDA_BLD_PATH + conda index $CONDA_BLD_PATH mamba search -c local --override-channels || true - name: Create Keychain @@ -225,70 +240,25 @@ jobs: echo "PKG_GLOB=$PKG_GLOB" >> $GITHUB_ENV echo "PKG_BASE_NAME=$PKG_BASE_NAME" >> $GITHUB_ENV - - name: Test macOS Installer - if: runner.os == 'macOS' - run: | - # Stream install.log to stdout to view all log messages. - tail -F /var/log/install.log & tail_id=$! - trap "kill -s TERM $tail_id" EXIT - - installer -pkg $PKG_PATH -target CurrentUserHomeDirectory >/dev/null - - root_prefix=$(compgen -G $HOME/Library/spyder-*) - - echo "Install info:" - echo "Contents of ${root_prefix}:" - ls -al $root_prefix - echo -e "\nContents of ${root_prefix}/uninstall-spyder.sh:" - cat $root_prefix/uninstall-spyder.sh - - app_path=/Applications/Spyder.app - [[ -e ${root_prefix}/.nonadmin ]] && app_path=${HOME}${app_path} - - if [[ -e "$app_path" ]]; then - echo "Contents of $app_path/Contents/MacOS:" - ls -al $app_path/Contents/MacOS - echo -e "\nContents of $app_path/Contents/Info.plist:" - cat $app_path/Contents/Info.plist - echo -e "\nContents of $app_path/Contents/MacOS/spyder-script:" - cat $app_path/Contents/MacOS/spyder-script - else - echo "$app_path does not exist" - exit 1 - fi - - - name: Test Linux Installer - if: runner.os == 'Linux' - run: | - $PKG_PATH -b - - root_prefix=$(compgen -G $HOME/.local/spyder-*) - - echo "Install info:" - echo "Contents of ${root_prefix}:" - ls -al $root_prefix - echo -e "\nContents of ${root_prefix}/uninstall-spyder.sh:" - cat ${root_prefix}/uninstall-spyder.sh - - shortcut_path=/share/applications/spyder_spyder.desktop - [[ -e ${root_prefix}/.nonadmin ]] && shortcut_path=${HOME}/.local${shortcut_path} || shortcut_path=/usr${shortcut_path} - - if [[ -e $shortcut_path ]]; then - echo -e "\nContents of ${shortcut_path}:" - cat $shortcut_path - else - echo "$shortcut_path does not exist" - exit 1 - fi + - name: Test macOS or Linux Installer + if: runner.os != 'Windows' + run: ${{ github.workspace }}/.github/scripts/installer_test.sh - name: Test Windows Installer if: runner.os == 'Windows' shell: cmd run: | + set base_prefix=%USERPROFILE%\AppData\Local\spyder-6 start /wait %PKG_PATH% /InstallationType=JustMe /NoRegistry=1 /S + if exist %base_prefix%\install.log type %base_prefix%\install.log - set "shortcut_path=%USERPROFILE%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\spyder\Spyder.lnk" - if exist "%shortcut_path%" ( + set mode=system + for /F "tokens=*" %%i in ( + '%base_prefix%\python %base_prefix%\Scripts\menuinst_cli.py shortcut --mode=%mode%' + ) do ( + set shortcut=%%~fi + ) + if exist "%shortcut%" ( echo "Spyder installed successfully" ) else ( echo "Spyder NOT installed successfully" diff --git a/installers-conda/build-environment.yml b/installers-conda/build-environment.yml index a553446a714..740ae28faa0 100644 --- a/installers-conda/build-environment.yml +++ b/installers-conda/build-environment.yml @@ -1,13 +1,13 @@ name: spy-inst channels: - - napari/label/bundle_tools_3 - conda-forge dependencies: - boa - - conda - - conda-standalone - - constructor + - conda >=23.11.0 + - conda-standalone >=23.11.0 + - constructor >=3.6.0 - gitpython - - menuinst + - mamba + - menuinst >=2.0.2 - ruamel.yaml.jinja2 - setuptools_scm diff --git a/installers-conda/build_conda_pkgs.py b/installers-conda/build_conda_pkgs.py index c760fd08736..4f52b937b53 100644 --- a/installers-conda/build_conda_pkgs.py +++ b/installers-conda/build_conda_pkgs.py @@ -50,6 +50,7 @@ class BuildCondaPkg: norm = True source = None feedstock = None + feedstock_branch = None shallow_ver = None def __init__(self, data={}, debug=False, shallow=False): @@ -66,11 +67,11 @@ def __init__(self, data={}, debug=False, shallow=False): self._get_source(shallow=shallow) self._get_version() - self._patch_source() - self.data = {'version': self.version} self.data.update(data) + self._patch_source() + self._recipe_patched = False def _get_source(self, shallow=False): @@ -104,7 +105,10 @@ def _get_source(self, shallow=False): # Clone feedstock self.logger.info("Cloning feedstock...") - Repo.clone_from(self.feedstock, to_path=self._fdstk_path) + kwargs = dict(to_path=self._fdstk_path) + if self.feedstock_branch: + kwargs.update(branch=self.feedstock_branch) + Repo.clone_from(self.feedstock, **kwargs) def _build_cleanup(self): """Remove cloned source and feedstock repositories""" @@ -124,9 +128,6 @@ def _patch_source(self): def _patch_meta(self, meta): return meta - def _patch_build_script(self): - pass - def patch_recipe(self): """ Patch conda build recipe @@ -158,8 +159,6 @@ def patch_recipe(self): self.logger.info(f"Patched 'meta.yaml' contents:\n{file.read_text()}") - self._patch_build_script() - self._recipe_patched = True def build(self): @@ -177,7 +176,7 @@ def build(self): self.logger.info("Building conda package " f"{self.name}={self.version}...") check_call([ - "mamba", "mambabuild", + "conda", "mambabuild", "--no-test", "--skip-existing", "--build-id-pat={n}", str(self._fdstk_path / "recipe") ]) @@ -197,27 +196,23 @@ class SpyderCondaPkg(BuildCondaPkg): norm = False source = os.environ.get('SPYDER_SOURCE', HERE.parent) feedstock = "https://github.com/conda-forge/spyder-feedstock" + feedstock_branch = "dev" shallow_ver = "v5.3.2" def _patch_source(self): - self.logger.info("Creating Spyder menu file...") - _menufile = RESOURCES / "spyder-menu.json" - self.menufile = BUILD / "spyder-menu.json" - commit, branch = self.repo.head.commit.name_rev.split() - text = _menufile.read_text() - text = text.replace("__PKG_VERSION__", self.version) - text = text.replace("__SPY_BRANCH__", branch) - text = text.replace("__SPY_COMMIT__", commit[:8]) - self.menufile.write_text(text) + self.logger.info("Patching Spyder source...") + file = self._bld_src / "spyder/__init__.py" + file_text = file.read_text() + ver_str = tuple(self.version.split('.')) + file_text = re.sub( + r'^(version_info = ).*', + rf'\g<1>{ver_str}', + file_text, + flags=re.MULTILINE + ) + file.write_text(file_text) def _patch_meta(self, meta): - # Remove osx_is_app - meta = re.sub(r'^(build:\n([ ]{2,}.*\n)*) osx_is_app:.*\n', - r'\g<1>', meta, flags=re.MULTILINE) - - # Remove app node - meta = re.sub(r'^app:\n( .*\n)+', '', meta, flags=re.MULTILINE) - # Get current Spyder requirements yaml = YAML() current_requirements = ['python'] @@ -246,49 +241,12 @@ def _patch_meta(self, meta): return meta - def _patch_build_script(self): - self.logger.info("Patching build script...") - - rel_menufile = self.menufile.relative_to(HERE.parent) - - if os.name == 'posix': - logomark = "branding/logo/logomark/spyder-logomark-background.png" - file = self._fdstk_path / "recipe" / "build.sh" - text = file.read_text() - text += dedent( - f""" - # Create the Menu directory - mkdir -p "${{PREFIX}}/Menu" - - # Copy menu.json template - cp "${{SRC_DIR}}/{rel_menufile}" "${{PREFIX}}/Menu/spyder-menu.json" - - # Copy application icons - if [[ $OSTYPE == "darwin"* ]]; then - cp "${{SRC_DIR}}/img_src/spyder.icns" "${{PREFIX}}/Menu/spyder.icns" - else - cp "${{SRC_DIR}}/{logomark}" "${{PREFIX}}/Menu/spyder.png" - fi - """ - ) - - if os.name == 'nt': - file = self._fdstk_path / "recipe" / "bld.bat" - text = file.read_text() - text = text.replace( - r"copy %RECIPE_DIR%\menu-windows.json %MENU_DIR%\spyder_shortcut.json", - fr"copy %SRC_DIR%\{rel_menufile} %MENU_DIR%\spyder-menu.json" - ) - file.rename(file.parent / ("_" + file.name)) # keep copy of original - file.write_text(text) - - self.logger.info(f"Patched build script contents:\n{file.read_text()}") - class PylspCondaPkg(BuildCondaPkg): name = "python-lsp-server" source = os.environ.get('PYTHON_LSP_SERVER_SOURCE') feedstock = "https://github.com/conda-forge/python-lsp-server-feedstock" + feedstock_branch = "main" shallow_ver = "v1.4.1" @@ -296,6 +254,7 @@ class QtconsoleCondaPkg(BuildCondaPkg): name = "qtconsole" source = os.environ.get('QTCONSOLE_SOURCE') feedstock = "https://github.com/conda-forge/qtconsole-feedstock" + feedstock_branch = "main" shallow_ver = "5.3.1" @@ -303,6 +262,7 @@ class SpyderKernelsCondaPkg(BuildCondaPkg): name = "spyder-kernels" source = os.environ.get('SPYDER_KERNELS_SOURCE') feedstock = "https://github.com/conda-forge/spyder-kernels-feedstock" + feedstock_branch = "rc" shallow_ver = "v2.3.1" diff --git a/installers-conda/build_installers.py b/installers-conda/build_installers.py index e2b2be15d29..26d4263e979 100644 --- a/installers-conda/build_installers.py +++ b/installers-conda/build_installers.py @@ -195,20 +195,18 @@ def _generate_background_images(installer_type): def _get_condarc(): - # we need defaults for tensorflow and others on windows only - defaults = "- defaults" if WINDOWS else "" - prompt = "[spyder]({default_env}) " contents = dedent( - f""" + """ channels: #!final + - conda-forge/label/spyder_kernels_rc + - conda-forge/label/spyder_dev - conda-forge - {defaults} repodata_fns: #!final - repodata.json auto_update_conda: false #!final notify_outdated_conda: false #!final - channel_priority: strict #!final - env_prompt: '{prompt}' #! final + channel_priority: flexible #!final + env_prompt: '[spyder]({default_env}) ' #! final """ ) # the undocumented #!final comment is explained here @@ -227,13 +225,14 @@ def _definitions(): "reverse_domain_identifier": "org.spyder-ide.Spyder", "version": SPYVER, "channels": [ - "napari/label/bundle_tools_3", "conda-forge/label/spyder_kernels_rc", "conda-forge", ], "conda_default_channels": ["conda-forge"], "specs": [ f"python={PY_VER}", + "conda >=23.11.0", + "menuinst >=2.0.2", "mamba", ], "installer_filename": OUTPUT_FILE.name, @@ -241,15 +240,12 @@ def _definitions(): "initialize_by_default": False, "initialize_conda": False, "register_python": False, + "register_envs": False, "extra_envs": { "spyder-runtime": { "specs": [k + v for k, v in specs.items()], }, }, - "extra_files": [ - {str(RESOURCES / "bundle_readme.md"): "README.txt"}, - {condarc: ".condarc"}, - ], } if not args.no_local: @@ -265,7 +261,13 @@ def _definitions(): "default_prefix": os.path.join( "$HOME", ".local", INSTALLER_DEFAULT_PATH_STEM ), + "pre_install": str(RESOURCES / "pre-install.sh"), "post_install": str(RESOURCES / "post-install.sh"), + "extra_files": [ + {str(RESOURCES / "bundle_readme.md"): "README.txt"}, + {condarc: ".condarc"}, + {str(RESOURCES / "menuinst_cli.py"): "bin/menuinst_cli.py"}, + ], } ) @@ -278,6 +280,8 @@ def _definitions(): definitions.update( { + "progress_notifications": True, + "pre_install": str(RESOURCES / "pre-install.sh"), "post_install": str(RESOURCES / "post-install.sh"), "conclusion_text": "", "readme_text": "", @@ -290,6 +294,11 @@ def _definitions(): "default_location_pkg": "Library", "welcome_image": str(WELCOME_IMG_MAC), "welcome_file": str(welcome_file), + "extra_files": [ + {str(RESOURCES / "bundle_readme.md"): "README.txt"}, + {condarc: ".condarc"}, + {str(RESOURCES / "menuinst_cli.py"): "bin/menuinst_cli.py"}, + ], } ) @@ -314,7 +323,13 @@ def _definitions(): "%ALLUSERSPROFILE%", INSTALLER_DEFAULT_PATH_STEM ), "check_path_length": False, + "pre_install": str(RESOURCES / "pre-install.bat"), "post_install": str(RESOURCES / "post-install.bat"), + "extra_files": [ + {str(RESOURCES / "bundle_readme.md"): "README.txt"}, + {condarc: ".condarc"}, + {str(RESOURCES / "menuinst_cli.py"): "Scripts/menuinst_cli.py"}, + ], } ) @@ -348,7 +363,7 @@ def _constructor(): cmd_args.append(str(BUILD)) env = os.environ.copy() - env["CONDA_CHANNEL_PRIORITY"] = "strict" + env["CONDA_CHANNEL_PRIORITY"] = "flexible" logger.info("Command: " + " ".join(cmd_args)) logger.info("Configuration:") diff --git a/installers-conda/certkeychain.sh b/installers-conda/certkeychain.sh index 3f46bb57454..e1a145d2a5b 100755 --- a/installers-conda/certkeychain.sh +++ b/installers-conda/certkeychain.sh @@ -1,13 +1,14 @@ #!/usr/bin/env bash set -e -CERTFILE=certificate.p12 +SPYTMPDIR=${TMPDIR}spyder +CERTFILE=$SPYTMPDIR/certificate.p12 KEY_PASS=keypass KEYCHAIN=build.keychain KEYCHAINFILE=$HOME/Library/Keychains/$KEYCHAIN-db help(){ cat < nul && ( - set shortcut_root=%APPDATA% -) || ( - set shortcut_root=%ALLUSERSPROFILE% +set mode=system +if exist "%PREFIX%\.nonadmin" set mode=user + +:: Get shortcut path +for /F "tokens=*" %%i in ( + '%PREFIX%\python %PREFIX%\Scripts\menuinst_cli.py shortcut --mode=%mode%' +) do ( + set shortcut=%%~fi ) -set shortcut="%shortcut_root%\Microsoft\Windows\Start Menu\Programs\spyder\Spyder.lnk" +:: Launch Spyder set tmpdir=%TMP%\spyder set launch_script=%tmpdir%\launch_script.bat -mkdir %tmpdir% 2> nul +if not exist "%tmpdir%" mkdir "%tmpdir%" ( echo @echo off echo :loop @@ -20,9 +24,9 @@ echo if "%%errorlevel%%"=="0" ^( echo timeout /t 1 /nobreak ^> nul echo goto loop echo ^) else ^( -echo start "" /B %shortcut% +echo start "" /B "%shortcut%" echo exit echo ^) -) > %launch_script% +) > "%launch_script%" C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe -WindowStyle hidden -Command "& {Start-Process -FilePath %launch_script% -NoNewWindow}" diff --git a/installers-conda/resources/post-install.sh b/installers-conda/resources/post-install.sh index 19a01dc23e0..afc5455c7be 100755 --- a/installers-conda/resources/post-install.sh +++ b/installers-conda/resources/post-install.sh @@ -9,48 +9,39 @@ echo "Environment variables:" env | sort echo "" -# ---- -name_lower=$(echo ${INSTALLER_NAME} | tr 'A-Z' 'a-z') -spy_exe=${PREFIX}/envs/spyder-runtime/bin/spyder -u_spy_exe=${PREFIX}/uninstall-spyder.sh -all_user=$([[ -e ${PREFIX}/.nonadmin ]] && echo false || echo true) - -sed_opts=("-i") -alias_text="alias uninstall-spyder=${u_spy_exe}" -if [[ "$OSTYPE" = "darwin"* ]]; then - sed_opts+=("", "-e") - shortcut_path="/Applications/${INSTALLER_NAME}.app" - if [[ "$all_user" = "false" ]]; then - shortcut_path="${HOME}${shortcut_path}" - else - unset alias_text # Do not create uninstall alias - fi +# ---- Sed options +# BSD sed requires extra "" after -i flag +if [[ $(sed --version 2>/dev/null) ]]; then + # GNU sed has --version + sed_opts=("-i" "-e") else - shortcut_path="/share/applications/${name_lower}_${name_lower}.desktop" - if [[ "$all_user" = "true" ]]; then - shortcut_path="/usr${shortcut_path}" - alias_text="alias spyder=${spy_exe}" # Do not create uninstall alias - else - shortcut_path="${HOME}/.local${shortcut_path}" - alias_text="alias spyder=${spy_exe}\n${alias_text}" - fi + # BSD sed does not have --version + sed_opts=("-i" "''" "-e") fi +# ---- Shortcut +pythonexe=${PREFIX}/bin/python +menuinst=${PREFIX}/bin/menuinst_cli.py +mode=$([[ -e "${PREFIX}/.nonadmin" ]] && echo "user" || echo "system") +shortcut_path=$($pythonexe $menuinst shortcut --mode=$mode) + +# ---- Aliases +spy_exe="${PREFIX}/envs/spyder-runtime/bin/spyder" +u_spy_exe="${PREFIX}/uninstall-spyder.sh" + +[[ "$OSTYPE" == "linux"* ]] && alias_text="alias spyder=${spy_exe}" +[[ "$mode" == "user" ]] && alias_text="${alias_text:+${alias_text}\n}alias uninstall-spyder=${u_spy_exe}" + m1="# >>> Added by Spyder >>>" m2="# <<< Added by Spyder <<<" add_alias() { - if [[ ! -f "$shell_init" || ! -s "$shell_init" ]]; then + if [[ ! -s "$shell_init" ]]; then echo -e "$m1\n${alias_text}\n$m2" > $shell_init - exit 0 + return fi - # Remove old-style markers, if present; discard after EXPERIMENTAL - # installer attrition. - sed ${sed_opts[@]} "/# <<<< Added by Spyder <<<>>> Added by Spyder >>>>/d" $shell_init - - # Posix compliant sed does not like semicolons. - # Must use newlines to work on macOS + # BSD sed does not like semicolons; newlines work for both BSD and GNU. sed ${sed_opts[@]} " /$m1/,/$m2/{ h @@ -68,30 +59,35 @@ add_alias() { }" $shell_init } -# ---- -if [[ "$all_user" = "true" && "$OSTYPE" = "darwin"* ]]; then +if [[ "$mode" == "system" && "$OSTYPE" == "darwin"* ]]; then shell_init_list=("/etc/zshrc" "/etc/bashrc") -elif [[ "$all_user" = "true" ]]; then +elif [[ "$mode" == "system" ]]; then shell_init_list=("/etc/zsh/zshrc" "/etc/bash.bashrc") else - case $SHELL in - (*"zsh") shell_init_list=("$HOME/.zshrc") ;; - (*"bash") shell_init_list=("$HOME/.bashrc") ;; - esac + shell_init_list=("$HOME/.zshrc" "$HOME/.bashrc") fi for shell_init in ${shell_init_list[@]}; do + # If shell rc path or alias_text is empty, don't do anything [[ -z "$shell_init" || -z "$alias_text" ]] && continue - [[ "$all_user" = "true" && ! -f "$shell_init" ]] && continue # Don't create non-existent global init file + + # Don't create non-existent global init file + [[ "$mode" == "system" && ! -f "$shell_init" ]] && continue + echo "Creating aliases in $shell_init ..." add_alias done -# ---- +# ---- Uninstall script echo "Creating uninstall script..." cat < ${u_spy_exe} #!/bin/bash +if [[ ! -w ${PREFIX} || ! -w "$shortcut_path" ]]; then + echo "Uninstalling Spyder requires sudo privileges." + exit 1 +fi + while getopts "f" option; do case "\$option" in (f) force=true ;; @@ -120,8 +116,8 @@ fi # Quit Spyder echo "Quitting Spyder..." -if [[ "\$OSTYPE" = "darwin"* ]]; then - osascript -e 'quit app "Spyder.app"' 2>/dev/null +if [[ "\$OSTYPE" == "darwin"* ]]; then + osascript -e 'quit app "$(basename "$shortcut_path")"' 2>/dev/null else pkill spyder 2>/dev/null fi @@ -139,16 +135,17 @@ for x in ${shell_init_list[@]}; do done # Remove shortcut and environment -echo "Removing Spyder and environment..." -rm -rf ${shortcut_path} +echo "Removing Spyder shortcut and environment..." +$pythonexe $menuinst remove + rm -rf ${PREFIX} echo "Spyder successfully uninstalled." END -chmod u+x ${u_spy_exe} +chmod +x ${u_spy_exe} -# ---- -if [[ "$OSTYPE" = "linux"* ]]; then +# ---- Linux post-install notes +if [[ "$OSTYPE" == "linux"* ]]; then cat < $launch_script #!/bin/bash while pgrep -fq Installer.app; do sleep 1 done -open -a $shortcut_path +open -a "$shortcut_path" EOF chmod +x $launch_script + cat $launch_script nohup $launch_script &>/dev/null & elif [[ -n "$(which gtk-launch)" ]]; then diff --git a/installers-conda/resources/pre-install.bat b/installers-conda/resources/pre-install.bat new file mode 100644 index 00000000000..b855319573c --- /dev/null +++ b/installers-conda/resources/pre-install.bat @@ -0,0 +1,4 @@ +:: Mark as conda-based-app +set menudir=%PREFIX%\envs\spyder-runtime\Menu +if not exist "%menudir%" mkdir "%menudir%" +echo. > "%menudir%\conda-based-app" diff --git a/installers-conda/resources/pre-install.sh b/installers-conda/resources/pre-install.sh new file mode 100644 index 00000000000..ae5b6737bb1 --- /dev/null +++ b/installers-conda/resources/pre-install.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -e +echo "*** Running pre install script for ${INSTALLER_NAME} ..." + +echo "Marking as conda-based-app..." +menudir="${PREFIX}/envs/spyder-runtime/Menu" +mkdir -p "$menudir" +touch "${menudir}/conda-based-app" + +echo "*** Pre install script for ${INSTALLER_NAME} complete" diff --git a/installers-conda/resources/spyder-menu.json b/installers-conda/resources/spyder-menu.json deleted file mode 100644 index 6c804e97390..00000000000 --- a/installers-conda/resources/spyder-menu.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft-07/schema", - "$id": "https://schemas.conda.io/menuinst-1.schema.json", - "menu_name": "spyder", - "menu_items": [ - { - "name": "Spyder", - "description": "Scientific PYthon Development EnviRonment", - "icon": "{{ MENU_DIR }}/spyder.{{ ICON_EXT }}", - "activate": false, - "terminal": false, - "platforms": { - "win": { - "desktop": true, - "app_user_model_id": "spyder.Spyder", - "command": ["{{ PREFIX }}/pythonw.exe", "{{ PREFIX }}/Scripts/spyder-script.py"] - }, - "linux": { - "Categories": [ - "Graphics", - "Science" - ], - "command": ["{{ PREFIX }}/bin/spyder", "$@"], - "StartupWMClass": "Spyder" - }, - "osx": { - "command": [ - "$(dirname $0)/python", - "{{ PREFIX }}/bin/spyder", - "$@" - ], - "link_in_bundle": { - "{{ PREFIX }}/bin/python": "{{ MENU_ITEM_LOCATION }}/Contents/MacOS/python" - }, - "CFBundleName": "Spyder", - "CFBundleDisplayName": "Spyder", - "CFBundleIdentifier": "org.spyder-ide.Spyder", - "CFBundleVersion": "__PKG_VERSION__" - } - } - } - ] -} diff --git a/spyder/config/base.py b/spyder/config/base.py index ded4e75cc37..5685611068a 100644 --- a/spyder/config/base.py +++ b/spyder/config/base.py @@ -557,7 +557,7 @@ def is_conda_based_app(pyexec=sys.executable): else: env_path = osp.dirname(osp.dirname(real_pyexec)) - menu_rel_path = '/Menu/spyder-menu.json' + menu_rel_path = '/Menu/conda-based-app' if ( osp.exists(env_path + menu_rel_path) or glob(env_path + '/envs/*' + menu_rel_path) diff --git a/spyder/plugins/updatemanager/scripts/install.bat b/spyder/plugins/updatemanager/scripts/install.bat index e87c86d43af..4bdb5533469 100644 --- a/spyder/plugins/updatemanager/scripts/install.bat +++ b/spyder/plugins/updatemanager/scripts/install.bat @@ -4,10 +4,10 @@ :: Create variables from arguments :parse IF "%~1"=="" GOTO endparse -IF "%~1"=="-p" set prefix=%2 & SHIFT -IF "%~1"=="-i" set install_exe=%2 & SHIFT -IF "%~1"=="-c" set conda=%2 & SHIFT -IF "%~1"=="-v" set spy_ver=%2 & SHIFT +IF "%~1"=="-p" set prefix=%~2& SHIFT +IF "%~1"=="-i" set install_exe=%~2& SHIFT +IF "%~1"=="-c" set conda=%~2& SHIFT +IF "%~1"=="-v" set spy_ver=%~2& SHIFT SHIFT GOTO parse :endparse @@ -71,7 +71,7 @@ exit %ERRORLEVEL% call :wait_for_spyder_quit - %conda% install -p %prefix% -c conda-forge --override-channels -y spyder=%spy_ver% + %conda% install -p %prefix% -y spyder=%spy_ver% set /P CONT=Press any key to exit... goto :EOF diff --git a/spyder/plugins/updatemanager/scripts/install.sh b/spyder/plugins/updatemanager/scripts/install.sh index 9b7b42c37c3..75b894ad8ec 100755 --- a/spyder/plugins/updatemanager/scripts/install.sh +++ b/spyder/plugins/updatemanager/scripts/install.sh @@ -13,7 +13,7 @@ done shift $(($OPTIND - 1)) update_spyder(){ - $conda install -p $prefix -c conda-forge --override-channels -y spyder=$spy_ver + $conda install -p $prefix -y spyder=$spy_ver read -p "Press any key to exit..." } diff --git a/spyder/utils/environ.py b/spyder/utils/environ.py index ee9ad3545a0..899e241ee3c 100644 --- a/spyder/utils/environ.py +++ b/spyder/utils/environ.py @@ -15,6 +15,7 @@ from pathlib import Path import re import sys +from textwrap import dedent try: import winreg except Exception: @@ -48,11 +49,12 @@ def _get_user_env_script(): user_env_script = Path(get_conf_path()) / 'user-env.sh' if Path(shell).name in ('bash', 'zsh'): - script_text = ( - f"#!{shell} -i\n" - "unset HISTFILE\n" - f"{shell} -l -c " - f"\"{sys.executable} -c 'import os; print(dict(os.environ))'\"\n" + script_text = dedent( + f"""\ + !{shell} -i + unset HISTFILE + {shell} -l -c "'{sys.executable}' -c 'import os; print(dict(os.environ))'" + """ ) else: logger.info("Getting user environment variables is not supported "