anitrack 0.1.7

CLI/TUI companion for ani-cli with watch-progress tracking
name: Release

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

permissions:
  contents: write

concurrency:
  group: release-${{ github.ref }}
  cancel-in-progress: false

jobs:
  validate:
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.version.outputs.version }}
      tag_name: ${{ steps.version.outputs.tag_name }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Read and validate version
        id: version
        run: |
          CARGO_VERSION="$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n1)"
          LOCK_VERSION="$(
            awk '
              $0 == "[[package]]" { in_pkg = 0; next }
              $0 == "name = \"anitrack\"" { in_pkg = 1; next }
              in_pkg && $1 == "version" {
                gsub(/"/, "", $3)
                print $3
                exit
              }
            ' Cargo.lock
          )"
          TAG_NAME="${GITHUB_REF_NAME}"
          TAG_VERSION="${TAG_NAME#v}"

          if [ -z "${CARGO_VERSION}" ]; then
            echo "Failed to read version from Cargo.toml"
            exit 1
          fi

          if [ -z "${LOCK_VERSION}" ]; then
            echo "Failed to read anitrack version from Cargo.lock"
            exit 1
          fi

          if [ "${CARGO_VERSION}" != "${LOCK_VERSION}" ]; then
            echo "Cargo.toml version (${CARGO_VERSION}) does not match Cargo.lock (${LOCK_VERSION})"
            echo "Run cargo test --all-features locally to refresh Cargo.lock, commit it, then retag."
            exit 1
          fi

          if [ "${CARGO_VERSION}" != "${TAG_VERSION}" ]; then
            echo "Tag version (${TAG_VERSION}) does not match Cargo.toml (${CARGO_VERSION})"
            exit 1
          fi

          echo "version=${CARGO_VERSION}" >> "${GITHUB_OUTPUT}"
          echo "tag_name=${TAG_NAME}" >> "${GITHUB_OUTPUT}"

      - name: Verify changelog entry exists
        run: |
          VERSION="${{ steps.version.outputs.version }}"
          if ! grep -q "^## \\[${VERSION}\\]" CHANGELOG.md; then
            echo "Missing CHANGELOG.md section for version ${VERSION}"
            echo "Expected header format: ## [${VERSION}] - YYYY-MM-DD"
            exit 1
          fi

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Cache Rust dependencies
        uses: Swatinem/rust-cache@v2

      - name: Run tests
        run: cargo test --all-features --locked

  build-linux-x86_64:
    runs-on: ubuntu-latest
    needs: validate
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Cache Rust dependencies
        uses: Swatinem/rust-cache@v2

      - name: Build release binary
        run: cargo build --release --locked

      - name: Package artifacts
        run: |
          VERSION="${{ needs.validate.outputs.version }}"
          ASSET_BASE="anitrack-v${VERSION}-x86_64-unknown-linux-gnu"
          mkdir -p dist
          cp target/release/anitrack dist/anitrack
          tar -C dist -czf "${ASSET_BASE}.tar.gz" anitrack
          sha256sum "${ASSET_BASE}.tar.gz" > "${ASSET_BASE}.tar.gz.sha256"

      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: release-assets
          path: |
            anitrack-v${{ needs.validate.outputs.version }}-x86_64-unknown-linux-gnu.tar.gz
            anitrack-v${{ needs.validate.outputs.version }}-x86_64-unknown-linux-gnu.tar.gz.sha256

  github-release:
    runs-on: ubuntu-latest
    needs: [validate, build-linux-x86_64]
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Download artifacts
        uses: actions/download-artifact@v4
        with:
          name: release-assets
          path: release-assets

      - name: Extract release notes from changelog
        run: |
          VERSION="${{ needs.validate.outputs.version }}"
          awk -v version="${VERSION}" '
            $0 ~ "^## \\[" version "\\]" { in_section = 1; next }
            in_section && $0 ~ "^## \\[" { exit }
            in_section { print }
          ' CHANGELOG.md > release-notes.md

          # Trim leading blank lines produced after section header removal.
          sed -i '/./,$!d' release-notes.md

          if ! grep -q '[^[:space:]]' release-notes.md; then
            echo "Failed to extract release notes for ${VERSION} from CHANGELOG.md"
            exit 1
          fi

      - name: Create GitHub release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ needs.validate.outputs.tag_name }}
          body_path: release-notes.md
          files: release-assets/*

  publish-crates:
    runs-on: ubuntu-latest
    needs: [validate, build-linux-x86_64, github-release]
    environment: release
    permissions:
      id-token: write
      contents: read
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Authenticate with crates.io (OIDC)
        id: crates-auth
        uses: rust-lang/crates-io-auth-action@v1

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

  aur-summary:
    runs-on: ubuntu-latest
    needs: [validate, build-linux-x86_64]
    steps:
      - name: Download artifacts
        uses: actions/download-artifact@v4
        with:
          name: release-assets
          path: release-assets

      - name: Write AUR update summary
        run: |
          VERSION="${{ needs.validate.outputs.version }}"
          TAG="${{ needs.validate.outputs.tag_name }}"
          ASSET="anitrack-v${VERSION}-x86_64-unknown-linux-gnu.tar.gz"
          SHA256="$(cut -d' ' -f1 "release-assets/${ASSET}.sha256")"
          URL="https://github.com/${GITHUB_REPOSITORY}/releases/download/${TAG}/${ASSET}"

          {
            echo "## AUR update values"
            echo ""
            echo "### anitrack-bin (prebuilt)"
            echo "- pkgver: \`${VERSION}\`"
            echo "- source URL: \`${URL}\`"
            echo "- sha256sums: \`${SHA256}\`"
            echo ""
            echo "### anitrack (source build)"
            echo "- pkgver: \`${VERSION}\`"
            echo "- source: \`https://crates.io/api/v1/crates/anitrack/${VERSION}/download\`"
            echo "- note: if you use pinned checksums, run \`updpkgsums\`."
            echo ""
            echo "Regenerate \`.SRCINFO\` and push both AUR repos."
          } >> "${GITHUB_STEP_SUMMARY}"