syswatch 0.6.1

Single-host, read-only system diagnostics TUI. Twelve tabs covering CPU, memory, disks, processes, GPU, power, services, network, plus a Timeline scrubber and an Insights anomaly engine. Sibling to netwatch.
name: Release

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

permissions:
  contents: write

jobs:
  build:
    strategy:
      matrix:
        include:
          - target: x86_64-unknown-linux-gnu
            os: ubuntu-latest
            name: syswatch-linux-x86_64
          - target: aarch64-unknown-linux-gnu
            os: ubuntu-latest
            name: syswatch-linux-aarch64
          - target: x86_64-unknown-linux-musl
            os: ubuntu-latest
            name: syswatch-linux-x86_64-static
          - target: aarch64-unknown-linux-musl
            os: ubuntu-latest
            name: syswatch-linux-aarch64-static
          - target: x86_64-apple-darwin
            os: macos-latest
            name: syswatch-macos-x86_64
          - target: aarch64-apple-darwin
            os: macos-latest
            name: syswatch-macos-aarch64

    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}
      - uses: Swatinem/rust-cache@v2

      - name: Install aarch64 cross-compiler (Linux aarch64 glibc)
        if: matrix.target == 'aarch64-unknown-linux-gnu'
        run: |
          sudo apt-get update
          sudo apt-get install -y gcc-aarch64-linux-gnu
          echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV

      - name: Install zig + cargo-zigbuild (Linux musl)
        if: contains(matrix.target, 'linux-musl')
        run: |
          python3 -m pip install --user ziglang cargo-zigbuild
          echo "$HOME/.local/bin" >> $GITHUB_PATH

      - name: Build
        shell: bash
        run: |
          if [[ "${{ matrix.target }}" == *"linux-musl" ]]; then
            cargo zigbuild --release --target ${{ matrix.target }}
          else
            cargo build --release --target ${{ matrix.target }}
          fi

      - name: Package
        run: |
          cp target/${{ matrix.target }}/release/syswatch ${{ matrix.name }}
          tar czf ${{ matrix.name }}.tar.gz ${{ matrix.name }}

      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: ${{ matrix.name }}
          path: ${{ matrix.name }}.tar.gz

  release:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/download-artifact@v4
        with:
          merge-multiple: true

      - name: Create Release
        uses: softprops/action-gh-release@v2
        with:
          generate_release_notes: true
          files: '*.tar.gz'

  publish:
    needs: release
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2

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

  homebrew:
    needs: release
    # Skip homebrew formula update for pre-release tags (e.g. v0.2.0-rc.1).
    # Brew has no pre-release concept, so any formula push reaches every
    # `brew upgrade` user.
    if: ${{ !contains(github.ref_name, '-') }}
    runs-on: ubuntu-latest
    steps:
      - name: Compute sha256 for each release asset
        id: sha
        run: |
          VERSION="${GITHUB_REF_NAME#v}"
          echo "version=${VERSION}" >> $GITHUB_OUTPUT
          for asset in \
            syswatch-macos-x86_64.tar.gz \
            syswatch-macos-aarch64.tar.gz \
            syswatch-linux-x86_64-static.tar.gz \
            syswatch-linux-aarch64-static.tar.gz
          do
            url="https://github.com/${{ github.repository }}/releases/download/${GITHUB_REF_NAME}/${asset}"
            for i in 1 2 3 4 5; do
              if curl -sLf "${url}" -o "${asset}"; then break; fi
              echo "Download ${asset} attempt $i failed, retrying..."; sleep 5
            done
            test -s "${asset}" || { echo "Failed to download ${asset}"; exit 1; }
            hash=$(sha256sum "${asset}" | awk '{print $1}')
            key=$(echo "${asset}" | sed -E 's/syswatch-//; s/\.tar\.gz$//; s/-/_/g')
            echo "sha_${key}=${hash}" >> $GITHUB_OUTPUT
          done

      - name: Check out homebrew-tap
        uses: actions/checkout@v4
        with:
          repository: matthart1983/homebrew-tap
          token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
          path: homebrew-tap

      - name: Regenerate Formula
        env:
          VERSION: ${{ steps.sha.outputs.version }}
          SHA_MACOS_X86_64: ${{ steps.sha.outputs.sha_macos_x86_64 }}
          SHA_MACOS_AARCH64: ${{ steps.sha.outputs.sha_macos_aarch64 }}
          SHA_LINUX_X86_64: ${{ steps.sha.outputs.sha_linux_x86_64_static }}
          SHA_LINUX_AARCH64: ${{ steps.sha.outputs.sha_linux_aarch64_static }}
        run: |
          cat > homebrew-tap/Formula/syswatch.rb <<EOF
          class Syswatch < Formula
            desc "Single-host system diagnostics TUI — sibling to netwatch"
            homepage "https://github.com/matthart1983/syswatch"
            version "${VERSION}"
            license "MIT"

            on_macos do
              on_arm do
                url "https://github.com/matthart1983/syswatch/releases/download/v${VERSION}/syswatch-macos-aarch64.tar.gz"
                sha256 "${SHA_MACOS_AARCH64}"

                def install
                  bin.install "syswatch-macos-aarch64" => "syswatch"
                end
              end
              on_intel do
                url "https://github.com/matthart1983/syswatch/releases/download/v${VERSION}/syswatch-macos-x86_64.tar.gz"
                sha256 "${SHA_MACOS_X86_64}"

                def install
                  bin.install "syswatch-macos-x86_64" => "syswatch"
                end
              end
            end

            on_linux do
              on_arm do
                url "https://github.com/matthart1983/syswatch/releases/download/v${VERSION}/syswatch-linux-aarch64-static.tar.gz"
                sha256 "${SHA_LINUX_AARCH64}"

                def install
                  bin.install "syswatch-linux-aarch64-static" => "syswatch"
                end
              end
              on_intel do
                url "https://github.com/matthart1983/syswatch/releases/download/v${VERSION}/syswatch-linux-x86_64-static.tar.gz"
                sha256 "${SHA_LINUX_X86_64}"

                def install
                  bin.install "syswatch-linux-x86_64-static" => "syswatch"
                end
              end
            end

            test do
              assert_match "syswatch", shell_output("#{bin}/syswatch --help 2>&1", 1)
            end
          end
          EOF

      - name: Commit and push formula
        run: |
          cd homebrew-tap
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          # Stage first, then diff against HEAD. `git diff` (unstaged)
          # ignores untracked files, so a brand-new formula would
          # falsely report "no change" on the first release.
          git add Formula/syswatch.rb
          if git diff --cached --quiet -- Formula/syswatch.rb; then
            echo "Formula already up to date"; exit 0
          fi
          git commit -m "syswatch ${{ steps.sha.outputs.version }}: auto-update formula"
          git push origin main