Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR: Fix issues with installers discovered after 6.0.0b2 was released #22204

Merged
merged 11 commits into from
Jul 3, 2024
Merged
10 changes: 9 additions & 1 deletion .github/workflows/installers-conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ jobs:
- name: Setup Remote SSH Connection
if: env.ENABLE_SSH == 'true'
uses: mxschmitt/action-tmate@v3
timeout-minutes: 10
timeout-minutes: 60
with:
detached: true

Expand Down Expand Up @@ -175,6 +175,10 @@ jobs:
conda_build:
pkg_format: '2'
zstd_compression_level: '19'
channels:
- conda-forge/label/spyder_dev
- conda-forge/label/spyder_kernels_rc
- conda-forge
environment-file: installers-conda/build-environment.yml
environment-name: spy-inst
create-args: >-
Expand All @@ -192,6 +196,10 @@ jobs:
conda_build:
pkg_format: '2'
zstd_compression_level: '19'
channels:
- conda-forge/label/spyder_dev
- conda-forge/label/spyder_kernels_rc
- conda-forge
environment-file: installers-conda/build-environment.yml
environment-name: spy-inst
create-args: >-
Expand Down
8 changes: 4 additions & 4 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:
- boa <0.17.0 # See https://github.com/conda-forge/boa-feedstock/issues/81
- conda >=23.11.0
- conda >=24.5.0
- conda-build >=24.5.0
- conda-lock >=2.5.7
- conda-standalone >=23.11.0
- conda-standalone >=24.5.0
- constructor >=3.6.0
- gitpython
- mamba
- menuinst >=2.0.2
- menuinst >=2.1.0
- ruamel.yaml.jinja2
- setuptools_scm
128 changes: 86 additions & 42 deletions installers-conda/build_conda_pkgs.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
BUILD.mkdir(exist_ok=True)
SPYPATCHFILE = BUILD / "installers-conda.patch"

yaml = YAML()
yaml.indent(mapping=2, sequence=4, offset=2)


class BuildCondaPkg:
"""Base class for building a conda package for conda-based installer"""
Expand All @@ -63,14 +66,16 @@ def __init__(self, data={}, debug=False, shallow=False):

self._bld_src = BUILD / self.name
self._fdstk_path = BUILD / self.feedstock.split("/")[-1]
self._patchfile = self._fdstk_path / "recipe" / "version.patch"

self._get_source(shallow=shallow)
self._get_version()

self.data = {'version': self.version}
self.data.update(data)

self._patch_source()
self.recipe_append = {}
self.recipe_clobber = {}

self._recipe_patched = False

Expand Down Expand Up @@ -125,58 +130,104 @@ def _get_version(self):
def _patch_source(self):
pass

def _patch_meta(self, meta):
return meta

def patch_recipe(self):
"""
Patch conda build recipe
def _patch_conda_build_config(self):
file = self._fdstk_path / "recipe" / "conda_build_config.yaml"
if file.exists():
contents = yaml.load(file.read_text())
file.rename(file.parent / ("_" + file.name)) # copy of original
else:
contents = {}

1. Patch meta.yaml
2. Patch build script
"""
if self._recipe_patched:
return
pyver = sys.version_info
contents['python'] = [f"{pyver.major}.{pyver.minor}.* *_cpython"]
yaml.dump(contents, file)

self.logger.info("Patching 'meta.yaml'...")
self.logger.info(
f"Patched 'conda_build_config.yaml' contents:\n{file.read_text()}"
)

def _patch_meta(self):
file = self._fdstk_path / "recipe" / "meta.yaml"
meta = file.read_text()

# Replace jinja variable values
for k, v in self.data.items():
meta = re.sub(f".*set {k} =.*", f'{{% set {k} = "{v}" %}}', meta)

# Replace source, but keep patches
meta = re.sub(r'^(source:\n)( (url|sha256):.*\n)*',
rf'\g<1> path: {self._bld_src.as_posix()}\n',
meta, flags=re.MULTILINE)

meta = self._patch_meta(meta)

file.rename(file.parent / ("_" + file.name)) # keep copy of original
file.write_text(meta)

self.logger.info(f"Patched 'meta.yaml' contents:\n{file.read_text()}")

def _add_recipe_append(self):
if self._patchfile.exists():
self.recipe_append.update(
{"source": {"patches": [self._patchfile.name]}}
)

if self.recipe_append:
file = self._fdstk_path / "recipe" / "recipe_append.yaml"
yaml.dump(self.recipe_append, file)
self.logger.info(
f"'recipe_append.yaml' contents:\n{file.read_text()}"
)
else:
self.logger.info("Skipping 'recipe_append.yaml'.")

def _add_recipe_clobber(self):
self.recipe_clobber.update({
"source": {
"url": None,
"sha256": None,
"path": self._bld_src.as_posix()},
})

if self.recipe_clobber:
file = self._fdstk_path / "recipe" / "recipe_clobber.yaml"
yaml.dump(self.recipe_clobber, file)
self.logger.info(
f"'recipe_clobber.yaml' contents:\n{file.read_text()}"
)
else:
self.logger.info("Skipping 'recipe_clobber.yaml'.")

def patch_recipe(self):
"""
Patch conda build recipe

1. Patch conda_build_config.yaml
2. Patch meta.yaml
3. Add recipe_append.yaml
4. Add recipe_clobber.yaml
"""
if self._recipe_patched:
return

