name: Continuous Delivery

on:
  workflow_dispatch:

  release:
    types:
      - created

permissions:
  contents: read

jobs:
  pre_flight_check:
    name: Preflight Checks
    uses: ./.github/workflows/ci.yml

  universal-wheel:
    name: Build Universal Wheel
    runs-on: ubuntu-latest
    needs:
      - pre_flight_check
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false
      - name: Set up Python
        uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
        with:
          python-version: '3'
      - name: Update pip, install build
        run: |
          python -m pip install --upgrade pip
          python -m pip install build
      - name: Build Wheel
        env:
          CHARSET_NORMALIZER_USE_MYPYC: '0'
        run: python -m build
      - name: Upload artifacts
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
        with:
          name: dist-universal
          path: dist

  build-wheels:
    name: Build wheels on ${{ matrix.os }} ${{ matrix.qemu }}
    runs-on: ${{ matrix.os }}
    needs: pre_flight_check
    strategy:
      matrix:
        os: [ ubuntu-22.04, windows-latest, macos-13 ]
        qemu: [ '' ]
        include:
          # Split ubuntu job for the sake of speed-up
          - os: ubuntu-22.04
            qemu: aarch64
          - os: ubuntu-22.04
            qemu: ppc64le
          - os: ubuntu-22.04
            qemu: s390x
    steps:
      - name: Checkout
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          submodules: true
      - name: Set up QEMU
        if: ${{ matrix.qemu }}
        uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
        with:
          platforms: all
        id: qemu
      - name: Prepare emulation
        run: |
          if [[ -n "${{ matrix.qemu }}" ]]; then
            # Build emulated architectures only if QEMU is set,
            # use default "auto" otherwise
            echo "CIBW_ARCHS_LINUX=${{ matrix.qemu }}" >> $GITHUB_ENV
          fi
        shell: bash
      - name: Setup Python
        uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
      - name: Build wheels
        uses: pypa/cibuildwheel@9e4e50bd76b3190f55304387e333f6234823ea9b # v3.1.2
        env:
          CIBW_BUILD_FRONTEND: build
          CIBW_ARCHS_MACOS: universal2
          CIBW_ENVIRONMENT: CHARSET_NORMALIZER_USE_MYPYC='1'
          CIBW_TEST_REQUIRES: pytest
          CIBW_TEST_COMMAND: pytest -c {package} {package}/tests
          CIBW_SKIP: "pp* cp31?t-*"
      - name: Upload artifacts
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
        with:
          name: dist-${{ matrix.os }}-${{ matrix.qemu }}
          path: ./wheelhouse/*.whl

  deploy:
    name: 🚀 Deploy to PyPi
    runs-on: ubuntu-latest
    if: startsWith(github.ref, 'refs/tags/')
    permissions:
      id-token: write
      contents: write
    needs:
      - build-wheels
      - universal-wheel
    environment:
      name: pypi
      url: https://pypi.org/project/charset-normalizer/
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          persist-credentials: false
      - name: Download distributions
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
        with:
          pattern: dist*
          merge-multiple: true
          path: dist
      - name: Collected dists
        run: |
          tree dist
      - name: Publish package distributions to PyPI
        uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
      - name: Install Syft
        run: |
          curl -sSfL https://get.anchore.io/syft | sudo sh -s -- -b /usr/local/bin
          syft version
      - name: Generate SBOMs
        run: |
          set -euo pipefail
          shopt -s nullglob

          restore_from=""
          restore_to=""

          for pkg in dist/*.whl dist/*.tar.gz; do
            original_pkg="$pkg"
            original_base="$(basename -- "$original_pkg")"
            out="dist/${original_base}.cdx.json"

            if [[ "$original_pkg" == *.whl ]]; then
              zip_pkg="${original_pkg%.whl}.zip"
              zip_base="${original_base%.whl}.zip"
              stem="${original_base%.whl}"

              echo "Temporarily renaming ${original_base} -> ${zip_base}"
              mv -- "$original_pkg" "$zip_pkg"
              restore_from="$zip_pkg"
              restore_to="$original_pkg"

              echo "Generating SBOM for $original_base via $zip_base"
              syft "file:$zip_pkg" -o cyclonedx-json > "$out"

              tmp="$out.tmp"
              jq --arg stem "$stem" '
                (. | .. | objects | select(has("name")) | .name)
                |= (if . == ($stem + ".zip") then $stem + ".whl" else . end)
              ' "$out" > "$tmp" && mv "$tmp" "$out"

              echo "Restoring ${zip_base} -> ${original_base}"
              mv -- "$zip_pkg" "$original_pkg"
              restore_from=""
              restore_to=""
            else
              echo "Generating SBOM for $original_base"
              syft "file:$original_pkg" -o cyclonedx-json > "$out"
            fi
          done
      - name: Upload dists to GitHub Release
        env:
          GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
        run: |
          gh release upload ${{ github.ref_name }} dist/* --repo ${{ github.repository }}
