Publish pywry to PyPI #66
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: Publish pywry to PyPI | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| skip_tests: | |
| description: 'Skip running tests (use with caution)' | |
| required: false | |
| default: false | |
| type: boolean | |
| publish_to_pypi: | |
| description: 'Publish wheels to PyPI after build + test' | |
| required: false | |
| default: false | |
| type: boolean | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| defaults: | |
| run: | |
| working-directory: pywry | |
| env: | |
| PYTHON_VERSION: '3.12' | |
| jobs: | |
| # ============================================================================= | |
| # Step 1: Run tests first and fail fast | |
| # ============================================================================= | |
| test: | |
| name: Test - ${{ matrix.os }} - Python ${{ matrix.python-version }} | |
| if: ${{ !inputs.skip_tests }} | |
| runs-on: ${{ matrix.os }} | |
| permissions: | |
| contents: read | |
| strategy: | |
| fail-fast: true | |
| matrix: | |
| include: | |
| - os: ubuntu-24.04 | |
| python-version: '3.10' | |
| - os: ubuntu-24.04 | |
| python-version: '3.14' | |
| - os: windows-2025 | |
| python-version: '3.12' | |
| - os: macos-15 | |
| python-version: '3.12' | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| allow-prereleases: true | |
| cache: 'pip' | |
| cache-dependency-path: pywry/pyproject.toml | |
| - name: Set up Rust (Linux only) | |
| if: runner.os == 'Linux' | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Install Linux dependencies | |
| if: runner.os == 'Linux' | |
| run: | | |
| for i in 1 2 3; do sudo apt-get update && break || sleep 15; done | |
| for i in 1 2 3; do sudo apt-get install -y --fix-missing xvfb dbus-x11 at-spi2-core libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0 libxcb-shape0 libgl1 libegl1 libwebkit2gtk-4.1-dev libgtk-3-dev && break || { sleep 30; sudo apt-get update; }; done | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install ".[dev]" | |
| - name: Run tests (Linux) | |
| if: runner.os == 'Linux' | |
| env: | |
| PYWRY_HEADLESS: "1" | |
| NO_AT_BRIDGE: "1" | |
| run: | | |
| dbus-run-session -- xvfb-run --auto-servernum --server-args="-screen 0 1920x1080x24" python -m pytest -c pytest.ini tests/ -v --tb=short -x | |
| - name: Run tests (Windows/macOS) | |
| if: runner.os != 'Linux' | |
| env: | |
| PYWRY_HEADLESS: "1" | |
| PYWRY_DEPLOY__STATE_BACKEND: "memory" | |
| run: | | |
| python -m pytest -c pytest.ini tests/ -v --tb=short -x --ignore=tests/test_state_redis_integration.py --ignore=tests/test_auth_rbac_integration.py --ignore=tests/test_deploy_mode_integration.py --ignore=tests/test_e2e_deploy_mode.py --ignore=tests/test_e2e_rbac_widgets.py -m "not redis and not container" | |
| # ============================================================================= | |
| # Step 2: Build platform-specific wheels - SEPARATE JOBS PER PLATFORM/ARCH | |
| # ============================================================================= | |
| build-linux-x86_64: | |
| name: Build wheels - Linux x86_64 | |
| needs: [test] | |
| if: ${{ !cancelled() && (needs.test.result == 'success' || needs.test.result == 'skipped') }} | |
| runs-on: ubuntu-24.04 | |
| permissions: | |
| contents: read | |
| strategy: | |
| matrix: | |
| python-version: ['3.10', '3.11', '3.12', '3.13', '3.14'] | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| allow-prereleases: true | |
| cache: 'pip' | |
| cache-dependency-path: pywry/pyproject.toml | |
| - name: Install Linux dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libwebkit2gtk-4.1-0 libgtk-3-0 | |
| - name: Extract version | |
| id: version | |
| shell: bash | |
| run: | | |
| VERSION=$(grep -oP '^version\s*=\s*"\K[^"]+' pyproject.toml) | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "📦 Building pywry version: $VERSION" | |
| - name: Clean vendor directory | |
| shell: bash | |
| run: rm -rf pywry/_vendor/pytauri_wheel dist/ | |
| - name: Build sdist | |
| if: matrix.python-version == '3.12' | |
| run: | | |
| pip install build | |
| python -m build --sdist | |
| - name: Build wheel | |
| run: | | |
| pip install build hatchling pytauri-wheel | |
| python -m build --wheel | |
| - name: List dist contents | |
| shell: bash | |
| run: ls -la dist/ | |
| - name: Upload wheels | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: wheels-linux-x86_64-py${{ matrix.python-version }} | |
| path: pywry/dist/*.whl | |
| retention-days: 30 | |
| - name: Upload sdist | |
| if: matrix.python-version == '3.12' | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: sdist | |
| path: pywry/dist/*.tar.gz | |
| retention-days: 30 | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| build-linux-aarch64: | |
| name: Build wheels - Linux aarch64 | |
| needs: [test] | |
| if: ${{ !cancelled() && (needs.test.result == 'success' || needs.test.result == 'skipped') }} | |
| runs-on: ubuntu-24.04-arm | |
| permissions: | |
| contents: read | |
| strategy: | |
| matrix: | |
| python-version: ['3.10', '3.11', '3.12', '3.13', '3.14'] | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| allow-prereleases: true | |
| cache: 'pip' | |
| cache-dependency-path: pywry/pyproject.toml | |
| - name: Install Linux dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libwebkit2gtk-4.1-0 libgtk-3-0 | |
| - name: Clean vendor directory | |
| shell: bash | |
| run: rm -rf pywry/_vendor/pytauri_wheel dist/ | |
| - name: Build wheel | |
| run: | | |
| pip install build hatchling pytauri-wheel | |
| python -m build --wheel | |
| - name: List dist contents | |
| shell: bash | |
| run: ls -la dist/ | |
| - name: Upload wheels | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: wheels-linux-aarch64-py${{ matrix.python-version }} | |
| path: pywry/dist/*.whl | |
| retention-days: 30 | |
| build-macos-x86_64: | |
| name: Build wheels - macOS x86_64 | |
| needs: [test] | |
| if: ${{ !cancelled() && (needs.test.result == 'success' || needs.test.result == 'skipped') }} | |
| runs-on: macos-26-intel | |
| permissions: | |
| contents: read | |
| strategy: | |
| matrix: | |
| python-version: ['3.10', '3.11', '3.12', '3.13', '3.14'] | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| allow-prereleases: true | |
| cache: 'pip' | |
| cache-dependency-path: pywry/pyproject.toml | |
| - name: Clean vendor directory | |
| shell: bash | |
| run: rm -rf pywry/_vendor/pytauri_wheel dist/ | |
| - name: Build wheel | |
| run: | | |
| pip install build hatchling pytauri-wheel | |
| python -m build --wheel | |
| - name: List dist contents | |
| shell: bash | |
| run: ls -la dist/ | |
| - name: Upload wheels | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: wheels-macos-x86_64-py${{ matrix.python-version }} | |
| path: pywry/dist/*.whl | |
| retention-days: 30 | |
| build-macos-arm64: | |
| name: Build wheels - macOS arm64 | |
| needs: [test] | |
| if: ${{ !cancelled() && (needs.test.result == 'success' || needs.test.result == 'skipped') }} | |
| runs-on: macos-26 | |
| permissions: | |
| contents: read | |
| strategy: | |
| matrix: | |
| python-version: ['3.10', '3.11', '3.12', '3.13', '3.14'] | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| allow-prereleases: true | |
| cache: 'pip' | |
| cache-dependency-path: pywry/pyproject.toml | |
| - name: Clean vendor directory | |
| shell: bash | |
| run: rm -rf pywry/_vendor/pytauri_wheel dist/ | |
| - name: Build wheel | |
| run: | | |
| pip install build hatchling pytauri-wheel | |
| python -m build --wheel | |
| - name: List dist contents | |
| shell: bash | |
| run: ls -la dist/ | |
| - name: Upload wheels | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: wheels-macos-arm64-py${{ matrix.python-version }} | |
| path: pywry/dist/*.whl | |
| retention-days: 30 | |
| build-windows-amd64: | |
| name: Build wheels - Windows AMD64 | |
| needs: [test] | |
| if: ${{ !cancelled() && (needs.test.result == 'success' || needs.test.result == 'skipped') }} | |
| runs-on: windows-2025 | |
| permissions: | |
| contents: read | |
| strategy: | |
| matrix: | |
| python-version: ['3.10', '3.11', '3.12', '3.13', '3.14'] | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| allow-prereleases: true | |
| cache: 'pip' | |
| cache-dependency-path: pywry/pyproject.toml | |
| - name: Clean vendor directory | |
| shell: bash | |
| run: rm -rf pywry/_vendor/pytauri_wheel dist/ | |
| - name: Build wheel | |
| run: | | |
| pip install build hatchling pytauri-wheel | |
| python -m build --wheel | |
| - name: List dist contents | |
| shell: bash | |
| run: ls -la dist/ | |
| - name: Upload wheels | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: wheels-windows-amd64-py${{ matrix.python-version }} | |
| path: pywry/dist/*.whl | |
| retention-days: 30 | |
| build-windows-arm64: | |
| name: Build wheels - Windows ARM64 | |
| needs: [test] | |
| if: ${{ !cancelled() && (needs.test.result == 'success' || needs.test.result == 'skipped') }} | |
| runs-on: windows-11-arm | |
| permissions: | |
| contents: read | |
| strategy: | |
| matrix: | |
| python-version: ['3.11', '3.12', '3.13', '3.14'] | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| allow-prereleases: true | |
| cache: 'pip' | |
| cache-dependency-path: pywry/pyproject.toml | |
| - name: Clean vendor directory | |
| shell: bash | |
| run: rm -rf pywry/_vendor/pytauri_wheel dist/ | |
| - name: Build wheel | |
| run: | | |
| pip install build hatchling pytauri-wheel | |
| python -m build --wheel | |
| - name: List dist contents | |
| shell: bash | |
| run: ls -la dist/ | |
| - name: Upload wheels | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: wheels-windows-arm64-py${{ matrix.python-version }} | |
| path: pywry/dist/*.whl | |
| retention-days: 30 | |
| # ============================================================================= | |
| # Step 3: Merge all artifacts into single dist | |
| # ============================================================================= | |
| merge-artifacts: | |
| name: Merge artifacts | |
| needs: [build-linux-x86_64, build-linux-aarch64, build-macos-x86_64, build-macos-arm64, build-windows-amd64, build-windows-arm64] | |
| if: ${{ !cancelled() && needs.build-linux-x86_64.result == 'success' }} | |
| runs-on: ubuntu-24.04 | |
| defaults: | |
| run: | |
| working-directory: . | |
| permissions: | |
| contents: read | |
| outputs: | |
| version: ${{ needs.build-linux-x86_64.outputs.version }} | |
| steps: | |
| - name: Download all wheel artifacts | |
| uses: actions/download-artifact@v8.0.1 | |
| with: | |
| pattern: wheels-* | |
| path: artifacts/ | |
| - name: Download sdist | |
| uses: actions/download-artifact@v8.0.1 | |
| with: | |
| name: sdist | |
| path: artifacts/sdist/ | |
| - name: Consolidate artifacts and fail on duplicate wheel names | |
| shell: bash | |
| run: | | |
| python - <<'PY' | |
| import hashlib | |
| import shutil | |
| import sys | |
| from pathlib import Path | |
| artifacts_dir = Path("artifacts") | |
| dist_dir = Path("dist") | |
| dist_dir.mkdir(parents=True, exist_ok=True) | |
| def sha256(path: Path) -> str: | |
| h = hashlib.sha256() | |
| with path.open("rb") as f: | |
| for chunk in iter(lambda: f.read(1024 * 1024), b""): | |
| h.update(chunk) | |
| return h.hexdigest() | |
| seen: dict[str, tuple[str, Path]] = {} | |
| copied = 0 | |
| skipped_identical = 0 | |
| for wheel in sorted(artifacts_dir.rglob("*.whl")): | |
| name = wheel.name | |
| digest = sha256(wheel) | |
| if name in seen: | |
| prev_digest, prev_path = seen[name] | |
| if digest == prev_digest: | |
| skipped_identical += 1 | |
| print( | |
| f"::warning::Duplicate wheel filename with identical content; " | |
| f"keeping first: {name} ({prev_path} == {wheel})" | |
| ) | |
| continue | |
| print(f"::error::Conflicting duplicate wheel filename: {name}") | |
| print(f" first: {prev_path} ({prev_digest})") | |
| print(f" second: {wheel} ({digest})") | |
| sys.exit(1) | |
| shutil.copy2(wheel, dist_dir / name) | |
| seen[name] = (digest, wheel) | |
| copied += 1 | |
| for sdist in sorted((artifacts_dir / "sdist").rglob("*.tar.gz")): | |
| shutil.copy2(sdist, dist_dir / sdist.name) | |
| print(f"Copied {copied} unique wheels to dist/") | |
| if skipped_identical: | |
| print(f"Skipped {skipped_identical} identical duplicate wheel artifact(s)") | |
| PY | |
| - name: Validate wheel archives for duplicate ZIP members | |
| shell: bash | |
| run: | | |
| python - <<'PY' | |
| import collections | |
| import glob | |
| import sys | |
| import zipfile | |
| failed = False | |
| for wheel in sorted(glob.glob('dist/*.whl')): | |
| with zipfile.ZipFile(wheel) as zf: | |
| names = [entry.filename for entry in zf.infolist()] | |
| duplicates = [name for name, count in collections.Counter(names).items() if count > 1] | |
| if duplicates: | |
| failed = True | |
| print(f"::error file={wheel}::duplicate ZIP members detected") | |
| for name in duplicates[:20]: | |
| print(f" - {name}") | |
| if failed: | |
| sys.exit(1) | |
| print('All wheel archives passed duplicate ZIP member validation.') | |
| PY | |
| - name: List all artifacts | |
| run: | | |
| echo "📦 All distribution artifacts:" | |
| ls -la dist/ | |
| - name: Upload merged dist | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: dist | |
| path: dist/ | |
| retention-days: 30 | |
| # ============================================================================= | |
| # Step 4: Test built wheels on all platforms | |
| # ============================================================================= | |
| test-wheels: | |
| name: Test wheel - ${{ matrix.os }} | |
| needs: [merge-artifacts] | |
| if: ${{ !cancelled() && needs.merge-artifacts.result == 'success' && github.event_name == 'workflow_dispatch' }} | |
| runs-on: ${{ matrix.os }} | |
| defaults: | |
| run: | |
| working-directory: . | |
| permissions: | |
| contents: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-24.04 | |
| - os: ubuntu-24.04-arm | |
| - os: macos-26-intel | |
| - os: macos-26 | |
| - os: windows-2025 | |
| - os: windows-11-arm | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| cache: 'pip' | |
| cache-dependency-path: pywry/pyproject.toml | |
| - name: Set up Rust (Linux only) | |
| if: runner.os == 'Linux' | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Set up Rust (Windows ARM64) | |
| if: runner.os == 'Windows' && runner.arch == 'ARM64' | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: aarch64-pc-windows-msvc | |
| - name: Install Linux dependencies | |
| if: runner.os == 'Linux' | |
| run: | | |
| for i in 1 2 3; do sudo apt-get update && break || sleep 15; done | |
| for i in 1 2 3; do sudo apt-get install -y --fix-missing xvfb dbus-x11 at-spi2-core libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0 libxcb-shape0 libgl1 libegl1 libwebkit2gtk-4.1-dev libgtk-3-dev && break || { sleep 30; sudo apt-get update; }; done | |
| - name: Cache vcpkg packages (Windows ARM) | |
| if: runner.os == 'Windows' && runner.arch == 'ARM64' | |
| uses: actions/cache@v5 | |
| with: | |
| path: C:/vcpkg/installed | |
| key: vcpkg-arm64-windows-openssl-static-md-lua-${{ hashFiles('.github/workflows/publish-pywry.yml') }} | |
| restore-keys: | | |
| vcpkg-arm64-windows-openssl-static-md-lua- | |
| - name: Install OpenSSL via vcpkg (Windows ARM) | |
| if: runner.os == 'Windows' && runner.arch == 'ARM64' | |
| shell: pwsh | |
| run: | | |
| $env:VCPKG_ROOT = "$env:VCPKG_INSTALLATION_ROOT" | |
| & "$env:VCPKG_ROOT\vcpkg" install openssl:arm64-windows-static-md | |
| $opensslDir = "$env:VCPKG_ROOT\installed\arm64-windows-static-md" | |
| echo "OPENSSL_DIR=$opensslDir" >> $env:GITHUB_ENV | |
| echo "OPENSSL_LIB_DIR=$opensslDir\lib" >> $env:GITHUB_ENV | |
| echo "OPENSSL_INCLUDE_DIR=$opensslDir\include" >> $env:GITHUB_ENV | |
| - name: Setup build environment (Windows ARM) | |
| if: runner.os == 'Windows' && runner.arch == 'ARM64' | |
| shell: pwsh | |
| run: | | |
| $buildTemp = "C:\buildtmp" | |
| New-Item -ItemType Directory -Force -Path $buildTemp | Out-Null | |
| echo "TMP=$buildTemp" >> $env:GITHUB_ENV | |
| echo "TEMP=$buildTemp" >> $env:GITHUB_ENV | |
| $env:VCPKG_ROOT = "$env:VCPKG_INSTALLATION_ROOT" | |
| & "$env:VCPKG_ROOT\vcpkg" install lua:arm64-windows | |
| $luaDir = "$env:VCPKG_ROOT\installed\arm64-windows" | |
| echo "LUA_DIR=$luaDir" >> $env:GITHUB_ENV | |
| echo "LUA_LIB=$luaDir\lib" >> $env:GITHUB_ENV | |
| echo "LUA_INC=$luaDir\include" >> $env:GITHUB_ENV | |
| - name: Build cryptography from source (Windows ARM) | |
| if: runner.os == 'Windows' && runner.arch == 'ARM64' | |
| shell: pwsh | |
| run: | | |
| pip install --no-cache-dir --no-binary=cryptography cryptography | |
| python -c "from cryptography.hazmat.bindings._rust import openssl; print('cryptography OK')" | |
| - name: Download dist artifacts | |
| uses: actions/download-artifact@v8.0.1 | |
| with: | |
| name: dist | |
| path: ${{ runner.temp }}/dist/ | |
| - name: Install from built wheel | |
| shell: bash | |
| run: | | |
| PYWRY_VERSION="${{ needs.merge-artifacts.outputs.version }}" | |
| pip install --find-links "$RUNNER_TEMP/dist/" "pywry[all]==$PYWRY_VERSION" | |
| pip install pytest pytest-asyncio pytest-timeout httpx plotly fakeredis testcontainers pandas | |
| - name: Install cryptography (non-Windows-ARM) | |
| if: "!(runner.os == 'Windows' && runner.arch == 'ARM64')" | |
| shell: bash | |
| run: pip install cryptography | |
| - name: Configure Docker (macOS Intel) | |
| if: runner.os == 'macOS' && runner.arch == 'X64' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if docker info >/dev/null 2>&1; then | |
| echo "Docker daemon already available" | |
| else | |
| if [ -d "/Applications/Docker.app" ]; then | |
| echo "Starting Docker Desktop" | |
| open -a Docker | |
| for _ in {1..30}; do | |
| if docker info >/dev/null 2>&1; then | |
| break | |
| fi | |
| sleep 5 | |
| done | |
| fi | |
| fi | |
| if ! docker info >/dev/null 2>&1; then | |
| echo "Falling back to Colima" | |
| brew install docker colima | |
| colima start --runtime docker --arch x86_64 --cpu 2 --memory 4 --disk 20 | |
| fi | |
| if [ -S "$HOME/.colima/default/docker.sock" ]; then | |
| sudo mkdir -p /var/run | |
| sudo ln -sf "$HOME/.colima/default/docker.sock" /var/run/docker.sock | |
| fi | |
| echo "DOCKER_HOST=unix:///var/run/docker.sock" >> "$GITHUB_ENV" | |
| docker info | |
| - name: Verify installation | |
| shell: bash | |
| run: | | |
| python -c "import pywry; print(f'pywry {pywry.__version__} installed successfully')" | |
| python -c "import pywry._vendor.pytauri_wheel.ext_mod as ext_mod; print('pytauri wheel ext_mod:', ext_mod.__file__)" | |
| python -c "from pywry import PyWry; print('PyWry class imported')" | |
| python -c "from pywry.toolbar import Button, Toolbar; print('Toolbar components imported')" | |
| - name: Copy tests outside source tree | |
| shell: bash | |
| run: | | |
| # Copy tests + config to a temp location so the checkout pywry/ directory | |
| # does NOT shadow the installed wheel package on sys.path. | |
| cp -r pywry/tests "$RUNNER_TEMP/tests" | |
| cp pywry/pytest.ini "$RUNNER_TEMP/pytest.ini" | |
| - name: Run tests (Linux) | |
| if: runner.os == 'Linux' | |
| working-directory: ${{ runner.temp }} | |
| env: | |
| PYWRY_TEST_USE_INSTALLED_WHEEL: "1" | |
| PYWRY_HEADLESS: "1" | |
| NO_AT_BRIDGE: "1" | |
| run: | | |
| dbus-run-session -- xvfb-run --auto-servernum --server-args="-screen 0 1920x1080x24" python -m pytest -c pytest.ini tests/ -v --tb=short -x | |
| - name: Run tests (Windows x64) | |
| if: runner.os == 'Windows' && runner.arch == 'X64' | |
| working-directory: ${{ runner.temp }} | |
| env: | |
| PYWRY_TEST_USE_INSTALLED_WHEEL: "1" | |
| PYWRY_HEADLESS: "1" | |
| PYWRY_DEPLOY__STATE_BACKEND: "memory" | |
| PYTHONUTF8: "1" | |
| run: | | |
| python -m pytest -c pytest.ini tests/ -v --tb=short -x --ignore=tests/test_state_redis_integration.py --ignore=tests/test_auth_rbac_integration.py --ignore=tests/test_deploy_mode_integration.py --ignore=tests/test_e2e_deploy_mode.py --ignore=tests/test_e2e_rbac_widgets.py -m "not redis and not container" | |
| - name: Run tests (Windows ARM) | |
| if: runner.os == 'Windows' && runner.arch == 'ARM64' | |
| working-directory: ${{ runner.temp }} | |
| env: | |
| PYWRY_TEST_USE_INSTALLED_WHEEL: "1" | |
| PYWRY_HEADLESS: "1" | |
| PYWRY_DEPLOY__STATE_BACKEND: "memory" | |
| PYTHONUTF8: "1" | |
| run: | | |
| python -m pytest -c pytest.ini tests/ -v --tb=short -x --ignore=tests/test_state_redis_integration.py --ignore=tests/test_auth_rbac_integration.py --ignore=tests/test_deploy_mode_integration.py --ignore=tests/test_e2e_deploy_mode.py --ignore=tests/test_e2e_rbac_widgets.py --ignore=tests/test_inline_ssl.py -m "not redis and not container" | |
| - name: Run tests (macOS ARM) | |
| if: runner.os == 'macOS' && runner.arch == 'ARM64' | |
| working-directory: ${{ runner.temp }} | |
| env: | |
| PYWRY_TEST_USE_INSTALLED_WHEEL: "1" | |
| PYWRY_HEADLESS: "1" | |
| PYWRY_DEPLOY__STATE_BACKEND: "memory" | |
| run: | | |
| python -m pytest -c pytest.ini tests/ -v --tb=short -x --ignore=tests/test_state_redis_integration.py --ignore=tests/test_auth_rbac_integration.py --ignore=tests/test_deploy_mode_integration.py --ignore=tests/test_e2e_deploy_mode.py --ignore=tests/test_e2e_rbac_widgets.py -m "not redis and not container" | |
| - name: Run tests (macOS Intel) | |
| if: runner.os == 'macOS' && runner.arch == 'X64' | |
| working-directory: ${{ runner.temp }} | |
| env: | |
| PYWRY_TEST_USE_INSTALLED_WHEEL: "1" | |
| PYWRY_HEADLESS: "1" | |
| PYWRY_DEPLOY__STATE_BACKEND: "memory" | |
| run: | | |
| python -m pytest -c pytest.ini tests/ -v --tb=short -x --ignore=tests/test_deploy_mode_integration.py --ignore=tests/test_e2e_deploy_mode.py --ignore=tests/test_e2e_rbac_widgets.py --deselect=tests/test_window_modes.py::TestMultiWindowMode::test_update_specific_window | |
| # ============================================================================= | |
| # Step 5: Create draft release with all artifacts (workflow_dispatch only) | |
| # ============================================================================= | |
| create-release: | |
| name: Create Draft Release | |
| needs: [merge-artifacts] | |
| if: ${{ !cancelled() && needs.merge-artifacts.result == 'success' && github.event_name == 'workflow_dispatch' }} | |
| runs-on: ubuntu-24.04 | |
| defaults: | |
| run: | |
| working-directory: . | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Create draft release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: pywry-${{ needs.merge-artifacts.outputs.version }} | |
| name: pywry ${{ needs.merge-artifacts.outputs.version }} | |
| draft: true | |
| prerelease: false | |
| generate_release_notes: true | |
| body: | | |
| ## pywry ${{ needs.merge-artifacts.outputs.version }} | |
| ### Installation | |
| ```bash | |
| pip install pywry==${{ needs.merge-artifacts.outputs.version }} | |
| ``` | |
| ### Supported Platforms | |
| | Platform | Architecture | Wheel | | |
| |----------|--------------|-------| | |
| | Linux | x86_64 | ✅ | | |
| | Linux | aarch64 | ✅ | | |
| | macOS | x86_64 (Intel) | ✅ | | |
| | macOS | arm64 (Apple Silicon) | ✅ | | |
| | Windows | AMD64 | ✅ | | |
| | Windows | ARM64 | ✅ | | |
| ### Python Versions | |
| - Python 3.10, 3.11, 3.12, 3.13, 3.14 | |
| --- | |
| *This is a draft release. Wheels are published to PyPI separately.* | |
| # ============================================================================= | |
| # Step 6: Publish to PyPI - Trusted Publisher (workflow_dispatch only) | |
| # ============================================================================= | |
| publish-pypi: | |
| name: Publish to PyPI | |
| needs: [merge-artifacts, test-wheels] | |
| if: ${{ !cancelled() && needs.merge-artifacts.result == 'success' && (needs.test-wheels.result == 'success' || needs.test-wheels.result == 'skipped') && inputs.publish_to_pypi }} | |
| runs-on: ubuntu-24.04 | |
| defaults: | |
| run: | |
| working-directory: . | |
| permissions: | |
| id-token: write | |
| steps: | |
| - name: Download dist artifacts | |
| uses: actions/download-artifact@v8.0.1 | |
| with: | |
| name: dist | |
| path: dist/ | |
| - name: List distribution files | |
| run: | | |
| echo "📦 Publishing to PyPI:" | |
| ls -la dist/ | |
| - name: Publish to PyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| verbose: true | |