self._patch_conda_build_config()
self._patch_meta()
self._add_recipe_append()
self._add_recipe_clobber()

self._recipe_patched = True

def build(self):
"""
Build the conda package.

1. Patch the recipe
2. Build the package
3. Remove cloned repositories
1. Patch source
2. Patch the recipe
3. Build the package
4. Remove cloned repositories
"""
t0 = time()
try:
self._patch_source()
self.patch_recipe()

self.logger.info("Building conda package "
f"{self.name}={self.version}...")
check_call([
"conda", "mambabuild",
"conda", "build",
"--skip-existing", "--build-id-pat={n}",
"--no-test", "--no-anaconda-upload",
str(self._fdstk_path / "recipe")
Expand All @@ -202,6 +253,7 @@ class SpyderCondaPkg(BuildCondaPkg):

def _patch_source(self):
self.logger.info("Patching Spyder source...")

file = self._bld_src / "spyder/__init__.py"
file_text = file.read_text()
ver_str = tuple(self.version.split('.'))
Expand All @@ -213,14 +265,14 @@ def _patch_source(self):
)
file.write_text(file_text)

self.repo.git.diff(
output=(self._fdstk_path / "recipe" / "version.patch").as_posix()
)
self.repo.git.stash()
# Only write patch if necessary
if self.repo.git.diff():
self.logger.info(f"Creating {self._patchfile.name}...")
self.repo.git.diff(output=self._patchfile.as_posix())
self.repo.git.stash()

def _patch_meta(self, meta):
def patch_recipe(self):
# Get current Spyder requirements
yaml = YAML()
current_requirements = ['python']
current_requirements += yaml.load(
REQ_MAIN.read_text())['dependencies']
Expand All @@ -240,16 +292,11 @@ def _patch_meta(self, meta):
REQ_LINUX.read_text())['dependencies']
current_requirements += linux_requirements

# Replace run requirements
cr_string = '\n - '.join(current_requirements)
meta = re.sub(r'^(requirements:\n(.*\n)+ run:\n)( .*\n)+',
rf'\g<1> - {cr_string}\n', meta, flags=re.MULTILINE)
self.recipe_clobber.update({
"requirements": {"run": current_requirements}
})

# Add version patch
meta = re.sub(r'^(source:\n(.*\n)* patches:\n)',
r'\g<1> - version.patch\n',
meta, flags=re.MULTILINE)
return meta
super().patch_recipe()


class PylspCondaPkg(BuildCondaPkg):
Expand Down Expand Up @@ -334,9 +381,6 @@ class SpyderKernelsCondaPkg(BuildCondaPkg):
logger.info(f"Building local conda packages {list(args.build)}...")
t0 = time()

yaml = YAML()
yaml.indent(mapping=2, sequence=4, offset=2)

if SPECS.exists():
specs = yaml.load(SPECS.read_text())
else:
Expand Down
17 changes: 11 additions & 6 deletions installers-conda/build_installers.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import json
from logging import getLogger
import os
from packaging.version import parse
from pathlib import Path
import platform
import re
Expand Down Expand Up @@ -156,13 +157,13 @@
k, *v = re.split('([<>=]+)[ ]*', spec)
specs[k] = "".join(v).strip()

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

LOCK_FILE = DIST / f"conda-{TARGET_PLATFORM}.lock"
TMP_LOCK_FILE = BUILD / f"conda-{TARGET_PLATFORM}.lock"
OUTPUT_FILE = DIST / f"{APP}-{OS}-{ARCH}.{args.install_type}"
INSTALLER_DEFAULT_PATH_STEM = f"{APP.lower()}-{SPYVER.split('.')[0]}"
INSTALLER_DEFAULT_PATH_STEM = f"{APP.lower()}-{SPYVER.major}"

WELCOME_IMG_WIN = BUILD / "welcome_img_win.png"
HEADER_IMG_WIN = BUILD / "header_img_win.png"
Expand Down Expand Up @@ -209,10 +210,14 @@ def _create_conda_lock():

def _patch_conda_lock():
# Replace local channel url with conda-forge and remove checksum
dev_channel = ""
if SPYVER.is_prerelease:
dev_channel = "/label/spyder_dev"

tmp_text = TMP_LOCK_FILE.read_text()
text = re.sub(
f"^{_get_conda_bld_path_url()}(.*)#.*$",
r"https://conda.anaconda.org/conda-forge\1",
fr"https://conda.anaconda.org/conda-forge{dev_channel}\1",
tmp_text, flags=re.MULTILINE
)
LOCK_FILE.write_text(text)
Expand Down Expand Up @@ -291,7 +296,7 @@ def _definitions():
"name": APP,
"company": "Spyder-IDE",
"reverse_domain_identifier": "org.spyder-ide.Spyder",
"version": SPYVER,
"version": str(SPYVER),
"channels": [
"conda-forge/label/spyder_dev",
"conda-forge/label/spyder_kernels_rc",
Expand Down Expand Up @@ -352,7 +357,7 @@ def _definitions():
(RESOURCES / "osx_pkg_welcome.rtf.tmpl").read_text()
welcome_file = BUILD / "osx_pkg_welcome.rtf"
welcome_file.write_text(
welcome_text_tmpl.replace("__VERSION__", SPYVER))
welcome_text_tmpl.replace("__VERSION__", str(SPYVER)))

definitions.update(
{
Expand Down