desmack 0.3.0

Extracts the assets of a SMK ROM (SNES)
name: Release

on:
  push:
    tags:
      - "v*"
  workflow_dispatch:

permissions:
  contents: write

jobs:
  build:
    # Overrides the workflow-level `contents: write`, which this job does not need —
    # `create-release` does the uploading. The other two are for build provenance:
    # `id-token` fetches the OIDC token Sigstore signs with, `attestations` writes the
    # resulting bundle to the repository's attestation store.
    permissions:
      contents: read
      id-token: write
      attestations: write
    strategy:
      matrix:
        include:
          - target: x86_64-unknown-linux-gnu
            os: ubuntu-latest
            artifact-name: linux-x64
          - target: aarch64-unknown-linux-gnu
            os: ubuntu-latest
            artifact-name: linux-arm64
            use-cross: true
          - target: x86_64-apple-darwin
            os: macos-latest
            artifact-name: darwin-x64
          - target: aarch64-apple-darwin
            os: macos-latest
            artifact-name: darwin-arm64
          - target: x86_64-pc-windows-msvc
            os: windows-latest
            artifact-name: windows-x64
          - target: aarch64-pc-windows-msvc
            os: ubuntu-latest
            artifact-name: windows-arm64
            use-cargo-xwin: true
    runs-on: ${{ matrix.os }}
    steps:
      - name: Check out repository
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

      - name: Set up mise

        uses: jdx/mise-action@e6a8b3978addb5a52f2b4cd9d91eafa7f0ab959d # v4.2.0

      - name: Install Rust toolchain
        if: ${{ !matrix.use-cross }}
        run: rustup target add ${{ matrix.target }}

      - name: Cache Rust build

        uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
        with:
          key: ${{ matrix.target }}

      - name: Install cross-compilation tools
        if: matrix.use-cross
        run: cargo install cross

      - name: Install cargo-xwin
        if: matrix.use-cargo-xwin
        run: cargo install cargo-xwin

      - name: Cache xwin MSVC CRT
        if: matrix.use-cargo-xwin
        uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
        with:
          path: ~/.cache/cargo-xwin
          key: cargo-xwin-msvc

      - name: Build (cross)
        if: matrix.use-cross
        run: cross build --release --target ${{ matrix.target }}

      - name: Build (cargo-xwin)
        if: matrix.use-cargo-xwin
        run: cargo xwin build --release --target ${{ matrix.target }}

      - name: Build (native)
        if: ${{ !matrix.use-cross && !matrix.use-cargo-xwin }}
        run: cargo build --release --target ${{ matrix.target }}

      - name: Verify no dynamic CRT dependency
        if: contains(matrix.target, 'windows')
        shell: bash
        run: |
          EXE="./target/${{ matrix.target }}/release/desmack.exe"
          if grep -qa 'VCRUNTIME140\|api-ms-win-crt-' "$EXE"; then
            echo "::error::$EXE imports the dynamic CRT; it will fail with 0xC0000135 on a clean Windows install. Check .cargo/config.toml crt-static."
            exit 1
          fi

      - name: Get version
        id: version
        shell: bash
        run: echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT

      - name: Create zip
        shell: bash
        run: |
          if [[ "${{ matrix.target }}" == *windows* ]]; then
            BIN=desmack.exe
          else
            BIN=desmack
          fi
          7z a "desmack-${{ steps.version.outputs.VERSION }}-${{ matrix.artifact-name }}.zip" \
            "./target/${{ matrix.target }}/release/${BIN}" LICENSE-MIT LICENSE-APACHE

      # Attested here rather than in `create-release` so the provenance names the job that
      # actually compiled the binary, not the one that collected it. Digests survive the
      # artifact round-trip, so the released zip verifies against this attestation.
      - name: Attest build provenance
        uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
        with:
          subject-path: desmack-*

      - name: Upload artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: desmack-${{ matrix.artifact-name }}
          path: desmack-*

  create-release:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Download artifacts
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          merge-multiple: true

      - name: Generate checksums
        run: sha256sum desmack-*.zip > SHA256SUMS

      - name: Create release
        uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
        with:
          files: |
            desmack-*.zip
            SHA256SUMS

  # Trusted publishing: the OIDC token is swapped for a short-lived crates.io token, so no
  # secret is stored. The crate's first publish is manual; link this workflow on crates.io after.
  publish:
    needs: create-release
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
    steps:
      - name: Check out repository
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

      - name: Authenticate with crates.io

        uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 # v1.0.5
        id: auth

      - name: Publish
        run: cargo publish
        env:
          CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}