Skip to content

Commit

Permalink
Merge pull request #22300 from mrclary/conda-lock-base
Browse files Browse the repository at this point in the history
PR: Update Spyder installer base environment with conda-lock file
  • Loading branch information
ccordoba12 authored Aug 6, 2024
2 parents bc7e84f + 080d05e commit 5140f45
Show file tree
Hide file tree
Showing 13 changed files with 164 additions and 118 deletions.
29 changes: 25 additions & 4 deletions .github/workflows/installers-conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -265,9 +265,7 @@ jobs:
python build_installers.py ${args[@]}
PKG_NAME=$(ls $DISTDIR | grep Spyder-)
LCK_NAME=$(ls $DISTDIR | grep conda-)
echo "PKG_NAME=$PKG_NAME" >> $GITHUB_ENV
echo "LCK_NAME=$LCK_NAME" >> $GITHUB_ENV
echo "ARTIFACT_NAME=${PKG_NAME%.*}" >> $GITHUB_ENV
echo "PKG_PATH=$DISTDIR/$PKG_NAME" >> $GITHUB_ENV
Expand Down Expand Up @@ -308,12 +306,35 @@ jobs:
fi
- name: Upload Artifact
if: env.IS_RELEASE == 'false'
uses: actions/upload-artifact@v4
with:
path: ${{ env.DISTDIR }}
name: ${{ env.ARTIFACT_NAME }}

upload-assets:
name: Upload Assets
runs-on: ubuntu-latest
needs:
- build-installers
defaults:
run:
shell: bash -le {0}

steps:
- name: Download Assets
uses: actions/download-artifact@v4
with:
merge-multiple: 'true'

- name: Zip Lock Files
run: zip -mT spyder-conda-lock *.lock

- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
path: spyder-conda-lock.zip
name: spyder-conda-lock

