Files
cursor-byok/.github/workflows/release.yml
T

284 lines
10 KiB
YAML

name: Release desktop app
on:
push:
tags:
- "v*"
permissions:
contents: write
concurrency:
group: desktop-release
cancel-in-progress: false
jobs:
prepare:
name: Prepare release
runs-on: ubuntu-latest
outputs:
should_publish: ${{ steps.release.outputs.should_publish }}
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Verify release author
env:
REPOSITORY_OWNER: ${{ github.repository_owner }}
shell: bash
run: |
if [[ "${GITHUB_ACTOR}" != "${REPOSITORY_OWNER}" ]]; then
echo "Only ${REPOSITORY_OWNER} may publish a release" >&2
exit 1
fi
- name: Verify updater signing key
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
shell: bash
run: |
if [[ -z "${TAURI_SIGNING_PRIVATE_KEY}" ]]; then
echo "TAURI_SIGNING_PRIVATE_KEY is not configured" >&2
exit 1
fi
- name: Read and verify app version
id: version
shell: bash
run: |
version=$(node -p "require('./apps/desktop/src-tauri/tauri.conf.json').version")
package_version=$(node -p "require('./apps/desktop/package.json').version")
cargo_version=$(sed -n '/^version = / { s/version = "\([^"]*\)"/\1/p; q; }' apps/desktop/src-tauri/Cargo.toml)
test "${version}" = "${package_version}"
test "${version}" = "${cargo_version}"
test "${GITHUB_REF_TYPE}" = "tag"
test "${GITHUB_REF_NAME}" = "v${version}"
git fetch origin main:refs/remotes/origin/main
if ! git merge-base --is-ancestor "${GITHUB_SHA}" origin/main; then
echo "Release tag must point to a commit contained in origin/main" >&2
exit 1
fi
echo "version=${version}" >> "${GITHUB_OUTPUT}"
- name: Check whether this version is already published
id: release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.version.outputs.version }}
shell: bash
run: |
state=$(gh release view "v${VERSION}" --json isDraft --jq 'if .isDraft then "draft" else "published" end' 2>/dev/null || true)
if [[ "${state}" = "published" ]]; then
echo "Version ${VERSION} is already published; nothing to do."
echo "should_publish=false" >> "${GITHUB_OUTPUT}"
else
echo "should_publish=true" >> "${GITHUB_OUTPUT}"
fi
publish:
name: Publish (${{ matrix.platform }})
needs: prepare
if: needs.prepare.outputs.should_publish == 'true'
strategy:
fail-fast: false
matrix:
include:
- platform: linux-x86_64
os: ubuntu-22.04
args: ""
target: ""
- platform: windows-x86_64
os: windows-latest
args: "--bundles nsis"
target: ""
- platform: macos-aarch64
os: macos-15
args: "--target aarch64-apple-darwin"
target: aarch64-apple-darwin
- platform: macos-x86_64
os: macos-15-intel
args: "--target x86_64-apple-darwin"
target: x86_64-apple-darwin
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Install Linux system dependencies
if: matrix.platform == 'linux-x86_64'
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
- uses: dtolnay/rust-toolchain@stable
- name: Install Rust target
if: matrix.target != ''
run: rustup target add ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
with:
workspaces: ". -> target"
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: apps/desktop/package-lock.json
- name: Install frontend dependencies
working-directory: apps/desktop
run: npm ci
- uses: tauri-apps/tauri-action@v1
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 }}
APPLE_SIGNING_IDENTITY: ${{ contains(matrix.platform, 'macos') && '-' || '' }}
with:
projectPath: apps/desktop
tagName: v__VERSION__
releaseName: Cursor BYOK v__VERSION__
releaseBody: ${{ contains(needs.prepare.outputs.version, '-') && 'Beta release. Download the installer for your platform from the assets below.' || 'Download the installer for your platform from the assets below.' }}
releaseDraft: true
prerelease: false
uploadUpdaterJson: true
args: ${{ matrix.args }}
- name: Package legacy Linux updater asset
if: matrix.platform == 'linux-x86_64'
shell: bash
env:
VERSION: ${{ needs.prepare.outputs.version }}
run: |
mkdir -p legacy-update
tar -czf "legacy-update/cursor-byok-${VERSION}-linux-amd64.tar.gz" -C target/release cursor-byok-desktop
- name: Package and sign legacy Windows updater asset
if: matrix.platform == 'windows-x86_64'
shell: pwsh
env:
VERSION: ${{ needs.prepare.outputs.version }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
run: |
New-Item -ItemType Directory -Force legacy-update | Out-Null
$asset = "legacy-update/cursor-byok-$env:VERSION-windows-amd64.zip"
Compress-Archive -LiteralPath target/release/cursor-byok-desktop.exe -DestinationPath $asset
Push-Location apps/desktop
npm exec tauri signer sign -- "../../$asset"
Pop-Location
$entries = @(tar -tf $asset)
if ($entries.Count -ne 1 -or [System.IO.Path]::GetFileName($entries[0]) -ne 'cursor-byok-desktop.exe') {
throw "Windows updater archive must contain only cursor-byok-desktop.exe"
}
if (!(Test-Path "$asset.sig")) {
throw "Windows updater archive signature was not generated"
}
- name: Package legacy macOS updater asset
if: contains(matrix.platform, 'macos')
shell: bash
env:
VERSION: ${{ needs.prepare.outputs.version }}
run: |
mkdir -p legacy-update
archive=$(find "target/${{ matrix.target }}/release/bundle/macos" -maxdepth 1 -name '*.app.tar.gz' -print -quit)
test -n "${archive}"
legacy_platform=macos-arm64
if [[ "${{ matrix.platform }}" = "macos-x86_64" ]]; then
legacy_platform=macos-amd64
fi
cp "${archive}" "legacy-update/cursor-byok-${VERSION}-${legacy_platform}.tar.gz"
- uses: actions/upload-artifact@v4
with:
name: legacy-update-${{ matrix.platform }}
path: legacy-update/*
if-no-files-found: error
finalize:
name: Publish GitHub Release
needs: [prepare, publish]
if: needs.prepare.outputs.should_publish == 'true' && needs.publish.result == 'success'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
pattern: legacy-update-*
path: legacy-update
merge-multiple: true
- name: Generate legacy update manifest
env:
VERSION: ${{ needs.prepare.outputs.version }}
run: |
node .github/scripts/generate-legacy-update.mjs \
--version "${VERSION}" \
--repository "${GITHUB_REPOSITORY}" \
--assets-dir legacy-update \
--output legacy-update/update.json \
--notes "Cursor BYOK v${VERSION}"
- name: Generate signed Windows portable update manifest
env:
VERSION: ${{ needs.prepare.outputs.version }}
run: |
asset="cursor-byok-${VERSION}-windows-amd64.zip"
test -f "legacy-update/${asset}"
test -f "legacy-update/${asset}.sig"
node .github/scripts/generate-portable-update.mjs \
--version "${VERSION}" \
--repository "${GITHUB_REPOSITORY}" \
--asset "${asset}" \
--signature "legacy-update/${asset}.sig" \
--output legacy-update/portable-latest.json
- name: Normalize Tauri updater download URLs
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ needs.prepare.outputs.version }}
run: |
mkdir -p tauri-update
release_id=$(
gh api "repos/${GITHUB_REPOSITORY}/releases?per_page=100" \
--jq ".[] | select(.tag_name == \"v${VERSION}\") | .id"
)
test -n "${release_id}"
gh api "repos/${GITHUB_REPOSITORY}/releases/${release_id}" > tauri-update/release.json
asset_id=$(node -p 'require("./tauri-update/release.json").assets.find(({ name }) => name === "latest.json")?.id ?? ""')
test -n "${asset_id}"
gh api \
-H "Accept: application/octet-stream" \
"repos/${GITHUB_REPOSITORY}/releases/assets/${asset_id}" \
> tauri-update/latest.json
node .github/scripts/normalize-tauri-update.mjs \
--manifest tauri-update/latest.json \
--release tauri-update/release.json \
--repository "${GITHUB_REPOSITORY}" \
--version "${VERSION}"
- name: Upload normalized Tauri updater manifest
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ needs.prepare.outputs.version }}
run: gh release upload "v${VERSION}" tauri-update/latest.json --clobber
- name: Upload legacy updater assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ needs.prepare.outputs.version }}
run: gh release upload "v${VERSION}" legacy-update/* --clobber
- name: Publish the completed release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ needs.prepare.outputs.version }}
run: gh release edit "v${VERSION}" --draft=false --latest