Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
276 changes: 276 additions & 0 deletions .github/workflows/desktop_cd_legacy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
# Legacy desktop CD workflow for 0.0.x releases
# This workflow builds and releases the legacy Hyprnote app (fastrepl/hyprnote)
# which uses desktop.hyprnote.com for updates
on:
workflow_dispatch:
inputs:
channel:
description: "Release channel"
required: true
type: choice
options:
- staging
- nightly
- stable

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.channel }}
cancel-in-progress: true

env:
CN_APPLICATION: "fastrepl/hyprnote"
RELEASE_CHANNEL: ${{ inputs.channel }}
TAURI_CONF_PATH: ./src-tauri/tauri.conf.${{ inputs.channel }}.json
NODE_OPTIONS: "--max-old-space-size=4096"
LEGACY_BRANCH: "legacy/0.0.x"

jobs:
compute-version:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@v4
with:
ref: ${{ env.LEGACY_BRANCH }}
lfs: true
fetch-depth: 0
fetch-tags: true
- run: git fetch --tags --force
- id: version
run: |
# Get the latest 0.0.x tag
LATEST_TAG=$(git tag -l 'desktop_v0.0.*' --sort=-v:refname | head -n1)
if [[ -z "$LATEST_TAG" ]]; then
echo "Error: No desktop_v0.0.* tags found" >&2
exit 1
fi
echo "Latest legacy tag: $LATEST_TAG"

# Extract version number and increment patch
CURRENT_VERSION=$(echo "$LATEST_TAG" | sed 's/desktop_v//')
MAJOR=$(echo "$CURRENT_VERSION" | cut -d. -f1)
MINOR=$(echo "$CURRENT_VERSION" | cut -d. -f2)
PATCH=$(echo "$CURRENT_VERSION" | cut -d. -f3)
Comment on lines +43 to +54
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Version computation incorrectly increments patch when nightly tags exist

The version computation logic on line 43 uses git tag -l 'desktop_v0.0.*' which matches BOTH stable tags (e.g., desktop_v0.0.83) and nightly tags (e.g., desktop_v0.0.84-nightly.1). After a nightly release is created, the nightly tag will sort higher than the stable tag due to version sorting (0.0.84-nightly.1 > 0.0.83).

When this happens:

  • LATEST_TAG becomes desktop_v0.0.84-nightly.1
  • PATCH is extracted as 84-nightly (line 54: cut -d. -f3)
  • Bash arithmetic strips the suffix, making PATCH=84
  • For a subsequent nightly: NEW_PATCH=85, producing version 0.0.85-nightly.1 (wrong - should be 0.0.84-nightly.2)
  • For a stable release: NEW_PATCH=85, producing version 0.0.85 (wrong - should be 0.0.84)

This causes version numbers to skip unexpectedly after any nightly release, breaking the sequential versioning scheme.

Recommendation: Filter to only stable tags when getting the latest version base. Change line 43 to: LATEST_TAG=$(git tag -l 'desktop_v0.0.*' --sort=-v:refname | grep -v '\-nightly' | head -n1) to exclude nightly tags from consideration.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


if [[ "${{ inputs.channel }}" == "staging" ]]; then
# For staging, use current version with dev suffix
DISTANCE=$(git rev-list --count $LATEST_TAG..HEAD)
SHORT_HASH=$(git rev-parse --short HEAD)
VERSION="${MAJOR}.${MINOR}.${PATCH}.dev.${DISTANCE}+${SHORT_HASH}"
elif [[ "${{ inputs.channel }}" == "nightly" ]]; then
# For nightly, increment patch and add nightly suffix
NEW_PATCH=$((PATCH + 1))
# Check if there's already a nightly for this version
NIGHTLY_COUNT=$(git tag -l "desktop_v${MAJOR}.${MINOR}.${NEW_PATCH}-nightly.*" | wc -l)
if [[ $NIGHTLY_COUNT -gt 0 ]]; then
LATEST_NIGHTLY=$(git tag -l "desktop_v${MAJOR}.${MINOR}.${NEW_PATCH}-nightly.*" --sort=-v:refname | head -n1)
NIGHTLY_NUM=$(echo "$LATEST_NIGHTLY" | sed 's/.*-nightly\.//')
NEW_NIGHTLY=$((NIGHTLY_NUM + 1))
else
NEW_NIGHTLY=1
fi
VERSION="${MAJOR}.${MINOR}.${NEW_PATCH}-nightly.${NEW_NIGHTLY}"
elif [[ "${{ inputs.channel }}" == "stable" ]]; then
# For stable, increment patch
NEW_PATCH=$((PATCH + 1))
VERSION="${MAJOR}.${MINOR}.${NEW_PATCH}"
fi

echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Computed version: $VERSION"

cn-draft:
if: ${{ inputs.channel != 'staging' }}
needs: compute-version
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ env.LEGACY_BRANCH }}
- run: ./scripts/version.sh "./apps/desktop/src-tauri/tauri.conf.json" "${{ needs.compute-version.outputs.version }}"
- uses: ./.github/actions/cn_release
with:
cmd: draft
app: ${{ env.CN_APPLICATION }}
key: ${{ secrets.CN_API_KEY }}
channel: ${{ env.RELEASE_CHANNEL }}
framework: tauri
working-directory: ./apps/desktop

build-macos:
needs: [compute-version, cn-draft]
if: ${{ !cancelled() && (needs.cn-draft.result == 'success' || needs.cn-draft.result == 'skipped') }}
permissions:
contents: write
runs-on: depot-macos-14
strategy:
fail-fast: true
matrix:
include: ${{ inputs.channel == 'staging' && fromJSON('[{"target":"aarch64-apple-darwin","arch":"aarch64","artifact_name":"silicon"}]') || fromJSON('[{"target":"aarch64-apple-darwin","arch":"aarch64","artifact_name":"silicon"}]') }}
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
with:
ref: ${{ env.LEGACY_BRANCH }}
lfs: true
fetch-depth: 0
fetch-tags: true
- run: git fetch --tags --force
- uses: ./.github/actions/macos_tcc
- run: ./scripts/version.sh "./apps/desktop/src-tauri/tauri.conf.json" "${{ needs.compute-version.outputs.version }}"
- uses: ./.github/actions/setup_protoc
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- uses: ./.github/actions/install_desktop_deps
with:
target: macos
- uses: ./.github/actions/rust_install
with:
platform: macos
- uses: ./.github/actions/pnpm_install
- uses: ./.github/actions/poetry_install
- run: poetry run python scripts/pre_build.py
- run: pnpm -F desktop lingui:compile
- run: pnpm -F ui build
- run: |
aws s3 cp s3://hyprnote-cache2/v0/binaries/stt apps/desktop/src-tauri/binaries/stt-${{ matrix.target }} \
--endpoint-url ${{ secrets.CLOUDFLARE_R2_ENDPOINT_URL }} \
--region auto
env:
AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }}
- run: |
chmod +x ./apps/desktop/src-tauri/binaries/stt-${{ matrix.target }}
./scripts/sidecar.sh "./apps/desktop/${{ env.TAURI_CONF_PATH }}" "binaries/stt"
- uses: ./.github/actions/apple_cert
id: apple-cert
with:
apple-certificate: ${{ secrets.APPLE_CERTIFICATE }}
apple-certificate-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
keychain-password: ${{ secrets.KEYCHAIN_PASSWORD }}
- run: |
FEATURES_FLAG=""
if [[ "${{ inputs.channel }}" == "staging" ]]; then
FEATURES_FLAG="--features devtools"
fi
pnpm -F desktop tauri build --target ${{ matrix.target }} --config ${{ env.TAURI_CONF_PATH }} --verbose $FEATURES_FLAG
env:
CI: false
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AM_API_KEY: ${{ secrets.AM_API_KEY }}
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
APP_VERSION: ${{ needs.compute-version.outputs.version }}
KEYGEN_ACCOUNT_ID: ${{ secrets.KEYGEN_ACCOUNT_ID }}
KEYGEN_VERIFY_KEY: ${{ secrets.KEYGEN_VERIFY_KEY }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ steps.apple-cert.outputs.cert-id }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
- run: |
mkdir -p target/release/
find target/${{ matrix.target }}/release -type f -not -path "*/\.*" -exec cp {} target/release/ \;
shell: bash
working-directory: ./apps/desktop/src-tauri
- if: ${{ inputs.channel != 'staging' }}
uses: ./.github/actions/cn_release
with:
cmd: upload
app: ${{ env.CN_APPLICATION }}
key: ${{ secrets.CN_API_KEY }}
channel: ${{ env.RELEASE_CHANNEL }}
framework: tauri
working-directory: ./apps/desktop
- if: ${{ inputs.channel != 'staging' }}
run: |
DMG_FILE=$(find "apps/desktop/src-tauri/target/${{ matrix.target }}/release/bundle/dmg/" -name "*.dmg" -type f)
cp "$DMG_FILE" "hyprnote-legacy-macos-${{ matrix.arch }}.dmg"
aws s3 cp "hyprnote-legacy-macos-${{ matrix.arch }}.dmg" \
"s3://hyprnote-build/desktop-legacy/${{ needs.compute-version.outputs.version }}/hyprnote-macos-${{ matrix.arch }}.dmg" \
--endpoint-url ${{ secrets.CLOUDFLARE_R2_ENDPOINT_URL }} \
--region auto
env:
AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }}
- if: ${{ inputs.channel == 'staging' }}
uses: actions/upload-artifact@v4
with:
name: hyprnote-legacy-staging-macos-${{ matrix.artifact_name }}
path: apps/desktop/src-tauri/target/${{ matrix.target }}/release/bundle/dmg/*.dmg
retention-days: 3

cn-publish:
if: ${{ inputs.channel != 'staging' }}
needs: [compute-version, build-macos]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ env.LEGACY_BRANCH }}
- run: ./scripts/version.sh "./apps/desktop/src-tauri/tauri.conf.json" "${{ needs.compute-version.outputs.version }}"
- uses: ./.github/actions/cn_release
with:
cmd: publish
app: ${{ env.CN_APPLICATION }}
key: ${{ secrets.CN_API_KEY }}
channel: ${{ env.RELEASE_CHANNEL }}
framework: tauri
working-directory: ./apps/desktop

release:
if: ${{ inputs.channel != 'staging' }}
needs: [compute-version, build-macos, cn-publish]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
ref: ${{ env.LEGACY_BRANCH }}
- if: ${{ needs.build-macos.result == 'success' }}
run: |
aws s3 cp \
"s3://hyprnote-build/desktop-legacy/${{ needs.compute-version.outputs.version }}/hyprnote-macos-aarch64.dmg" \
"hyprnote-macos-aarch64.dmg" \
--endpoint-url ${{ secrets.CLOUDFLARE_R2_ENDPOINT_URL }} \
--region auto
env:
AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }}
- uses: mathieudutour/github-tag-action@v6.2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
custom_tag: desktop_v${{ needs.compute-version.outputs.version }}
tag_prefix: ""
- id: checksums
uses: ./.github/actions/generate_checksums
with:
files: |
${{ needs.build-macos.result == 'success' && 'hyprnote-macos-aarch64.dmg' || '' }}
- id: artifacts
run: |
ARTIFACTS=""
if [[ "${{ needs.build-macos.result }}" == "success" ]]; then
ARTIFACTS="hyprnote-macos-aarch64.dmg"
fi
if [[ -z "$ARTIFACTS" ]]; then
echo "No artifacts to release" >&2
exit 1
fi
if [[ -n "${{ steps.checksums.outputs.checksum_files }}" ]]; then
ARTIFACTS="$ARTIFACTS,${{ steps.checksums.outputs.checksum_files }}"
fi
echo "list=$ARTIFACTS" >> $GITHUB_OUTPUT
- uses: ncipollo/release-action@v1
with:
tag: desktop_v${{ needs.compute-version.outputs.version }}
name: desktop_v${{ needs.compute-version.outputs.version }} (Legacy)
body: "Legacy release for Hyprnote 0.0.x series"
artifacts: ${{ steps.artifacts.outputs.list }}