pockingbird 0.2.0

Report-only CLI that finds duplicate translation keys across gettext .po catalogs
Documentation
name: Release

on:
  push:
    tags: ['v*']

env:
  CARGO_TERM_COLOR: always

# Each job needs to be able to attach assets to the draft release.
permissions:
  contents: write

jobs:
  create-release:
    name: Create draft release
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Create draft release
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          gh release create "${GITHUB_REF_NAME}" \
            --title "${GITHUB_REF_NAME}" \
            --generate-notes \
            --draft

  build:
    name: ${{ matrix.target }}
    needs: create-release
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: x86_64-unknown-linux-gnu
            os: ubuntu-latest
            archive: tar.gz
          - target: aarch64-unknown-linux-gnu
            os: ubuntu-24.04-arm
            archive: tar.gz
          # Both Darwin targets build on Apple Silicon — `macos-13` (Intel)
          # runners are deprecated and frequently sit in queue for hours.
          # Xcode on ARM cross-compiles to x86_64 natively, output is identical.
          - target: x86_64-apple-darwin
            os: macos-latest
            archive: tar.gz
          - target: aarch64-apple-darwin
            os: macos-latest
            archive: tar.gz
          - target: x86_64-pc-windows-msvc
            os: windows-latest
            archive: zip
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}
      - uses: Swatinem/rust-cache@v2
        with:
          key: release-${{ matrix.target }}

      - name: Build
        run: cargo build --release --target ${{ matrix.target }} --bin pockingbird

      - name: Package
        shell: bash
        run: |
          set -euo pipefail
          version="${GITHUB_REF_NAME}"
          name="pockingbird-${version}-${{ matrix.target }}"
          staging="dist/${name}"
          mkdir -p "${staging}"
          if [[ "${{ matrix.target }}" == *"windows"* ]]; then
            cp "target/${{ matrix.target }}/release/pockingbird.exe" "${staging}/"
          else
            cp "target/${{ matrix.target }}/release/pockingbird" "${staging}/"
          fi
          cp README.md LICENSE "${staging}/"

          cd dist
          if [[ "${{ matrix.archive }}" == "zip" ]]; then
            7z a "${name}.zip" "${name}" >/dev/null
            echo "ASSET=dist/${name}.zip" >> "$GITHUB_ENV"
          else
            tar czf "${name}.tar.gz" "${name}"
            echo "ASSET=dist/${name}.tar.gz" >> "$GITHUB_ENV"
          fi

      - name: Upload artifact to release
        env:
          GH_TOKEN: ${{ github.token }}
        shell: bash
        # `--clobber` so re-running failed jobs after a partial upload doesn't
        # fail on already-present assets from the previous attempt.
        run: gh release upload "${GITHUB_REF_NAME}" "${ASSET}" --clobber

  # `PUBLISH_PACKAGES` gates the crates.io publish. Leave it unset for the very
  # first release (the crate name doesn't exist yet — publish it by hand once);
  # flip it to `true` afterwards so every subsequent tag ships automatically.
  publish-crate:
    name: Publish to crates.io
    needs: build
    runs-on: ubuntu-latest
    if: vars.PUBLISH_PACKAGES == 'true'
    env:
      # Modern replacement for the deprecated `cargo publish --token` flag.
      CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable

      # Skip cleanly when the version is already on crates.io. Lets us re-run
      # this workflow after fixing a downstream failure without `cargo publish`
      # failing with "crate version already exists".
      - name: Check whether this version is already published
        id: check
        run: |
          set -euo pipefail
          version=$(grep -E '^version\s*=' Cargo.toml | head -1 | cut -d'"' -f2)
          echo "version=${version}" >> "$GITHUB_OUTPUT"
          status=$(curl -s -o /dev/null -w "%{http_code}" \
            "https://crates.io/api/v1/crates/pockingbird/${version}" || true)
          if [[ "${status}" == "200" ]]; then
            echo "pockingbird ${version} is already on crates.io — skipping publish"
            echo "skip=true" >> "$GITHUB_OUTPUT"
          else
            echo "skip=false" >> "$GITHUB_OUTPUT"
          fi

      - name: Publish
        if: steps.check.outputs.skip != 'true'
        run: cargo publish

  publish-release:
    name: Publish GitHub release
    needs: [build, publish-crate]
    runs-on: ubuntu-latest
    # Drop the draft flag only when the build succeeded and the crate publish
    # either succeeded or was intentionally skipped (first release with
    # `PUBLISH_PACKAGES` unset). If a channel failed, the release stays in draft
    # for a clean re-run.
    if: |
      always() && !cancelled() &&
      needs.build.result == 'success' &&
      (needs.publish-crate.result == 'success' || needs.publish-crate.result == 'skipped')
    steps:
      - name: Mark release as non-draft
        env:
          GH_TOKEN: ${{ github.token }}
        run: gh release edit "${GITHUB_REF_NAME}" --repo "${GITHUB_REPOSITORY}" --draft=false