Merge pull request #38 from diazMelgarejo/claude/2026-07-30-pypi-pack… #11
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Desktop Release | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| build-macos: | |
| name: Desktop Build (macOS ${{ matrix.arch }}) | |
| runs-on: macos-15 | |
| env: | |
| PERISCOPE_TARGET_TRIPLE: ${{ matrix.target_triple }} | |
| CARGO_BUILD_TARGET: ${{ matrix.target_triple }} | |
| TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} | |
| TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} | |
| PERISCOPE_UPDATER_PUBKEY: ${{ secrets.PERISCOPE_UPDATER_PUBKEY != '' && secrets.PERISCOPE_UPDATER_PUBKEY || secrets.AGENTSVIEW_UPDATER_PUBKEY }} | |
| APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} | |
| APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} | |
| APPLE_API_KEY_CONTENT: ${{ secrets.APPLE_API_KEY_CONTENT }} | |
| APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - arch: aarch64 | |
| target_triple: aarch64-apple-darwin | |
| - arch: x86_64 | |
| target_triple: x86_64-apple-darwin | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| persist-credentials: false | |
| fetch-depth: 0 | |
| - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 | |
| with: | |
| go-version-file: go.mod | |
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | |
| with: | |
| node-version: "24" | |
| - name: Install Rust target | |
| run: rustup target add "$PERISCOPE_TARGET_TRIPLE" | |
| - name: Install desktop dependencies | |
| run: npm ci | |
| working-directory: desktop | |
| - name: Prepare sidecar | |
| env: | |
| PERISCOPE_VERSION: ${{ github.ref_name }} | |
| run: | | |
| export TAURI_ENV_TARGET_TRIPLE="$PERISCOPE_TARGET_TRIPLE" | |
| npm run prepare-sidecar | |
| working-directory: desktop | |
| - name: Patch updater config for current repository | |
| run: | | |
| sed -i.bak \ | |
| -e "s|wesm/agentsview|${GITHUB_REPOSITORY}|g" \ | |
| -e "s|NOT_SET|${PERISCOPE_UPDATER_PUBKEY}|g" \ | |
| desktop/src-tauri/tauri.conf.json | |
| rm -f desktop/src-tauri/tauri.conf.json.bak | |
| - name: Import signing certificate | |
| if: env.APPLE_CERTIFICATE != '' | |
| run: | | |
| KEYCHAIN_PATH="$RUNNER_TEMP/build.keychain" | |
| KEYCHAIN_PASS="$(openssl rand -base64 32)" | |
| security create-keychain -p "$KEYCHAIN_PASS" "$KEYCHAIN_PATH" | |
| security set-keychain-settings -lut 3600 "$KEYCHAIN_PATH" | |
| security unlock-keychain -p "$KEYCHAIN_PASS" "$KEYCHAIN_PATH" | |
| CERT_PATH="$RUNNER_TEMP/certificate.p12" | |
| echo "$APPLE_CERTIFICATE" | base64 -d > "$CERT_PATH" | |
| security import "$CERT_PATH" \ | |
| -k "$KEYCHAIN_PATH" \ | |
| -P "$APPLE_CERTIFICATE_PASSWORD" \ | |
| -T /usr/bin/codesign \ | |
| -T /usr/bin/security | |
| rm -f "$CERT_PATH" | |
| security set-key-partition-list \ | |
| -S apple-tool:,apple: \ | |
| -k "$KEYCHAIN_PASS" "$KEYCHAIN_PATH" | |
| security list-keychains -d user -s "$KEYCHAIN_PATH" login.keychain | |
| - name: Write API key | |
| if: env.APPLE_API_KEY != '' && env.APPLE_API_KEY_CONTENT != '' | |
| run: | | |
| KEY_DIR="$RUNNER_TEMP/apple-keys" | |
| mkdir -p "$KEY_DIR" | |
| echo "$APPLE_API_KEY_CONTENT" | base64 -d \ | |
| > "$KEY_DIR/AuthKey_${APPLE_API_KEY}.p8" | |
| echo "APPLE_API_KEY_PATH=$KEY_DIR/AuthKey_${APPLE_API_KEY}.p8" \ | |
| >> "$GITHUB_ENV" | |
| - name: Build DMG (signed + updater) | |
| if: env.TAURI_SIGNING_PRIVATE_KEY != '' | |
| env: | |
| APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} | |
| APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} | |
| run: npx tauri build --target "$PERISCOPE_TARGET_TRIPLE" | |
| working-directory: desktop | |
| - name: Build DMG (unsigned, no updater) | |
| if: env.TAURI_SIGNING_PRIVATE_KEY == '' | |
| run: | | |
| npx tauri build --target "$PERISCOPE_TARGET_TRIPLE" \ | |
| --config '{"plugins":{"updater":{"active":false}},"bundle":{"createUpdaterArtifacts":false}}' | |
| working-directory: desktop | |
| - name: Collect build artifacts | |
| run: | | |
| bundle_dir="desktop/src-tauri/target/${PERISCOPE_TARGET_TRIPLE}/release/bundle" | |
| mkdir -p staging | |
| cp "$bundle_dir"/dmg/*.dmg staging/ | |
| app_bundle="$(find "$bundle_dir"/macos -maxdepth 1 -name '*.app.tar.gz' -print -quit 2>/dev/null || true)" | |
| app_sig="$(find "$bundle_dir"/macos -maxdepth 1 -name '*.app.tar.gz.sig' -print -quit 2>/dev/null || true)" | |
| if [ -n "$app_bundle" ] && [ -f "$app_bundle" ]; then | |
| app_name="$(basename "$app_bundle")" | |
| cp "$app_bundle" "staging/${app_name%.app.tar.gz}_${{ matrix.arch }}.app.tar.gz" | |
| fi | |
| if [ -n "$app_sig" ] && [ -f "$app_sig" ]; then | |
| app_sig_name="$(basename "$app_sig")" | |
| cp "$app_sig" "staging/${app_sig_name%.app.tar.gz.sig}_${{ matrix.arch }}.app.tar.gz.sig" | |
| fi | |
| ls -la staging/ | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: periscope-desktop-macos-${{ matrix.arch }} | |
| path: staging/* | |
| if-no-files-found: error | |
| - name: Cleanup signing secrets | |
| if: always() && env.APPLE_CERTIFICATE != '' | |
| run: | | |
| KEYCHAIN_PATH="$RUNNER_TEMP/build.keychain" | |
| security delete-keychain "$KEYCHAIN_PATH" 2>/dev/null || true | |
| rm -rf "$RUNNER_TEMP/apple-keys" 2>/dev/null || true | |
| build-windows: | |
| name: Desktop Build (Windows) | |
| runs-on: windows-latest | |
| env: | |
| TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} | |
| TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} | |
| PERISCOPE_UPDATER_PUBKEY: ${{ secrets.PERISCOPE_UPDATER_PUBKEY != '' && secrets.PERISCOPE_UPDATER_PUBKEY || secrets.AGENTSVIEW_UPDATER_PUBKEY }} | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| persist-credentials: false | |
| fetch-depth: 0 | |
| - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 | |
| with: | |
| go-version-file: go.mod | |
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | |
| with: | |
| node-version: "24" | |
| - name: Setup MinGW | |
| id: setup_mingw | |
| continue-on-error: true | |
| uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2 | |
| with: | |
| msystem: MINGW64 | |
| update: false | |
| install: mingw-w64-x86_64-gcc | |
| path-type: inherit | |
| - name: Setup MinGW (retry on transient failure) | |
| if: steps.setup_mingw.outcome == 'failure' | |
| uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2 | |
| with: | |
| msystem: MINGW64 | |
| update: false | |
| install: mingw-w64-x86_64-gcc | |
| path-type: inherit | |
| - name: Install desktop dependencies | |
| run: npm ci | |
| working-directory: desktop | |
| - name: Prepare sidecar | |
| shell: bash | |
| env: | |
| PERISCOPE_VERSION: ${{ github.ref_name }} | |
| run: npm run prepare-sidecar | |
| working-directory: desktop | |
| - name: Patch updater config for current repository | |
| shell: bash | |
| run: | | |
| sed -i.bak \ | |
| -e "s|wesm/agentsview|${GITHUB_REPOSITORY}|g" \ | |
| -e "s|NOT_SET|${PERISCOPE_UPDATER_PUBKEY}|g" \ | |
| desktop/src-tauri/tauri.conf.json | |
| rm -f desktop/src-tauri/tauri.conf.json.bak | |
| - name: Build NSIS installer and updater bundle (signed) | |
| if: env.TAURI_SIGNING_PRIVATE_KEY != '' | |
| shell: bash | |
| run: npx tauri build --bundles nsis | |
| working-directory: desktop | |
| - name: Build NSIS installer (unsigned, no updater) | |
| if: env.TAURI_SIGNING_PRIVATE_KEY == '' | |
| shell: bash | |
| run: | | |
| npx tauri build --bundles nsis \ | |
| --config '{"plugins":{"updater":{"active":false}},"bundle":{"createUpdaterArtifacts":false}}' | |
| working-directory: desktop | |
| - name: Collect build artifacts | |
| shell: bash | |
| run: | | |
| mkdir -p staging | |
| cp desktop/src-tauri/target/release/bundle/nsis/*.exe staging/ | |
| find desktop/src-tauri/target/release/bundle/nsis/ -name '*.nsis.zip' -exec cp {} staging/ \; 2>/dev/null || true | |
| find desktop/src-tauri/target/release/bundle/nsis/ -name '*.nsis.zip.sig' -exec cp {} staging/ \; 2>/dev/null || true | |
| ls -la staging/ | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: periscope-desktop-windows | |
| path: staging/* | |
| if-no-files-found: error | |
| build-linux: | |
| name: Desktop Build (Linux ${{ matrix.arch }}) | |
| runs-on: ${{ matrix.os }} | |
| env: | |
| PERISCOPE_TARGET_TRIPLE: ${{ matrix.target_triple }} | |
| TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} | |
| TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} | |
| PERISCOPE_UPDATER_PUBKEY: ${{ secrets.PERISCOPE_UPDATER_PUBKEY != '' && secrets.PERISCOPE_UPDATER_PUBKEY || secrets.AGENTSVIEW_UPDATER_PUBKEY }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - arch: x86_64 | |
| os: ubuntu-22.04 | |
| target_triple: "" | |
| artifact_name: periscope-desktop-linux | |
| artifact_dir: desktop/src-tauri/target/release/bundle/appimage/ | |
| create_updater_artifacts: "true" | |
| - arch: arm64 | |
| os: ubuntu-22.04-arm | |
| target_triple: aarch64-unknown-linux-gnu | |
| artifact_name: periscope-desktop-linux-arm64 | |
| artifact_dir: desktop/src-tauri/target/aarch64-unknown-linux-gnu/release/bundle/appimage/ | |
| create_updater_artifacts: "false" | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| persist-credentials: false | |
| fetch-depth: 0 | |
| - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 | |
| with: | |
| go-version-file: go.mod | |
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | |
| with: | |
| node-version: "24" | |
| - name: Install Rust target | |
| if: env.PERISCOPE_TARGET_TRIPLE != '' | |
| run: rustup target add "$PERISCOPE_TARGET_TRIPLE" | |
| - name: Install Linux system dependencies | |
| run: | | |
| sudo apt-get update -q | |
| sudo apt-get install -y \ | |
| libwebkit2gtk-4.1-dev libgtk-3-dev \ | |
| libappindicator3-dev librsvg2-dev \ | |
| patchelf squashfs-tools xdg-utils | |
| - name: Install desktop dependencies | |
| run: npm ci | |
| working-directory: desktop | |
| - name: Prepare sidecar | |
| env: | |
| PERISCOPE_VERSION: ${{ github.ref_name }} | |
| run: | | |
| export TAURI_ENV_TARGET_TRIPLE="$PERISCOPE_TARGET_TRIPLE" | |
| npm run prepare-sidecar | |
| working-directory: desktop | |
| - name: Patch updater config for current repository | |
| run: | | |
| sed -i.bak \ | |
| -e "s|wesm/agentsview|${GITHUB_REPOSITORY}|g" \ | |
| -e "s|NOT_SET|${PERISCOPE_UPDATER_PUBKEY}|g" \ | |
| desktop/src-tauri/tauri.conf.json | |
| rm -f desktop/src-tauri/tauri.conf.json.bak | |
| - name: Build AppImage and updater bundle | |
| run: | | |
| build_cmd=(npx tauri build --bundles appimage) | |
| if [ -n "$PERISCOPE_TARGET_TRIPLE" ]; then | |
| build_cmd+=(--target "$PERISCOPE_TARGET_TRIPLE") | |
| fi | |
| if [ "${{ matrix.create_updater_artifacts }}" = "false" ] || [ -z "$TAURI_SIGNING_PRIVATE_KEY" ]; then | |
| build_cmd+=(--config '{"plugins":{"updater":{"active":false}},"bundle":{"createUpdaterArtifacts":false}}') | |
| fi | |
| "${build_cmd[@]}" | |
| bash scripts/repair-appimage-diricon.sh \ | |
| "../${{ matrix.artifact_dir }}"/*.AppImage | |
| working-directory: desktop | |
| - name: Collect build artifacts | |
| run: | | |
| mkdir -p staging | |
| cp "${{ matrix.artifact_dir }}"/*.AppImage staging/ | |
| if [ "${{ matrix.create_updater_artifacts }}" = "true" ]; then | |
| find "${{ matrix.artifact_dir }}" -name '*.AppImage.tar.gz' -exec cp {} staging/ \; 2>/dev/null || true | |
| find "${{ matrix.artifact_dir }}" -name '*.AppImage.tar.gz.sig' -exec cp {} staging/ \; 2>/dev/null || true | |
| fi | |
| ls -la staging/ | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: ${{ matrix.artifact_name }} | |
| path: staging/* | |
| if-no-files-found: error | |
| release: | |
| name: Upload Desktop Installers | |
| needs: [build-macos, build-windows, build-linux] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| path: artifacts | |
| pattern: periscope-desktop-* | |
| merge-multiple: true | |
| - name: Generate checksums | |
| run: | | |
| cd artifacts | |
| sha256sum *.dmg *.exe *.AppImage *.tar.gz *.nsis.zip > SHA256SUMS-desktop 2>/dev/null || true | |
| cat SHA256SUMS-desktop | |
| - name: Check for updater signatures | |
| id: check_sigs | |
| run: | | |
| HAS_MACOS_AARCH64=false | |
| HAS_MACOS_X86_64=false | |
| HAS_WINDOWS=false | |
| HAS_LINUX=false | |
| ls artifacts/ 2>/dev/null || true | |
| [ -n "$(find artifacts -name '*_aarch64.app.tar.gz.sig' 2>/dev/null)" ] && HAS_MACOS_AARCH64=true | |
| [ -n "$(find artifacts -name '*_x86_64.app.tar.gz.sig' 2>/dev/null)" ] && HAS_MACOS_X86_64=true | |
| [ -n "$(find artifacts -name '*.nsis.zip.sig' 2>/dev/null)" ] && HAS_WINDOWS=true | |
| [ -n "$(find artifacts -name '*.AppImage.tar.gz.sig' 2>/dev/null)" ] && HAS_LINUX=true | |
| if [ "$HAS_MACOS_AARCH64" = "true" ] && \ | |
| [ "$HAS_MACOS_X86_64" = "true" ] && \ | |
| [ "$HAS_WINDOWS" = "true" ] && \ | |
| [ "$HAS_LINUX" = "true" ]; then | |
| echo "has_sigs=true" >> "$GITHUB_OUTPUT" | |
| echo "All updater signatures present — will generate updater manifest." | |
| else | |
| echo "has_sigs=false" >> "$GITHUB_OUTPUT" | |
| echo "Missing one or more updater signatures — skipping updater manifest." | |
| echo " macOS aarch64: $HAS_MACOS_AARCH64" | |
| echo " macOS x86_64: $HAS_MACOS_X86_64" | |
| echo " Windows: $HAS_WINDOWS" | |
| echo " Linux: $HAS_LINUX" | |
| fi | |
| - name: Generate updater manifest | |
| if: steps.check_sigs.outputs.has_sigs == 'true' | |
| run: | | |
| set -euo pipefail | |
| VERSION="${GITHUB_REF_NAME#v}" | |
| DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" | |
| TAG="updater" | |
| MACOS_AARCH64_SIG="" | |
| MACOS_AARCH64_URL="" | |
| MACOS_X86_64_SIG="" | |
| MACOS_X86_64_URL="" | |
| WINDOWS_SIG="" | |
| WINDOWS_URL="" | |
| LINUX_SIG="" | |
| LINUX_URL="" | |
| for f in artifacts/*_aarch64.app.tar.gz.sig; do | |
| if [ -f "$f" ]; then | |
| MACOS_AARCH64_SIG="$(cat "$f")" | |
| MACOS_AARCH64_TARBALL="$(basename "${f%.sig}")" | |
| MACOS_AARCH64_URL="https://github.com/${GITHUB_REPOSITORY}/releases/download/${TAG}/${MACOS_AARCH64_TARBALL}" | |
| fi | |
| done | |
| for f in artifacts/*_x86_64.app.tar.gz.sig; do | |
| if [ -f "$f" ]; then | |
| MACOS_X86_64_SIG="$(cat "$f")" | |
| MACOS_X86_64_TARBALL="$(basename "${f%.sig}")" | |
| MACOS_X86_64_URL="https://github.com/${GITHUB_REPOSITORY}/releases/download/${TAG}/${MACOS_X86_64_TARBALL}" | |
| fi | |
| done | |
| for f in artifacts/*.nsis.zip.sig; do | |
| if [ -f "$f" ]; then | |
| WINDOWS_SIG="$(cat "$f")" | |
| NSIS_ZIP="$(basename "${f%.sig}")" | |
| WINDOWS_URL="https://github.com/${GITHUB_REPOSITORY}/releases/download/${TAG}/${NSIS_ZIP}" | |
| fi | |
| done | |
| for f in artifacts/*.AppImage.tar.gz.sig; do | |
| if [ -f "$f" ]; then | |
| LINUX_SIG="$(cat "$f")" | |
| LINUX_TARBALL="$(basename "${f%.sig}")" | |
| LINUX_URL="https://github.com/${GITHUB_REPOSITORY}/releases/download/${TAG}/${LINUX_TARBALL}" | |
| fi | |
| done | |
| cat > artifacts/latest.json << MANIFEST | |
| { | |
| "version": "${VERSION}", | |
| "pub_date": "${DATE}", | |
| "platforms": { | |
| "darwin-aarch64": { | |
| "url": "${MACOS_AARCH64_URL}", | |
| "signature": "${MACOS_AARCH64_SIG}" | |
| }, | |
| "darwin-x86_64": { | |
| "url": "${MACOS_X86_64_URL}", | |
| "signature": "${MACOS_X86_64_SIG}" | |
| }, | |
| "windows-x86_64": { | |
| "url": "${WINDOWS_URL}", | |
| "signature": "${WINDOWS_SIG}" | |
| }, | |
| "linux-x86_64": { | |
| "url": "${LINUX_URL}", | |
| "signature": "${LINUX_SIG}" | |
| } | |
| } | |
| } | |
| MANIFEST | |
| echo "Generated latest.json:" | |
| cat artifacts/latest.json | |
| - name: Upload installers to versioned release | |
| uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1 | |
| with: | |
| files: | | |
| artifacts/*.dmg | |
| artifacts/*.exe | |
| artifacts/*.AppImage | |
| artifacts/SHA256SUMS-desktop | |
| - name: Upload updater artifacts | |
| if: steps.check_sigs.outputs.has_sigs == 'true' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -euo pipefail | |
| if ! gh release view updater --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then | |
| gh release create updater \ | |
| --repo "$GITHUB_REPOSITORY" \ | |
| --title "Auto-Updater Artifacts" \ | |
| --notes "Internal release used by the desktop auto-updater. Do not delete." \ | |
| --prerelease | |
| fi | |
| gh release upload updater \ | |
| --repo "$GITHUB_REPOSITORY" \ | |
| --clobber \ | |
| artifacts/*.app.tar.gz \ | |
| artifacts/*.app.tar.gz.sig \ | |
| artifacts/*.nsis.zip \ | |
| artifacts/*.nsis.zip.sig \ | |
| artifacts/*.AppImage.tar.gz \ | |
| artifacts/*.AppImage.tar.gz.sig \ | |
| artifacts/latest.json |