mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
Simplify desktop release pipeline: single build, no process smoke test
Replace the 855-line managed-daemon-smoke.mjs (which spawned relay servers, daemons, and tested E2E connectivity in CI) with a fast validate-managed-runtime.mjs that checks the bundle is correctly assembled without launching any processes. Structural changes: - Eliminate double-build: removed the pre-build step that compiled the Tauri app just for smoke testing before tauri-action rebuilt it - Move version-setting before the build so there's no version confusion - Sign managed runtime before tauri-action build (macOS) - Smoke tags now do a real tauri build instead of --no-bundle, giving actual signal about whether the release would succeed - Reduce each platform job from ~15 steps to ~12 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
363
.github/workflows/desktop-release.yml
vendored
363
.github/workflows/desktop-release.yml
vendored
@@ -54,29 +54,61 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref }}
|
ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref }}
|
||||||
|
|
||||||
- name: Normalize release tag
|
- name: Resolve release metadata
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
source_tag="${SOURCE_TAG}"
|
source_tag="${SOURCE_TAG}"
|
||||||
if [[ "$source_tag" == desktop-windows-v* ]]; then
|
release_tag="$source_tag"
|
||||||
release_tag="v${source_tag#desktop-windows-v}"
|
for prefix in desktop-windows-v desktop-linux-v desktop-macos-v desktop-v; do
|
||||||
elif [[ "$source_tag" == desktop-linux-v* ]]; then
|
if [[ "$source_tag" == ${prefix}* ]]; then
|
||||||
release_tag="v${source_tag#desktop-linux-v}"
|
release_tag="v${source_tag#${prefix}}"
|
||||||
elif [[ "$source_tag" == desktop-macos-v* ]]; then
|
break
|
||||||
release_tag="v${source_tag#desktop-macos-v}"
|
fi
|
||||||
elif [[ "$source_tag" == desktop-v* ]]; then
|
done
|
||||||
release_tag="v${source_tag#desktop-v}"
|
|
||||||
else
|
|
||||||
release_tag="$source_tag"
|
|
||||||
fi
|
|
||||||
echo "RELEASE_TAG=$release_tag" >> "$GITHUB_ENV"
|
echo "RELEASE_TAG=$release_tag" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
version="${release_tag#v}"
|
||||||
|
echo "DESKTOP_VERSION=$version" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
if [[ "$source_tag" == *gha-smoke* ]]; then
|
if [[ "$source_tag" == *gha-smoke* ]]; then
|
||||||
echo "IS_SMOKE_TAG=true" >> "$GITHUB_ENV"
|
echo "IS_SMOKE_TAG=true" >> "$GITHUB_ENV"
|
||||||
else
|
else
|
||||||
echo "IS_SMOKE_TAG=false" >> "$GITHUB_ENV"
|
echo "IS_SMOKE_TAG=false" >> "$GITHUB_ENV"
|
||||||
fi
|
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
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
@@ -107,79 +139,23 @@ jobs:
|
|||||||
- name: Build web app for Tauri
|
- name: Build web app for Tauri
|
||||||
run: npm run build:web --workspace=@getpaseo/app
|
run: npm run build:web --workspace=@getpaseo/app
|
||||||
|
|
||||||
- name: Build managed runtime for macOS
|
- name: Build managed runtime
|
||||||
run: npm run prepare:managed-runtime --workspace=@getpaseo/desktop
|
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
|
- name: Import Apple code-signing certificate
|
||||||
uses: apple-actions/import-codesign-certs@v3
|
uses: apple-actions/import-codesign-certs@v3
|
||||||
with:
|
with:
|
||||||
p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }}
|
p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }}
|
||||||
p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||||
|
|
||||||
- name: Build packaged app for macOS smoke
|
- name: Sign bundled managed runtime
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
app_path="packages/desktop/src-tauri/target/${{ matrix.rust_target }}/release/bundle/macos/Paseo.app/Contents/MacOS/Paseo"
|
|
||||||
if npm run tauri --workspace=@getpaseo/desktop build -- --target ${{ matrix.rust_target }}; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
if [[ -f "$app_path" ]]; then
|
|
||||||
echo "Smoke app created at $app_path despite tauri build exiting non-zero; continuing."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
exit 1
|
|
||||||
|
|
||||||
- name: Smoke check managed runtime for macOS
|
|
||||||
env:
|
|
||||||
PASEO_MANAGED_SMOKE_SKIP_BUILD: "1"
|
|
||||||
PASEO_MANAGED_SMOKE_RUST_TARGET: ${{ matrix.rust_target }}
|
|
||||||
run: npm run smoke:managed-daemon --workspace=@getpaseo/desktop
|
|
||||||
|
|
||||||
- name: Sign bundled managed runtime for macOS
|
|
||||||
env:
|
env:
|
||||||
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||||
run: node ./packages/desktop/scripts/sign-managed-runtime-macos.mjs
|
run: node ./packages/desktop/scripts/sign-managed-runtime-macos.mjs
|
||||||
|
|
||||||
- name: Set desktop version from tag
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
node <<'NODE'
|
|
||||||
const fs = require('node:fs');
|
|
||||||
const path = require('node:path');
|
|
||||||
|
|
||||||
const rawTag = process.env.SOURCE_TAG;
|
|
||||||
if (!rawTag) throw new Error('SOURCE_TAG env var is missing');
|
|
||||||
|
|
||||||
const version = rawTag.replace(/^desktop-(windows-|linux-|macos-)?/, '').replace(/^v/, '');
|
|
||||||
console.log(`Using desktop version ${version} from tag ${rawTag}`);
|
|
||||||
|
|
||||||
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 field in ${tauriConfPath}`);
|
|
||||||
}
|
|
||||||
fs.writeFileSync(tauriConfPath, tauriConfText.replace(tauriRe, `$1${version}$3`));
|
|
||||||
|
|
||||||
const cargoTomlPath = path.join('packages', 'desktop', 'src-tauri', 'Cargo.toml');
|
|
||||||
const cargoLines = fs.readFileSync(cargoTomlPath, 'utf8').split(/\r?\n/);
|
|
||||||
let inPackage = false;
|
|
||||||
let updated = false;
|
|
||||||
const nextLines = cargoLines.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 package version in ${cargoTomlPath}`);
|
|
||||||
fs.writeFileSync(cargoTomlPath, `${nextLines.join('\n')}\n`);
|
|
||||||
NODE
|
|
||||||
|
|
||||||
- name: Detect existing GitHub release state
|
- name: Detect existing GitHub release state
|
||||||
if: env.IS_SMOKE_TAG != 'true'
|
if: env.IS_SMOKE_TAG != 'true'
|
||||||
env:
|
env:
|
||||||
@@ -216,6 +192,13 @@ jobs:
|
|||||||
prerelease: false
|
prerelease: false
|
||||||
args: --target ${{ matrix.rust_target }}
|
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 }}
|
||||||
|
env:
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||||
|
|
||||||
publish-linux:
|
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'))) }}
|
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:
|
permissions:
|
||||||
@@ -229,29 +212,61 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref }}
|
ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref }}
|
||||||
|
|
||||||
- name: Normalize release tag
|
- name: Resolve release metadata
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
source_tag="${SOURCE_TAG}"
|
source_tag="${SOURCE_TAG}"
|
||||||
if [[ "$source_tag" == desktop-windows-v* ]]; then
|
release_tag="$source_tag"
|
||||||
release_tag="v${source_tag#desktop-windows-v}"
|
for prefix in desktop-windows-v desktop-linux-v desktop-macos-v desktop-v; do
|
||||||
elif [[ "$source_tag" == desktop-linux-v* ]]; then
|
if [[ "$source_tag" == ${prefix}* ]]; then
|
||||||
release_tag="v${source_tag#desktop-linux-v}"
|
release_tag="v${source_tag#${prefix}}"
|
||||||
elif [[ "$source_tag" == desktop-macos-v* ]]; then
|
break
|
||||||
release_tag="v${source_tag#desktop-macos-v}"
|
fi
|
||||||
elif [[ "$source_tag" == desktop-v* ]]; then
|
done
|
||||||
release_tag="v${source_tag#desktop-v}"
|
|
||||||
else
|
|
||||||
release_tag="$source_tag"
|
|
||||||
fi
|
|
||||||
echo "RELEASE_TAG=$release_tag" >> "$GITHUB_ENV"
|
echo "RELEASE_TAG=$release_tag" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
version="${release_tag#v}"
|
||||||
|
echo "DESKTOP_VERSION=$version" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
if [[ "$source_tag" == *gha-smoke* ]]; then
|
if [[ "$source_tag" == *gha-smoke* ]]; then
|
||||||
echo "IS_SMOKE_TAG=true" >> "$GITHUB_ENV"
|
echo "IS_SMOKE_TAG=true" >> "$GITHUB_ENV"
|
||||||
else
|
else
|
||||||
echo "IS_SMOKE_TAG=false" >> "$GITHUB_ENV"
|
echo "IS_SMOKE_TAG=false" >> "$GITHUB_ENV"
|
||||||
fi
|
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
|
- name: Install Linux packaging dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
@@ -285,50 +300,11 @@ jobs:
|
|||||||
- name: Build web app for Tauri
|
- name: Build web app for Tauri
|
||||||
run: npm run build:web --workspace=@getpaseo/app
|
run: npm run build:web --workspace=@getpaseo/app
|
||||||
|
|
||||||
- name: Build managed runtime for Linux
|
- name: Build managed runtime
|
||||||
run: npm run prepare:managed-runtime --workspace=@getpaseo/desktop
|
run: npm run prepare:managed-runtime --workspace=@getpaseo/desktop
|
||||||
|
|
||||||
- name: Smoke check managed runtime for Linux
|
- name: Validate managed runtime bundle
|
||||||
run: npm run smoke:managed-daemon --workspace=@getpaseo/desktop
|
run: npm run validate:managed-runtime --workspace=@getpaseo/desktop
|
||||||
|
|
||||||
- name: Set desktop version from tag
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
node <<'NODE'
|
|
||||||
const fs = require('node:fs');
|
|
||||||
const path = require('node:path');
|
|
||||||
|
|
||||||
const rawTag = process.env.SOURCE_TAG;
|
|
||||||
if (!rawTag) throw new Error('SOURCE_TAG env var is missing');
|
|
||||||
|
|
||||||
const version = rawTag.replace(/^desktop-(windows-|linux-|macos-)?/, '').replace(/^v/, '');
|
|
||||||
console.log(`Using desktop version ${version} from tag ${rawTag}`);
|
|
||||||
|
|
||||||
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 field in ${tauriConfPath}`);
|
|
||||||
}
|
|
||||||
fs.writeFileSync(tauriConfPath, tauriConfText.replace(tauriRe, `$1${version}$3`));
|
|
||||||
|
|
||||||
const cargoTomlPath = path.join('packages', 'desktop', 'src-tauri', 'Cargo.toml');
|
|
||||||
const cargoLines = fs.readFileSync(cargoTomlPath, 'utf8').split(/\r?\n/);
|
|
||||||
let inPackage = false;
|
|
||||||
let updated = false;
|
|
||||||
const nextLines = cargoLines.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 package version in ${cargoTomlPath}`);
|
|
||||||
fs.writeFileSync(cargoTomlPath, `${nextLines.join('\n')}\n`);
|
|
||||||
NODE
|
|
||||||
|
|
||||||
- name: Detect existing GitHub release state
|
- name: Detect existing GitHub release state
|
||||||
if: env.IS_SMOKE_TAG != 'true'
|
if: env.IS_SMOKE_TAG != 'true'
|
||||||
@@ -360,6 +336,13 @@ jobs:
|
|||||||
prerelease: false
|
prerelease: false
|
||||||
args: --bundles appimage
|
args: --bundles appimage
|
||||||
|
|
||||||
|
- name: Build Linux app (smoke only)
|
||||||
|
if: env.IS_SMOKE_TAG == 'true'
|
||||||
|
run: npm run tauri --workspace=@getpaseo/desktop build -- --bundles appimage
|
||||||
|
env:
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||||
|
|
||||||
publish-windows:
|
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'))) }}
|
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:
|
permissions:
|
||||||
@@ -373,29 +356,61 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref }}
|
ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref }}
|
||||||
|
|
||||||
- name: Normalize release tag
|
- name: Resolve release metadata
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
source_tag="${SOURCE_TAG}"
|
source_tag="${SOURCE_TAG}"
|
||||||
if [[ "$source_tag" == desktop-windows-v* ]]; then
|
release_tag="$source_tag"
|
||||||
release_tag="v${source_tag#desktop-windows-v}"
|
for prefix in desktop-windows-v desktop-linux-v desktop-macos-v desktop-v; do
|
||||||
elif [[ "$source_tag" == desktop-linux-v* ]]; then
|
if [[ "$source_tag" == ${prefix}* ]]; then
|
||||||
release_tag="v${source_tag#desktop-linux-v}"
|
release_tag="v${source_tag#${prefix}}"
|
||||||
elif [[ "$source_tag" == desktop-macos-v* ]]; then
|
break
|
||||||
release_tag="v${source_tag#desktop-macos-v}"
|
fi
|
||||||
elif [[ "$source_tag" == desktop-v* ]]; then
|
done
|
||||||
release_tag="v${source_tag#desktop-v}"
|
|
||||||
else
|
|
||||||
release_tag="$source_tag"
|
|
||||||
fi
|
|
||||||
echo "RELEASE_TAG=$release_tag" >> "$GITHUB_ENV"
|
echo "RELEASE_TAG=$release_tag" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
version="${release_tag#v}"
|
||||||
|
echo "DESKTOP_VERSION=$version" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
if [[ "$source_tag" == *gha-smoke* ]]; then
|
if [[ "$source_tag" == *gha-smoke* ]]; then
|
||||||
echo "IS_SMOKE_TAG=true" >> "$GITHUB_ENV"
|
echo "IS_SMOKE_TAG=true" >> "$GITHUB_ENV"
|
||||||
else
|
else
|
||||||
echo "IS_SMOKE_TAG=false" >> "$GITHUB_ENV"
|
echo "IS_SMOKE_TAG=false" >> "$GITHUB_ENV"
|
||||||
fi
|
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
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
@@ -424,68 +439,11 @@ jobs:
|
|||||||
- name: Build web app for Tauri
|
- name: Build web app for Tauri
|
||||||
run: npm run build:web --workspace=@getpaseo/app
|
run: npm run build:web --workspace=@getpaseo/app
|
||||||
|
|
||||||
- name: Build managed runtime for Windows
|
- name: Build managed runtime
|
||||||
run: npm run prepare:managed-runtime --workspace=@getpaseo/desktop
|
run: npm run prepare:managed-runtime --workspace=@getpaseo/desktop
|
||||||
|
|
||||||
- name: Build packaged app for Windows smoke
|
- name: Validate managed runtime bundle
|
||||||
shell: bash
|
run: npm run validate:managed-runtime --workspace=@getpaseo/desktop
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
app_path="packages/desktop/src-tauri/target/release/Paseo.exe"
|
|
||||||
if npm run tauri --workspace=@getpaseo/desktop build -- --no-bundle; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
if [[ -f "$app_path" ]]; then
|
|
||||||
echo "Smoke app created at $app_path despite tauri build exiting non-zero; continuing."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
echo "Expected Windows smoke app at $app_path"
|
|
||||||
exit 1
|
|
||||||
|
|
||||||
- name: Smoke check managed runtime for Windows
|
|
||||||
timeout-minutes: 12
|
|
||||||
env:
|
|
||||||
PASEO_MANAGED_SMOKE_SKIP_BUILD: "1"
|
|
||||||
run: npm run smoke:managed-daemon --workspace=@getpaseo/desktop
|
|
||||||
|
|
||||||
- name: Set desktop version from tag
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
node <<'NODE'
|
|
||||||
const fs = require('node:fs');
|
|
||||||
const path = require('node:path');
|
|
||||||
|
|
||||||
const rawTag = process.env.SOURCE_TAG;
|
|
||||||
if (!rawTag) throw new Error('SOURCE_TAG env var is missing');
|
|
||||||
|
|
||||||
const version = rawTag.replace(/^desktop-(windows-|linux-|macos-)?/, '').replace(/^v/, '');
|
|
||||||
console.log(`Using desktop version ${version} from tag ${rawTag}`);
|
|
||||||
|
|
||||||
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 field in ${tauriConfPath}`);
|
|
||||||
}
|
|
||||||
fs.writeFileSync(tauriConfPath, tauriConfText.replace(tauriRe, `$1${version}$3`));
|
|
||||||
|
|
||||||
const cargoTomlPath = path.join('packages', 'desktop', 'src-tauri', 'Cargo.toml');
|
|
||||||
const cargoLines = fs.readFileSync(cargoTomlPath, 'utf8').split(/\r?\n/);
|
|
||||||
let inPackage = false;
|
|
||||||
let updated = false;
|
|
||||||
const nextLines = cargoLines.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 package version in ${cargoTomlPath}`);
|
|
||||||
fs.writeFileSync(cargoTomlPath, `${nextLines.join('\n')}\n`);
|
|
||||||
NODE
|
|
||||||
|
|
||||||
- name: Detect existing GitHub release state
|
- name: Detect existing GitHub release state
|
||||||
if: env.IS_SMOKE_TAG != 'true'
|
if: env.IS_SMOKE_TAG != 'true'
|
||||||
@@ -516,3 +474,10 @@ jobs:
|
|||||||
releaseDraft: ${{ env.RELEASE_DRAFT }}
|
releaseDraft: ${{ env.RELEASE_DRAFT }}
|
||||||
prerelease: false
|
prerelease: false
|
||||||
args: --bundles nsis,msi
|
args: --bundles nsis,msi
|
||||||
|
|
||||||
|
- name: Build Windows app (smoke only)
|
||||||
|
if: env.IS_SMOKE_TAG == 'true'
|
||||||
|
run: npm run tauri --workspace=@getpaseo/desktop build -- --bundles nsis,msi
|
||||||
|
env:
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"prepare:managed-runtime": "npm --prefix ../.. run build:daemon && npm run build:managed-runtime",
|
"prepare:managed-runtime": "npm --prefix ../.. run build:daemon && npm run build:managed-runtime",
|
||||||
"dev": "npm run prepare:managed-runtime && tauri dev",
|
"dev": "npm run prepare:managed-runtime && tauri dev",
|
||||||
"build": "npm --prefix ../.. run build:web --workspace=@getpaseo/app && npm run prepare:managed-runtime && tauri build",
|
"build": "npm --prefix ../.. run build:web --workspace=@getpaseo/app && npm run prepare:managed-runtime && tauri build",
|
||||||
|
"validate:managed-runtime": "node ./scripts/validate-managed-runtime.mjs",
|
||||||
"smoke:managed-daemon": "node ./scripts/managed-daemon-smoke.mjs",
|
"smoke:managed-daemon": "node ./scripts/managed-daemon-smoke.mjs",
|
||||||
"tauri": "tauri"
|
"tauri": "tauri"
|
||||||
},
|
},
|
||||||
|
|||||||
62
packages/desktop/scripts/validate-managed-runtime.mjs
Normal file
62
packages/desktop/scripts/validate-managed-runtime.mjs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import assert from "node:assert/strict";
|
||||||
|
import fs from "node:fs/promises";
|
||||||
|
import path from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
const desktopRoot = fileURLToPath(new URL("..", import.meta.url));
|
||||||
|
const resourcesRoot = path.join(desktopRoot, "src-tauri", "resources", "managed-runtime");
|
||||||
|
|
||||||
|
const desktopPackageJson = JSON.parse(
|
||||||
|
await fs.readFile(path.join(desktopRoot, "package.json"), "utf8")
|
||||||
|
);
|
||||||
|
const expectedVersion = desktopPackageJson.version;
|
||||||
|
|
||||||
|
const currentRuntime = JSON.parse(
|
||||||
|
await fs.readFile(path.join(resourcesRoot, "current-runtime.json"), "utf8")
|
||||||
|
);
|
||||||
|
assert.equal(currentRuntime.runtimeVersion, expectedVersion, "current-runtime.json version mismatch");
|
||||||
|
assert.ok(currentRuntime.runtimeId, "current-runtime.json missing runtimeId");
|
||||||
|
assert.ok(currentRuntime.relativeRoot, "current-runtime.json missing relativeRoot");
|
||||||
|
|
||||||
|
const runtimeRoot = path.join(resourcesRoot, currentRuntime.relativeRoot);
|
||||||
|
const manifest = JSON.parse(
|
||||||
|
await fs.readFile(path.join(runtimeRoot, "runtime-manifest.json"), "utf8")
|
||||||
|
);
|
||||||
|
assert.equal(manifest.runtimeId, currentRuntime.runtimeId, "manifest runtimeId mismatch");
|
||||||
|
assert.equal(manifest.runtimeVersion, expectedVersion, "manifest version mismatch");
|
||||||
|
assert.ok(manifest.nodeRelativePath, "manifest missing nodeRelativePath");
|
||||||
|
assert.ok(manifest.cliEntrypointRelativePath, "manifest missing cliEntrypointRelativePath");
|
||||||
|
assert.ok(manifest.serverRunnerRelativePath, "manifest missing serverRunnerRelativePath");
|
||||||
|
|
||||||
|
const nodeBinary = path.join(runtimeRoot, manifest.nodeRelativePath);
|
||||||
|
await fs.access(nodeBinary).catch(() => {
|
||||||
|
throw new Error(`Bundled Node binary not found: ${nodeBinary}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
const cliEntry = path.join(runtimeRoot, manifest.cliEntrypointRelativePath);
|
||||||
|
await fs.access(cliEntry).catch(() => {
|
||||||
|
throw new Error(`CLI entrypoint not found: ${cliEntry}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
const serverRunner = path.join(runtimeRoot, manifest.serverRunnerRelativePath);
|
||||||
|
await fs.access(serverRunner).catch(() => {
|
||||||
|
throw new Error(`Server runner not found: ${serverRunner}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
const runtimePackageJson = JSON.parse(
|
||||||
|
await fs.readFile(path.join(runtimeRoot, "package.json"), "utf8")
|
||||||
|
);
|
||||||
|
assert.equal(runtimePackageJson.version, expectedVersion, "runtime package.json version mismatch");
|
||||||
|
|
||||||
|
for (const pkg of ["@getpaseo/relay", "@getpaseo/server", "@getpaseo/cli"]) {
|
||||||
|
const pkgDir = path.join(runtimeRoot, "node_modules", ...pkg.split("/"));
|
||||||
|
await fs.access(pkgDir).catch(() => {
|
||||||
|
throw new Error(`Missing bundled dependency: ${pkg} (expected at ${pkgDir})`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[validate-managed-runtime] PASS`);
|
||||||
|
console.log(` runtimeId: ${manifest.runtimeId}`);
|
||||||
|
console.log(` version: ${expectedVersion}`);
|
||||||
|
console.log(` platform: ${manifest.platform}`);
|
||||||
|
console.log(` arch: ${manifest.arch}`);
|
||||||
Reference in New Issue
Block a user