Synchronize CI/CD, Flatbuffers vendoring and wamp-ai and wamp-cicd Submodules between autobahn-python and zlmdb #204
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: wheels-arm64 | |
| on: | |
| # Build wheels on feature branches and PRs (test only) | |
| push: | |
| branches: ["**"] | |
| tags: | |
| - 'v*' | |
| pull_request: | |
| branches: [master] | |
| # Publish to GitHub Releases when merged to master | |
| # Publish to PyPI when tagged | |
| workflow_dispatch: | |
| env: | |
| # Platform target | |
| ARCH: aarch64 | |
| jobs: | |
| identifiers: | |
| # GitHub needs to know where .cicd/workflows/identifiers.yml lives at parse time, | |
| # and submodules aren't included in that context! thus the following does NOT work: | |
| # uses: ./.cicd/workflows/identifiers.yml | |
| # we MUST reference the remote repo directly: | |
| uses: wamp-proto/wamp-cicd/.github/workflows/identifiers.yml@main | |
| # IMPORTANT: we still need .cicd as a Git submodule in the using repo though! | |
| # because e.g. identifiers.yml wants to access scripts/sanitize.sh ! | |
| build-wheels: | |
| name: Build ARM64 wheels (${{ matrix.target.name }}) | |
| needs: identifiers | |
| runs-on: ubuntu-latest | |
| env: | |
| BASE_REPO: ${{ needs.identifiers.outputs.base_repo }} | |
| BASE_BRANCH: ${{ needs.identifiers.outputs.base_branch }} | |
| PR_NUMBER: ${{ needs.identifiers.outputs.pr_number }} | |
| PR_REPO: ${{ needs.identifiers.outputs.pr_repo }} | |
| PR_BRANCH: ${{ needs.identifiers.outputs.pr_branch }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: | |
| # ============================================================ | |
| # CPython ARM64 wheels (using official PyPA manylinux images) | |
| # ============================================================ | |
| # CPython 3.11 - manylinux_2_28_aarch64 (glibc 2.28) | |
| # Modern baseline (Debian 10+, Ubuntu 18.04+, RHEL 8+) | |
| # Note: Using manylinux_2_28 as manylinux2014 segfaults under QEMU | |
| # and manylinux_2_17 doesn't exist for ARM64 | |
| - name: "cpython-3.11-manylinux_2_28_aarch64" | |
| base_image: "quay.io/pypa/manylinux_2_28_aarch64" | |
| manylinux_tag: "manylinux_2_28_aarch64" | |
| glibc_version: "2.28" | |
| python_impl: "cpython" | |
| build_type: "official" | |
| python_versions: "cpy311" | |
| # CPython 3.13 - manylinux_2_28_aarch64 (glibc 2.28) | |
| # Modern baseline (Debian 10+, Ubuntu 18.04+, RHEL 8+) | |
| - name: "cpython-3.13-manylinux_2_28_aarch64" | |
| base_image: "quay.io/pypa/manylinux_2_28_aarch64" | |
| manylinux_tag: "manylinux_2_28_aarch64" | |
| glibc_version: "2.28" | |
| python_impl: "cpython" | |
| build_type: "official" | |
| python_versions: "cpy313" | |
| # ============================================================ | |
| # PyPy ARM64 wheels (using our custom manylinux images) | |
| # ============================================================ | |
| # PyPy 3.11 on Debian 12 (Bookworm) - manylinux_2_36_aarch64 | |
| - name: "pypy-3.11-bookworm-manylinux_2_36_aarch64" | |
| dockerfile: "docker/Dockerfile.pypy-bookworm-manylinux-arm64" | |
| manylinux_tag: "manylinux_2_36_aarch64" | |
| glibc_version: "2.36" | |
| python_impl: "pypy" | |
| build_type: "custom" | |
| python_versions: "pypy311" | |
| # PyPy 3.11 on Debian 13 (Trixie) - manylinux_2_38_aarch64 | |
| - name: "pypy-3.11-trixie-manylinux_2_38_aarch64" | |
| dockerfile: "docker/Dockerfile.pypy-trixie-manylinux-arm64" | |
| manylinux_tag: "manylinux_2_38_aarch64" | |
| glibc_version: "2.38" | |
| python_impl: "pypy" | |
| build_type: "custom" | |
| python_versions: "pypy311" | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| # ============================================================ | |
| # QEMU + Docker Buildx Setup | |
| # ============================================================ | |
| - name: Install modern QEMU (tonistiigi/binfmt) | |
| run: | | |
| echo "==> Installing modern QEMU 8.x via tonistiigi/binfmt" | |
| echo "This provides much better stability for PyPy on ARM64 emulation" | |
| docker run --rm --privileged tonistiigi/binfmt --install all | |
| echo "✅ Modern QEMU installed" | |
| - name: Set up QEMU for ARM64 emulation | |
| uses: docker/setup-qemu-action@v3 | |
| with: | |
| platforms: linux/arm64 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Verify QEMU setup | |
| run: | | |
| echo "==> QEMU platforms available:" | |
| docker run --rm --privileged multiarch/qemu-user-static --reset -p yes | |
| docker buildx ls | |
| # ============================================================ | |
| # Build custom PyPy images (only for build_type == custom) | |
| # ============================================================ | |
| - name: Build custom PyPy manylinux image (with retry for QEMU flakiness) | |
| if: matrix.target.build_type == 'custom' | |
| uses: nick-fields/retry@v3 | |
| with: | |
| # CFFI compilation under QEMU ARM64 emulation is very slow | |
| # CPython builds can take 20+ minutes due to emulation overhead | |
| timeout_minutes: 30 | |
| max_attempts: 3 | |
| retry_on: error | |
| warning_on_retry: true | |
| command: | | |
| set -e # Exit immediately if docker build fails | |
| echo "==> Building custom PyPy manylinux image for ${{ matrix.target.name }}" | |
| docker buildx build \ | |
| --platform linux/arm64 \ | |
| --tag ${{ matrix.target.name }}:latest \ | |
| --file ${{ matrix.target.dockerfile }} \ | |
| --load \ | |
| . | |
| echo "✅ Custom image built: ${{ matrix.target.name }}:latest" | |
| # ============================================================ | |
| # Build wheels inside ARM64 containers | |
| # ============================================================ | |
| - name: Build ARM64 wheels with NVX extension (with retry for QEMU flakiness) | |
| uses: nick-fields/retry@v3 | |
| with: | |
| # CFFI compilation under QEMU ARM64 emulation is very slow | |
| # CPython builds can take 20+ minutes due to emulation overhead | |
| timeout_minutes: 30 | |
| max_attempts: 3 | |
| retry_on: error | |
| warning_on_retry: true | |
| command: | | |
| set -e # Exit immediately if docker build fails | |
| echo "==> Building ARM64 wheels for ${{ matrix.target.name }}" | |
| echo "Container: ${{ matrix.target.base_image || matrix.target.name }}:latest" | |
| echo "Platform: linux/arm64" | |
| echo "Manylinux tag: ${{ matrix.target.manylinux_tag }}" | |
| echo "glibc: ${{ matrix.target.glibc_version }}" | |
| # Determine which image to use | |
| if [ "${{ matrix.target.build_type }}" = "custom" ]; then | |
| IMAGE="${{ matrix.target.name }}:latest" | |
| else | |
| IMAGE="${{ matrix.target.base_image }}" | |
| fi | |
| # Create wheelhouse directory on host | |
| mkdir -p wheelhouse | |
| # Run build script inside ARM64 container via QEMU | |
| docker run --rm \ | |
| --platform linux/arm64 \ | |
| -v $PWD:/io \ | |
| -w /io \ | |
| -e AUTOBAHN_USE_NVX=1 \ | |
| -e PYTHON_VERSIONS="${{ matrix.target.python_versions }}" \ | |
| -e GITHUB_TOKEN="${{ secrets.GITHUB_TOKEN }}" \ | |
| $IMAGE \ | |
| /bin/bash /io/.github/scripts/build-arm64-wheel.sh | |
| echo "✅ ARM64 wheels built successfully" | |
| - name: Force file system sync (post-build, pre-validation) | |
| run: | | |
| echo "======================================================================" | |
| echo "==> Forcing File System Sync (QEMU Buffer Flush)" | |
| echo "======================================================================" | |
| echo "" | |
| echo "Flushing all file system buffers to disk to prevent QEMU delayed" | |
| echo "write corruption. This ensures wheels are fully written before" | |
| echo "validation and checksumming." | |
| echo "" | |
| sync | |
| echo "✅ All buffers flushed to disk" | |
| echo "" | |
| - name: Validate wheels integrity | |
| run: | | |
| set -o pipefail | |
| echo "======================================================================" | |
| echo "==> Validating Wheel Integrity (Fail Fast)" | |
| echo "======================================================================" | |
| echo "" | |
| echo "Installing twine for validation..." | |
| # Ensure pip is available for the Python being used | |
| python3 -m ensurepip --upgrade 2>/dev/null || true | |
| # Install both packaging and twine from master for PEP 639 (Core Metadata 2.4) support | |
| # Note: No --break-system-packages needed in containers (isolated environment) | |
| python3 -m pip install git+https://github.com/pypa/packaging.git | |
| python3 -m pip install git+https://github.com/pypa/twine.git | |
| echo "" | |
| echo "==> Validation environment:" | |
| echo "Python: $(python3 --version)" | |
| echo "setuptools: $(python3 -m pip show setuptools | grep '^Version:' || echo 'not installed')" | |
| echo "packaging: $(python3 -m pip show packaging | grep '^Version:' || echo 'not installed')" | |
| echo "twine: $(twine --version)" | |
| echo "" | |
| # Initialize validation output file | |
| VALIDATION_FILE="wheelhouse/VALIDATION.txt" | |
| echo "Wheel Validation Results - Build Time" > "$VALIDATION_FILE" | |
| echo "======================================" >> "$VALIDATION_FILE" | |
| echo "" >> "$VALIDATION_FILE" | |
| echo "Validation Date: $(date -u +"%Y-%m-%d %H:%M:%S UTC")" >> "$VALIDATION_FILE" | |
| echo "Python: $(python3 --version)" >> "$VALIDATION_FILE" | |
| echo "twine: $(twine --version)" >> "$VALIDATION_FILE" | |
| echo "" >> "$VALIDATION_FILE" | |
| HAS_ERRORS=0 | |
| for wheel in wheelhouse/*.whl; do | |
| if [ ! -f "$wheel" ]; then | |
| echo "⚠️ No wheels found in wheelhouse/" | |
| HAS_ERRORS=1 | |
| continue | |
| fi | |
| WHEEL_NAME=$(basename "$wheel") | |
| echo "==> Validating: $WHEEL_NAME" | |
| echo "" >> "$VALIDATION_FILE" | |
| echo "Wheel: $WHEEL_NAME" >> "$VALIDATION_FILE" | |
| echo "---" >> "$VALIDATION_FILE" | |
| # Test 1: Can unzip read the wheel? | |
| echo " [1/3] ZIP integrity test..." | |
| if unzip -t "$wheel" > /dev/null 2>&1; then | |
| echo " ✅ ZIP test PASS" | |
| echo " ZIP test: PASS" >> "$VALIDATION_FILE" | |
| else | |
| echo " ❌ ZIP test FAIL - wheel is corrupted!" | |
| echo " This wheel cannot be unzipped and is unusable." | |
| echo " ZIP test: FAIL - wheel is corrupted!" >> "$VALIDATION_FILE" | |
| HAS_ERRORS=1 | |
| fi | |
| # Test 2: Python zipfile module validation | |
| echo " [2/3] Python zipfile test..." | |
| if python3 -m zipfile -t "$wheel" > /dev/null 2>&1; then | |
| echo " ✅ Python zipfile test PASS" | |
| echo " Python zipfile test: PASS" >> "$VALIDATION_FILE" | |
| else | |
| echo " ❌ Python zipfile test FAIL - wheel is corrupted!" | |
| echo " Python zipfile test: FAIL - wheel is corrupted!" >> "$VALIDATION_FILE" | |
| HAS_ERRORS=1 | |
| fi | |
| # Test 3: twine check (validates wheel metadata and structure) | |
| echo " [3/3] Twine validation..." | |
| twine check "$wheel" 2>&1 | tee /tmp/twine_output.txt | |
| TWINE_EXIT=${PIPESTATUS[0]} | |
| # Fail on nonzero exit or any error-like output | |
| if [ "$TWINE_EXIT" -eq 0 ] && ! grep -Eqi "ERROR|FAILED|InvalidDistribution" /tmp/twine_output.txt; then | |
| echo " ✅ Twine check PASS" | |
| echo " Twine check: PASS" >> "$VALIDATION_FILE" | |
| else | |
| echo " ❌ Twine check FAIL" | |
| cat /tmp/twine_output.txt | |
| echo " Twine check: FAIL" >> "$VALIDATION_FILE" | |
| cat /tmp/twine_output.txt >> "$VALIDATION_FILE" | |
| HAS_ERRORS=1 | |
| fi | |
| rm -f /tmp/twine_output.txt | |
| echo "" | |
| done | |
| if [ $HAS_ERRORS -eq 1 ]; then | |
| echo "" >> "$VALIDATION_FILE" | |
| echo "RESULT: VALIDATION FAILED" >> "$VALIDATION_FILE" | |
| echo "======================================================================" | |
| echo "❌ WHEEL VALIDATION FAILED" | |
| echo "======================================================================" | |
| echo "" | |
| echo "One or more wheels failed integrity checks." | |
| echo "This indicates a build or packaging problem." | |
| echo "" | |
| echo "DO NOT PROCEED - corrupted wheels must NOT become artifacts!" | |
| echo "" | |
| exit 1 | |
| else | |
| echo "" >> "$VALIDATION_FILE" | |
| echo "RESULT: ALL VALIDATIONS PASSED" >> "$VALIDATION_FILE" | |
| echo "======================================================================" | |
| echo "✅ All wheels validated successfully" | |
| echo "======================================================================" | |
| echo "" | |
| echo "All integrity checks passed. Wheels are valid and ready for upload." | |
| echo "" | |
| echo "Validation results written to: $VALIDATION_FILE" | |
| fi | |
| - name: Generate SHA256 checksums (chain of custody) | |
| run: | | |
| echo "======================================================================" | |
| echo "==> Generating SHA256 Checksums for Chain of Custody" | |
| echo "======================================================================" | |
| echo "" | |
| echo "OpenSSL version:" | |
| openssl version | |
| echo "" | |
| # Force sync before checksumming to ensure files are on disk | |
| echo "Forcing sync before checksumming..." | |
| sync | |
| echo "✅ Buffers flushed" | |
| echo "" | |
| # Change to wheelhouse directory to generate relative paths (basename only) | |
| cd wheelhouse | |
| CHECKSUM_FILE="CHECKSUMS.sha256" | |
| # Generate checksums for all wheels (using basename only) | |
| echo "Generating checksums for wheels..." | |
| for wheel in *.whl; do | |
| if [ -f "$wheel" ]; then | |
| # Sync before each checksum to ensure file is on disk | |
| sync | |
| openssl sha256 "$wheel" | tee -a "$CHECKSUM_FILE" | |
| fi | |
| done | |
| echo "" | |
| echo "==> Generated checksum file:" | |
| cat "$CHECKSUM_FILE" | |
| echo "" | |
| echo "This file will be uploaded with artifacts to verify integrity" | |
| echo "when artifacts are downloaded by the release workflow." | |
| echo "" | |
| # Return to project root | |
| cd .. | |
| - name: Force file system sync (post-checksum, pre-upload) | |
| run: | | |
| echo "======================================================================" | |
| echo "==> Forcing File System Sync (Post-Checksum)" | |
| echo "======================================================================" | |
| echo "" | |
| echo "Final sync to ensure validation results and checksums are on disk" | |
| echo "before artifact packaging." | |
| echo "" | |
| sync | |
| echo "✅ All buffers flushed to disk" | |
| echo "" | |
| - name: Generate build metadata | |
| run: | | |
| BUILD_INFO=wheelhouse/build-info.txt | |
| echo "ARM64 manylinux Build Information for ${{ matrix.target.name }}" > $BUILD_INFO | |
| echo "================================================================" >> $BUILD_INFO | |
| echo "" >> $BUILD_INFO | |
| echo "Build Date: $(date -u +"%Y-%m-%d %H:%M:%S UTC")" >> $BUILD_INFO | |
| echo "Base Image: ${{ matrix.target.base_image || 'custom' }}" >> $BUILD_INFO | |
| echo "Dockerfile: ${{ matrix.target.dockerfile || 'N/A (official image)' }}" >> $BUILD_INFO | |
| echo "Platform: linux/arm64 (aarch64)" >> $BUILD_INFO | |
| echo "Manylinux Tag: ${{ matrix.target.manylinux_tag }}" >> $BUILD_INFO | |
| echo "glibc Version: ${{ matrix.target.glibc_version }}" >> $BUILD_INFO | |
| echo "Python Implementation: ${{ matrix.target.python_impl }}" >> $BUILD_INFO | |
| echo "Build Method: GitHub Actions + QEMU + Docker (ARM64 emulation)" >> $BUILD_INFO | |
| echo "NVX Acceleration: ENABLED (binary wheels with native extensions)" >> $BUILD_INFO | |
| echo "" >> $BUILD_INFO | |
| echo "Wheels Built:" >> $BUILD_INFO | |
| echo "-------------" >> $BUILD_INFO | |
| for whl in wheelhouse/*.whl; do | |
| echo "- $(basename "$whl")" >> $BUILD_INFO | |
| done | |
| echo "" >> $BUILD_INFO | |
| echo "Note: These wheels were built using QEMU emulation on x86_64 runners." >> $BUILD_INFO | |
| echo " They are fully functional native ARM64 binaries." >> $BUILD_INFO | |
| echo "" | |
| echo "==> Generated build-info.txt:" | |
| cat $BUILD_INFO | |
| - name: List built artifacts | |
| run: | | |
| echo "==> Built artifacts for ${{ matrix.target.name }}:" | |
| ls -la wheelhouse/ 2>/dev/null || echo "No wheelhouse/ directory found" | |
| echo "" | |
| echo "==> Build metadata:" | |
| cat wheelhouse/build-info.txt 2>/dev/null || echo "No build info found" | |
| echo "" | |
| echo "==> Wheel inventory:" | |
| find wheelhouse/ -name "*.whl" -exec basename {} \; 2>/dev/null | sort || echo "No wheels found" | |
| # ============================================================================ | |
| # Note on paths: This workflow uses Docker containers but NOT the GitHub | |
| # Actions `container:` directive. Instead, it runs `docker run` commands | |
| # from the host, mounting the workspace. The upload step runs directly on | |
| # the host (not inside Docker), so ${{ github.workspace }} works correctly. | |
| # | |
| # However, for consistency and safety, we use a relative path here as well. | |
| # This ensures the workflow remains robust if the architecture changes. | |
| # ============================================================================ | |
| - name: Upload wheels and build metadata with cryptographic verification | |
| uses: wamp-proto/wamp-cicd/actions/upload-artifact-verified@main | |
| with: | |
| name: artifacts-arm64-${{ matrix.target.name }} | |
| path: wheelhouse/ | |
| retention-days: 30 | |
| # GitHub Releases, PyPI, and RTD publishing are now handled by the centralized 'release' workflow |