release(3.0.0): consolidate Dependabot #276/#278–#284 + fold #285 + CHANGELOG release line #1771
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 - Build and Test | |
| on: | |
| push: | |
| branches: [ main, develop, 'claude/**' ] | |
| pull_request: | |
| branches: [ main, develop ] | |
| workflow_dispatch: | |
| # Collapse overlapping runs on the same ref; feature branches and PR | |
| # heads cancel in-flight runs, main is preserved. | |
| concurrency: | |
| group: ci-build-test-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.event_name != 'schedule' }} | |
| jobs: | |
| # C Library Build and Test | |
| c-library: | |
| name: C Library (${{ matrix.os }}, ${{ matrix.compiler }}) | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 15 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest] | |
| compiler: [gcc, clang] | |
| include: | |
| - os: ubuntu-latest | |
| compiler: gcc | |
| cc: gcc-13 | |
| cxx: g++-13 | |
| - os: ubuntu-latest | |
| compiler: clang | |
| cc: clang-18 | |
| cxx: clang++-18 | |
| - os: macos-latest | |
| compiler: gcc | |
| cc: gcc-12 | |
| cxx: g++-12 | |
| - os: macos-latest | |
| compiler: clang | |
| cc: clang | |
| cxx: clang++ | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Install dependencies (Ubuntu) | |
| if: matrix.os == 'ubuntu-latest' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y cmake libssl-dev ${{ matrix.cc }} ${{ matrix.cxx }} | |
| - name: Install dependencies (macOS) | |
| if: matrix.os == 'macos-latest' | |
| run: | | |
| brew install cmake gcc@12 | |
| - name: Configure CMake | |
| run: | | |
| mkdir -p build | |
| cd build | |
| # Set compiler path | |
| if [ "$RUNNER_OS" = "macOS" ]; then | |
| # For GCC on macOS, use full path | |
| if [ "${{ matrix.compiler }}" = "gcc" ]; then | |
| CC_PATH=$(brew --prefix gcc@12)/bin/gcc-12 | |
| CXX_PATH=$(brew --prefix gcc@12)/bin/g++-12 | |
| else | |
| CC_PATH=${{ matrix.cc }} | |
| CXX_PATH=${{ matrix.cxx }} | |
| fi | |
| else | |
| CC_PATH=${{ matrix.cc }} | |
| CXX_PATH=${{ matrix.cxx }} | |
| fi | |
| cmake .. \ | |
| -DCMAKE_C_COMPILER=${CC_PATH} \ | |
| -DCMAKE_CXX_COMPILER=${CXX_PATH} \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DAMA_BUILD_SHARED=ON \ | |
| -DAMA_BUILD_STATIC=ON \ | |
| -DAMA_BUILD_TESTS=ON \ | |
| -DAMA_ENABLE_AVX2=ON | |
| - name: Build | |
| run: cmake --build build --config Release -j4 | |
| - name: Test | |
| run: cd build && ctest --output-on-failure | |
| # Python Package Build and Test | |
| python-package: | |
| name: Python ${{ matrix.python-version }} on ${{ matrix.os }} | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 20 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install system dependencies (Ubuntu) | |
| if: matrix.os == 'ubuntu-latest' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y build-essential python3-dev cmake libssl-dev | |
| - name: Install system dependencies (macOS) | |
| if: matrix.os == 'macos-latest' | |
| run: brew install cmake | |
| - name: Install system dependencies (Windows) | |
| if: matrix.os == 'windows-latest' | |
| shell: pwsh | |
| run: | | |
| # CMake is pre-installed on GitHub-hosted Windows runners. | |
| # Only install via Chocolatey (with retries) if it is missing, | |
| # so a transient Chocolatey CDN outage cannot break the build. | |
| if (Get-Command cmake -ErrorAction SilentlyContinue) { | |
| Write-Host "CMake already available: $(cmake --version | Select-Object -First 1)" | |
| } else { | |
| Write-Host "CMake not found; installing via Chocolatey with retries..." | |
| $maxAttempts = 5 | |
| for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { | |
| Write-Host "Attempt ${attempt}/${maxAttempts}: choco install cmake..." | |
| choco install cmake --installargs 'ADD_CMAKE_TO_PATH=System' -y 2>&1 | |
| if ($LASTEXITCODE -eq 0) { | |
| Write-Host "Successfully installed cmake" | |
| break | |
| } | |
| if ($attempt -lt $maxAttempts) { | |
| $delay = $attempt * 15 | |
| Write-Host "Install failed, waiting ${delay}s before retry..." | |
| Start-Sleep -Seconds $delay | |
| } | |
| } | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Error "All ${maxAttempts} attempts to install cmake via Chocolatey failed" | |
| exit 1 | |
| } | |
| # Refresh PATH so cmake is available to subsequent steps | |
| Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1" | |
| refreshenv | |
| } | |
| - name: Install Python build dependencies | |
| run: | | |
| python -m pip install --upgrade pip setuptools wheel | |
| pip install "Cython>=3.0.0" "numpy>=1.24.0,<3.0.0" | |
| # Pre-build the native C library so the install step can rely on | |
| # an already-populated ``build/`` directory. Mirrors the | |
| # proven-working pattern from ``ci.yml::test`` and avoids the | |
| # latent setup.py CMakeBuild fragility on Windows / macOS that | |
| # surfaces as "Python X.Y on {os} -- exit 1/2" without the | |
| # pre-build. | |
| - name: Build native C library (Linux / macOS) | |
| if: matrix.os != 'windows-latest' | |
| run: | | |
| cmake -B build -DAMA_USE_NATIVE_PQC=ON -DCMAKE_BUILD_TYPE=Release -DAMA_BUILD_TESTS=OFF -DAMA_BUILD_EXAMPLES=OFF | |
| # Use Python's os.cpu_count() for portability — `nproc` is GNU | |
| # coreutils and not guaranteed on macos-latest. Bare `-j` would | |
| # unbound make's parallelism and OOM the runner. | |
| cmake --build build -j"$(python -c 'import os; print(os.cpu_count() or 1)')" | |
| - name: Build native C library (Windows) | |
| if: matrix.os == 'windows-latest' | |
| run: | | |
| cmake -B build -DAMA_USE_NATIVE_PQC=ON -DCMAKE_BUILD_TYPE=Release -DAMA_BUILD_TESTS=OFF -DAMA_BUILD_EXAMPLES=OFF | |
| cmake --build build --config Release --parallel | |
| - name: Install Python dependencies + package (with [dev] extras) | |
| # Use the [dev] extras so hypothesis / pytest-cov / mypy / etc. | |
| # come in via the canonical pyproject.toml declaration rather | |
| # than the legacy requirements-dev.txt. Matches ci.yml::test. | |
| run: pip install -e ".[dev]" | |
| - name: Verify module integrity | |
| run: python -m ama_cryptography.integrity --verify | |
| - name: Run tests | |
| env: | |
| # Force backend availability check to fail loudly if the | |
| # native C library didn't build / didn't load — otherwise | |
| # tests silently skip and mask backend regressions. | |
| AMA_CI_REQUIRE_BACKENDS: "1" | |
| # UTF-8 encoding for Windows to support Unicode symbols in output | |
| PYTHONIOENCODING: ${{ matrix.os == 'windows-latest' && 'utf-8' || '' }} | |
| PYTHONUTF8: ${{ matrix.os == 'windows-latest' && '1' || '' }} | |
| run: pytest tests/ -v --tb=short --cov=ama_cryptography --cov-report=xml | |
| - name: Upload coverage | |
| if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11' | |
| uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| files: ./coverage.xml | |
| flags: python | |
| name: python-${{ matrix.python-version }} | |
| # Linting and Code Quality | |
| lint: | |
| name: Lint and Format Check | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Set up Python | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: '3.11' | |
| - name: Install dependencies | |
| run: | | |
| pip install "black==26.3.1" "ruff>=0.4" "mypy>=1.9,<2" | |
| - name: Lint (ruff) | |
| run: ruff check . | |
| - name: Check formatting (black) | |
| run: black --check --diff . | |
| - name: Type check (mypy --strict) | |
| run: mypy --strict ama_cryptography/ tests/ | |
| continue-on-error: false | |
| # Security Audit | |
| security: | |
| name: Security Audit | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Set up Python | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: '3.11' | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install pip-audit bandit | |
| - name: Audit dependencies | |
| # CVE-2026-4539: ReDoS in pygments AdlLexer (dev-only, local access, not used at runtime). | |
| # pygments is a transitive dev dependency (via rich/bandit) — see INVARIANT-14. | |
| # Remove ignore when Pygments >2.20.0 ships a fix. | |
| # CVE-2026-3219: pip itself (build-time tool, not a runtime cryptographic | |
| # dependency); no fix version available yet. Remove when upstream ships one. | |
| run: pip-audit --ignore-vuln CVE-2026-4539 --ignore-vuln CVE-2026-3219 | |
| - name: Security scan (bandit) | |
| run: bandit -r ama_cryptography/ -l | |
| # Benchmark Regression Detection | |
| # Required job — fails the workflow on detected regression. | |
| # Environment variance on shared CI runners (25-50%) means baselines | |
| # must account for ctypes-fallback throughput, not Cython-optimized peaks. | |
| benchmark-regression: | |
| name: Benchmark Regression Detection | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| needs: [c-library] | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Set up Python | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: '3.11' | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y build-essential cmake libssl-dev | |
| - name: Build native C library | |
| run: | | |
| cmake -B build -DAMA_USE_NATIVE_PQC=ON -DCMAKE_BUILD_TYPE=Release -DAMA_BUILD_TESTS=OFF -DAMA_BUILD_EXAMPLES=OFF | |
| cmake --build build -j4 | |
| - name: Install Python package | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e . | |
| - name: Run benchmark regression detection | |
| run: | | |
| python benchmarks/benchmark_runner.py \ | |
| --verbose \ | |
| --baseline benchmarks/baseline.json \ | |
| --output benchmarks/regression_results.json \ | |
| --markdown benchmarks/regression_report.md | |
| - name: Upload regression report | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: benchmark-regression-report | |
| path: | | |
| benchmarks/regression_results.json | |
| benchmarks/regression_report.md | |
| # Docker Build — non-blocking: transient Docker Hub auth/rate-limit | |
| # failures must not gate PRs. | |
| docker: | |
| name: Docker Build | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| continue-on-error: true | |
| # Job-level env so the optional-login step can gate on env.* in `if:`. | |
| # `secrets.*` is not allowed in step-level `if:` expressions, which | |
| # otherwise causes a workflow-validation error before any job runs. | |
| env: | |
| DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} | |
| DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Pre-pull BuildKit image (with retries) | |
| run: | | |
| # Pre-pull the BuildKit image used by setup-buildx-action to | |
| # survive transient Docker Hub outages (504, network timeouts). | |
| IMAGE="moby/buildkit:buildx-stable-1" | |
| for attempt in 1 2 3 4 5; do | |
| echo "Attempt ${attempt}/5: pulling ${IMAGE}..." | |
| if docker pull "${IMAGE}"; then | |
| echo "Successfully pulled ${IMAGE}" | |
| exit 0 | |
| fi | |
| echo "Pull failed, waiting $((attempt * 10))s before retry..." | |
| sleep $((attempt * 10)) | |
| done | |
| echo "::warning::All 5 pull attempts for ${IMAGE} failed; buildx setup will retry internally" | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 | |
| - name: Log in to Docker Hub (optional, avoids rate limits) | |
| # Gate on BOTH secrets: a half-configured fork (e.g. username | |
| # set but token missing) would otherwise reach docker/login-action | |
| # and fail noisily, defeating the "optional" intent of the step. | |
| if: env.DOCKERHUB_USERNAME != '' && env.DOCKERHUB_TOKEN != '' | |
| uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 | |
| with: | |
| username: ${{ env.DOCKERHUB_USERNAME }} | |
| password: ${{ env.DOCKERHUB_TOKEN }} | |
| - name: Build Ubuntu image | |
| uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7 | |
| with: | |
| context: . | |
| file: docker/Dockerfile | |
| push: false | |
| load: true | |
| tags: ama-cryptography:ubuntu | |
| cache-from: type=gha,scope=ubuntu | |
| cache-to: ${{ github.event_name == 'push' && 'type=gha,scope=ubuntu,mode=max' || '' }} | |
| - name: Build Alpine image | |
| uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7 | |
| with: | |
| context: . | |
| file: docker/Dockerfile.alpine | |
| push: false | |
| load: true | |
| tags: ama-cryptography:alpine | |
| cache-from: type=gha,scope=alpine | |
| cache-to: ${{ github.event_name == 'push' && 'type=gha,scope=alpine,mode=max' || '' }} | |
| - name: Test Ubuntu image | |
| run: | | |
| docker run --rm ama-cryptography:ubuntu python3 -c "import ama_cryptography; print('OK')" | |
| - name: Test Alpine image | |
| run: | | |
| docker run --rm ama-cryptography:alpine python3 -c "import ama_cryptography; print('OK')" |