Skip to content

Weekly conda-based installers #2678

Weekly conda-based installers

Weekly conda-based installers #2678

on:
schedule:
# 6:30 UTC Sunday
- cron: '30 6 * * 0'
pull_request:
paths:
- 'installers-conda/**'
- '.github/workflows/installers-conda.yml'
- '.github/scripts/installer_test.sh'
- 'requirements/*.yml'
- 'MANIFEST.in'
release:
types:
- created
workflow_dispatch:
inputs:
pre:
description: 'Build as release candidate'
required: false
default: true
type: boolean
ssh:
description: 'Enable ssh debugging'
required: false
default: false
type: boolean
macos-x86_64:
description: 'Build macOS x86_64 installer'
required: false
default: true
type: boolean
macos-arm64:
description: 'Build macOS arm64 installer'
required: false
default: true
type: boolean
linux:
description: 'Build Linux installer'
required: false
default: true
type: boolean
win:
description: 'Build Windows installer'
required: false
default: true
type: boolean
concurrency:
group: installers-conda-${{ github.ref }}
cancel-in-progress: true
name: Weekly conda-based installers
env:
IS_RELEASE: ${{ github.event_name == 'release' }}
IS_PRE_RELEASE: ${{ github.event_name == 'workflow_dispatch' && inputs.pre }}
ENABLE_SSH: ${{ github.event_name == 'workflow_dispatch' && inputs.ssh }}
BUILD_MAC: ${{ github.event_name != 'workflow_dispatch' || inputs.macos-x86_64 }}
BUILD_ARM: ${{ github.event_name != 'workflow_dispatch' || inputs.macos-arm64 }}
BUILD_LNX: ${{ github.event_name != 'workflow_dispatch' || inputs.linux }}
BUILD_WIN: ${{ github.event_name != 'workflow_dispatch' || inputs.win }}
USE_SUBREPOS: ${{ github.event_name == 'schedule' || github.event_name == 'pull_request' || (github.event_name == 'workflow_dispatch' && ! inputs.pre) }}
NOTARIZE: ${{ github.event_name == 'schedule' || github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.pre) }}
jobs:
build-matrix:
name: Determine Build Matrix
runs-on: ubuntu-latest
outputs:
target_platform: ${{ steps.build-matrix.outputs.target_platform }}
branch: ${{ steps.build-matrix.outputs.branch }}
include: ${{ steps.build-matrix.outputs.include }}
python_version: ${{ steps.build-matrix.outputs.python_version }}
steps:
- name: Determine Build Matrix
id: build-matrix
run: |
if [[ $BUILD_MAC == "true" ]]; then
target_platform="'osx-64'"
include="{'os': 'macos-13', 'target-platform': 'osx-64', 'spyk-arch': 'unix'}"
fi
if [[ $BUILD_ARM == "true" ]]; then
target_platform=${target_platform:+"$target_platform, "}"'osx-arm64'"
include=${include:+"$include, "}"{'os': 'macos-14', 'target-platform': 'osx-arm64', 'spyk-arch': 'unix'}"
fi
if [[ $BUILD_LNX == "true" ]]; then
target_platform=${target_platform:+"$target_platform, "}"'linux-64'"
include=${include:+"$include, "}"{'os': 'ubuntu-latest', 'target-platform': 'linux-64', 'spyk-arch': 'unix'}"
fi
if [[ $BUILD_WIN == "true" ]]; then
target_platform=${target_platform:+"$target_platform, "}"'win-64'"
include=${include:+"$include, "}"{'os': 'windows-latest', 'target-platform': 'win-64', 'spyk-arch': 'win-64'}"
fi
if [[ $GITHUB_EVENT_NAME == "schedule" ]]; then
branch="'master', '6.x'"
else
branch="''"
fi
echo "target_platform=[$target_platform]" >> $GITHUB_OUTPUT
echo "branch=[$branch]" >> $GITHUB_OUTPUT
echo "include=[$include]" >> $GITHUB_OUTPUT
build-subrepos:
name: Build Subrepos
# env.USE_SUBREPOS is not available at job level; must copy-paste here
if: github.event_name == 'schedule' || github.event_name == 'pull_request' || (github.event_name == 'workflow_dispatch' && ! inputs.pre)
needs:
- build-matrix
strategy:
fail-fast: false
matrix:
branch: ${{ fromJson(needs.build-matrix.outputs.branch) }}
uses: ./.github/workflows/build-subrepos.yml
with:
branch: ${{ matrix.branch }}
build-installers:
name: Build installer
if: ${{ ! failure() && ! cancelled() }}
runs-on: ${{ matrix.os }}
needs:
- build-matrix
- build-subrepos
strategy:
fail-fast: false
matrix:
target-platform: ${{ fromJson(needs.build-matrix.outputs.target_platform) }}
python-version: ['3.11']
branch: ${{ fromJson(needs.build-matrix.outputs.branch) }}
include: ${{ fromJson(needs.build-matrix.outputs.include) }}
defaults:
run:
shell: bash -le {0}
working-directory: ${{ github.workspace }}/installers-conda
env:
DISTDIR: ${{ github.workspace }}/installers-conda/dist
MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }}
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_INSTALLER_CERTIFICATE: ${{ secrets.MACOS_INSTALLER_CERTIFICATE }}
APPLICATION_PWD: ${{ secrets.APPLICATION_PWD }}
CONSTRUCTOR_TARGET_PLATFORM: ${{ matrix.target-platform }}
MATRIX_BRANCH: ${{ matrix.branch }}
steps:
- name: Checkout Code
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ matrix.branch }}
- name: Setup Remote SSH Connection
if: env.ENABLE_SSH == 'true'
uses: mxschmitt/action-tmate@v3
timeout-minutes: 60
with:
detached: true
- name: Restore python-lsp-server Cache
if: env.USE_SUBREPOS == 'true'
uses: actions/cache/restore@v4
with:
path: installers-conda/build/conda-bld/**/*.tar.bz2
key: python-lsp-server_noarch_${{ matrix.python-version }}_${{ hashFiles('external-deps/python-lsp-server/.gitrepo') }}
enableCrossOsArchive: true
fail-on-cache-miss: true
- name: Restore qtconsole Cache
if: env.USE_SUBREPOS == 'true'
uses: actions/cache/restore@v4
with:
path: installers-conda/build/conda-bld/**/*.tar.bz2
key: qtconsole_noarch_${{ matrix.python-version }}_${{ hashFiles('external-deps/qtconsole/.gitrepo') }}
enableCrossOsArchive: true
fail-on-cache-miss: true
- name: Restore ${{ matrix.spyk-arch }} spyder-kernels Cache
if: env.USE_SUBREPOS == 'true'
uses: actions/cache/restore@v4
with:
path: installers-conda/build/conda-bld/**/*.tar.bz2
key: spyder-kernels_${{ matrix.spyk-arch }}_${{ matrix.python-version }}_${{ hashFiles('external-deps/spyder-kernels/.gitrepo') }}
enableCrossOsArchive: true
fail-on-cache-miss: true
- name: Setup Build Environment (Windows)
if: runner.os == 'Windows'
uses: mamba-org/setup-micromamba@v1
with:
condarc: |
conda_build:
pkg_format: '2'
zstd_compression_level: '19'
channels:
- conda-forge/label/spyder_rc
- conda-forge/label/spyder_dev
- conda-forge/label/spyder_kernels_rc
# TODO: Add this channel when we create it
# - conda-forge/label/spyder_kernels_dev
- conda-forge
environment-file: installers-conda/build-environment.yml
environment-name: spy-inst
create-args: >-
--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:
condarc: |
conda_build:
pkg_format: '2'
zstd_compression_level: '19'
channels:
- conda-forge/label/spyder_rc
- conda-forge/label/spyder_dev
- conda-forge/label/spyder_kernels_rc
# TODO: Add this channel when we create it
# - conda-forge/label/spyder_kernels_dev
- conda-forge
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
- name: Env Variables
run: |
if [[ "$IS_RELEASE" != "true" && "$IS_PRE_RELEASE" != "true" ]]; then
NSIS_USING_LOG_BUILD=1
echo "NSIS_USING_LOG_BUILD=$NSIS_USING_LOG_BUILD" >> $GITHUB_ENV
fi
CONDA_BLD_PATH=${RUNNER_TEMP}/conda-bld
echo "CONDA_BLD_PATH=$CONDA_BLD_PATH" >> $GITHUB_ENV
env | sort
- name: Build ${{ matrix.target-platform }} spyder Conda Package
if: env.IS_RELEASE == 'false'
run: |
# Copy built packages to new build location because spyder cannot be
# built in workspace
[[ -d build/conda-bld ]] && cp -Rv build/conda-bld $CONDA_BLD_PATH
python build_conda_pkgs.py --build spyder
- name: Create Local Conda Channel
run: |
mkdir -p $CONDA_BLD_PATH
conda config --set bld_path $CONDA_BLD_PATH
conda index $CONDA_BLD_PATH
mamba search -c local --override-channels || true
- name: Create Keychain
if: runner.os == 'macOS' && env.NOTARIZE == 'true'
run: |
_codesign=$(which codesign)
if [[ $_codesign =~ ${CONDA_PREFIX}.* ]]; then
# Find correct codesign
echo "Moving $_codesign..."
mv $_codesign ${_codesign}.bak
fi
./certkeychain.sh "${MACOS_CERTIFICATE_PWD}" "${MACOS_CERTIFICATE}" "${MACOS_INSTALLER_CERTIFICATE}"
CNAME=$(security find-identity -p codesigning -v | pcre2grep -o1 "\(([0-9A-Z]+)\)")
echo "CNAME=$CNAME" >> $GITHUB_ENV
- name: Load signing certificate (Windows)
if: runner.os == 'Windows' && env.NOTARIZE == 'true'
run: |
echo "${MACOS_CERTIFICATE}" > "${{ runner.temp }}/certificate.b64.txt"
certutil.exe -decode "${{ runner.temp }}/certificate.b64.txt" "${{ runner.temp }}/certificate.pfx"
echo "CONSTRUCTOR_SIGNING_CERTIFICATE=${{ runner.temp }}/certificate.pfx" >> $GITHUB_ENV
echo "CONSTRUCTOR_PFX_CERTIFICATE_PASSWORD=${MACOS_CERTIFICATE_PWD}" >> $GITHUB_ENV
echo "CONSTRUCTOR_SIGNTOOL_PATH=C:/Program Files (x86)/Windows Kits/10/bin/10.0.17763.0/x86/signtool.exe" >> $GITHUB_ENV
- name: Build Package Installer
run: |
[[ -n $CNAME ]] && args=("--cert-id" "$CNAME") || args=()
python build_installers.py ${args[@]}
SPYVER=$(python build_installers.py --version)
PKG_PATH=$(python build_installers.py --artifact-name)
PKG_NAME=$(basename $PKG_PATH)
ARTIFACT_NAME=${PKG_NAME%.*}
[[ "$GITHUB_EVENT_NAME" == "schedule" ]] && ARTIFACT_NAME=$ARTIFACT_NAME-$MATRIX_BRANCH
echo "SPYVER=$SPYVER" >> $GITHUB_ENV
echo "PKG_NAME=$PKG_NAME" >> $GITHUB_ENV
echo "ARTIFACT_NAME=$ARTIFACT_NAME" >> $GITHUB_ENV
echo "PKG_PATH=$PKG_PATH" >> $GITHUB_ENV
- 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
echo.
if exist %base_prefix%\install.log (
echo Log output:
type %base_prefix%\install.log
) else (
echo No log found at %base_prefix%\install.log
)
set mode=system
for /F "tokens=*" %%i in (
'%base_prefix%\python %base_prefix%\Scripts\menuinst_cli.py shortcut --mode=%mode%'
) do (
set shortcut=%%~fi
)
echo.
if exist "%shortcut%" (
echo Spyder installed successfully
) else (
echo Spyder NOT installed successfully
EXIT /B 1
)
set runtime_python=%base_prefix%\envs\spyder-runtime\python
for /F "tokens=*" %%i in (
'%runtime_python% -c "import spyder; print(spyder.__version__)"'
) do (
set actual_version=%%~i
)
echo.
echo Expected version = %SPYVER%
echo Actual version = %actual_version%
if %SPYVER% neq %actual_version% (
echo Error: installed Spyder version is incorrect!
EXIT /B 1
)
EXIT /B %ERRORLEVEL%
- name: Notarize or Compute Checksum
if: env.NOTARIZE == 'true'
run: |
if [[ $RUNNER_OS == "macOS" ]]; then
./notarize.sh -p $APPLICATION_PWD $PKG_PATH
else
cd $DISTDIR
echo $(sha256sum $PKG_NAME) > "${ARTIFACT_NAME}-sha256sum.txt"
fi
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
path: ${{ env.DISTDIR }}
name: ${{ env.ARTIFACT_NAME }}
upload-assets:
name: Upload Assets
if: ${{ ! failure() && ! cancelled() }}
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/get-release@v1.3.2
id: get_release
env:
GITHUB_TOKEN: ${{ github.token }}
- name: Upload Release Asset
if: env.IS_RELEASE == 'true'
uses: shogo82148/actions-upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ github.token }}
with:
upload_url: ${{ steps.get_release.outputs.upload_url }}
asset_path: ${{ github.workspace }}/*.*