|
| 1 | +name: Publish sdist and wheels |
| 2 | + |
| 3 | +env: |
| 4 | + # A tag or branch name or a commit hash for the scipy/scipy repo, for which |
| 5 | + # to build wheels. This is normally set to `main` in the main branch of this |
| 6 | + # repo, and to a tag name (e.g., `v2.3.2`) on a release branch. |
| 7 | + SOURCE_REF_TO_BUILD: main |
| 8 | + |
| 9 | +on: |
| 10 | + schedule: |
| 11 | + # ┌───────────── minute (0 - 59) |
| 12 | + # │ ┌───────────── hour (0 - 23) |
| 13 | + # │ │ ┌───────────── day of the month (1 - 31) |
| 14 | + # │ │ │ ┌───────────── month (1 - 12 or JAN-DEC) |
| 15 | + # │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT) |
| 16 | + # │ │ │ │ │ |
| 17 | + - cron: "9 9 * * MON" |
| 18 | + pull_request: |
| 19 | + branches: |
| 20 | + - main |
| 21 | + - maintenance/** |
| 22 | + push: |
| 23 | + branches: |
| 24 | + - main # FOR TESTING ONLY |
| 25 | + workflow_dispatch: |
| 26 | + inputs: |
| 27 | + environment: |
| 28 | + description: Which PyPI environment to upload to, if any |
| 29 | + required: true |
| 30 | + type: choice |
| 31 | + options: ["none", "testpypi", "pypi"] |
| 32 | + |
| 33 | +concurrency: |
| 34 | + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} |
| 35 | + cancel-in-progress: true |
| 36 | + |
| 37 | +permissions: |
| 38 | + contents: read # to fetch code (actions/checkout) |
| 39 | + |
| 40 | +jobs: |
| 41 | + build_wheels: |
| 42 | + name: BLD > ${{ matrix.python[0] }}-${{ matrix.buildplat[1] }}-${{ matrix.buildplat[2] }}-${{ matrix.buildplat[3] }} |
| 43 | + runs-on: ${{ matrix.buildplat[0] }} |
| 44 | + strategy: |
| 45 | + # Ensure that a wheel builder finishes even if another fails |
| 46 | + fail-fast: false |
| 47 | + matrix: |
| 48 | + # Github Actions doesn't support pairing matrix values together, let's improvise |
| 49 | + # https://github.com/github/feedback/discussions/7835#discussioncomment-1769026 |
| 50 | + buildplat: |
| 51 | + # should also be able to do multi-archs on a single entry, e.g. |
| 52 | + # [windows-2025, win*, "AMD64 x86"]. However, those two require a different compiler setup |
| 53 | + # so easier to separate out here. |
| 54 | + - [ubuntu-24.04, manylinux, x86_64, "", ""] |
| 55 | + - [ubuntu-24.04, musllinux, x86_64, "", ""] |
| 56 | + - [ubuntu-24.04-arm, manylinux, aarch64, "", ""] |
| 57 | + - [ubuntu-24.04-arm, musllinux, aarch64, "", ""] |
| 58 | + - [macos-13, macosx, x86_64, openblas, "10.14"] |
| 59 | + - [macos-15-intel, macosx, x86_64, accelerate, "14.0"] |
| 60 | + - [macos-14, macosx, arm64, openblas, "12.3"] |
| 61 | + - [macos-14, macosx, arm64, accelerate, "14.0"] |
| 62 | + - [windows-2025, win, AMD64, "", ""] |
| 63 | + - [windows-11-arm, win, ARM64, "", ""] |
| 64 | + python: [["cp311", "3.11"], ["cp312", "3.12"], ["cp313", "3.13"], ["cp313t", "3.13"], ["cp314", "cp314"], ["cp314t", "cp314"]] |
| 65 | + # python[0] is used to specify the python versions made by cibuildwheel |
| 66 | + |
| 67 | + steps: |
| 68 | + - name: Checkout scipy-release |
| 69 | + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 |
| 70 | + with: |
| 71 | + persist-credentials: false |
| 72 | + |
| 73 | + - name: Checkout scipy |
| 74 | + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 |
| 75 | + with: |
| 76 | + repository: scipy/scipy |
| 77 | + ref: ${{ env.SOURCE_REF_TO_BUILD }} |
| 78 | + path: scipy-src |
| 79 | + submodules: true |
| 80 | + persist-credentials: false |
| 81 | + |
| 82 | + # the version of python we'll run cibuildwheel with |
| 83 | + - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 |
| 84 | + with: |
| 85 | + python-version: 3.13 |
| 86 | + |
| 87 | + - name: win_amd64 - install rtools |
| 88 | + if: ${{ matrix.buildplat[1] == 'win' && matrix.buildplat[2] == 'AMD64' }} |
| 89 | + run: | |
| 90 | + # mingw-w64 |
| 91 | + choco install rtools -y --no-progress --force --version=4.0.0.20220206 |
| 92 | + echo "c:\rtools40\ucrt64\bin;" >> $env:GITHUB_PATH |
| 93 | +
|
| 94 | + - name: Set environment variables for ARM64 |
| 95 | + if: matrix.buildplat[1] == 'win' && matrix.buildplat[2] == 'ARM64' |
| 96 | + run: | |
| 97 | + echo "CC=clang-cl" >> $env:GITHUB_ENV |
| 98 | + echo "CXX=clang-cl" >> $env:GITHUB_ENV |
| 99 | + echo "FC=flang" >> $env:GITHUB_ENV |
| 100 | + echo "TARGET_ARCH=${{ matrix.buildplat[2] }}" >> $env:GITHUB_ENV |
| 101 | +
|
| 102 | + - name: Set up Flang and pkgconf for ARM64 |
| 103 | + if: matrix.buildplat[1] == 'win' && matrix.buildplat[2] == 'ARM64' |
| 104 | + uses: ./.github/windows_arm64_steps |
| 105 | + |
| 106 | + - name: pkg-config-for-win |
| 107 | + if: runner.os == 'windows' |
| 108 | + run: | |
| 109 | + choco install -y --no-progress --stoponfirstfailure --checksum 6004DF17818F5A6DBF19CB335CC92702 pkgconfiglite |
| 110 | + $CIBW = "${{ github.workspace }}/.openblas" |
| 111 | + # pkgconfig needs a complete path, and not just "./openblas since the |
| 112 | + # build is run in a tmp dir (?) |
| 113 | + # It seems somewhere in the env passing, `\` is not |
| 114 | + # passed through, so convert it to '/' |
| 115 | + $CIBW = $CIBW.replace("\","/") |
| 116 | + echo "CIBW_ENVIRONMENT_WINDOWS=PKG_CONFIG_PATH=$CIBW" >> $env:GITHUB_ENV |
| 117 | +
|
| 118 | + # the macOS matrix is complicated. |
| 119 | + # - arm64, accelerate, >=14.0 |
| 120 | + # The easiest of them all (sort of). However, if you wish to target 14 you |
| 121 | + # can only build on macos-14 with the system compiler. If you try building on |
| 122 | + # macos-15 there are libraries with a minimum target of 15. Presumably when |
| 123 | + # the macos-14 is dropped we'll need to customise the compiler a bit. |
| 124 | + # In addition, we can't use Accelerate for < 14 because the library had bugs. |
| 125 | + # This means OpenBLAS has to be used. |
| 126 | + # - x86_64, accelerate, >=14.0 |
| 127 | + # Complicated because x86_64 is only available on the macos-15-intel image. |
| 128 | + # - x86_64, openblas, "10.14" |
| 129 | + # Complicated because x86_64 is only available on the macos-15-intel image. |
| 130 | + # Needs an older compiler. |
| 131 | + # - arm64, openblas, "12.3" |
| 132 | + # Complicated because MACOS_DEPLOYMENT_TARGET has to be set to 12.3 (there's |
| 133 | + # some reason why we don't support <12.3), which results in 12_3 in the wheel |
| 134 | + # name. This is illegal, so we have to change the wheel name to 12_0 after |
| 135 | + # the build. |
| 136 | + # OpenBLAS is also complicated because the `delocate` step tries |
| 137 | + # to copy two libgfortran libraries into the wheel, and falls over. This |
| 138 | + # necessitates using install_name_tool on the scipy_openblas32 dylibs. |
| 139 | + # The libraries are also built with an older version of gfortran (11), which |
| 140 | + # requires installation of that version. Whilst this version is available |
| 141 | + # from homebrew, it doesn't come with an old enough deployment target. |
| 142 | + # We therefore have to jump through hoops to install a community packaged |
| 143 | + # compiler. |
| 144 | + - name: Setup macOS |
| 145 | + if: startsWith( matrix.buildplat[0], 'macos-' ) |
| 146 | + run: | |
| 147 | + if [[ ${{ matrix.buildplat[3] }} == 'accelerate' ]]; then |
| 148 | + echo CIBW_CONFIG_SETTINGS=\"setup-args=-Dblas=accelerate\" >> "$GITHUB_ENV" |
| 149 | + |
| 150 | + # use preinstalled gfortran for Accelerate builds |
| 151 | + ln -s $(which gfortran-13) gfortran |
| 152 | + export PATH=$PWD:$PATH |
| 153 | + echo "PATH=$PATH" >> "$GITHUB_ENV" |
| 154 | + |
| 155 | + # Builds with Accelerate only target macOS>=14.0 |
| 156 | + CIBW_ENV="CIBW_ENVIRONMENT_MACOS=MACOSX_DEPLOYMENT_TARGET=14.0 INSTALL_OPENBLAS=false" |
| 157 | + else |
| 158 | + # use conda-forge compilers the difficult targets |
| 159 | + INSTALL_DIR=/Users/runner/miniforge3 |
| 160 | + |
| 161 | + wget -q -O Miniforge3.sh "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh" |
| 162 | + |
| 163 | + # Make the installer executable |
| 164 | + chmod +x Miniforge3.sh |
| 165 | + |
| 166 | + # Run the installer in batch mode (-b) and specify the installation prefix (-p) |
| 167 | + # The -u flag is for updating an existing installation if it exists |
| 168 | + ./Miniforge3.sh -b -u -p "${INSTALL_DIR}" |
| 169 | + |
| 170 | + source "${INSTALL_DIR}/etc/profile.d/conda.sh" |
| 171 | + conda init |
| 172 | + conda create -q -y -n build compilers |
| 173 | + echo "conda activate build" >> $HOME/.bash_profile |
| 174 | + cat $HOME/.bash_profile |
| 175 | + |
| 176 | + CIBW_ENV="CIBW_ENVIRONMENT_MACOS=PKG_CONFIG_PATH=$PWD/.openblas \ |
| 177 | + MACOSX_DEPLOYMENT_TARGET=${{ matrix.buildplat[4] }}" |
| 178 | + fi |
| 179 | + echo "$CIBW_ENV" >> $GITHUB_ENV |
| 180 | + |
| 181 | + PREFIX=DYLD_LIBRARY_PATH="\$(dirname \$(gfortran --print-file-name libgfortran.dylib))" |
| 182 | + CIBW="$PREFIX delocate-wheel -v $EXCLUDE --require-archs {delocate_archs} -w {dest_dir} {wheel}" |
| 183 | + echo "CIBW_REPAIR_WHEEL_COMMAND_MACOS=$CIBW" >> $GITHUB_ENV |
| 184 | + |
| 185 | + - name: Build Wheels |
| 186 | + shell: bash -el {0} |
| 187 | + run: | |
| 188 | + pip install cibuildwheel |
| 189 | + export CIBW_BUILD=${{ matrix.python[0] }}-${{ matrix.buildplat[1] }}* |
| 190 | + export CIBW_ARCHS=${{ matrix.buildplat[2] }} |
| 191 | + export CIBW_ENABLE="cpython-freethreading cpython-prerelease" |
| 192 | + cibuildwheel --output-dir=dist --config-file=cibuildwheel.toml scipy-src |
| 193 | +
|
| 194 | + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 |
| 195 | + with: |
| 196 | + path: ./dist/*.whl |
| 197 | + name: ${{ matrix.python[0] }}-${{ matrix.buildplat[1] }}-${{ matrix.buildplat[2] }}-${{ matrix.buildplat[3] }}-${{ matrix.buildplat[4] }} |
| 198 | + |
| 199 | + # We upload nightlies only on cron job runs, so it doesn't overlap with |
| 200 | + # builds from which we publish to PyPI - we don't want these extra |
| 201 | + # dependencies when building release artifacts. |
| 202 | + - name: install micromamba |
| 203 | + # win-arm64 is unsupported by micromamba at the moment |
| 204 | + if: github.event_name == 'schedule' && matrix.buildplat[1] != 'win_arm64' |
| 205 | + uses: mamba-org/setup-micromamba@b09ef9b599704322748535812ca03efb2625677b # v2.0.5 |
| 206 | + with: |
| 207 | + # For installation of anaconda-client, required for upload to anaconda.org |
| 208 | + init-shell: bash |
| 209 | + environment-name: upload-env |
| 210 | + create-args: >- |
| 211 | + anaconda-client |
| 212 | +
|
| 213 | + - name: win-arm64 install anaconda client |
| 214 | + if: github.event_name == 'schedule' && matrix.buildplat[1] == 'win_arm64' |
| 215 | + run: | |
| 216 | + # Rust installation needed for rpds-py. |
| 217 | + Invoke-WebRequest https://static.rust-lang.org/rustup/dist/aarch64-pc-windows-msvc/rustup-init.exe -UseBasicParsing -Outfile rustup-init.exe |
| 218 | + .\rustup-init.exe -y |
| 219 | + $env:PATH="$env:PATH;$env:USERPROFILE\.cargo\bin" |
| 220 | + pip install anaconda-client |
| 221 | +
|
| 222 | + - name: Upload to anaconda.org |
| 223 | + if: github.event_name == 'schedule' && github.repository == 'scipy/scipy-release' |
| 224 | + shell: bash -el {0} # required for micromamba |
| 225 | + env: |
| 226 | + TOKEN: ${{ secrets.scipy_NIGHTLY_UPLOAD_TOKEN }} |
| 227 | + run: | |
| 228 | + anaconda -q -t ${TOKEN} upload --force -u scientific-python-nightly-wheels ./dist/*.whl |
| 229 | +
|
| 230 | +# ------------------------------------------------------------------------------------- |
| 231 | + build_sdist: |
| 232 | + name: Build sdist |
| 233 | + runs-on: ubuntu-latest |
| 234 | + if: github.repository == 'andyfaff/scipy' |
| 235 | + |
| 236 | + steps: |
| 237 | + - name: Checkout scipy |
| 238 | + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 |
| 239 | + with: |
| 240 | + repository: scipy/scipy |
| 241 | + submodules: true |
| 242 | + ref: ${{ env.SOURCE_REF_TO_BUILD }} |
| 243 | + persist-credentials: false |
| 244 | + |
| 245 | + - name: Build sdist |
| 246 | + run: | |
| 247 | + python -m pip install -U pip build |
| 248 | +
|
| 249 | + # need openblas even for the sdist build |
| 250 | + OPENBLAS=openblas32 |
| 251 | + pkgconf_path=$GITHUB_WORKSPACE/.openblas |
| 252 | +
|
| 253 | + rm -rf $pkgconf_path |
| 254 | + mkdir -p $pkgconf_path |
| 255 | +
|
| 256 | + python -m pip install -r $GITHUB_WORKSPACE/requirements/openblas.txt |
| 257 | + python -c "import scipy_${OPENBLAS}; print(scipy_${OPENBLAS}.get_pkg_config())" > $pkgconf_path/scipy-openblas.pc |
| 258 | + export PKG_CONFIG_PATH=$pkgconf_path |
| 259 | + echo "PKG_CONFIG_PATH=$pkgconf_path" >> "$GITHUB_ENV" |
| 260 | +
|
| 261 | + python -m build --sdist |
| 262 | +
|
| 263 | + - name: Test the sdist |
| 264 | + run: | |
| 265 | + python -m pip install -v dist/*.gz |
| 266 | + pip install -r $GITHUB_WORKSPACE/requirements/test.txt |
| 267 | + cd .. # Can't import scipy within scipy src directory |
| 268 | + python -c "import scipy, sys; print(scipy.__version__); sys.exit(scipy.test() is False)" |
| 269 | +
|
| 270 | + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 |
| 271 | + with: |
| 272 | + name: sdist |
| 273 | + path: ./dist/* |
| 274 | + |
| 275 | +# ------------------------------------------------------------------------------------- |
| 276 | + testpypi-publish: |
| 277 | + name: Publish release to TestPyPI |
| 278 | + if: github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'testpypi' |
| 279 | + needs: [build_wheels, build_sdist] |
| 280 | + runs-on: ubuntu-latest |
| 281 | + environment: |
| 282 | + name: testpypi |
| 283 | + url: https://test.pypi.org/p/scipy |
| 284 | + permissions: |
| 285 | + id-token: write # mandatory for trusted publishing |
| 286 | + steps: |
| 287 | + - name: Download sdist and wheels |
| 288 | + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 |
| 289 | + with: |
| 290 | + path: dist |
| 291 | + merge-multiple: true |
| 292 | + |
| 293 | + - name: Publish |
| 294 | + uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 |
| 295 | + with: |
| 296 | + repository-url: https://test.pypi.org/legacy/ |
| 297 | + skip-existing: true |
| 298 | + print-hash: true |
| 299 | + attestations: true |
| 300 | + |
| 301 | +# ------------------------------------------------------------------------------------- |
| 302 | + check_version: |
| 303 | + name: Ensure commit is tag before upload to PyPi |
| 304 | + |
| 305 | + if: github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'pypi' |
| 306 | + needs: [build_wheels, build_sdist] |
| 307 | + runs-on: ubuntu-latest |
| 308 | + steps: |
| 309 | + - name: Checkout scipy |
| 310 | + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 |
| 311 | + with: |
| 312 | + repository: scipy/scipy |
| 313 | + ref: ${{ env.SOURCE_REF_TO_BUILD }} |
| 314 | + path: scipy-src |
| 315 | + fetch-depth: 0 |
| 316 | + fetch-tags: true |
| 317 | + submodules: false |
| 318 | + persist-credentials: false |
| 319 | + |
| 320 | + - name: Examine git commit |
| 321 | + run: | |
| 322 | + cd scipy-src |
| 323 | + hash=$(git describe HEAD) |
| 324 | + echo $hash |
| 325 | + if [[ $hash == *"-"*"-"* ]]; then |
| 326 | + echo "SOURCE_REF_TO_BUILD is not a tag" |
| 327 | + exit 1 |
| 328 | + else |
| 329 | + echo "SOURCE_REF_TO_BUILD is a tag" |
| 330 | + fi; |
| 331 | +
|
| 332 | +# ------------------------------------------------------------------------------------- |
| 333 | + pypi-publish: |
| 334 | + name: Publish release to PyPI |
| 335 | + if: github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'pypi' |
| 336 | + needs: [check_version] |
| 337 | + runs-on: ubuntu-latest |
| 338 | + environment: |
| 339 | + name: pypi |
| 340 | + url: https://pypi.org/p/scipy |
| 341 | + permissions: |
| 342 | + id-token: write # mandatory for trusted publishing |
| 343 | + steps: |
| 344 | + - name: Download sdist and wheels |
| 345 | + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 |
| 346 | + with: |
| 347 | + path: dist |
| 348 | + merge-multiple: true |
| 349 | + |
| 350 | + - name: Publish |
| 351 | + uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 |
| 352 | + with: |
| 353 | + print-hash: true |
| 354 | + attestations: true |
0 commit comments