feat: windows support (#216) #28
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: Build and Test PyPi Wheels on Windows | |
| on: | |
| push: | |
| branches: [ "main" ] | |
| workflow_call: | |
| inputs: | |
| publish_target: | |
| description: 'Publish Destination' | |
| required: true | |
| default: 'testpypi' | |
| type: string | |
| workflow_dispatch: | |
| inputs: | |
| publish_target: | |
| description: 'Publish Destination' | |
| required: true | |
| default: 'testpypi' | |
| type: choice | |
| options: | |
| - testpypi | |
| - pypi | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref || '' }}-${{ github.base_ref || '' }}-${{ github.ref != 'refs/heads/main' || github.sha }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| compute_matrix: | |
| name: Determine matrix to run on | |
| runs-on: linux_x64 | |
| outputs: | |
| matrix: ${{ steps.main.outputs.matrix }} | |
| steps: | |
| - id: main | |
| run: | | |
| PLATFORMS=("windows-g9i" "windows-2025" "windows-2022") | |
| PYTHON_VERSIONS=("3.10" "3.11" "3.12" "3.13" "3.14") | |
| JSON_ARRAY="[" | |
| for p in "${PLATFORMS[@]}"; do | |
| for v in "${PYTHON_VERSIONS[@]}"; do | |
| JSON_ARRAY+="{\"platform\":\"$p\",\"python-version\":\"$v\"}," | |
| done | |
| done | |
| # Remove the trailing comma and close the bracket | |
| JSON_ARRAY="${JSON_ARRAY%,}]" | |
| # Wrap in the "include" key and send to GITHUB_OUTPUT | |
| RESULT="{\"include\":$JSON_ARRAY}" | |
| echo "matrix=$RESULT" >> "$GITHUB_OUTPUT" | |
| build_wheels_windows_2025: | |
| name: Build wheels on windows-2025 (x64) | |
| runs-on: windows-g9i | |
| outputs: | |
| zvec_version: ${{ steps.log_vars.outputs.zvec_version }} | |
| steps: | |
| - name: Show env info | |
| run: | | |
| Get-CimInstance -ClassName Win32_Processor | |
| where cl | |
| & "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -all -products * -prerelease -format json | |
| shell: powershell | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| with: | |
| submodules: recursive | |
| - name: Cleanup left marker files | |
| run: | | |
| git submodule foreach --recursive 'git reset --hard && git clean -ffdx' | |
| - name: Set up Python (for cibuildwheel controller) | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.11' | |
| - name: Install cibuildwheel | |
| run: | | |
| pip install --upgrade pip | |
| pip install cibuildwheel==3.4.0 | |
| - name: Add Git bin to PATH | |
| run: echo "C:\Program Files\Git\usr\bin" >> $env:GITHUB_PATH | |
| shell: powershell | |
| - name: Cache built wheels | |
| id: cache-wheels | |
| uses: actions/cache@v4 | |
| with: | |
| path: wheelhouse | |
| key: ${{ runner.os }}-wheels-${{ hashFiles('**/pyproject.toml', '**/setup.py', '**/*.py', '**/*.c', '**/*.cpp') }} | |
| - name: Build wheels using cibuildwheel | |
| if: steps.cache-wheels.outputs.cache-hit != 'true' | |
| shell: powershell | |
| run: | | |
| python -m cibuildwheel --output-dir wheelhouse | |
| - name: Install and test wheel | |
| shell: powershell | |
| run: | | |
| $wheels = (Get-ChildItem wheelhouse/zvec-*.whl)[1] # cp311 | |
| $wheelPath = $wheels.FullName | |
| python -m venv test_env | |
| $venvPython = "test_env/Scripts/python.exe" | |
| & $venvPython -m pip install --upgrade pip | |
| & $venvPython -m pip install $wheelPath | |
| & $venvPython -c "import zvec; print('Import OK:', zvec.__version__)" | |
| - name: Log wheel variables | |
| id: log_vars | |
| shell: powershell | |
| run: | | |
| $wheels = Get-ChildItem wheelhouse/*.whl | Select-Object -ExpandProperty FullName | |
| # Extract zvec version from wheel filename | |
| $wheel = Get-ChildItem wheelhouse/zvec-*.whl | Select-Object -First 1 | |
| $filename = $wheel.Name | |
| $version = [regex]::Match($filename, '^zvec-([^-]+)-').Groups[1].Value | |
| Write-Host "Detected zvec version: $version" | |
| # Output version for job outputs | |
| "zvec_version=$version" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | |
| if ($env:GITHUB_STEP_SUMMARY) { | |
| "Wheel files:" | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append | |
| $wheels | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append | |
| "zvec version: $version" | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append | |
| } | |
| "wheels=$($wheels -join ' ')" | Out-File -FilePath $env:GITHUB_ENV -Append | |
| - name: Upload built wheels | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: windows-wheels | |
| path: wheelhouse/*.whl | |
| publish_wheels: | |
| name: Publish wheels | |
| runs-on: windows-g9i | |
| needs: build_wheels_windows_2025 | |
| steps: | |
| - name: Download built wheels | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: windows-wheels | |
| path: wheelhouse/ | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.11' | |
| - name: Publish to PyPI/TestPyPI | |
| env: | |
| TWINE_USERNAME: __token__ | |
| TWINE_PASSWORD: ${{ inputs.publish_target == 'pypi' && secrets.PYPI_API_TOKEN || secrets.TEST_PYPI_API_TOKEN }} | |
| TWINE_REPOSITORY_URL: ${{ inputs.publish_target == 'pypi' && 'https://upload.pypi.org/legacy/' || 'https://test.pypi.org/legacy/' }} | |
| run: | | |
| pip install twine | |
| twine upload --skip-existing --verbose wheelhouse/*.whl | |
| smoke_test: | |
| needs: [ publish_wheels, compute_matrix, build_wheels_windows_2025 ] | |
| strategy: | |
| fail-fast: false | |
| max-parallel: 4 | |
| matrix: ${{ fromJson(needs.compute_matrix.outputs.matrix) }} | |
| runs-on: ${{ matrix.platform }} | |
| steps: | |
| - name: Smoke test from PyPI | |
| shell: powershell | |
| env: | |
| PUBLISH_TARGET: ${{ inputs.publish_target || 'testpypi' }} | |
| ZVEC_VERSION: ${{ needs.build_wheels_windows_2025.outputs.zvec_version }} | |
| run: | | |
| # Check if version is empty | |
| if ([string]::IsNullOrWhiteSpace($env:ZVEC_VERSION)) { | |
| Write-Host "ERROR: ZVEC_VERSION is empty or not set. Cannot proceed with smoke test." | |
| exit 1 | |
| } | |
| $version = $env:ZVEC_VERSION | |
| Write-Host "Using zvec version from build job: $version" | |
| if ($env:PUBLISH_TARGET -eq 'testpypi') { | |
| $indexArgs = @("--index-url", "https://test.pypi.org/simple/", "--extra-index-url", "https://pypi.org/simple/") | |
| Write-Host "Waiting for zvec==$version to become available on TestPyPI..." | |
| } else { | |
| $indexArgs = @() | |
| Write-Host "Waiting for zvec==$version to become available on PyPI..." | |
| } | |
| $ErrorActionPreference = 'Continue' | |
| $PSNativeCommandUseErrorActionPreference = $false | |
| $found = $false | |
| for ($i = 1; $i -le 30; $i++) { | |
| python -m pip install -f @indexArgs --dry-run "zvec==$version" 1>$null 2>$null | |
| if ($LASTEXITCODE -eq 0) { | |
| Write-Host "Version $version is available." | |
| $found = $true | |
| break | |
| } | |
| Write-Host "Attempt $i/30: not yet available, retrying in 10s..." | |
| Start-Sleep -Seconds 10 | |
| } | |
| if (-not $found) { | |
| Write-Host "ERROR: Timed out waiting for zvec==$version." | |
| exit 1 | |
| } | |
| python -m venv test_env | |
| $venvPython = "test_env/Scripts/python.exe" | |
| & $venvPython -m pip install --upgrade pip | |
| & $venvPython -m pip install @indexArgs "zvec==$version" | |
| & $venvPython -c "import zvec; print('Import OK:', zvec.__version__)" |