From 25ceddb1610441d8847bf2ebc08b6cf8f1c9d5ea Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Tue, 21 Apr 2026 11:22:04 -0400 Subject: [PATCH] add a goose2 signed release flow --- .github/workflows/bundle-goose2.yml | 81 +++++++++- .github/workflows/release-goose2.yml | 199 +++++++++++++++++++++++++ ui/goose2/src-tauri/entitlements.plist | 22 +++ ui/goose2/src-tauri/tauri.conf.json | 6 +- 4 files changed, 303 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/release-goose2.yml create mode 100644 ui/goose2/src-tauri/entitlements.plist diff --git a/.github/workflows/bundle-goose2.yml b/.github/workflows/bundle-goose2.yml index 1784e7bde6..9269eeac10 100644 --- a/.github/workflows/bundle-goose2.yml +++ b/.github/workflows/bundle-goose2.yml @@ -38,6 +38,11 @@ on: required: false default: "" type: string + windows-signing: + description: "Whether to perform Windows signing via Azure Trusted Signing" + required: false + default: false + type: boolean cli-run-id: description: > Run ID of a prior build-cli.yml workflow run to download the goose @@ -125,7 +130,7 @@ jobs: - name: Cache Rust dependencies if: inputs.cli-run-id == '' - uses: Swatinem/rust-cache@v2 + uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 with: key: goose2-macos-arm64 @@ -291,7 +296,7 @@ jobs: - name: Cache Rust dependencies if: inputs.cli-run-id == '' - uses: Swatinem/rust-cache@v2 + uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 with: key: goose2-macos-x86_64 @@ -477,7 +482,7 @@ jobs: - name: Cache Rust dependencies if: inputs.cli-run-id == '' - uses: Swatinem/rust-cache@v2 + uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 with: key: goose2-linux-x86_64 @@ -564,6 +569,7 @@ jobs: runs-on: windows-latest timeout-minutes: 60 permissions: + id-token: write contents: read actions: read steps: @@ -621,7 +627,7 @@ jobs: - name: Cache Rust dependencies if: inputs.cli-run-id == '' - uses: Swatinem/rust-cache@v2 + uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 with: key: goose2-windows-x86_64 @@ -697,3 +703,70 @@ jobs: name: Goose2-windows-x64-msi path: ui/goose2/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/msi/*.msi if-no-files-found: warn + + sign-windows: + name: "Sign Windows installers" + needs: bundle-windows + if: inputs.windows-signing + runs-on: windows-latest + environment: signing + permissions: + id-token: write + contents: read + actions: read + steps: + - name: Download NSIS installer + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 + with: + name: Goose2-windows-x64-nsis + path: unsigned/nsis + + - name: Download MSI installer + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 + with: + name: Goose2-windows-x64-msi + path: unsigned/msi + + - name: Azure login + uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Sign Windows installers with Azure Trusted Signing + uses: azure/trusted-signing-action@db7a3a6bd3912025c705162fb7475389f5b69ec6 # v1 + with: + endpoint: ${{ secrets.AZURE_SIGNING_ENDPOINT }} + trusted-signing-account-name: ${{ secrets.AZURE_SIGNING_ACCOUNT_NAME }} + certificate-profile-name: ${{ secrets.AZURE_CERTIFICATE_PROFILE_NAME }} + files-folder: ${{ github.workspace }}/unsigned + files-folder-filter: exe,msi + files-folder-recurse: true + + - name: Verify signed installers + shell: pwsh + run: | + $files = Get-ChildItem -Path unsigned -Recurse -Include *.exe,*.msi + foreach ($file in $files) { + Write-Output "Verifying signature: $($file.FullName)" + $sig = Get-AuthenticodeSignature $file.FullName + if ($sig.Status -ne "Valid") { + throw "Signature invalid for $($file.Name): $($sig.Status)" + } + Write-Output "✅ Signature valid: $($file.Name)" + } + + - name: Upload signed NSIS installer + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: Goose2-windows-x64-nsis-signed + path: unsigned/nsis/*.exe + if-no-files-found: error + + - name: Upload signed MSI installer + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: Goose2-windows-x64-msi-signed + path: unsigned/msi/*.msi + if-no-files-found: error diff --git a/.github/workflows/release-goose2.yml b/.github/workflows/release-goose2.yml new file mode 100644 index 0000000000..e56b1732a8 --- /dev/null +++ b/.github/workflows/release-goose2.yml @@ -0,0 +1,199 @@ +# Release workflow for Goose 2 (Tauri desktop app). +# Triggered by pushing a tag matching v2.*. +# +# Pipeline: +# 1. Build CLI binaries for all platforms (reuses build-cli.yml) +# 2. Bundle Goose 2 desktop app with code signing: +# - macOS: Apple codesign + notarization via keychain & APPLE_* env vars +# - Windows: Azure Trusted Signing (post-build) +# - Linux: unsigned (standard for Linux packages) +# 3. Create a versioned GitHub Release with all artifacts +on: + push: + paths-ignore: + - "documentation/**" + tags: + - "v2.*" + +name: "Release Goose 2" + +permissions: + id-token: write # Sigstore OIDC signing + Azure OIDC (Windows signing) + contents: write # Creating releases + actions/checkout + actions: read # Downloading artifacts across workflow runs + attestations: write # SLSA build provenance attestations + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + # ------------------------------------ + # 1) Derive version from the tag + # ------------------------------------ + prepare-version: + name: Prepare Version + runs-on: ubuntu-latest + outputs: + version: ${{ steps.set-version.outputs.version }} + steps: + - name: Extract version from tag + id: set-version + run: | + # Strip the leading "v" from the tag (e.g. v2.0.0 → 2.0.0) + VERSION="${GITHUB_REF_NAME#v}" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "Release version: $VERSION" + + # ------------------------------------ + # 2) Build CLI for all platforms + # ------------------------------------ + build-cli: + needs: [prepare-version] + uses: ./.github/workflows/build-cli.yml + with: + version: ${{ needs.prepare-version.outputs.version }} + + # ------------------------------------ + # 3) Bundle Goose 2 desktop app + # (macOS signed + notarized, Windows signed, Linux unsigned) + # ------------------------------------ + bundle-goose2: + needs: [prepare-version, build-cli] + uses: ./.github/workflows/bundle-goose2.yml + permissions: + id-token: write + contents: read + actions: read + with: + version: ${{ needs.prepare-version.outputs.version }} + signing: true + windows-signing: true + environment: signing + cli-run-id: ${{ github.run_id }} + secrets: inherit + + # ------------------------------------ + # 4) Upload install script + # ------------------------------------ + install-script: + name: Upload Install Script + runs-on: ubuntu-latest + needs: [build-cli] + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: download_cli.sh + path: download_cli.sh + + # ------------------------------------ + # 5) Create GitHub Release + # ------------------------------------ + release: + name: Release + runs-on: ubuntu-latest + needs: [prepare-version, build-cli, install-script, bundle-goose2] + permissions: + contents: write + id-token: write + actions: read + attestations: write + steps: + - name: Download CLI artifacts + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 + with: + pattern: goose-* + merge-multiple: true + path: release + + - name: Download install script + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 + with: + name: download_cli.sh + path: release + + - name: Download macOS ARM64 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 + with: + name: Goose2-darwin-arm64 + path: release + + - name: Download macOS Intel + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 + with: + name: Goose2-darwin-x64 + path: release + + - name: Download Linux .deb + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 + with: + name: Goose2-linux-x64-deb + path: release + continue-on-error: true + + - name: Download Linux AppImage + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 + with: + name: Goose2-linux-x64-appimage + path: release + continue-on-error: true + + - name: Download Linux RPM + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 + with: + name: Goose2-linux-x64-rpm + path: release + continue-on-error: true + + - name: Download signed Windows NSIS installer + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 + with: + name: Goose2-windows-x64-nsis-signed + path: release + + - name: Download signed Windows MSI installer + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 + with: + name: Goose2-windows-x64-msi-signed + path: release + + - name: List downloaded artifacts + run: | + echo "=== All release artifacts ===" + find release -type f | sort + + - name: Attest build provenance + uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3 + with: + subject-path: | + release/goose-*.tar.bz2 + release/goose-*.tar.gz + release/goose-*.zip + release/*.dmg + release/*.exe + release/*.msi + release/*.deb + release/*.rpm + release/*.AppImage + release/download_cli.sh + + # Create/update the versioned pre-release (e.g. v2.0.0) + - name: Release versioned + uses: ncipollo/release-action@339a81892b84b4eeb0f6e744e4574d79d0d9b8dd # v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + prerelease: true + artifacts: | + release/goose-*.tar.bz2 + release/goose-*.tar.gz + release/goose-*.zip + release/*.dmg + release/*.exe + release/*.msi + release/*.deb + release/*.rpm + release/*.AppImage + release/download_cli.sh + allowUpdates: true + omitBody: true diff --git a/ui/goose2/src-tauri/entitlements.plist b/ui/goose2/src-tauri/entitlements.plist new file mode 100644 index 0000000000..a5f008eaaa --- /dev/null +++ b/ui/goose2/src-tauri/entitlements.plist @@ -0,0 +1,22 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.allow-dyld-environment-variables + + com.apple.security.network.client + + com.apple.security.network.server + + com.apple.security.device.audio-input + + com.apple.security.files.user-selected.read-write + + com.apple.security.files.downloads.read-write + + + diff --git a/ui/goose2/src-tauri/tauri.conf.json b/ui/goose2/src-tauri/tauri.conf.json index 11f0ef4aef..8b555dad1a 100644 --- a/ui/goose2/src-tauri/tauri.conf.json +++ b/ui/goose2/src-tauri/tauri.conf.json @@ -44,6 +44,10 @@ "icons/icon.icns", "icons/icon.ico" ], - "externalBin": ["../../../target/release/goose"] + "externalBin": ["../../../target/release/goose"], + "macOS": { + "entitlements": "entitlements.plist", + "hardenedRuntime": true + } } }