CI #34
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| target: | |
| description: OS and architecture target | |
| required: false | |
| type: choice | |
| default: all | |
| options: | |
| - all | |
| - linux-x64 | |
| - linux-arm64 | |
| - macos-arm64 | |
| - macos-x64 | |
| - windows-x64 | |
| - windows-arm64 | |
| python_version: | |
| description: Python version | |
| required: false | |
| type: choice | |
| default: all | |
| options: | |
| - all | |
| - "3.10" | |
| - "3.11" | |
| - "3.12" | |
| - "3.13" | |
| - "3.14" | |
| workflow_call: | |
| inputs: | |
| target: | |
| description: OS and architecture target (all, linux-x64, linux-arm64, macos-arm64, macos-x64, windows-x64, windows-arm64) | |
| required: false | |
| type: string | |
| default: all | |
| python_version: | |
| description: Python version (all, 3.10, 3.11, 3.12, 3.13, 3.14) | |
| required: false | |
| type: string | |
| default: all | |
| pull_request: | |
| branches: [main] | |
| jobs: | |
| setup: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| run_linux: ${{ steps.resolve.outputs.run_linux }} | |
| run_macos: ${{ steps.resolve.outputs.run_macos }} | |
| run_windows: ${{ steps.resolve.outputs.run_windows }} | |
| linux_matrix: ${{ steps.resolve.outputs.linux_matrix }} | |
| macos_matrix: ${{ steps.resolve.outputs.macos_matrix }} | |
| windows_matrix: ${{ steps.resolve.outputs.windows_matrix }} | |
| steps: | |
| - name: Resolve selected targets and matrix values | |
| id: resolve | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| TARGET_INPUT: ${{ inputs.target || 'all' }} | |
| PYTHON_INPUT: ${{ inputs.python_version || 'all' }} | |
| run: | | |
| set -euo pipefail | |
| target="$TARGET_INPUT" | |
| python_version="$PYTHON_INPUT" | |
| if [[ "$EVENT_NAME" == "pull_request" ]]; then | |
| target="all" | |
| python_version="all" | |
| fi | |
| case "$python_version" in | |
| all) | |
| py_versions=("3.10" "3.11" "3.12" "3.13" "3.14") | |
| ;; | |
| 3.10|3.11|3.12|3.13|3.14) | |
| py_versions=("$python_version") | |
| ;; | |
| *) | |
| echo "Unsupported python_version: $python_version" | |
| exit 1 | |
| ;; | |
| esac | |
| python_json='[' | |
| first=true | |
| for py in "${py_versions[@]}"; do | |
| if [[ "$first" == true ]]; then | |
| first=false | |
| else | |
| python_json+=',' | |
| fi | |
| python_json+="\"$py\"" | |
| done | |
| python_json+=']' | |
| run_linux=false | |
| run_macos=false | |
| run_windows=false | |
| linux_targets=() | |
| macos_versions='[]' | |
| windows_targets=() | |
| case "$target" in | |
| all) | |
| run_linux=true | |
| run_macos=true | |
| run_windows=true | |
| linux_targets+=("ubuntu-latest|x64") | |
| macos_versions='["macos-15","macos-15-intel"]' | |
| windows_targets+=("windows-latest|x64") | |
| ;; | |
| linux-x64) | |
| run_linux=true | |
| linux_targets+=("ubuntu-latest|x64") | |
| ;; | |
| linux-arm64) | |
| run_linux=true | |
| linux_targets+=("ubuntu-24.04-arm|arm64") | |
| ;; | |
| macos-arm64) | |
| run_macos=true | |
| macos_versions='["macos-15"]' | |
| ;; | |
| macos-x64) | |
| run_macos=true | |
| macos_versions='["macos-15-intel"]' | |
| ;; | |
| windows-x64) | |
| run_windows=true | |
| windows_targets+=("windows-latest|x64") | |
| ;; | |
| windows-arm64) | |
| run_windows=true | |
| windows_targets+=("windows-11-arm|arm64") | |
| ;; | |
| *) | |
| echo "Unsupported target: $target" | |
| exit 1 | |
| ;; | |
| esac | |
| build_include_matrix() { | |
| local -n target_ref=$1 | |
| local matrix='{"include":[' | |
| local first_item=true | |
| for target_item in "${target_ref[@]}"; do | |
| local runs_on="${target_item%%|*}" | |
| local arch="${target_item##*|}" | |
| for py in "${py_versions[@]}"; do | |
| if [[ "$first_item" == true ]]; then | |
| first_item=false | |
| else | |
| matrix+=',' | |
| fi | |
| matrix+="{\"runs-on\":\"$runs_on\",\"arch\":\"$arch\",\"python-version\":\"$py\"}" | |
| done | |
| done | |
| matrix+=']}' | |
| echo "$matrix" | |
| } | |
| if [[ "$run_linux" == "true" ]]; then | |
| linux_matrix="$(build_include_matrix linux_targets)" | |
| else | |
| linux_matrix='{"include":[]}' | |
| fi | |
| if [[ "$run_macos" == "true" ]]; then | |
| macos_matrix="{\"macos-version\":$macos_versions,\"python-version\":$python_json}" | |
| else | |
| macos_matrix='{"macos-version":[],"python-version":[]}' | |
| fi | |
| if [[ "$run_windows" == "true" ]]; then | |
| windows_matrix="$(build_include_matrix windows_targets)" | |
| else | |
| windows_matrix='{"include":[]}' | |
| fi | |
| { | |
| echo "run_linux=$run_linux" | |
| echo "run_macos=$run_macos" | |
| echo "run_windows=$run_windows" | |
| echo "linux_matrix=$linux_matrix" | |
| echo "macos_matrix=$macos_matrix" | |
| echo "windows_matrix=$windows_matrix" | |
| } >> "$GITHUB_OUTPUT" | |
| build-linux: | |
| needs: setup | |
| if: ${{ needs.setup.outputs.run_linux == 'true' }} | |
| runs-on: ${{ matrix.runs-on }} | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJson(needs.setup.outputs.linux_matrix) }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| submodules: recursive | |
| - name: Set up Python | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Select manylinux Python tag | |
| run: | | |
| case "${{ matrix.python-version }}" in | |
| "3.10") echo "MANYLINUX_PYTAG=cp310-cp310" >> "$GITHUB_ENV" ;; | |
| "3.11") echo "MANYLINUX_PYTAG=cp311-cp311" >> "$GITHUB_ENV" ;; | |
| "3.12") echo "MANYLINUX_PYTAG=cp312-cp312" >> "$GITHUB_ENV" ;; | |
| "3.13") echo "MANYLINUX_PYTAG=cp313-cp313" >> "$GITHUB_ENV" ;; | |
| "3.14") echo "MANYLINUX_PYTAG=cp314-cp314" >> "$GITHUB_ENV" ;; | |
| *) | |
| echo "Unsupported python-version: ${{ matrix.python-version }}" | |
| exit 1 | |
| ;; | |
| esac | |
| - name: Build and repair wheel in manylinux container | |
| run: | | |
| rm -rf dist wheelhouse | |
| mkdir -p dist wheelhouse | |
| if [[ "${{ matrix.arch }}" == "arm64" ]]; then | |
| MANYLINUX_IMAGE="quay.io/pypa/manylinux_2_34_aarch64:latest" | |
| else | |
| MANYLINUX_IMAGE="quay.io/pypa/manylinux_2_34:latest" | |
| fi | |
| docker run --rm \ | |
| -e PYTAG="${MANYLINUX_PYTAG}" \ | |
| -v "${{ github.workspace }}:/io" \ | |
| -w /io \ | |
| "$MANYLINUX_IMAGE" \ | |
| /bin/bash -lc ' | |
| set -euo pipefail | |
| if ! command -v cmake >/dev/null 2>&1; then | |
| dnf install -y cmake gcc gcc-c++ make ninja-build | |
| fi | |
| "/opt/python/${PYTAG}/bin/python" -m pip install --upgrade pip setuptools wheel build auditwheel | |
| "/opt/python/${PYTAG}/bin/python" -m build --wheel -o dist | |
| auditwheel repair dist/*.whl -w wheelhouse | |
| ' | |
| - name: Install wheel | |
| run: | | |
| python3 -m venv env | |
| source env/bin/activate | |
| python -m pip install --upgrade wheelhouse/*.whl | |
| - name: Smoke test import (non-3.14) | |
| if: matrix.python-version != '3.14' | |
| run: | | |
| source env/bin/activate | |
| python -c "import JupiterMag; print(JupiterMag.__version__)" | |
| - name: Upload wheel artifact | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: wheels-linux-${{ matrix.arch }}-py${{ matrix.python-version }} | |
| path: wheelhouse/*.whl | |
| - name: Build sdist (Linux 3.14) | |
| if: matrix.python-version == '3.14' | |
| run: | | |
| docker run --rm \ | |
| -e PYTAG="${MANYLINUX_PYTAG}" \ | |
| -v "${{ github.workspace }}:/io" \ | |
| -w /io \ | |
| quay.io/pypa/manylinux_2_34:latest \ | |
| /bin/bash -lc ' | |
| set -euo pipefail | |
| "/opt/python/${PYTAG}/bin/python" -m pip install --upgrade pip setuptools wheel build | |
| "/opt/python/${PYTAG}/bin/python" -m build --sdist -o dist | |
| ' | |
| - name: Upload sdist artifact (Linux 3.14) | |
| if: matrix.python-version == '3.14' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: sdist-linux-${{ matrix.arch }}-py${{ matrix.python-version }} | |
| path: dist/*.tar.gz | |
| - name: Run pytest from tests directory | |
| if: matrix.python-version == '3.14' | |
| run: | | |
| source env/bin/activate | |
| pip install pytest black flake8 | |
| cd tests | |
| pytest -q | |
| build-macos: | |
| needs: setup | |
| if: ${{ needs.setup.outputs.run_macos == 'true' }} | |
| runs-on: ${{ matrix.macos-version }} | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJson(needs.setup.outputs.macos_matrix) }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| submodules: recursive | |
| - name: Set up Python | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install build tools | |
| run: | | |
| brew update | |
| brew install libtool | |
| python3 -m venv env | |
| source env/bin/activate | |
| python -m pip install --upgrade pip setuptools wheel build | |
| - name: Build wheel | |
| run: | | |
| source env/bin/activate | |
| export CMAKE_OSX_ARCHITECTURES="$(uname -m)" | |
| export CMAKE_OSX_DEPLOYMENT_TARGET="11.0" | |
| export MACOSX_DEPLOYMENT_TARGET="11.0" | |
| rm -rf dist | |
| mkdir -p dist | |
| python -m build --wheel -o dist | |
| ls -1 dist | |
| if ls dist/*universal2*.whl >/dev/null 2>&1; then | |
| echo "Unexpected universal2 wheel produced" | |
| exit 1 | |
| fi | |
| - name: Install wheel | |
| run: | | |
| source env/bin/activate | |
| python -m pip install --upgrade dist/*.whl | |
| - name: Smoke test import (non-3.14) | |
| if: matrix.python-version != '3.14' | |
| run: | | |
| source env/bin/activate | |
| python -c "import JupiterMag; print(JupiterMag.__version__)" | |
| - name: Upload wheel artifact | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: wheels-${{ matrix.macos-version }}-py${{ matrix.python-version }} | |
| path: dist/*.whl | |
| - name: Run tests | |
| if: matrix.python-version == '3.14' | |
| run: | | |
| source env/bin/activate | |
| if [[ "$(uname -m)" == "x86_64" ]]; then | |
| # DateTimeTools may resolve to an arm64 wheel on Intel runners. | |
| # Rebuild from source to guarantee a native x86_64 libdatetime.dylib. | |
| python -m pip install --force-reinstall --no-binary DateTimeTools DateTimeTools | |
| fi | |
| pip install pytest black flake8 | |
| cd tests | |
| pytest -q | |
| build-windows: | |
| needs: setup | |
| if: ${{ needs.setup.outputs.run_windows == 'true' }} | |
| runs-on: ${{ matrix.runs-on }} | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJson(needs.setup.outputs.windows_matrix) }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| submodules: recursive | |
| - name: Set up Python | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Create venv | |
| shell: pwsh | |
| run: | | |
| python -m venv env | |
| - name: Build wheel | |
| shell: pwsh | |
| run: | | |
| .\env\Scripts\Activate.ps1 | |
| $pythonExe = Join-Path $env:VIRTUAL_ENV "Scripts\python.exe" | |
| & $pythonExe -m pip install --upgrade build wheel | |
| $targetArch = if ("${{ matrix.arch }}" -eq "arm64") { "arm64" } else { "x64" } | |
| $hostArch = $targetArch | |
| $vswhere = Join-Path ${env:ProgramFiles(x86)} "Microsoft Visual Studio\Installer\vswhere.exe" | |
| if (-not (Test-Path $vswhere)) { | |
| throw "vswhere.exe not found at $vswhere" | |
| } | |
| $vsInstallPath = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath | |
| if (-not $vsInstallPath) { | |
| throw "Could not locate a Visual Studio installation with C++ tools" | |
| } | |
| $vsDevCmd = Join-Path $vsInstallPath "Common7\Tools\VsDevCmd.bat" | |
| if (-not (Test-Path $vsDevCmd)) { | |
| throw "VsDevCmd.bat not found at $vsDevCmd" | |
| } | |
| # Import Visual Studio developer environment vars into this PowerShell step. | |
| $envDump = & cmd.exe /s /c "`"$vsDevCmd`" -no_logo -host_arch=$hostArch -arch=$targetArch && set" | |
| foreach ($line in $envDump) { | |
| if ($line -match '^(.*?)=(.*)$') { | |
| Set-Item -Path ("Env:" + $matches[1]) -Value $matches[2] | |
| } | |
| } | |
| $clCmd = Get-Command cl.exe -ErrorAction SilentlyContinue | |
| $cmakeCmd = Get-Command cmake.exe -ErrorAction SilentlyContinue | |
| if (-not $clCmd -or -not $cmakeCmd) { | |
| throw "MSVC or CMake not available in PATH after loading VsDevCmd" | |
| } | |
| $env:CMAKE_GENERATOR = "Visual Studio 17 2022" | |
| $env:CMAKE_GENERATOR_PLATFORM = $targetArch | |
| Remove-Item Env:CMAKE_ARGS -ErrorAction SilentlyContinue | |
| Write-Host "Using cl: $($clCmd.Source)" | |
| Write-Host "Using cmake: $($cmakeCmd.Source)" | |
| & cmd.exe /c "cl" | |
| & $cmakeCmd.Source --version | |
| & $pythonExe -m build --wheel -o dist | |
| - name: Install wheel | |
| shell: pwsh | |
| run: | | |
| .\env\Scripts\Activate.ps1 | |
| $wheel = Get-ChildItem -Path dist -Filter *.whl | Select-Object -First 1 | |
| if (-not $wheel) { throw "No wheel found in dist/" } | |
| python -m pip install --upgrade $wheel.FullName | |
| - name: Smoke test import (non-3.14) | |
| if: matrix.python-version != '3.14' | |
| shell: pwsh | |
| run: | | |
| .\env\Scripts\Activate.ps1 | |
| python -c "import JupiterMag; print(JupiterMag.__version__)" | |
| - name: Upload wheel artifact | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: wheels-windows-${{ matrix.arch }}-py${{ matrix.python-version }} | |
| path: dist/*.whl | |
| - name: Run tests | |
| if: matrix.python-version == '3.14' | |
| shell: pwsh | |
| run: | | |
| .\env\Scripts\Activate.ps1 | |
| Set-Location tests | |
| pip install pytest black flake8 | |
| pytest -q |