c2patool 0.28.4

Tool for displaying and creating C2PA manifests.
name: Publish c2patool binaries

# Triggered by `v*` tags. Those tags come from two places (see
# docs/release-process.md):
#   * release-plz, when a release PR merges on a release-line branch -> a real
#     release tag like `v0.27.0`.
#   * release-rc.yml, when a release-candidate build is cut -> a prerelease tag
#     like `v0.27.0-rc.1`.
#
# Either way the tag carries the version, so this workflow is fully tag-driven
# and independent of how the tag was created. `-rc.` tags produce *prerelease*
# GitHub releases; real tags produce normal releases. Nothing here touches
# crates.io. Git tags are repo-global, so no branch filter is needed.

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

permissions:
  contents: write

jobs:
  build:
    name: Build binary
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: macos-latest
            artifact_name: c2patool_mac_universal.zip
            asset_name: ${{ github.ref_name }}-universal-apple-darwin.zip
            sbom_asset_name: ${{ github.ref_name }}-universal-apple-darwin-sbom.json
          - os: ubuntu-latest
            artifact_name: c2patool_linux_intel.tar.gz
            asset_name: ${{ github.ref_name }}-x86_64-unknown-linux-gnu.tar.gz
            sbom_asset_name: ${{ github.ref_name }}-x86_64-unknown-linux-gnu-sbom.json
          - os: windows-latest
            artifact_name: c2patool_win_intel.zip
            asset_name: ${{ github.ref_name }}-x86_64-pc-windows-msvc.zip
            sbom_asset_name: ${{ github.ref_name }}-x86_64-pc-windows-msvc-sbom.json

    steps:
      - name: Checkout repository
        uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@d1031067263f94b142dd6c0ce24c5eb9d02d52a0 # master@2026-06-30
        with:
          toolchain: stable
          components: llvm-tools-preview

      - name: Install cargo-sbom
        uses: baptiste0928/cargo-install@f204293d9709061b7bc1756fec3ec4e2cd57dec0 # v3.4.0
        with:
          crate: cargo-sbom
          version: "0.9.1"

      - name: Cache Rust dependencies
        uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1

      - name: Run make release
        run: make release

      - name: Generate SBOM
        run: cargo sbom > c2patool-sbom.json

      - name: Stage named artifacts
        shell: bash
        run: |
          mkdir -p dist
          cp "target/${{ matrix.artifact_name }}" "dist/${{ matrix.asset_name }}"
          cp "c2patool-sbom.json" "dist/${{ matrix.sbom_asset_name }}"

      - name: Upload build artifacts
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: c2patool-${{ matrix.os }}
          path: dist/

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

      - name: List artifacts
        run: ls -lh dist

      # Generate the release notes ourselves, scoped to the previous release tag
      # (excludes non-release refs that might otherwise sort in between).
      - name: Generate release notes
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          set -euo pipefail
          TAG="${{ github.ref_name }}"
          PREFIX="v"
          repo="${{ github.repository }}"
          matching=$(gh api --paginate "repos/$repo/git/matching-refs/tags/$PREFIX" \
                       --jq '.[].ref' | sed 's#refs/tags/##')
          sorted=$(printf '%s\n%s\n' "$matching" "$TAG" | sort -V -u)
          prev=$(printf '%s\n' "$sorted" | { grep -F -x -B1 -- "$TAG" || true; } | head -n1)
          [ "$prev" = "$TAG" ] && prev=""
          if [ -n "$prev" ]; then
            echo "Previous $PREFIX tag: $prev"
            gh api "repos/$repo/releases/generate-notes" \
              -f tag_name="$TAG" -f previous_tag_name="$prev" --jq .body > release-notes.md
          else
            echo "No previous $PREFIX tag; letting GitHub choose the range."
            gh api "repos/$repo/releases/generate-notes" \
              -f tag_name="$TAG" --jq .body > release-notes.md
          fi
          echo "----- release notes -----"; cat release-notes.md

      # Creates the release if it does not exist yet (the release-candidate
      # case, where release-plz never ran) and updates it in place otherwise
      # (the promotion case, where release-plz already opened the release). A
      # `-rc.` tag is marked as a prerelease.
      - name: Publish GitHub release
        uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
        with:
          tag_name: ${{ github.ref_name }}
          target_commitish: ${{ github.sha }}
          body_path: release-notes.md
          files: dist/*
          draft: false
          prerelease: ${{ contains(github.ref_name, '-rc.') }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}