name: release # Build + publish installers for every platform and generate the updater's # latest.json. Triggered by pushing a `silo-v*` tag (e.g. `silo-v0.1.0`), or # manually via the Actions tab. on: workflow_dispatch: push: tags: - "silo-v*" permissions: contents: write jobs: release: strategy: fail-fast: false matrix: include: - platform: macos-latest # Apple Silicon args: "--target aarch64-apple-darwin" - platform: macos-latest # Intel args: "--target x86_64-apple-darwin" # Linux + Windows builds are enabled for testing but may not be fully # functional yet — treat them as experimental/pre-release. - platform: ubuntu-22.04 args: "" - platform: windows-latest args: "" runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@v6 - name: install Linux dependencies if: matrix.platform == 'ubuntu-22.04' run: | sudo apt-get update sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf - name: setup pnpm uses: pnpm/action-setup@v6 - name: setup node uses: actions/setup-node@v6 with: node-version: lts/* cache: pnpm - name: install Rust stable uses: dtolnay/rust-toolchain@stable with: targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }} - name: Rust cache uses: swatinem/rust-cache@v2 with: workspaces: "./apps/desktop/src-tauri -> target" - name: install frontend dependencies run: pnpm install --frozen-lockfile # Materialize the App Store Connect API key (.p8) that notarization uses. # Stored as a base64 secret (single line, newline-safe) and decoded to a # file that APPLE_API_KEY_PATH points at. - name: Write App Store Connect API key if: matrix.platform == 'macos-latest' run: echo "${{ secrets.APPLE_API_KEY_P8_BASE64 }}" | base64 --decode > "${{ runner.temp }}/apple_api_key.p8" # Release notes = the latest CHANGELOG section (header line stripped) plus a # short install footer, so the GitHub release shows real notes instead of a # placeholder. Works for both manual-tag and release-please-driven runs. - name: Build release notes from CHANGELOG id: notes shell: bash run: | { echo "body< **Linux and Windows builds are experimental.** They are provided for testing and may not work correctly. Please report issues on GitHub." echo "SILO_EOF" } >> "$GITHUB_OUTPUT" - uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Updater signing (minisign) — NOT Apple/Windows code signing. TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} # macOS code signing (Developer ID). Unset → build is left unsigned. APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} # base64 of the Developer ID Application .p12 APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} # .p12 export passphrase APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} # "Developer ID Application: Name (TEAMID)" # Notarization via App Store Connect API key — the app-specific-password # endpoint (/notary/v2/asp) returns persistent HTTP 500s; the API-key # path is the reliable one (see tauri-action / Apple notarytool issues). APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} # issuer UUID APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} # key id (10-char) APPLE_API_KEY_PATH: ${{ runner.temp }}/apple_api_key.p8 # decoded above APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} # 10-char team id # The frontend build (tsc + vite bundling Monaco/dockview/xterm) blows # past Node's default ~2GB heap; give it room so beforeBuildCommand # doesn't OOM. Inherited by the spawned `pnpm run build`. NODE_OPTIONS: --max-old-space-size=6144 with: # The Tauri app lives in apps/desktop (monorepo); tauri-action runs the # build there and its beforeBuildCommand (pnpm run build) resolves the # workspace packages via the symlinks pnpm install created at the root. projectPath: apps/desktop tagName: silo-v__VERSION__ releaseName: "Silo v__VERSION__" releaseBody: ${{ steps.notes.outputs.body }} releaseDraft: true prerelease: false args: ${{ matrix.args }} # tauri-action notarizes + staples the .app but ships an UN-notarized .dmg, # so a freshly-downloaded dmg trips Gatekeeper on mount. Notarize + staple # the dmg itself (same API key), then replace the release asset with the # stapled copy. The .app inside is already notarized; this tickets the dmg. - name: Notarize + staple the DMG if: matrix.platform == 'macos-latest' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -euo pipefail DMG=$(find apps/desktop/src-tauri/target -name "Silo_*.dmg" | head -1) echo "dmg: $DMG" xcrun notarytool submit "$DMG" \ --key "${{ runner.temp }}/apple_api_key.p8" \ --key-id "${{ secrets.APPLE_API_KEY }}" \ --issuer "${{ secrets.APPLE_API_ISSUER }}" \ --wait xcrun stapler staple "$DMG" xcrun stapler validate "$DMG" TAG="silo-v$(node -p "require('./apps/desktop/package.json').version")" # Retry up to 3 times — parallel matrix jobs uploading to the same # draft release can hit transient 404s on GitHub's uploads endpoint. for attempt in 1 2 3; do gh release upload "$TAG" "$DMG" --clobber -R silo-code/silo && break if [ "$attempt" -lt 3 ]; then echo "Upload attempt $attempt failed, retrying in 15s..." sleep 15 else echo "Upload failed after 3 attempts" exit 1 fi done