deadbranch 0.1.3

Clean up stale git branches safely
name: Release

on:
  push:
    tags:
      - 'v[0-9]+.*'
  workflow_dispatch:

permissions:
  contents: write
  id-token: write  # Required for npm OIDC trusted publishers

env:
  CARGO_TERM_COLOR: always

jobs:
  # Build binaries for all platforms
  build:
    name: Build ${{ matrix.target }}
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          # Linux x86_64 (glibc)
          - target: x86_64-unknown-linux-gnu
            os: ubuntu-latest
            archive: tar.gz

          # Linux x86_64 (musl - static)
          - target: x86_64-unknown-linux-musl
            os: ubuntu-latest
            archive: tar.gz

          # Linux ARM64
          - target: aarch64-unknown-linux-gnu
            os: ubuntu-latest
            archive: tar.gz
            cross: true

          # macOS Intel
          - target: x86_64-apple-darwin
            os: macos-latest
            archive: tar.gz

          # macOS Apple Silicon
          - target: aarch64-apple-darwin
            os: macos-latest
            archive: tar.gz

          # Windows
          - target: x86_64-pc-windows-msvc
            os: windows-latest
            archive: zip

    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Get version from tag
        id: version
        shell: bash
        run: echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}

      - name: Install musl-tools (Linux musl)
        if: matrix.target == 'x86_64-unknown-linux-musl'
        run: sudo apt-get update && sudo apt-get install -y musl-tools

      - name: Install cross-compilation tools (Linux ARM64)
        if: matrix.cross == true
        run: |
          sudo apt-get update
          sudo apt-get install -y gcc-aarch64-linux-gnu

      - name: Build release binary
        run: cargo build --release --target ${{ matrix.target }}
        env:
          CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc

      - name: Create archive (Unix)
        if: matrix.os != 'windows-latest'
        run: |
          mkdir -p dist
          cp target/${{ matrix.target }}/release/deadbranch dist/
          MAN_PAGE=$(find target/${{ matrix.target }}/release/build -name "deadbranch.1" | head -1)
          cp "$MAN_PAGE" dist/
          cp README.md LICENSE dist/ 2>/dev/null || true
          cd dist
          tar -czf ../deadbranch-${{ steps.version.outputs.version }}-${{ matrix.target }}.tar.gz *

      - name: Create archive (Windows)
        if: matrix.os == 'windows-latest'
        shell: pwsh
        run: |
          New-Item -ItemType Directory -Force -Path dist
          Copy-Item "target/${{ matrix.target }}/release/deadbranch.exe" -Destination "dist/"
          Copy-Item "README.md" -Destination "dist/" -ErrorAction SilentlyContinue
          Copy-Item "LICENSE" -Destination "dist/" -ErrorAction SilentlyContinue
          Compress-Archive -Path "dist/*" -DestinationPath "deadbranch-${{ steps.version.outputs.version }}-${{ matrix.target }}.zip"

      # Upload binaries to the GitHub Release created by release-please
      - name: Upload release asset
        uses: softprops/action-gh-release@v2
        with:
          files: |
            deadbranch-${{ steps.version.outputs.version }}-${{ matrix.target }}.${{ matrix.archive }}

  # Publish to crates.io
  publish-crates:
    name: Publish to crates.io
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

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

      - name: Publish to crates.io
        run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }}

  # Publish npm package
  publish-npm:
    name: Publish to npm
    needs: build
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write  # Required for npm OIDC
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Setup Node.js
        uses: actions/setup-node@v6
        with:
          node-version: '24'
          registry-url: 'https://registry.npmjs.org'

      - name: Publish to npm (with provenance)
        working-directory: pkg/npm
        run: npm publish --access public --provenance

  # Update Homebrew tap formula with new version and SHA256 hashes
  publish-homebrew:
    name: Publish to Homebrew tap
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Get version from tag
        id: version
        run: echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT

      - name: Download release tarballs and compute SHA256
        id: sha256
        run: |
          BASE="https://github.com/armgabrielyan/deadbranch/releases/download/v${{ steps.version.outputs.version }}"
          V="${{ steps.version.outputs.version }}"

          download_with_retry() {
            local URL="$1"
            local OUT="$2"
            local MAX_ATTEMPTS=10
            local WAIT=15
            for attempt in $(seq 1 $MAX_ATTEMPTS); do
              HTTP_CODE=$(curl -sL -w "%{http_code}" "$URL" -o "$OUT")
              if [ "$HTTP_CODE" = "200" ]; then
                echo "Downloaded $URL (attempt $attempt)"
                return 0
              fi
              echo "Attempt $attempt/$MAX_ATTEMPTS: got HTTP $HTTP_CODE for $URL, retrying in ${WAIT}s..."
              sleep $WAIT
            done
            echo "Failed to download $URL after $MAX_ATTEMPTS attempts"
            return 1
          }

          download_with_retry "$BASE/deadbranch-${V}-x86_64-apple-darwin.tar.gz"       x86_64-apple-darwin.tar.gz
          download_with_retry "$BASE/deadbranch-${V}-aarch64-apple-darwin.tar.gz"      aarch64-apple-darwin.tar.gz
          download_with_retry "$BASE/deadbranch-${V}-x86_64-unknown-linux-gnu.tar.gz"  x86_64-linux-gnu.tar.gz
          download_with_retry "$BASE/deadbranch-${V}-aarch64-unknown-linux-gnu.tar.gz" aarch64-linux-gnu.tar.gz

          echo "x86_64_darwin=$(sha256sum x86_64-apple-darwin.tar.gz | awk '{print $1}')"       >> $GITHUB_OUTPUT
          echo "aarch64_darwin=$(sha256sum aarch64-apple-darwin.tar.gz | awk '{print $1}')"     >> $GITHUB_OUTPUT
          echo "x86_64_linux=$(sha256sum x86_64-linux-gnu.tar.gz | awk '{print $1}')"           >> $GITHUB_OUTPUT
          echo "aarch64_linux=$(sha256sum aarch64-linux-gnu.tar.gz | awk '{print $1}')"         >> $GITHUB_OUTPUT

      - name: Checkout deadbranch (main repo)
        uses: actions/checkout@v6
        with:
          token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
          ref: main
          fetch-depth: 0
          path: deadbranch

      - name: Checkout homebrew-tap
        uses: actions/checkout@v6
        with:
          repository: armgabrielyan/homebrew-tap
          token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
          path: homebrew-tap

      - name: Update formula files
        run: |
          patch_formula() {
            local FORMULA="$1"
            local V="${{ steps.version.outputs.version }}"
            # Preserve any trailing comment (e.g. # x-release-please-version) on the version line
            sed -i "s/\(version \"\)[^\"]*\(\"[^#]*\)\(#.*\)\?/\1${V}\2\3/" "$FORMULA"
            sed -i "/x86_64-apple-darwin\.tar\.gz\"/{n;s/sha256 \".*\"/sha256 \"${{ steps.sha256.outputs.x86_64_darwin }}\"/}" "$FORMULA"
            sed -i "/aarch64-apple-darwin\.tar\.gz\"/{n;s/sha256 \".*\"/sha256 \"${{ steps.sha256.outputs.aarch64_darwin }}\"/}" "$FORMULA"
            sed -i "/x86_64-unknown-linux-gnu\.tar\.gz\"/{n;s/sha256 \".*\"/sha256 \"${{ steps.sha256.outputs.x86_64_linux }}\"/}" "$FORMULA"
            sed -i "/aarch64-unknown-linux-gnu\.tar\.gz\"/{n;s/sha256 \".*\"/sha256 \"${{ steps.sha256.outputs.aarch64_linux }}\"/}" "$FORMULA"
          }
          patch_formula "deadbranch/pkg/homebrew/deadbranch.rb"
          patch_formula "homebrew-tap/Formula/deadbranch.rb"

      - name: Commit and push to homebrew-tap
        run: |
          cd homebrew-tap
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add Formula/deadbranch.rb
          git commit -m "chore: update deadbranch to v${{ steps.version.outputs.version }}"
          git push origin main

      - name: Commit and push pkg/homebrew to main repo
        run: |
          cd deadbranch
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add pkg/homebrew/deadbranch.rb
          git commit -m "chore: update homebrew formula to v${{ steps.version.outputs.version }}"
          git push origin main