mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
The patch step cd's to a temp directory for extraction, so the AppImage path must be absolute (via $GITHUB_WORKSPACE).
638 lines
25 KiB
YAML
638 lines
25 KiB
YAML
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}"
|
|
if [[ "$source_tag" =~ ^(desktop-(windows|linux|macos)-|desktop-)?v([0-9]+\.[0-9]+\.[0-9]+) ]]; then
|
|
release_tag="v${BASH_REMATCH[3]}"
|
|
else
|
|
release_tag="$source_tag"
|
|
fi
|
|
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'
|
|
id: tauri_build
|
|
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: Notarize and re-upload DMG
|
|
if: env.IS_SMOKE_TAG != 'true'
|
|
env:
|
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
|
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
|
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
artifacts='${{ steps.tauri_build.outputs.artifactPaths }}'
|
|
dmg_path=$(echo "$artifacts" | jq -r '.[] | select(endswith(".dmg"))')
|
|
if [ -z "$dmg_path" ]; then
|
|
echo "::error::No DMG found in tauri build artifacts"
|
|
exit 1
|
|
fi
|
|
echo "DMG: $dmg_path"
|
|
|
|
echo "Signing DMG..."
|
|
codesign --force --sign "$APPLE_SIGNING_IDENTITY" --timestamp "$dmg_path"
|
|
|
|
echo "Submitting DMG for notarization..."
|
|
xcrun notarytool submit "$dmg_path" \
|
|
--apple-id "$APPLE_ID" \
|
|
--password "$APPLE_PASSWORD" \
|
|
--team-id "$APPLE_TEAM_ID" \
|
|
--wait
|
|
|
|
echo "Stapling notarization ticket..."
|
|
xcrun stapler staple "$dmg_path"
|
|
|
|
echo "Verifying..."
|
|
spctl --assess --type install --verbose "$dmg_path"
|
|
|
|
echo "Replacing release asset with notarized DMG..."
|
|
gh release upload "$RELEASE_TAG" "$dmg_path" --repo "${{ github.repository }}" --clobber
|
|
|
|
- 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}"
|
|
if [[ "$source_tag" =~ ^(desktop-(windows|linux|macos)-|desktop-)?v([0-9]+\.[0-9]+\.[0-9]+) ]]; then
|
|
release_tag="v${BASH_REMATCH[3]}"
|
|
else
|
|
release_tag="$source_tag"
|
|
fi
|
|
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 libfuse2
|
|
|
|
- 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: Strip CUDA dependencies from onnxruntime
|
|
shell: bash
|
|
run: |
|
|
find packages/desktop/src-tauri/resources/managed-runtime -path '*/onnxruntime-node/bin/*' \( -name '*cuda*' -o -name '*tensorrt*' \) -delete || true
|
|
# Remove CUDA shared library references from onnxruntime .so files so linuxdeploy
|
|
# doesn't try to bundle them (they're optional runtime deps, not needed for CPU inference)
|
|
for f in $(find packages/desktop/src-tauri/resources/managed-runtime -path '*/onnxruntime-node/bin/*' \( -name '*.so' -o -name '*.so.*' \)); do
|
|
for lib in $(patchelf --print-needed "$f" 2>/dev/null | grep -iE 'cublas|cudnn|cudart|cufft|curand|cusolver|cusparse|nccl|nvrtc|tensorrt|nvinfer'); do
|
|
echo "Removing needed $lib from $f"
|
|
patchelf --remove-needed "$lib" "$f"
|
|
done
|
|
done
|
|
|
|
- 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 Linux Tauri release
|
|
if: env.IS_SMOKE_TAG != 'true'
|
|
id: linux_tauri
|
|
continue-on-error: true
|
|
env:
|
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
|
NO_STRIP: "1"
|
|
APPIMAGE_EXTRACT_AND_RUN: "1"
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
npm run tauri --workspace=@getpaseo/desktop build -- --bundles appimage
|
|
|
|
- name: Attempt manual Linux AppImage fallback
|
|
if: env.IS_SMOKE_TAG != 'true' && steps.linux_tauri.outcome == 'failure'
|
|
env:
|
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
|
shell: bash
|
|
run: |
|
|
set -euxo pipefail
|
|
appimage_dir="packages/desktop/src-tauri/target/release/bundle/appimage"
|
|
appdir_path="$appimage_dir/Paseo.AppDir"
|
|
canonical_appimage="$appimage_dir/Paseo_${DESKTOP_VERSION}_amd64.AppImage"
|
|
existing_appimage="$(find "$appimage_dir" -maxdepth 1 -type f -name '*.AppImage' | head -n 1)"
|
|
if [ -n "$existing_appimage" ]; then
|
|
if [ "$existing_appimage" != "$canonical_appimage" ]; then
|
|
mv "$existing_appimage" "$canonical_appimage"
|
|
fi
|
|
if [ ! -f "$canonical_appimage.sig" ]; then
|
|
npx tauri signer sign "$canonical_appimage"
|
|
fi
|
|
exit 0
|
|
fi
|
|
if [ ! -d "$appdir_path" ]; then
|
|
echo "::error::AppDir was not generated at $appdir_path"
|
|
exit 1
|
|
fi
|
|
cp --remove-destination "$appdir_path/usr/share/applications/Paseo.desktop" "$appdir_path/Paseo.desktop"
|
|
cp --remove-destination "$appdir_path/Paseo.png" "$appdir_path/.DirIcon"
|
|
env | sort | grep -E '^(APPIMAGE|DESKTOP_VERSION|NO_STRIP|RELEASE_TAG|SOURCE_TAG|TAURI_)' || true
|
|
tools_dir="$(mktemp -d)"
|
|
curl -fsSL https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage -o "$tools_dir/appimagetool-x86_64.AppImage"
|
|
chmod +x "$tools_dir/appimagetool-x86_64.AppImage"
|
|
ARCH=x86_64 APPIMAGE_EXTRACT_AND_RUN=1 "$tools_dir/appimagetool-x86_64.AppImage" "$appdir_path" "$canonical_appimage"
|
|
npx tauri signer sign "$canonical_appimage"
|
|
|
|
- name: Fail Linux release when AppImage bundling fails
|
|
if: env.IS_SMOKE_TAG != 'true' && steps.linux_tauri.outcome == 'failure'
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
shopt -s nullglob
|
|
assets=(
|
|
packages/desktop/src-tauri/target/release/bundle/appimage/*.AppImage
|
|
packages/desktop/src-tauri/target/release/bundle/appimage/*.AppImage.sig
|
|
)
|
|
if [ "${#assets[@]}" -eq 0 ]; then
|
|
echo "::error::Linux AppImage assets are still missing after the manual fallback."
|
|
exit 1
|
|
fi
|
|
|
|
- name: Patch AppImage for Wayland compatibility
|
|
if: env.IS_SMOKE_TAG != 'true'
|
|
env:
|
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
|
APPIMAGE_EXTRACT_AND_RUN: "1"
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
appimage_dir="$GITHUB_WORKSPACE/packages/desktop/src-tauri/target/release/bundle/appimage"
|
|
appimage="$(find "$appimage_dir" -maxdepth 1 -type f -name '*.AppImage' | head -n 1)"
|
|
if [ -z "$appimage" ]; then
|
|
echo "::error::No AppImage found to patch"
|
|
exit 1
|
|
fi
|
|
echo "Patching $appimage for Wayland compatibility..."
|
|
|
|
# Extract the AppImage
|
|
workdir="$(mktemp -d)"
|
|
cd "$workdir"
|
|
"$appimage" --appimage-extract
|
|
|
|
# Remove GDK_BACKEND=x11 from the GTK plugin hook.
|
|
# The bundled libgdk-3.so has Wayland support built in; forcing x11
|
|
# prevents the app from starting on Wayland-only systems.
|
|
gtk_hook="squashfs-root/apprun-hooks/linuxdeploy-plugin-gtk.sh"
|
|
if [ -f "$gtk_hook" ]; then
|
|
sed -i 's/^export GDK_BACKEND=x11/#export GDK_BACKEND=x11/' "$gtk_hook"
|
|
echo "Patched $gtk_hook — removed GDK_BACKEND=x11"
|
|
fi
|
|
|
|
# Repackage
|
|
tools_dir="$(mktemp -d)"
|
|
curl -fsSL https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage \
|
|
-o "$tools_dir/appimagetool"
|
|
chmod +x "$tools_dir/appimagetool"
|
|
ARCH=x86_64 "$tools_dir/appimagetool" squashfs-root "$appimage"
|
|
|
|
# Re-sign
|
|
rm -f "$appimage.sig"
|
|
cd "$GITHUB_WORKSPACE"
|
|
npx tauri signer sign "$appimage"
|
|
echo "Repackaged and re-signed $appimage"
|
|
|
|
- name: Upload Linux release assets
|
|
if: env.IS_SMOKE_TAG != 'true'
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
shopt -s nullglob
|
|
assets=(
|
|
packages/desktop/src-tauri/target/release/bundle/appimage/*.AppImage
|
|
packages/desktop/src-tauri/target/release/bundle/appimage/*.AppImage.sig
|
|
)
|
|
if [ "${#assets[@]}" -eq 0 ]; then
|
|
echo "::error::No Linux AppImage assets were produced."
|
|
exit 1
|
|
fi
|
|
printf 'Uploading Linux assets:\n%s\n' "${assets[@]}"
|
|
gh release upload "$RELEASE_TAG" "${assets[@]}" --repo "${{ github.repository }}" --clobber
|
|
|
|
- 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}"
|
|
if [[ "$source_tag" =~ ^(desktop-(windows|linux|macos)-|desktop-)?v([0-9]+\.[0-9]+\.[0-9]+) ]]; then
|
|
release_tag="v${BASH_REMATCH[3]}"
|
|
else
|
|
release_tag="$source_tag"
|
|
fi
|
|
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
|
|
shell: pwsh
|
|
run: |
|
|
$patchPath = (Get-Item "$env:GITHUB_WORKSPACE/scripts/metro-config-windows-loader-patch.cjs").FullName
|
|
$env:NODE_OPTIONS = "--require=$patchPath"
|
|
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
|
|
|
|
- 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 }}
|