name: Desktop Release on: push: tags: - "v*" - "desktop-v*" - "desktop-macos-v*" - "desktop-linux-v*" - "desktop-windows-v*" workflow_dispatch: inputs: tag: description: "Existing tag to build (e.g. v0.1.0)" required: true type: string platform: description: "Optional desktop platform to build." required: false default: "all" type: choice options: - all - macos - linux - windows concurrency: group: desktop-release-${{ github.ref }} cancel-in-progress: false env: SOURCE_TAG: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref_name }} jobs: publish-macos: if: ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.platform == 'all' || github.event.inputs.platform == 'macos')) || (github.event_name == 'push' && (startsWith(github.ref_name, 'v') || startsWith(github.ref_name, 'desktop-v') || startsWith(github.ref_name, 'desktop-macos-v'))) }} strategy: fail-fast: false matrix: include: - runner: macos-14 rust_target: aarch64-apple-darwin - runner: macos-15-intel rust_target: x86_64-apple-darwin permissions: contents: write packages: read runs-on: ${{ matrix.runner }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref }} - name: Resolve release metadata shell: bash run: | set -euo pipefail source_tag="${SOURCE_TAG}" release_tag="$source_tag" for prefix in desktop-windows-v desktop-linux-v desktop-macos-v desktop-v; do if [[ "$source_tag" == ${prefix}* ]]; then release_tag="v${source_tag#${prefix}}" break fi done echo "RELEASE_TAG=$release_tag" >> "$GITHUB_ENV" version="${release_tag#v}" echo "DESKTOP_VERSION=$version" >> "$GITHUB_ENV" if [[ "$source_tag" == *gha-smoke* ]]; then echo "IS_SMOKE_TAG=true" >> "$GITHUB_ENV" else echo "IS_SMOKE_TAG=false" >> "$GITHUB_ENV" fi - name: Set desktop version from tag shell: bash run: | node <<'NODE' const fs = require('node:fs'); const path = require('node:path'); const version = process.env.DESKTOP_VERSION; if (!version) throw new Error('DESKTOP_VERSION env var is missing'); console.log(`Setting desktop version to ${version}`); const tauriConfPath = path.join('packages', 'desktop', 'src-tauri', 'tauri.conf.json'); const tauriConfText = fs.readFileSync(tauriConfPath, 'utf8'); const tauriRe = /("version"\s*:\s*")([^"]+)(")/; if (!tauriRe.test(tauriConfText)) throw new Error('Failed to find version in tauri.conf.json'); fs.writeFileSync(tauriConfPath, tauriConfText.replace(tauriRe, `$1${version}$3`)); const cargoTomlPath = path.join('packages', 'desktop', 'src-tauri', 'Cargo.toml'); const lines = fs.readFileSync(cargoTomlPath, 'utf8').split(/\r?\n/); let inPackage = false, updated = false; const result = lines.map((line) => { if (/^\[package\]\s*$/.test(line)) inPackage = true; else if (inPackage && /^\[/.test(line)) inPackage = false; if (inPackage && /^version\s*=\s*".*"\s*$/.test(line)) { updated = true; return `version = "${version}"`; } return line; }); if (!updated) throw new Error('Failed to update Cargo.toml version'); fs.writeFileSync(cargoTomlPath, result.join('\n') + '\n'); NODE - name: Setup Node uses: actions/setup-node@v4 with: node-version: "22" cache: "npm" cache-dependency-path: package-lock.json registry-url: "https://npm.pkg.github.com" scope: "@boudra" - name: Install Rust stable uses: dtolnay/rust-toolchain@stable with: targets: ${{ matrix.rust_target }} - name: Restore Rust cache uses: Swatinem/rust-cache@v2 with: shared-key: desktop-release-macos-${{ matrix.rust_target }} workspaces: | . packages/desktop/src-tauri -> target - name: Install JS dependencies run: npm ci env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Build web app for Tauri run: npm run build:web --workspace=@getpaseo/app - name: Build managed runtime run: npm run prepare:managed-runtime --workspace=@getpaseo/desktop - name: Validate managed runtime bundle run: npm run validate:managed-runtime --workspace=@getpaseo/desktop - name: Import Apple code-signing certificate uses: apple-actions/import-codesign-certs@v3 with: p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }} p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - name: Sign bundled managed runtime env: APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} run: node ./packages/desktop/scripts/sign-managed-runtime-macos.mjs - name: Detect existing GitHub release state if: env.IS_SMOKE_TAG != 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} shell: bash run: | set -euo pipefail if release_draft="$(gh release view "$RELEASE_TAG" --repo "${{ github.repository }}" --json isDraft --jq '.isDraft' 2>/dev/null)"; then : else release_draft="false" fi echo "RELEASE_DRAFT=$release_draft" >> "$GITHUB_ENV" - name: Build and publish macOS Tauri release if: env.IS_SMOKE_TAG != 'true' uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} with: projectPath: packages/desktop tagName: ${{ env.RELEASE_TAG }} releaseName: Paseo ${{ env.RELEASE_TAG }} releaseBody: See the assets to download and install this version. releaseDraft: ${{ env.RELEASE_DRAFT }} prerelease: false args: --target ${{ matrix.rust_target }} - name: Build macOS app (smoke only) if: env.IS_SMOKE_TAG == 'true' run: npm run tauri --workspace=@getpaseo/desktop build -- --target ${{ matrix.rust_target }} --no-bundle publish-linux: if: ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.platform == 'all' || github.event.inputs.platform == 'linux')) || (github.event_name == 'push' && (startsWith(github.ref_name, 'v') || startsWith(github.ref_name, 'desktop-v') || startsWith(github.ref_name, 'desktop-linux-v'))) }} permissions: contents: write packages: read runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref }} - name: Resolve release metadata shell: bash run: | set -euo pipefail source_tag="${SOURCE_TAG}" release_tag="$source_tag" for prefix in desktop-windows-v desktop-linux-v desktop-macos-v desktop-v; do if [[ "$source_tag" == ${prefix}* ]]; then release_tag="v${source_tag#${prefix}}" break fi done echo "RELEASE_TAG=$release_tag" >> "$GITHUB_ENV" version="${release_tag#v}" echo "DESKTOP_VERSION=$version" >> "$GITHUB_ENV" if [[ "$source_tag" == *gha-smoke* ]]; then echo "IS_SMOKE_TAG=true" >> "$GITHUB_ENV" else echo "IS_SMOKE_TAG=false" >> "$GITHUB_ENV" fi - name: Set desktop version from tag shell: bash run: | node <<'NODE' const fs = require('node:fs'); const path = require('node:path'); const version = process.env.DESKTOP_VERSION; if (!version) throw new Error('DESKTOP_VERSION env var is missing'); console.log(`Setting desktop version to ${version}`); const tauriConfPath = path.join('packages', 'desktop', 'src-tauri', 'tauri.conf.json'); const tauriConfText = fs.readFileSync(tauriConfPath, 'utf8'); const tauriRe = /("version"\s*:\s*")([^"]+)(")/; if (!tauriRe.test(tauriConfText)) throw new Error('Failed to find version in tauri.conf.json'); fs.writeFileSync(tauriConfPath, tauriConfText.replace(tauriRe, `$1${version}$3`)); const cargoTomlPath = path.join('packages', 'desktop', 'src-tauri', 'Cargo.toml'); const lines = fs.readFileSync(cargoTomlPath, 'utf8').split(/\r?\n/); let inPackage = false, updated = false; const result = lines.map((line) => { if (/^\[package\]\s*$/.test(line)) inPackage = true; else if (inPackage && /^\[/.test(line)) inPackage = false; if (inPackage && /^version\s*=\s*".*"\s*$/.test(line)) { updated = true; return `version = "${version}"`; } return line; }); if (!updated) throw new Error('Failed to update Cargo.toml version'); fs.writeFileSync(cargoTomlPath, result.join('\n') + '\n'); NODE - name: Install Linux packaging dependencies run: | sudo apt-get update sudo apt-get install -y libwebkit2gtk-4.1-dev libgtk-3-dev libappindicator3-dev librsvg2-dev patchelf - name: Setup Node uses: actions/setup-node@v4 with: node-version: "22" cache: "npm" cache-dependency-path: package-lock.json registry-url: "https://npm.pkg.github.com" scope: "@boudra" - name: Install Rust stable uses: dtolnay/rust-toolchain@stable - name: Restore Rust cache uses: Swatinem/rust-cache@v2 with: shared-key: desktop-release-linux workspaces: | . packages/desktop/src-tauri -> target - name: Install JS dependencies run: npm ci env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Build web app for Tauri run: npm run build:web --workspace=@getpaseo/app - name: Build managed runtime run: npm run prepare:managed-runtime --workspace=@getpaseo/desktop - name: Validate managed runtime bundle run: npm run validate:managed-runtime --workspace=@getpaseo/desktop - name: Detect existing GitHub release state if: env.IS_SMOKE_TAG != 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} shell: bash run: | set -euo pipefail if release_draft="$(gh release view "$RELEASE_TAG" --repo "${{ github.repository }}" --json isDraft --jq '.isDraft' 2>/dev/null)"; then : else release_draft="false" fi echo "RELEASE_DRAFT=$release_draft" >> "$GITHUB_ENV" - name: Build and publish Linux Tauri release if: env.IS_SMOKE_TAG != 'true' uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} NO_STRIP: "true" with: projectPath: packages/desktop tagName: ${{ env.RELEASE_TAG }} releaseName: Paseo ${{ env.RELEASE_TAG }} releaseBody: See the assets to download and install this version. releaseDraft: ${{ env.RELEASE_DRAFT }} prerelease: false args: --bundles appimage --verbose - name: Build Linux app (smoke only) if: env.IS_SMOKE_TAG == 'true' run: npm run tauri --workspace=@getpaseo/desktop build -- --no-bundle publish-windows: if: ${{ (github.event_name == 'workflow_dispatch' && (github.event.inputs.platform == 'all' || github.event.inputs.platform == 'windows')) || (github.event_name == 'push' && (startsWith(github.ref_name, 'v') || startsWith(github.ref_name, 'desktop-v') || startsWith(github.ref_name, 'desktop-windows-v'))) }} permissions: contents: write packages: read runs-on: windows-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref }} - name: Resolve release metadata shell: bash run: | set -euo pipefail source_tag="${SOURCE_TAG}" release_tag="$source_tag" for prefix in desktop-windows-v desktop-linux-v desktop-macos-v desktop-v; do if [[ "$source_tag" == ${prefix}* ]]; then release_tag="v${source_tag#${prefix}}" break fi done echo "RELEASE_TAG=$release_tag" >> "$GITHUB_ENV" version="${release_tag#v}" echo "DESKTOP_VERSION=$version" >> "$GITHUB_ENV" if [[ "$source_tag" == *gha-smoke* ]]; then echo "IS_SMOKE_TAG=true" >> "$GITHUB_ENV" else echo "IS_SMOKE_TAG=false" >> "$GITHUB_ENV" fi - name: Set desktop version from tag shell: bash run: | node <<'NODE' const fs = require('node:fs'); const path = require('node:path'); const version = process.env.DESKTOP_VERSION; if (!version) throw new Error('DESKTOP_VERSION env var is missing'); console.log(`Setting desktop version to ${version}`); const tauriConfPath = path.join('packages', 'desktop', 'src-tauri', 'tauri.conf.json'); const tauriConfText = fs.readFileSync(tauriConfPath, 'utf8'); const tauriRe = /("version"\s*:\s*")([^"]+)(")/; if (!tauriRe.test(tauriConfText)) throw new Error('Failed to find version in tauri.conf.json'); fs.writeFileSync(tauriConfPath, tauriConfText.replace(tauriRe, `$1${version}$3`)); const cargoTomlPath = path.join('packages', 'desktop', 'src-tauri', 'Cargo.toml'); const lines = fs.readFileSync(cargoTomlPath, 'utf8').split(/\r?\n/); let inPackage = false, updated = false; const result = lines.map((line) => { if (/^\[package\]\s*$/.test(line)) inPackage = true; else if (inPackage && /^\[/.test(line)) inPackage = false; if (inPackage && /^version\s*=\s*".*"\s*$/.test(line)) { updated = true; return `version = "${version}"`; } return line; }); if (!updated) throw new Error('Failed to update Cargo.toml version'); fs.writeFileSync(cargoTomlPath, result.join('\n') + '\n'); NODE - name: Setup Node uses: actions/setup-node@v4 with: node-version: "22" cache: "npm" cache-dependency-path: package-lock.json registry-url: "https://npm.pkg.github.com" scope: "@boudra" - name: Install Rust stable uses: dtolnay/rust-toolchain@stable - name: Restore Rust cache uses: Swatinem/rust-cache@v2 with: shared-key: desktop-release-windows workspaces: | . packages/desktop/src-tauri -> target - name: Install JS dependencies run: npm ci env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Build web app for Tauri run: npm run build:web --workspace=@getpaseo/app - name: Build managed runtime run: npm run prepare:managed-runtime --workspace=@getpaseo/desktop - name: Validate managed runtime bundle run: npm run validate:managed-runtime --workspace=@getpaseo/desktop - name: Detect existing GitHub release state if: env.IS_SMOKE_TAG != 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} shell: bash run: | set -euo pipefail if release_draft="$(gh release view "$RELEASE_TAG" --repo "${{ github.repository }}" --json isDraft --jq '.isDraft' 2>/dev/null)"; then : else release_draft="false" fi echo "RELEASE_DRAFT=$release_draft" >> "$GITHUB_ENV" - name: Build and publish Windows Tauri release if: env.IS_SMOKE_TAG != 'true' uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} with: projectPath: packages/desktop tagName: ${{ env.RELEASE_TAG }} releaseName: Paseo ${{ env.RELEASE_TAG }} releaseBody: See the assets to download and install this version. releaseDraft: ${{ env.RELEASE_DRAFT }} prerelease: false args: --bundles nsis,msi - name: Build Windows app (smoke only) if: env.IS_SMOKE_TAG == 'true' run: npm run tauri --workspace=@getpaseo/desktop build -- --no-bundle env: TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}