name: Build Sidecar (Cloud) on: workflow_dispatch: inputs: tag: description: 'Sidecar release tag to build (e.g. sidecar-v1.0.5)' required: true jobs: build-cloud-linux: name: Build Cloud Sidecar (Linux) runs-on: ubuntu-latest env: PYTHON_VERSION: "3.11" RELEASE_TAG: ${{ inputs.tag }} steps: - name: Show tag run: echo "Building cloud sidecar for tag ${RELEASE_TAG}" - uses: actions/checkout@v4 with: ref: ${{ inputs.tag }} - name: Install uv run: | curl -LsSf https://astral.sh/uv/install.sh | sh echo "$HOME/.local/bin" >> $GITHUB_PATH - name: Set up Python run: uv python install ${{ env.PYTHON_VERSION }} - name: Install system dependencies run: | sudo apt-get update sudo apt-get install -y portaudio19-dev - name: Build cloud sidecar env: UV_NO_SOURCES: "1" run: | uv venv uv pip install pyinstaller numpy sounddevice fastapi uvicorn websockets pydantic requests pyyaml packaging .venv/bin/pyinstaller local-transcription-cloud.spec - name: Package run: | cd dist/local-transcription-backend && zip -r ../../sidecar-linux-x86_64-cloud.zip . - name: Upload to release env: BUILD_TOKEN: ${{ secrets.BUILD_TOKEN }} run: | sudo apt-get install -y jq REPO_API="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}" TAG="${RELEASE_TAG}" for i in $(seq 1 30); do RELEASE_ID=$(curl -s -H "Authorization: token ${BUILD_TOKEN}" \ "${REPO_API}/releases/tags/${TAG}" | jq -r '.id // empty') if [ -n "${RELEASE_ID}" ] && [ "${RELEASE_ID}" != "null" ]; then echo "Found release ${TAG} (ID: ${RELEASE_ID})" break fi echo "Attempt ${i}/30: waiting for release..." sleep 10 done if [ -z "${RELEASE_ID}" ] || [ "${RELEASE_ID}" = "null" ]; then echo "ERROR: Release not found"; exit 1 fi for file in sidecar-*-cloud.zip; do filename=$(basename "$file") ASSET_ID=$(curl -s -H "Authorization: token ${BUILD_TOKEN}" \ "${REPO_API}/releases/${RELEASE_ID}/assets" | jq -r ".[] | select(.name == \"${filename}\") | .id // empty") [ -n "${ASSET_ID}" ] && curl -s -X DELETE -H "Authorization: token ${BUILD_TOKEN}" "${REPO_API}/releases/${RELEASE_ID}/assets/${ASSET_ID}" curl -s -o /dev/null -w "Upload ${filename}: HTTP %{http_code}\n" -X POST \ -H "Authorization: token ${BUILD_TOKEN}" -H "Content-Type: application/octet-stream" \ -T "$file" "${REPO_API}/releases/${RELEASE_ID}/assets?name=${filename}" done build-cloud-windows: name: Build Cloud Sidecar (Windows) runs-on: windows-latest env: PYTHON_VERSION: "3.11" RELEASE_TAG: ${{ inputs.tag }} steps: - name: Show tag shell: powershell run: Write-Host "Building cloud sidecar for tag $env:RELEASE_TAG" - uses: actions/checkout@v4 with: ref: ${{ inputs.tag }} - name: Install uv shell: powershell run: | if (Get-Command uv -ErrorAction SilentlyContinue) { Write-Host "uv already installed" } else { irm https://astral.sh/uv/install.ps1 | iex $uvPaths = @("$env:USERPROFILE\.local\bin", "$env:USERPROFILE\.cargo\bin", "$env:LOCALAPPDATA\uv\bin") foreach ($p in $uvPaths) { if (Test-Path $p) { echo $p | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append } } } - name: Set up Python shell: powershell run: uv python install ${{ env.PYTHON_VERSION }} - name: Build cloud sidecar shell: powershell env: UV_NO_SOURCES: "1" run: | uv venv uv pip install pyinstaller numpy sounddevice fastapi uvicorn websockets pydantic requests pyyaml packaging .venv\Scripts\pyinstaller.exe local-transcription-cloud.spec - name: Package shell: powershell run: | if (-not (Get-Command 7z -ErrorAction SilentlyContinue)) { choco install 7zip -y } 7z a -tzip -mx=5 sidecar-windows-x86_64-cloud.zip .\dist\local-transcription-backend\* - name: Upload to release shell: powershell env: BUILD_TOKEN: ${{ secrets.BUILD_TOKEN }} run: | $REPO_API = "${{ github.server_url }}/api/v1/repos/${{ github.repository }}" $Headers = @{ "Authorization" = "token $env:BUILD_TOKEN" } $TAG = $env:RELEASE_TAG $RELEASE_ID = $null for ($i = 1; $i -le 30; $i++) { try { $release = Invoke-RestMethod -Uri "$REPO_API/releases/tags/$TAG" -Headers $Headers -ErrorAction Stop $RELEASE_ID = $release.id if ($RELEASE_ID) { Write-Host "Found release $TAG (ID: $RELEASE_ID)"; break } } catch {} Write-Host "Attempt ${i}/30: waiting..."; Start-Sleep -Seconds 10 } if (-not $RELEASE_ID) { Write-Host "ERROR: Release not found"; exit 1 } Get-ChildItem -Path . -Filter "sidecar-*-cloud.zip" | ForEach-Object { $fn = $_.Name; $enc = [System.Uri]::EscapeDataString($fn) try { $assets = Invoke-RestMethod -Uri "$REPO_API/releases/$RELEASE_ID/assets" -Headers $Headers $existing = $assets | Where-Object { $_.name -eq $fn } if ($existing) { Invoke-RestMethod -Uri "$REPO_API/releases/$RELEASE_ID/assets/$($existing.id)" -Method Delete -Headers $Headers } } catch {} curl.exe --fail -s -X POST -H "Authorization: token $env:BUILD_TOKEN" -H "Content-Type: application/octet-stream" -T "$($_.FullName)" "$REPO_API/releases/$RELEASE_ID/assets?name=$enc" Write-Host "Uploaded $fn" } build-cloud-macos: name: Build Cloud Sidecar (macOS) runs-on: macos-latest env: PYTHON_VERSION: "3.11" RELEASE_TAG: ${{ inputs.tag }} steps: - name: Show tag run: echo "Building cloud sidecar for tag ${RELEASE_TAG}" - uses: actions/checkout@v4 with: ref: ${{ inputs.tag }} - name: Install uv run: | curl -LsSf https://astral.sh/uv/install.sh | sh echo "$HOME/.local/bin" >> $GITHUB_PATH - name: Set up Python run: uv python install ${{ env.PYTHON_VERSION }} - name: Install system dependencies run: brew install portaudio - name: Build cloud sidecar env: UV_NO_SOURCES: "1" run: | uv venv uv pip install pyinstaller numpy sounddevice fastapi uvicorn websockets pydantic requests pyyaml packaging .venv/bin/pyinstaller local-transcription-cloud.spec - name: Package run: | cd dist/local-transcription-backend && zip -r ../../sidecar-macos-aarch64-cloud.zip . - name: Upload to release env: BUILD_TOKEN: ${{ secrets.BUILD_TOKEN }} run: | which jq || brew install jq REPO_API="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}" TAG="${RELEASE_TAG}" for i in $(seq 1 30); do RELEASE_ID=$(curl -s -H "Authorization: token ${BUILD_TOKEN}" \ "${REPO_API}/releases/tags/${TAG}" | jq -r '.id // empty') if [ -n "${RELEASE_ID}" ] && [ "${RELEASE_ID}" != "null" ]; then echo "Found release ${TAG} (ID: ${RELEASE_ID})" break fi echo "Attempt ${i}/30: waiting for release..." sleep 10 done if [ -z "${RELEASE_ID}" ] || [ "${RELEASE_ID}" = "null" ]; then echo "ERROR: Release not found"; exit 1 fi for file in sidecar-*-cloud.zip; do filename=$(basename "$file") ASSET_ID=$(curl -s -H "Authorization: token ${BUILD_TOKEN}" \ "${REPO_API}/releases/${RELEASE_ID}/assets" | jq -r ".[] | select(.name == \"${filename}\") | .id // empty") [ -n "${ASSET_ID}" ] && curl -s -X DELETE -H "Authorization: token ${BUILD_TOKEN}" "${REPO_API}/releases/${RELEASE_ID}/assets/${ASSET_ID}" curl -s -o /dev/null -w "Upload ${filename}: HTTP %{http_code}\n" -X POST \ -H "Authorization: token ${BUILD_TOKEN}" -H "Content-Type: application/octet-stream" \ -T "$file" "${REPO_API}/releases/${RELEASE_ID}/assets?name=${filename}" done