fix(release): support metadata version 2.5 #271
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: PR | |
| on: | |
| pull_request: | |
| branches: [main] | |
| types: [opened, synchronize, reopened, ready_for_review, converted_to_draft, labeled, unlabeled] | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| jobs: | |
| plan: | |
| name: PR plan | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| outputs: | |
| plan_json: ${{ steps.plan.outputs.plan }} | |
| markdown: ${{ steps.plan.outputs.markdown }} | |
| lint: ${{ steps.plan.outputs.lint }} | |
| mypy: ${{ steps.plan.outputs.mypy }} | |
| pyrefly: ${{ steps.plan.outputs.pyrefly }} | |
| contracts: ${{ steps.plan.outputs.contracts }} | |
| compatibility: ${{ steps.plan.outputs.compatibility }} | |
| coverage: ${{ steps.plan.outputs.coverage }} | |
| primary: ${{ steps.plan.outputs.primary }} | |
| targeted: ${{ steps.plan.outputs.targeted }} | |
| pytorch: ${{ steps.plan.outputs.pytorch }} | |
| dependency_audit: ${{ steps.plan.outputs.dependency_audit }} | |
| workflow_audit: ${{ steps.plan.outputs.workflow_audit }} | |
| legal: ${{ steps.plan.outputs.legal }} | |
| package: ${{ steps.plan.outputs.package }} | |
| install_smoke: ${{ steps.plan.outputs.install_smoke }} | |
| release_preflight: ${{ steps.plan.outputs.release_preflight }} | |
| pytest_targets: ${{ steps.plan.outputs.pytest_targets }} | |
| version_change: ${{ steps.plan.outputs.version_change }} | |
| steps: | |
| - name: Checkout pull request | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Set up plan environment | |
| uses: ./.github/actions/setup-ci | |
| with: | |
| python-version: "3.12" | |
| dependency-group: ci-quality | |
| - name: Collect changed paths | |
| env: | |
| BASE_SHA: ${{ github.event.pull_request.base.sha }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| run: | | |
| mkdir -p ci-plan | |
| git diff --name-only -z "${BASE_SHA}" "${HEAD_SHA}" -- > ci-plan/changed-files.z | |
| git show "${BASE_SHA}:pyproject.toml" > ci-plan/base-pyproject.toml | |
| cp pyproject.toml ci-plan/head-pyproject.toml | |
| - name: Select pull-request checks | |
| id: plan | |
| env: | |
| DRAFT: ${{ github.event.pull_request.draft }} | |
| FORCE_FULL: ${{ contains(github.event.pull_request.labels.*.name, 'full-ci') }} | |
| run: >- | |
| python -m tools.ci_plan | |
| --paths-file ci-plan/changed-files.z --null | |
| --base-pyproject ci-plan/base-pyproject.toml | |
| --head-pyproject ci-plan/head-pyproject.toml | |
| --draft "${DRAFT}" --force-full "${FORCE_FULL}" | |
| --github-output "${GITHUB_OUTPUT}" | |
| --summary "${GITHUB_STEP_SUMMARY}" | |
| - name: Upload routing evidence | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: pr-ci-plan | |
| path: ci-plan/ | |
| if-no-files-found: error | |
| retention-days: 7 | |
| markdown: | |
| name: Markdown | |
| needs: plan | |
| if: needs.plan.outputs.markdown == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Set up quality environment | |
| uses: ./.github/actions/setup-ci | |
| with: | |
| python-version: "3.12" | |
| dependency-group: ci-quality | |
| - name: Download changed paths | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 | |
| with: | |
| name: pr-ci-plan | |
| path: ci-plan | |
| - name: Check changed Markdown | |
| run: python -m tools.quality_gate markdown --paths-file ci-plan/changed-files.z --null | |
| lint: | |
| name: Ruff and formatting | |
| needs: plan | |
| if: needs.plan.outputs.lint == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Set up quality environment | |
| uses: ./.github/actions/setup-ci | |
| with: | |
| python-version: "3.12" | |
| dependency-group: ci-quality | |
| - name: Run Ruff and formatting checks | |
| run: python -m tools.quality_gate lint | |
| mypy: | |
| name: mypy | |
| needs: plan | |
| if: needs.plan.outputs.mypy == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Set up typing environment | |
| uses: ./.github/actions/setup-ci | |
| with: | |
| python-version: "3.12" | |
| dependency-group: ci-quality | |
| - name: Run mypy | |
| run: python -m tools.quality_gate mypy | |
| pyrefly: | |
| name: Pyrefly | |
| needs: plan | |
| if: needs.plan.outputs.pyrefly == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Set up typing environment | |
| uses: ./.github/actions/setup-ci | |
| with: | |
| python-version: "3.10" | |
| dependency-group: ci-types | |
| - name: Run Pyrefly | |
| run: python -m tools.quality_gate pyrefly | |
| contracts: | |
| name: Repository contracts | |
| needs: plan | |
| if: needs.plan.outputs.contracts == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Set up contract environment | |
| uses: ./.github/actions/setup-ci | |
| with: | |
| python-version: "3.12" | |
| dependency-group: ci-quality | |
| - name: Download changed paths | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 | |
| with: | |
| name: pr-ci-plan | |
| path: ci-plan | |
| - name: Run repository contracts | |
| run: python -m tools.quality_gate contracts --paths-file ci-plan/changed-files.z --null | |
| fast_checks: | |
| name: Fast checks | |
| needs: [plan, markdown, lint, mypy, pyrefly, contracts] | |
| if: ${{ always() }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Aggregate fast checks | |
| env: | |
| PLAN_JSON: ${{ needs.plan.outputs.plan_json }} | |
| run: >- | |
| python -m tools.ci_gate --gate fast --plan-json "${PLAN_JSON}" | |
| --result "markdown=${{ needs.markdown.result }}" | |
| --result "lint=${{ needs.lint.result }}" | |
| --result "mypy=${{ needs.mypy.result }}" | |
| --result "pyrefly=${{ needs.pyrefly.result }}" | |
| --result "contracts=${{ needs.contracts.result }}" | |
| compatibility: | |
| name: Compatibility (${{ matrix.operating-system }}, Python ${{ matrix.python-version }}, ${{ matrix.shard-label }}) | |
| needs: plan | |
| if: needs.plan.outputs.compatibility == 'true' | |
| runs-on: ${{ matrix.operating-system }} | |
| timeout-minutes: 15 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| operating-system: [ubuntu-latest, macos-latest] | |
| python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] | |
| shard-count: [1] | |
| shard-index: [0] | |
| shard-label: [full] | |
| include: | |
| - operating-system: windows-latest | |
| python-version: "3.10" | |
| shard-count: 1 | |
| shard-index: 0 | |
| shard-label: full | |
| - operating-system: windows-latest | |
| python-version: "3.11" | |
| shard-count: 2 | |
| shard-index: 0 | |
| shard-label: 1-of-2 | |
| - operating-system: windows-latest | |
| python-version: "3.11" | |
| shard-count: 2 | |
| shard-index: 1 | |
| shard-label: 2-of-2 | |
| - operating-system: windows-latest | |
| python-version: "3.12" | |
| shard-count: 2 | |
| shard-index: 0 | |
| shard-label: 1-of-2 | |
| - operating-system: windows-latest | |
| python-version: "3.12" | |
| shard-count: 2 | |
| shard-index: 1 | |
| shard-label: 2-of-2 | |
| - operating-system: windows-latest | |
| python-version: "3.13" | |
| shard-count: 2 | |
| shard-index: 0 | |
| shard-label: 1-of-2 | |
| - operating-system: windows-latest | |
| python-version: "3.13" | |
| shard-count: 2 | |
| shard-index: 1 | |
| shard-label: 2-of-2 | |
| - operating-system: windows-latest | |
| python-version: "3.14" | |
| shard-count: 1 | |
| shard-index: 0 | |
| shard-label: full | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Set up test environment | |
| uses: ./.github/actions/setup-ci | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| dependency-group: ci-test | |
| - name: Run duration-balanced base-suite shard | |
| shell: bash | |
| run: | | |
| set -o pipefail | |
| mkdir -p ci-evidence | |
| python -m tools.ci_shard select \ | |
| --shard-count "${{ matrix.shard-count }}" \ | |
| --shard-index "${{ matrix.shard-index }}" \ | |
| --exclude-file tests/test_pytorch.py \ | |
| --exclude-file tests/transforms3d/test_pytorch.py \ | |
| | xargs -0 python -m pytest \ | |
| -n 2 --dist=worksteal -m "not pytorch" \ | |
| --hypothesis-profile=ci-fast \ | |
| --junitxml=ci-evidence/junit.xml | |
| - name: Collect test environment evidence | |
| if: always() | |
| shell: bash | |
| run: >- | |
| python tools/collect_test_environment.py | |
| --output ci-evidence/environment.json | |
| --command "python -m pytest -n 2 --dist=worksteal -m not-pytorch" | |
| - name: Summarize pytest results | |
| if: always() | |
| run: >- | |
| python tools/pytest_summary.py | |
| --junit ci-evidence/junit.xml | |
| --output ci-evidence/pytest-summary.json | |
| --allow-incomplete | |
| - name: Upload compatibility evidence | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: compatibility-${{ matrix.operating-system }}-${{ matrix.python-version }}-${{ matrix.shard-label }} | |
| path: ci-evidence/ | |
| if-no-files-found: ignore | |
| retention-days: 7 | |
| coverage: | |
| name: Primary coverage | |
| needs: plan | |
| if: needs.plan.outputs.coverage == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Set up test environment | |
| uses: ./.github/actions/setup-ci | |
| with: | |
| python-version: "3.12" | |
| dependency-group: ci-test | |
| - name: Run base suite with branch coverage | |
| shell: bash | |
| run: | | |
| mapfile -d '' -t test_files < <( | |
| python -m tools.ci_shard select --shard-count 1 --shard-index 0 \ | |
| --exclude-file tests/test_pytorch.py \ | |
| --exclude-file tests/transforms3d/test_pytorch.py | |
| ) | |
| python -m pytest "${test_files[@]}" \ | |
| -n 2 --dist=worksteal -m "not pytorch" \ | |
| --hypothesis-profile=ci-fast \ | |
| --cov=albumentations --cov-branch --cov-report=xml | |
| - name: Upload coverage report | |
| continue-on-error: true | |
| uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| slug: albumentations-team/AlbumentationsX | |
| files: ./coverage.xml | |
| disable_search: true | |
| fail_ci_if_error: false | |
| use_pypi: true | |
| version: 11.3.1 | |
| primary: | |
| name: Primary suite | |
| needs: plan | |
| if: needs.plan.outputs.primary == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Set up test environment | |
| uses: ./.github/actions/setup-ci | |
| with: | |
| python-version: "3.12" | |
| dependency-group: ci-test | |
| - name: Run complete base suite | |
| shell: bash | |
| run: | | |
| mapfile -d '' -t test_files < <( | |
| python -m tools.ci_shard select --shard-count 1 --shard-index 0 \ | |
| --exclude-file tests/test_pytorch.py \ | |
| --exclude-file tests/transforms3d/test_pytorch.py | |
| ) | |
| python -m pytest "${test_files[@]}" \ | |
| -n 2 --dist=worksteal -m "not pytorch" --hypothesis-profile=ci-fast | |
| targeted: | |
| name: Targeted tests (${{ matrix.operating-system }}, Python ${{ matrix.python-version }}) | |
| needs: plan | |
| if: needs.plan.outputs.targeted == 'true' | |
| runs-on: ${{ matrix.operating-system }} | |
| timeout-minutes: 10 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - operating-system: ubuntu-latest | |
| python-version: "3.12" | |
| - operating-system: windows-latest | |
| python-version: "3.10" | |
| - operating-system: windows-latest | |
| python-version: "3.14" | |
| - operating-system: macos-latest | |
| python-version: "3.10" | |
| - operating-system: macos-latest | |
| python-version: "3.14" | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Set up test environment | |
| uses: ./.github/actions/setup-ci | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| dependency-group: ci-test | |
| - name: Run changed test modules | |
| env: | |
| PYTEST_TARGETS: ${{ needs.plan.outputs.pytest_targets }} | |
| shell: bash | |
| run: | | |
| python -c 'import json, os, pathlib; pathlib.Path("targets.z").write_bytes(b"\0".join(path.encode() for path in json.loads(os.environ["PYTEST_TARGETS"])) + b"\0")' | |
| xargs -0 python -m pytest \ | |
| -n 2 --dist=worksteal -m "not pytorch" --hypothesis-profile=ci-fast \ | |
| < targets.z | |
| pytorch: | |
| name: PyTorch tests | |
| needs: plan | |
| if: needs.plan.outputs.pytorch == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Set up PyTorch test environment | |
| uses: ./.github/actions/setup-ci | |
| with: | |
| python-version: "3.12" | |
| dependency-group: ci-pytorch | |
| - name: Install CPU-only PyTorch | |
| run: >- | |
| uv pip install torch==2.13.0+cpu torchvision==0.28.0+cpu | |
| --extra-index-url https://download.pytorch.org/whl/cpu | |
| - name: Run PyTorch-marked tests | |
| run: >- | |
| python -m pytest -n 2 --dist=worksteal -m pytorch | |
| tests/test_pytorch.py | |
| tests/transforms3d/test_pytorch.py | |
| tests/test_per_worker_seed.py | |
| tests/test_flip_masks_comprehensive.py | |
| tests/test_additional_targets.py | |
| tests/test_benchmark_coverage.py | |
| tests/test_serialization.py | |
| - name: Validate optional benchmark policy | |
| run: | | |
| python -m tools.benchmark_coverage check | |
| python -m tools.performance_budget check | |
| correctness: | |
| name: Correctness | |
| needs: [plan, compatibility, coverage, primary, targeted, pytorch] | |
| if: ${{ always() }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Aggregate correctness checks | |
| env: | |
| PLAN_JSON: ${{ needs.plan.outputs.plan_json }} | |
| run: >- | |
| python -m tools.ci_gate --gate correctness --plan-json "${PLAN_JSON}" | |
| --result "compatibility=${{ needs.compatibility.result }}" | |
| --result "coverage=${{ needs.coverage.result }}" | |
| --result "primary=${{ needs.primary.result }}" | |
| --result "targeted=${{ needs.targeted.result }}" | |
| --result "pytorch=${{ needs.pytorch.result }}" | |
| dependency_audit: | |
| name: Dependency audit | |
| needs: plan | |
| if: needs.plan.outputs.dependency_audit == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Set up security environment | |
| uses: ./.github/actions/setup-ci | |
| with: | |
| python-version: "3.12" | |
| dependency-group: ci-security | |
| - name: Export locked runtime dependencies | |
| run: >- | |
| uv export --frozen --no-dev --no-emit-project | |
| --format requirements-txt --output-file runtime-requirements.txt | |
| - name: Audit locked runtime dependencies | |
| run: >- | |
| pip-audit --requirement runtime-requirements.txt | |
| --no-deps --disable-pip --format=json --output dependency-audit.json | |
| - name: Upload dependency audit evidence | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: dependency-audit-evidence | |
| path: dependency-audit.json | |
| if-no-files-found: ignore | |
| retention-days: 7 | |
| workflow_audit: | |
| name: GitHub Actions hardening audit | |
| needs: plan | |
| if: needs.plan.outputs.workflow_audit == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Set up security environment | |
| uses: ./.github/actions/setup-ci | |
| with: | |
| python-version: "3.12" | |
| dependency-group: ci-security | |
| - name: Run zizmor | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: zizmor --format=github --min-severity=medium --min-confidence=medium .github | |
| legal: | |
| name: Legal source integrity | |
| needs: plan | |
| if: needs.plan.outputs.legal == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Set up package environment | |
| uses: ./.github/actions/setup-ci | |
| with: | |
| python-version: "3.12" | |
| dependency-group: ci-package | |
| - name: Verify source-tree legal integrity | |
| run: python tools/verify_legal_integrity.py | |
| - name: Test legal-integrity verifier | |
| run: python -m pytest --noconftest -q tests/test_legal_integrity.py | |
| package: | |
| name: Package artifacts | |
| needs: plan | |
| if: needs.plan.outputs.package == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Set up package environment | |
| uses: ./.github/actions/setup-ci | |
| with: | |
| python-version: "3.12" | |
| dependency-group: ci-package | |
| - name: Build wheel and source distribution | |
| run: uv build --out-dir dist | |
| - name: Verify packaged legal notices | |
| run: python tools/verify_legal_integrity.py --artifacts dist/*.whl dist/*.tar.gz | |
| - name: Check distribution metadata | |
| run: twine check dist/* | |
| install_smoke: | |
| name: Install smoke (${{ matrix.operating-system }}, Python ${{ matrix.python-version }}) | |
| needs: plan | |
| if: needs.plan.outputs.install_smoke == 'true' | |
| runs-on: ${{ matrix.operating-system }} | |
| timeout-minutes: 10 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| operating-system: [ubuntu-latest, windows-latest, macos-latest] | |
| python-version: ["3.10", "3.14"] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Set up package environment | |
| uses: ./.github/actions/setup-ci | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| dependency-group: ci-package | |
| - name: Build wheel | |
| run: uv build --wheel --out-dir dist | |
| - name: Install wheel into a clean environment | |
| shell: bash | |
| run: | | |
| uv venv --python "${{ matrix.python-version }}" "${RUNNER_TEMP}/install-smoke" | |
| if [ "${{ runner.os }}" = "Windows" ]; then | |
| smoke_python="${RUNNER_TEMP}/install-smoke/Scripts/python.exe" | |
| else | |
| smoke_python="${RUNNER_TEMP}/install-smoke/bin/python" | |
| fi | |
| uv pip install --python "${smoke_python}" dist/*.whl "opencv-python-headless>=5.0.0.93" | |
| "${smoke_python}" -I -c "import albumentations; print(albumentations.__version__)" | |
| release_preflight: | |
| name: Release preflight | |
| needs: plan | |
| if: needs.plan.outputs.release_preflight == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout release source | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Set up exact release environment | |
| uses: ./.github/actions/setup-ci | |
| with: | |
| python-version: "3.12" | |
| dependency-group: ci-release | |
| - name: Prepare release bundle | |
| id: metadata | |
| run: | | |
| mkdir -p \ | |
| "${RUNNER_TEMP}/release-bundle/dist" \ | |
| "${RUNNER_TEMP}/release-bundle/evidence" \ | |
| "${RUNNER_TEMP}/release-bundle/public" | |
| python -m tools.release_bundle metadata \ | |
| --source-root . \ | |
| --github-output "${GITHUB_OUTPUT}" | |
| - name: Build final distributions | |
| run: uv build --out-dir "${RUNNER_TEMP}/release-bundle/dist" | |
| - name: Verify distribution license and notice integrity | |
| run: >- | |
| python tools/verify_legal_integrity.py | |
| --artifacts "${RUNNER_TEMP}"/release-bundle/dist/*.whl "${RUNNER_TEMP}"/release-bundle/dist/*.tar.gz | |
| - name: Check final distribution metadata | |
| run: twine check "${RUNNER_TEMP}"/release-bundle/dist/* | |
| - name: Smoke test final wheel | |
| shell: bash | |
| run: | | |
| smoke_venv="${RUNNER_TEMP}/albumentationsx-release-smoke" | |
| uv venv --clear --python 3.12 "${smoke_venv}" | |
| wheels=("${RUNNER_TEMP}"/release-bundle/dist/*.whl) | |
| uv pip install \ | |
| --python "${smoke_venv}/bin/python" \ | |
| "${wheels[0]}" "opencv-python-headless>=5.0.0.93" | |
| cd "${RUNNER_TEMP}" | |
| "${smoke_venv}/bin/python" -I - <<'PY' | |
| import albumentations | |
| import numpy as np | |
| image = np.zeros((64, 64, 3), dtype=np.uint8) | |
| albumentations.HorizontalFlip(p=1)(image=image) | |
| print("albumentations release smoke passed:", albumentations.__version__) | |
| PY | |
| - name: Verify lock freshness | |
| run: uv lock --check | |
| - name: Collect release environment evidence | |
| run: >- | |
| python tools/collect_test_environment.py | |
| --output "${RUNNER_TEMP}/release-bundle/evidence/environment-release.json" | |
| --command "version-bump PR release preflight" | |
| - name: Run release correctness evidence tests | |
| run: | | |
| python tools/verify_regression_vectors.py --all | |
| python -m pytest -q tests/regression tests/property \ | |
| --hypothesis-profile=ci-fast \ | |
| --junitxml="${RUNNER_TEMP}/release-bundle/evidence/junit-release.xml" | |
| - name: Summarize release pytest results | |
| if: always() | |
| run: >- | |
| python tools/pytest_summary.py | |
| --junit "${RUNNER_TEMP}/release-bundle/evidence/junit-release.xml" | |
| --output "${RUNNER_TEMP}/release-bundle/evidence/pytest-summary-release.json" | |
| --allow-incomplete | |
| - name: Collect core benchmark coverage evidence | |
| run: | | |
| python -m tools.benchmark_coverage summary \ | |
| --output "${RUNNER_TEMP}/release-bundle/evidence/benchmark-coverage-release.json" | |
| python -m tools.benchmark_coverage details \ | |
| --output "${RUNNER_TEMP}/release-bundle/evidence/benchmark-coverage-detail-release.json" | |
| python -m tools.performance_budget summarize \ | |
| --coverage-summary "${RUNNER_TEMP}/release-bundle/evidence/benchmark-coverage-release.json" \ | |
| --coverage-detail "${RUNNER_TEMP}/release-bundle/evidence/benchmark-coverage-detail-release.json" \ | |
| --core-only \ | |
| --fail-on-release-blockers \ | |
| --output "${RUNNER_TEMP}/release-bundle/evidence/benchmark-performance-budget-release.json" | |
| - name: Export locked runtime dependencies | |
| run: >- | |
| uv export --frozen --no-dev --no-emit-project | |
| --format requirements-txt | |
| --output-file "${RUNNER_TEMP}/release-bundle/evidence/runtime-requirements.txt" | |
| - name: Collect release security evidence | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| pip-audit \ | |
| --requirement "${RUNNER_TEMP}/release-bundle/evidence/runtime-requirements.txt" \ | |
| --no-deps \ | |
| --disable-pip \ | |
| --format=json \ | |
| --output "${RUNNER_TEMP}/release-bundle/evidence/security-pip-audit.json" | |
| zizmor \ | |
| --format=json \ | |
| --min-severity=medium \ | |
| --min-confidence=medium \ | |
| .github > "${RUNNER_TEMP}/release-bundle/evidence/security-zizmor.json" | |
| - name: Generate CycloneDX SBOM | |
| env: | |
| PACKAGE_VERSION: ${{ steps.metadata.outputs.package_version }} | |
| run: >- | |
| cyclonedx-py requirements "${RUNNER_TEMP}/release-bundle/evidence/runtime-requirements.txt" | |
| --output-format JSON | |
| --output-file | |
| "${RUNNER_TEMP}/release-bundle/public/albumentationsx-${PACKAGE_VERSION}-sbom.cdx.json" | |
| - name: Generate correctness and compatibility report | |
| env: | |
| PACKAGE_VERSION: ${{ steps.metadata.outputs.package_version }} | |
| run: >- | |
| python tools/generate_correctness_report.py | |
| --evidence-dir "${RUNNER_TEMP}/release-bundle/evidence" | |
| --output | |
| "${RUNNER_TEMP}/release-bundle/public/albumentationsx-${PACKAGE_VERSION}-correctness-compatibility-report.md" | |
| - name: Finalize immutable release bundle | |
| id: finalize | |
| run: >- | |
| python -m tools.release_bundle finalize | |
| --bundle-dir "${RUNNER_TEMP}/release-bundle" | |
| --source-root . | |
| --check clean_install | |
| --check correctness | |
| --check legal | |
| --check lock | |
| --check package | |
| --check performance_core | |
| --check report | |
| --check security | |
| --retention-days 90 | |
| --github-output "${GITHUB_OUTPUT}" | |
| - name: Upload publishable release bundle | |
| if: success() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: ${{ steps.finalize.outputs.artifact_name }} | |
| path: ${{ runner.temp }}/release-bundle/ | |
| include-hidden-files: true | |
| if-no-files-found: error | |
| retention-days: 90 | |
| - name: Upload failed preflight diagnostics | |
| if: failure() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: release-diagnostics-${{ steps.metadata.outputs.package_version }}-${{ github.run_id }}-${{ github.run_attempt }} | |
| path: ${{ runner.temp }}/release-bundle/evidence/ | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| security_policy: | |
| name: Security and policy | |
| needs: [plan, dependency_audit, workflow_audit, legal, package, install_smoke, release_preflight] | |
| if: ${{ always() }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Aggregate security and policy checks | |
| env: | |
| PLAN_JSON: ${{ needs.plan.outputs.plan_json }} | |
| run: >- | |
| python -m tools.ci_gate --gate policy --plan-json "${PLAN_JSON}" | |
| --result "dependency-audit=${{ needs.dependency_audit.result }}" | |
| --result "workflow-audit=${{ needs.workflow_audit.result }}" | |
| --result "legal=${{ needs.legal.result }}" | |
| --result "package=${{ needs.package.result }}" | |
| --result "install-smoke=${{ needs.install_smoke.result }}" | |
| --result "release-preflight=${{ needs.release_preflight.result }}" |