brogz 0.1.4

Measure wire bytes and timing for identity / gzip / brotli responses across a site's assets.
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 brogz

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

  # crates.io and Homebrew tap publish must stay in lockstep: if either fails,
  # neither should ship and the GitHub Release stays in draft so the whole
  # release can be re-run cleanly. To get this, `notify-tap` waits on
  # `publish-crate`, and `publish-release` waits on both.
  #
  # `PUBLISH_PACKAGES` gates both. Leave it unset for the very first release
  # (crate name and tap repo don't exist yet — you publish those by hand);
  # flip it to `true` afterwards so every subsequent tag ships everywhere
  # 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 (Homebrew tap, etc.)
      # 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/brogz/${version}" || true)
          if [[ "${status}" == "200" ]]; then
            echo "brogz ${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

  notify-tap:
    name: Bump Homebrew tap
    needs: [build, publish-crate]
    runs-on: ubuntu-latest
    # Only fires when the crate publish actually succeeded — otherwise the tap
    # would point at a version that doesn't exist on crates.io.
    if: vars.PUBLISH_PACKAGES == 'true' && needs.publish-crate.result == 'success'
    steps:
      - name: Trigger formula bump
        env:
          GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
          TAG: ${{ github.ref_name }}
        run: |
          gh api repos/Segodnya/homebrew-brogz/dispatches \
            -X POST \
            -H "Accept: application/vnd.github+json" \
            -f event_type=brogz-released \
            -F "client_payload[version]=${TAG}"

  publish-release:
    name: Publish GitHub release
    needs: [build, publish-crate, notify-tap]
    runs-on: ubuntu-latest
    # Drop the draft flag only when every channel either succeeded or was
    # intentionally skipped (first release with `PUBLISH_PACKAGES` unset).
    # If any channel failed, the release stays in draft for clean re-runs.
    if: |
      always() && !cancelled() &&
      needs.build.result == 'success' &&
      (needs.publish-crate.result == 'success' || needs.publish-crate.result == 'skipped') &&
      (needs.notify-tap.result == 'success' || needs.notify-tap.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