name: Release

on:
  push:
    tags: ["v*"]
  workflow_dispatch:

permissions:
  contents: read

jobs:
  release-crates-io:
    name: Release crates.io
    runs-on: ubuntu-latest
    if: startsWith(github.ref, 'refs/tags/')
    environment:
      name: crates.io
      url: ${{ steps.set_url.outputs.env_url }}
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - name: cargo login
        run: cargo login ${{ secrets.CRATES_IO_TOKEN }}
      - name: cargo publish
        run: cargo publish
      - name: Set environment url
        id: set_url
        run: |
          VERSION=$(echo $GITHUB_REF | sed -e "s#refs/tags/v##g")
          echo "env_url=https://crates.io/crates/maturin/$VERSION" >> $GITHUB_OUTPUT

  build:
    name: Build ${{ matrix.target }}
    strategy:
      fail-fast: false
      matrix:
        target:
          - x86_64-unknown-linux-musl
          - x86_64-apple-darwin
          - x86_64-pc-windows-msvc
          - i686-pc-windows-msvc
          - aarch64-pc-windows-msvc
        include:
          - target: x86_64-unknown-linux-musl
            os: ubuntu-latest
            name: maturin-x86_64-unknown-linux-musl.tar.gz
          - target: x86_64-apple-darwin
            os: macos-latest
            name: maturin-x86_64-apple-darwin.tar.gz
          - target: x86_64-pc-windows-msvc
            os: windows-latest
            name: maturin-x86_64-pc-windows-msvc.zip
          - target: i686-pc-windows-msvc
            os: windows-latest
            name: maturin-i686-pc-windows-msvc.zip
          - target: aarch64-pc-windows-msvc
            os: windows-latest
            name: maturin-aarch64-pc-windows-msvc.zip
    runs-on: ${{ matrix.os }}
    env:
      CARGO_PROFILE_RELEASE_LTO: "fat"
    steps:
      # Largely inspired by https://github.com/starship/starship/blob/35a0a20f5c4fea6a08e1b91ff631b089eef8fc50/.github/workflows/deploy.yml
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}

      - name: Install musl tools
        if: matrix.os == 'ubuntu-latest'
        run: sudo apt-get install -y musl-tools

      # Install gnu-tar because BSD tar is buggy
      # https://github.com/actions/cache/issues/403
      - name: Install GNU tar (macOS)
        if: matrix.os == 'macos-latest'
        run: |
          brew install gnu-tar
          echo "/usr/local/opt/gnu-tar/libexec/gnubin" >> $GITHUB_PATH

      # Those two will also create target/${{ matrix.target }}/maturin
      - name: Build wheel (with sdist)
        if: matrix.target == 'x86_64-unknown-linux-musl'
        run: |
          cargo run -- build --release -b bin --sdist -o dist --target ${{ matrix.target }} --features password-storage --compatibility manylinux2010 musllinux_1_1

      # ring doesn't support aarch64 windows yet
      - name: Build wheel (windows aarch64)
        if: matrix.target == 'aarch64-pc-windows-msvc'
        run: cargo run -- build --release -b bin -o dist --target ${{ matrix.target }} --no-default-features --features full,native-tls

      - name: Build wheel (without sdist)
        if: ${{ matrix.target != 'x86_64-unknown-linux-musl' && matrix.target != 'aarch64-pc-windows-msvc' }}
        run: cargo run -- build --release -b bin -o dist --target ${{ matrix.target }} --features password-storage

      - name: Build wheel (macOS universal2)
        if: matrix.target == 'x86_64-apple-darwin'
        env:
          DEVELOPER_DIR: /Applications/Xcode.app/Contents/Developer
          MACOSX_DEPLOYMENT_TARGET: "10.12"
        run: |
          # set SDKROOT for C dependencies like ring and bzip2
          export SDKROOT=$(xcrun --sdk macosx --show-sdk-path)
          rustup target add aarch64-apple-darwin
          cargo run -- build --release -b bin -o dist --target universal2-apple-darwin --features password-storage

      - name: Archive binary (windows)
        if: matrix.os == 'windows-latest'
        run: |
          cd target/${{ matrix.target }}/release
          7z a ../../../${{ matrix.name }} ${{ github.event.repository.name }}.exe
          cd -

      - name: Archive binary (linux and macOS)
        if: matrix.os != 'windows-latest'
        run: |
          cd target/${{ matrix.target }}/release
          tar czvf ../../../${{ matrix.name }} ${{ github.event.repository.name }}
          cd -

      - name: Archive binary (macOS aarch64)
        if: matrix.os == 'macos-latest'
        run: |
          cd target/aarch64-apple-darwin/release
          tar czvf ../../../maturin-aarch64-apple-darwin.tar.gz ${{ github.event.repository.name }}
          cd -

      - name: Upload wheel artifacts
        uses: actions/upload-artifact@v4
        with:
          name: wheels-${{ matrix.target }}
          path: dist

      - name: Upload binary artifacts
        uses: actions/upload-artifact@v4
        with:
          name: binaries-${{ matrix.target }}
          path: |
            *.tar.gz
            *.zip

  build-musl:
    name: Build ${{ matrix.platform.target }}
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        platform:
          - target: "aarch64-unknown-linux-musl"
            image: "rust-musl-cross:aarch64-musl"
            compatibility: "manylinux2014 musllinux_1_1"
          - target: "armv7-unknown-linux-musleabihf"
            image: "rust-musl-cross:armv7-musleabihf"
            compatibility: "manylinux2014 musllinux_1_1"
          - target: "i686-unknown-linux-musl"
            image: "rust-musl-cross:i686-musl"
            compatibility: "manylinux2010 musllinux_1_1"
          - target: "arm-unknown-linux-musleabihf"
            image: "rust-musl-cross:arm-musleabihf"
            compatibility: "linux"
          - target: "powerpc64le-unknown-linux-musl"
            image: "rust-musl-cross:powerpc64le-musl"
            compatibility: "manylinux2014 musllinux_1_1"
            lto: "off"
          - target: "s390x-unknown-linux-gnu"
            image: "manylinux2014-cross:s390x"
            compatibility: "manylinux2014"
          - target: "loongarch64-unknown-linux-gnu"
            image: "manylinux_2_36-cross:loongarch64"
            compatibility: "manylinux_2_36"
          - target: "riscv64gc-unknown-linux-gnu"
            image: "manylinux_2_31-cross:riscv64"
            compatibility: "manylinux_2_31"
    container:
      image: docker://ghcr.io/rust-cross/${{ matrix.platform.image }}
      env:
        RUSTUP_HOME: /root/.rustup
        CARGO_HOME: /root/.cargo
        CARGO_PROFILE_RELEASE_LTO: "${{ matrix.platform.lto || 'fat' }}"
    steps:
      - uses: actions/checkout@v4
      # powerpc64le-unknown-linux-musl doesn't have official std library release
      - run: rustup target add --toolchain stable ${{ matrix.platform.target }}
        if: ${{ !contains(fromJson('["powerpc64le-unknown-linux-musl", "s390x-unknown-linux-gnu", "loongarch64-unknown-linux-gnu", "riscv64gc-unknown-linux-gnu"]'), matrix.platform.target) }}
      - uses: dtolnay/rust-toolchain@stable
        if: contains(fromJson('["s390x-unknown-linux-gnu", "loongarch64-unknown-linux-gnu", "riscv64gc-unknown-linux-gnu"]'), matrix.platform.target)
        with:
          targets: ${{ matrix.platform.target }}
      - name: Build wheel
        env:
          # Make psm compile, see https://github.com/rust-lang/stacker/issues/79
          CFLAGS_s390x_unknown_linux_gnu: "-march=z10"
        run: |
          sudo python3 -m pip install -U --pre maturin
          maturin build --release -b bin -o dist \
            --target ${{ matrix.platform.target }} \
            --compatibility ${{ matrix.platform.compatibility }} \
            --features password-storage
      - name: Archive binary
        run: tar czvf target/release/maturin-${{ matrix.platform.target }}.tar.gz -C target/${{ matrix.platform.target }}/release maturin
      - name: Upload wheel artifacts
        # loongarch64 is not allowed to publish to pypi currently, see https://github.com/pypi/warehouse/blob/348117529910181cb8c14e9b5fb00fcf4dbaf07c/warehouse/forklift/legacy.py#L114-L185
        if: matrix.platform.target != 'loongarch64-unknown-linux-gnu'
        uses: actions/upload-artifact@v4
        with:
          name: wheels-${{ matrix.platform.target }}
          path: dist
      - name: Upload binary artifacts
        uses: actions/upload-artifact@v4
        with:
          name: binaries-${{ matrix.platform.target }}
          path: target/release/maturin-${{ matrix.platform.target }}.tar.gz

  release-pypi:
    permissions:
      # Used to sign the release's artifacts
      # and upload to PyPI using trusted publisher.
      id-token: write
      # Used to upload release artifacts.
      contents: write
      # Use to generate artifact attestation.
      attestations: write
    name: Publish to PyPI
    runs-on: ubuntu-latest
    environment:
      name: PyPI
      url: ${{ steps.set_url.outputs.env_url }}
    needs: [build, build-musl]
    steps:
      - uses: actions/download-artifact@v4
        with:
          pattern: wheels-*
          merge-multiple: true
      - name: Generate artifact attestation
        uses: actions/attest-build-provenance@v2
        with:
          subject-path: |
            ./*.tar.gz
            ./*.whl
      - uses: actions/setup-python@v5
        if: startsWith(github.ref, 'refs/tags/')
        with:
          python-version: "3.10"
      - name: Publish
        if: startsWith(github.ref, 'refs/tags/')
        run: |
          pip install maturin
          maturin upload --skip-existing *
      - name: Set environment url
        id: set_url
        run: |
          VERSION=$(echo $GITHUB_REF | sed -e "s#refs/tags/v##g")
          echo "env_url=https://pypi.org/project/maturin/$VERSION" >> $GITHUB_OUTPUT

  release-github:
    permissions:
      # Used to sign the release's artifacts.
      id-token: write
      # Used to upload release artifacts.
      contents: write
      # Use to generate artifact attestation.
      attestations: write
    name: Publish to GitHub releases
    runs-on: ubuntu-latest
    needs: [build, build-musl]
    steps:
      - uses: actions/download-artifact@v4
        with:
          pattern: binaries-*
          merge-multiple: true
      - name: Generate artifact attestation
        uses: actions/attest-build-provenance@v2
        with:
          subject-path: |
            ./*.tar.gz
            ./*.zip
            ./*.deb
      - name: Release
        if: startsWith(github.ref, 'refs/tags/')
        uses: softprops/action-gh-release@v2
        with:
          files: |
            *.tar.gz
            *.zip
            *.deb
          prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') }}
          generate_release_notes: true
