piopulse 1.0.1

A terminal user interface (TUI) embedded debugger for serial debugging, telemetry plotting, dashboards, and ESP32 flashing.
name: Release

on:
  push:
    tags:
      - 'v*'

permissions:
  contents: write

jobs:
  build:
    name: Build - ${{ matrix.platform.os }} (${{ matrix.platform.target }})
    runs-on: ${{ matrix.platform.os }}
    strategy:
      fail-fast: false
      matrix:
        platform:
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
            binary_name: piopulse
            archive_name: piopulse-linux-x86_64.tar.gz
          - os: macos-latest
            target: x86_64-apple-darwin
            binary_name: piopulse
            archive_name: piopulse-macos-x86_64.tar.gz
          - os: macos-latest
            target: aarch64-apple-darwin
            binary_name: piopulse
            archive_name: piopulse-macos-aarch64.tar.gz
          - os: windows-latest
            target: x86_64-pc-windows-msvc
            binary_name: piopulse.exe
            archive_name: piopulse-windows-x86_64.zip

    steps:
      - name: Checkout Source
        uses: actions/checkout@v4

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

      - name: Cache Cargo Dependencies
        uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/bin/
            ~/.cargo/registry/index/
            ~/.cargo/registry/cache/
            ~/.cargo/git/db/
            target/
          key: ${{ runner.os }}-cargo-${{ matrix.platform.target }}-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            ${{ runner.os }}-cargo-${{ matrix.platform.target }}-

      - name: Install System Dependencies (Linux)
        if: matrix.platform.os == 'ubuntu-latest'
        run: |
          sudo apt-get update
          sudo apt-get install -y libudev-dev

      - name: Build Binary
        run: cargo build --release --target ${{ matrix.platform.target }}

      - name: Package Release Asset (Unix)
        if: matrix.platform.os != 'windows-latest'
        run: |
          cd target/${{ matrix.platform.target }}/release
          tar -czf ../../../${{ matrix.platform.archive_name }} ${{ matrix.platform.binary_name }}
          cd ../../../

      - name: Package Release Asset (Windows)
        if: matrix.platform.os == 'windows-latest'
        shell: pwsh
        run: |
          Compress-Archive -Path target\${{ matrix.platform.target }}\release\${{ matrix.platform.binary_name }} -DestinationPath ${{ matrix.platform.archive_name }}

      - name: Generate SHA256 Sum
        shell: bash
        run: |
          if [ "${{ runner.os }}" = "Windows" ]; then
            sha256sum ${{ matrix.platform.archive_name }} > ${{ matrix.platform.archive_name }}.sha256
          else
            shasum -a 256 ${{ matrix.platform.archive_name }} > ${{ matrix.platform.archive_name }}.sha256
          fi

      - name: Publish to Chocolatey
        if: matrix.platform.os == 'windows-latest'
        env:
          CHOCO_API_KEY: ${{ secrets.CHOCO_API_KEY }}
        shell: pwsh
        run: |
          if ($env:CHOCO_API_KEY) {
            $version = "${{ github.ref_name }}".SubString(1) # strip 'v'
            $sha = (Get-Content ${{ matrix.platform.archive_name }}.sha256).Split(" ")[0]
            
            # Update versions & checksums in templates
            (Get-Content packaging\choco\piopulse.nuspec) -replace '0.1.1', "$version" | Set-Content packaging\choco\piopulse.nuspec
            (Get-Content packaging\choco\tools\chocolateyinstall.ps1) `
              -replace 'v0.1.1', "${{ github.ref_name }}" `
              -replace 'INSERT_WINDOWS_X86_64_SHA256_HERE', "$sha" | Set-Content packaging\choco\tools\chocolateyinstall.ps1
              
            cd packaging\choco
            choco pack
            choco apikey --key $env:CHOCO_API_KEY --source https://push.chocolatey.org/
            choco push (Get-Item *.nupkg).FullName --source https://push.chocolatey.org/
          } else {
            Write-Host "CHOCO_API_KEY secret is not set, skipping Chocolatey publishing."
          }

      - name: Publish to crates.io
        if: matrix.platform.target == 'x86_64-unknown-linux-gnu'
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
        run: |
          if [ -n "$CARGO_REGISTRY_TOKEN" ]; then
            cargo publish --token $CARGO_REGISTRY_TOKEN
          else
            echo "CARGO_REGISTRY_TOKEN is not set, skipping crates.io publishing."
          fi

      - name: Upload Artifacts
        uses: actions/upload-artifact@v4
        with:
          name: build-${{ matrix.platform.target }}
          path: |
            ${{ matrix.platform.archive_name }}
            ${{ matrix.platform.archive_name }}.sha256

  release:
    name: Create GitHub Release
    needs: build
    runs-on: ubuntu-latest
    outputs:
      release_version: ${{ steps.get_version.outputs.version }}
    steps:
      - name: Checkout Source
        uses: actions/checkout@v4

      - name: Get clean version
        id: get_version
        run: |
          VERSION=${{ github.ref_name }}
          echo "version=${VERSION#v}" >> $GITHUB_OUTPUT

      - name: Download Build Artifacts
        uses: actions/download-artifact@v4
        with:
          path: artifacts
          merge-multiple: true

      - name: Generate Checksums file
        run: |
          cat artifacts/*.sha256 > checksums.txt
          mv checksums.txt artifacts/

      - name: Create Release
        uses: softprops/action-gh-release@v2
        with:
          files: |
            artifacts/*
          draft: false
          prerelease: false
          generate_release_notes: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  publish:
    name: Publish to Package Managers
    needs: [build, release]
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Source
        uses: actions/checkout@v4

      - name: Download Build Artifacts
        uses: actions/download-artifact@v4
        with:
          path: artifacts
          merge-multiple: true

      - name: Publish to Homebrew & Scoop
        env:
          GH_PAT: ${{ secrets.GH_PAT }}
        run: |
          if [ -n "$GH_PAT" ]; then
            git config --global user.name "github-actions[bot]"
            git config --global user.email "github-actions[bot]@users.noreply.github.com"
            
            VERSION=${{ needs.release.outputs.release_version }}
            OWNER=${{ github.repository_owner }}
            
            # Read SHA256 checksums
            SHA_MAC_ARM=$(cat artifacts/piopulse-macos-aarch64.tar.gz.sha256 | awk '{print $1}')
            SHA_MAC_X64=$(cat artifacts/piopulse-macos-x86_64.tar.gz.sha256 | awk '{print $1}')
            SHA_LINUX_X64=$(cat artifacts/piopulse-linux-x86_64.tar.gz.sha256 | awk '{print $1}')
            SHA_WIN_X64=$(cat artifacts/piopulse-windows-x86_64.zip.sha256 | awk '{print $1}')

            repo_url() {
              echo "https://x-access-token:${GH_PAT}@github.com/${OWNER}/$1.git"
            }

            repo_exists() {
              curl --silent --fail -I -H "Authorization: Bearer ${GH_PAT}" "https://api.github.com/repos/${OWNER}/$1" >/dev/null 2>&1
            }
            
            # 1. Update Homebrew tap
            if repo_exists "homebrew-tap"; then
              git clone "$(repo_url "homebrew-tap")"
              cd homebrew-tap
              mkdir -p Formula
              cat ../packaging/brew/piopulse.rb | \
                sed "s/version \"0.1.1\"/version \"${VERSION}\"/g" | \
                sed "s/INSERT_MACOS_AARCH64_SHA256_HERE/${SHA_MAC_ARM}/g" | \
                sed "s/INSERT_MACOS_X86_64_SHA256_HERE/${SHA_MAC_X64}/g" | \
                sed "s/INSERT_LINUX_X86_64_SHA256_HERE/${SHA_LINUX_X64}/g" > Formula/piopulse.rb
              git add Formula/piopulse.rb
              if ! git diff --cached --quiet; then
                git commit -m "bump: piopulse to v${VERSION}"
                git push origin HEAD
              else
                echo "Homebrew formula already up to date."
              fi
              cd ..
            else
              echo "::warning::Skipping Homebrew publish: ${OWNER}/homebrew-tap does not exist or GH_PAT cannot access it."
            fi
            
            # 2. Update Scoop bucket
            if repo_exists "scoop-bucket"; then
              git clone "$(repo_url "scoop-bucket")"
              cd scoop-bucket
              cat ../packaging/scoop/piopulse.json | \
                sed "s/\"version\": \"0.1.1\"/\"version\": \"${VERSION}\"/g" | \
                sed "s/releases\/download\/v0.1.1/releases\/download\/${{ github.ref_name }}/g" | \
                sed "s/INSERT_WINDOWS_X86_64_SHA256_HERE/${SHA_WIN_X64}/g" > piopulse.json
              git add piopulse.json
              if ! git diff --cached --quiet; then
                git commit -m "bump: piopulse to v${VERSION}"
                git push origin HEAD
              else
                echo "Scoop manifest already up to date."
              fi
              cd ..
            else
              echo "::warning::Skipping Scoop publish: ${OWNER}/scoop-bucket does not exist or GH_PAT cannot access it."
            fi
          else
            echo "GH_PAT secret is not set, skipping Homebrew and Scoop publishing."
          fi

      - name: Publish to AUR
        env:
          AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
        run: |
          if [ -n "$AUR_SSH_PRIVATE_KEY" ]; then
            VERSION=${{ needs.release.outputs.release_version }}
            SHA_LINUX_X64=$(cat artifacts/piopulse-linux-x86_64.tar.gz.sha256 | awk '{print $1}')
            
            mkdir -p ~/.ssh
            echo "${AUR_SSH_PRIVATE_KEY}" > ~/.ssh/id_rsa
            chmod 600 ~/.ssh/id_rsa
            ssh-keyscan -t ed25519 aur.archlinux.org >> ~/.ssh/known_hosts
            
            git clone ssh://aur@aur.archlinux.org/piopulse-bin.git
            cd piopulse-bin
            
            cat ../packaging/aur/PKGBUILD | \
              sed "s/pkgver=0.1.1/pkgver=${VERSION}/g" | \
              sed "s/INSERT_LINUX_X86_64_SHA256_HERE/${SHA_LINUX_X64}/g" > PKGBUILD
              
            # Generate AUR SRCINFO
            docker run --rm -v "$PWD:/pkg" -w /pkg archlinux bash -c "pacman -Sy --noconfirm base-devel && sudo -u nobody makepkg --printsrcinfo > .SRCINFO" || \
            echo "Failed to generate .SRCINFO via docker, updating PKGBUILD directly. Please verify .SRCINFO on next local run."
            
            git config --global user.name "github-actions[bot]"
            git config --global user.email "github-actions[bot]@users.noreply.github.com"
            git add PKGBUILD .SRCINFO
            git commit -m "bump: release v${VERSION}" || true
            git push origin master || true
          else
            echo "AUR_SSH_PRIVATE_KEY secret is not set, skipping AUR publishing."
          fi

      - name: Publish to WinGet
        uses: vedantmgoyal9/winget-releaser@v2
        if: env.GH_PAT != ''
        with:
          identifier: Wang-Yang.PioPulse
          version: ${{ needs.release.outputs.release_version }}
          release-tag: ${{ github.ref_name }}
          git-token: ${{ secrets.GH_PAT }}