- name: Get Release
if: env.IS_RELEASE == 'true'
uses: bruceadams/[email protected]
Expand All @@ -328,4 +349,4 @@ jobs:
GITHUB_TOKEN: ${{ github.token }}
with:
upload_url: ${{ steps.get_release.outputs.upload_url }}
asset_path: ${{ env.DISTDIR }}/*.*
asset_path: ${{ github.workspace }}/*.*
2 changes: 1 addition & 1 deletion binder/environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ dependencies:
# Please *DO NOT* make changes directly just to here - make changes
# to requirements/main.yml, and copy it here.
- aiohttp >=3.9.3
- asyncssh >=2.0.0,<3.0.0
- asyncssh >=2.14.0,<3.0.0
- atomicwrites >=1.2.0
- chardet >=2.0.0
- cloudpickle >=0.5.0
Expand Down
12 changes: 6 additions & 6 deletions installers-conda/build-environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ name: spy-inst
channels:
- conda-forge
dependencies:
- conda >=24.5.0
- conda-build >=24.5.0
- conda-lock >=2.5.7
- conda-standalone >=24.5.0
- constructor >=3.6.0
- conda =24.5.0
- conda-build =24.5.1
- conda-lock =2.5.7
- conda-standalone =24.5.0
- constructor =3.8.1
- gitpython
- mamba
- menuinst >=2.1.0
- menuinst =2.1.0
- ruamel.yaml.jinja2
- setuptools_scm
84 changes: 44 additions & 40 deletions installers-conda/build_installers.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
# Standard library imports
from argparse import ArgumentParser
from datetime import timedelta
from distutils.spawn import find_executable
from functools import partial
import json
from logging import getLogger
Expand All @@ -34,6 +33,7 @@
from pathlib import Path
import platform
import re
import shutil
from subprocess import run
import sys
from textwrap import dedent, indent
Expand Down Expand Up @@ -134,9 +134,15 @@
yaml.indent(mapping=2, sequence=4, offset=2)
indent4 = partial(indent, prefix=" ")

specs = {
"python": "=" + PY_VER,
"spyder": "=" + SPYVER,
base_specs = {
"python": f"={PY_VER}",
"conda": "=24.5.0",
"menuinst": "=2.1.0",
"mamba": "",
}
rt_specs = {
"python": f"={PY_VER}",
"spyder": f"={SPYVER}",
"cython": "",
"matplotlib": "",
"numpy": "",
Expand All @@ -148,20 +154,20 @@

if SPECS.exists():
logger.info(f"Reading specs from {SPECS}...")
_specs = yaml.load(SPECS.read_text())
specs.update(_specs)
_rt_specs = yaml.load(SPECS.read_text())
rt_specs.update(_rt_specs)
else:
logger.info(f"Did not read specs from {SPECS}")

for spec in args.extra_specs:
k, *v = re.split('([<>=]+)[ ]*', spec)
specs[k] = "".join(v).strip()
rt_specs[k] = "".join(v).strip()

PY_VER = parse(re.split('([<>=]+)[ ]*', specs['python'])[-1])
SPYVER = parse(re.split('([<>=]+)[ ]*', specs['spyder'])[-1])
PY_VER = parse(re.split('([<>=]+)[ ]*', rt_specs['python'])[-1])
SPYVER = parse(re.split('([<>=]+)[ ]*', rt_specs['spyder'])[-1])

LOCK_FILE = DIST / f"conda-{TARGET_PLATFORM}.lock"
TMP_LOCK_FILE = BUILD / f"conda-{TARGET_PLATFORM}.lock"
BASE_LOCK_FILE = BUILD / f"conda-base-{TARGET_PLATFORM}.lock"
RT_LOCK_FILE = BUILD / f"conda-runtime-{TARGET_PLATFORM}.lock"
OUTPUT_FILE = DIST / f"{APP}-{OS}-{ARCH}.{args.install_type}"
INSTALLER_DEFAULT_PATH_STEM = f"{APP.lower()}-{SPYVER.major}"

Expand All @@ -171,7 +177,13 @@
CONSTRUCTOR_FILE = BUILD / "construct.yaml"


def _create_conda_lock():
def _create_conda_lock(env_type='base'):
specs = base_specs
lock_file = BASE_LOCK_FILE
if env_type == "runtime":
specs = rt_specs
lock_file = RT_LOCK_FILE

definitions = {
"channels": [
CONDA_BLD_PATH,
Expand All @@ -183,11 +195,14 @@ def _create_conda_lock():
"platforms": [TARGET_PLATFORM]
}

logger.info("Conda lock configuration:")
if args.no_local:
definitions['channels'].remove(CONDA_BLD_PATH)

logger.info(f"Conda lock configuration ({env_type}):")
if logger.getEffectiveLevel() <= 20:
yaml.dump(definitions, sys.stdout)

env_file = BUILD / "runtime_env.yml"
env_file = BUILD / f"{env_type}_env.yml"
yaml.dump(definitions, env_file)

env = os.environ.copy()
Expand All @@ -197,17 +212,18 @@ def _create_conda_lock():
"conda-lock", "lock",
"--kind", "explicit",
"--file", str(env_file),
"--filename-template", str(BUILD / "conda-{platform}.lock")
"--filename-template", str(BUILD / f"conda-{env_type}-{{platform}}.lock")
# Note conda-lock doesn't provide output file option, only template
]

run(cmd_args, check=True, env=env)

logger.info(f"Contents of {TMP_LOCK_FILE}:")
logger.info(f"Contents of {lock_file}:")
if logger.getEffectiveLevel() <= 20:
print(TMP_LOCK_FILE.read_text(), flush=True)
print(lock_file.read_text(), flush=True)

LOCK_FILE.write_text(TMP_LOCK_FILE.read_text())
# Write to dist directory also
(DIST / lock_file.name).write_text(lock_file.read_text())


def _generate_background_images(installer_type):
Expand Down Expand Up @@ -280,19 +296,7 @@ def _definitions():
"company": "Spyder-IDE",
"reverse_domain_identifier": "org.spyder-ide.Spyder",
"version": str(SPYVER),
"channels": [
"conda-forge/label/spyder_dev",
"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",
],
"exclude": ["pip"],
"environment_file": str(BASE_LOCK_FILE),
"installer_filename": OUTPUT_FILE.name,
"installer_type": args.install_type,
"initialize_by_default": False,
Expand All @@ -301,7 +305,7 @@ def _definitions():
"register_envs": False,
"extra_envs": {
"spyder-runtime": {
"environment_file": str(TMP_LOCK_FILE),
"environment_file": str(RT_LOCK_FILE),
}
},
"channels_remap": [
Expand All @@ -312,9 +316,6 @@ def _definitions():
]
}

if not args.no_local:
definitions["channels"].insert(0, "local")

definitions["license_file"] = str(RESOURCES / "bundle_license.rtf")
if args.install_type == "sh":
definitions["license_file"] = str(SPYREPO / "LICENSE.txt")
Expand Down Expand Up @@ -371,9 +372,9 @@ def _definitions():
definitions["notarization_identity_name"] = args.cert_id

if WINDOWS:
definitions["conda_default_channels"].append("defaults")
definitions.update(
{
"uninstall_name": f"Spyder {SPYVER.major}",
"welcome_image": str(WELCOME_IMG_WIN),
"header_image": str(HEADER_IMG_WIN),
"icon_image": str(SPYREPO / "img_src" / "spyder.ico"),
Expand Down Expand Up @@ -416,7 +417,7 @@ def _constructor():
Create a temporary `construct.yaml` input file and
run `constructor`.
"""
constructor = find_executable("constructor")
constructor = shutil.which("constructor")
if not constructor:
raise RuntimeError("Constructor must be installed and in PATH.")

Expand Down Expand Up @@ -472,8 +473,10 @@ def main():
t0 = time()
try:
DIST.mkdir(exist_ok=True)
_create_conda_lock()
assert TMP_LOCK_FILE.exists()
_create_conda_lock(env_type='base')
assert BASE_LOCK_FILE.exists()
_create_conda_lock(env_type='runtime')
assert RT_LOCK_FILE.exists()
finally:
elapse = timedelta(seconds=int(time() - t0))
logger.info(f"Build time: {elapse}")
Expand Down Expand Up @@ -505,7 +508,8 @@ def main():
_generate_background_images()
sys.exit()
if args.conda_lock:
_create_conda_lock()
_create_conda_lock(env_type='base')
_create_conda_lock(env_type='runtime')
sys.exit()

main()
6 changes: 3 additions & 3 deletions installers-conda/resources/post-install.bat
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
:: This script launches Spyder after install
rem This script launches Spyder after install
@echo off

set mode=system
if exist "%PREFIX%\.nonadmin" set mode=user

:: Get shortcut path
rem Get shortcut path
for /F "tokens=*" %%i in (
'%PREFIX%\python %PREFIX%\Scripts\menuinst_cli.py shortcut --mode=%mode%'
) do (
set shortcut=%%~fi
)

:: Launch Spyder
rem Launch Spyder
set tmpdir=%TMP%\spyder
set launch_script=%tmpdir%\launch_script.bat

Expand Down
2 changes: 1 addition & 1 deletion installers-conda/resources/pre-install.bat
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
:: Mark as conda-based-app
rem Mark as conda-based-app
set menudir=%PREFIX%\envs\spyder-runtime\Menu
if not exist "%menudir%" mkdir "%menudir%"
echo. > "%menudir%\conda-based-app"
2 changes: 1 addition & 1 deletion requirements/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ channels:
- conda-forge
dependencies:
- aiohttp >=3.9.3
- asyncssh >=2.0.0,<3.0.0
- asyncssh >=2.14.0,<3.0.0
- atomicwrites >=1.2.0
- chardet >=2.0.0
- cloudpickle >=0.5.0
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ def run(self):
install_requires = [
'aiohttp>=3.9.3',
'applaunchservices>=0.3.0;platform_system=="Darwin"',
'asyncssh>=2.0.0,<3.0.0',
'asyncssh>=2.14.0,<3.0.0',
'atomicwrites>=1.2.0',
'chardet>=2.0.0',
'cloudpickle>=0.5.0',
Expand Down
2 changes: 1 addition & 1 deletion spyder/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
# Hard dependencies
AIOHTTP_REQVER = '>=3.9.3'
APPLAUNCHSERVICES_REQVER = '>=0.3.0'
ASYNCSSH_REQVER = '>=2.0.0,<3.0.0'
ASYNCSSH_REQVER = '>=2.14.0,<3.0.0'
ATOMICWRITES_REQVER = '>=1.2.0'
CHARDET_REQVER = '>=2.0.0'
CLOUDPICKLE_REQVER = '>=0.5.0'
Expand Down
Loading

0 comments on commit 5140f45

Please sign in to comment.