c2pa-html 0.2.0

C2PA manifest embedding, referencing, and hard binding for HTML documents
Documentation
name: Release

# Cut a release by pushing a version tag that matches the crate version:
#   git tag v0.1.0 && git push origin v0.1.0
#
# crates.io publishing is gated on a repository secret; the job is skipped
# cleanly if it is absent, so a tag can drive a partial release:
#   CARGO_REGISTRY_TOKEN  — crates.io API token
#
# PyPI publishing uses Trusted Publishing (OIDC), not a secret. Register the
# publisher on PyPI for project `c2pa-html`, owner `writerslogic`, repository
# `c2pa-html`, workflow `release.yml`, environment `pypi`.

on:
  push:
    tags: ['v*.*.*']
  # Manual re-run for the current Cargo.toml version (e.g. to complete a partial
  # release). The version is read from Cargo.toml, not the ref, and every
  # publish job is idempotent, so this is safe to run repeatedly.
  workflow_dispatch:

permissions:
  contents: write

env:
  CARGO_TERM_COLOR: always

jobs:
  verify:
    name: Verify
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt, clippy
          targets: wasm32-unknown-unknown
      - uses: actions/setup-python@v5
        with:
          python-version: '3.x'
      - name: Tag matches crate version
        if: github.ref_type == 'tag'
        run: |
          tag_version="${GITHUB_REF_NAME#v}"
          if ! grep -q "^version = \"${tag_version}\"$" Cargo.toml; then
            echo "::error::tag ${tag_version} does not match the version in Cargo.toml"
            exit 1
          fi
      - run: cargo fmt --all -- --check
      # Not --all-features: pyo3's `extension-module` leaves Python symbols to
      # be supplied by the interpreter, so a test binary linked with it fails.
      - run: cargo clippy --all-targets -- -D warnings
      - run: cargo clippy --target wasm32-unknown-unknown -- -D warnings
      - run: cargo clippy --features python -- -D warnings
      - run: cargo test

  crates-io:
    name: Publish to crates.io
    needs: verify
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@stable
      - name: cargo publish
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
        run: |
          if [ -z "$CARGO_REGISTRY_TOKEN" ]; then
            echo "::warning::CARGO_REGISTRY_TOKEN not set; skipping crates.io publish"
            exit 0
          fi
          # Ask cargo rather than guessing from `cargo search`: the index can
          # lag a publish by minutes, so a search-based guard reports "not
          # published" for something that is, and the re-run then fails.
          if out=$(cargo publish 2>&1); then
            printf '%s\n' "$out"
          elif printf '%s' "$out" | grep -q 'already exists'; then
            echo "crates.io already has this version; skipping"
          else
            printf '%s\n' "$out"
            exit 1
          fi

  npm:
    name: Publish to npm
    needs: verify
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: wasm32-unknown-unknown
      - uses: jetli/wasm-pack-action@v0.4.0
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          registry-url: https://registry.npmjs.org
      - name: Build and publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
        run: |
          if [ -z "$NODE_AUTH_TOKEN" ]; then
            echo "::warning::NPM_TOKEN not set; skipping npm publish"
            exit 0
          fi
          ver="$(grep '^version = ' Cargo.toml | head -1 | cut -d'"' -f2)"
          if npm view "c2pa-html@${ver}" version >/dev/null 2>&1; then
            echo "npm already has c2pa-html@${ver}; skipping"
            exit 0
          fi
          wasm-pack build --target bundler --out-dir pkg
          # The leading ./ stops npm resolving "pkg" as a registry package spec.
          npm publish ./pkg --access public

  wheels:
    name: Build wheels (${{ matrix.platform.os }} ${{ matrix.platform.target }})
    needs: verify
    runs-on: ${{ matrix.platform.os }}
    strategy:
      fail-fast: false
      matrix:
        platform:
          - { os: ubuntu-latest, target: x86_64 }
          - { os: ubuntu-latest, target: aarch64 }
          - { os: macos-latest, target: aarch64 }
          # Cross-compile the Intel-mac wheel on the fast arm64 runner; the
          # macos-13 (Intel) runner is scarce and times out.
          - { os: macos-latest, target: x86_64 }
          - { os: windows-latest, target: x64 }
    steps:
      - uses: actions/checkout@v7
      - uses: PyO3/maturin-action@v1
        with:
          target: ${{ matrix.platform.target }}
          args: --release --out dist --features python
          manylinux: auto
      - uses: actions/upload-artifact@v4
        with:
          name: wheels-${{ matrix.platform.os }}-${{ matrix.platform.target }}
          path: dist

  sdist:
    name: Build sdist
    needs: verify
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: PyO3/maturin-action@v1
        with:
          command: sdist
          args: --out dist
      - uses: actions/upload-artifact@v4
        with:
          name: wheels-sdist
          path: dist

  pypi:
    name: Publish to PyPI
    needs: [wheels, sdist]
    runs-on: ubuntu-latest
    environment: pypi
    permissions:
      id-token: write # Trusted Publishing (OIDC); no PyPI token needed
    steps:
      - uses: actions/download-artifact@v4
        with:
          pattern: wheels-*
          merge-multiple: true
          path: dist
      - uses: pypa/gh-action-pypi-publish@release/v1
        with:
          packages-dir: dist
          skip-existing: true

  github-release:
    name: GitHub release
    needs: verify
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
        with:
          fetch-depth: 0
      - name: Create release
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          ver="$(grep '^version = ' Cargo.toml | head -1 | cut -d'"' -f2)"
          tag="v${ver}"
          if gh release view "$tag" >/dev/null 2>&1; then
            echo "release $tag already exists; skipping"
            exit 0
          fi
          gh release create "$tag" --title "$tag" --generate-notes