Skip to content

Commit 2890ae1

Browse files
committed
merge: integrate origin/main into windows distribution smoke
2 parents 8d97e3b + 75b5353 commit 2890ae1

File tree

251 files changed

+29223
-3581
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

251 files changed

+29223
-3581
lines changed

.github/workflows/desktop-build.yml

Lines changed: 95 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,27 @@ permissions:
5353

5454
jobs:
5555
build:
56-
runs-on: macos-14
56+
strategy:
57+
matrix:
58+
include:
59+
- runner: macos-14
60+
arch: arm64
61+
- runner: macos-15-intel
62+
arch: x64
63+
runs-on: ${{ matrix.runner }}
5764

5865
steps:
5966
- name: Checkout
6067
uses: actions/checkout@v4
6168

69+
- name: Validate channel input
70+
shell: bash
71+
run: |
72+
case "${{ inputs.channel }}" in
73+
stable|beta|nightly) ;;
74+
*) echo "Invalid channel: ${{ inputs.channel }}" >&2; exit 1 ;;
75+
esac
76+
6277
- name: Setup pnpm
6378
uses: pnpm/action-setup@v4
6479
with:
@@ -74,6 +89,8 @@ jobs:
7489
- name: Resolve build metadata
7590
id: meta
7691
shell: bash
92+
env:
93+
ARCH: ${{ matrix.arch }}
7794
run: |
7895
set -euo pipefail
7996
@@ -103,7 +120,7 @@ jobs:
103120
echo "build_date=$build_date"
104121
echo "short_sha=$short_sha"
105122
echo "channel=$channel"
106-
echo "artifact_name=desktop-${channel}-${build_date}-${short_sha}"
123+
echo "artifact_name=desktop-${channel}-${ARCH}-${build_date}-${short_sha}"
107124
} >> "$GITHUB_OUTPUT"
108125
109126
- name: Prepare Apple signing certificate
@@ -142,6 +159,7 @@ jobs:
142159
BUILD_SOURCE: ${{ inputs.build_source }}
143160
BUILD_BRANCH: ${{ github.ref_name }}
144161
BUILD_COMMIT: ${{ github.sha }}
162+
BUILD_ARCH: ${{ matrix.arch }}
145163
SENTRY_DSN_TEST: ${{ secrets.SENTRY_DSN_NEXU_DESKTOP_TEST }}
146164
SENTRY_DSN_PROD: ${{ secrets.SENTRY_DSN_NEXU_DESKTOP_PROD }}
147165
run: |
@@ -158,13 +176,14 @@ jobs:
158176
NEXU_LINK_URL: process.env.LINK_URL === "null" ? null : process.env.LINK_URL,
159177
NEXU_SENTRY_ENV: process.env.SENTRY_ENV,
160178
NEXU_DESKTOP_APP_VERSION: version,
179+
NEXU_DESKTOP_UPDATE_CHANNEL: "${{ inputs.channel }}",
161180
NEXU_DESKTOP_BUILD_SOURCE: process.env.BUILD_SOURCE,
162181
NEXU_DESKTOP_BUILD_BRANCH: process.env.BUILD_BRANCH,
163182
NEXU_DESKTOP_BUILD_COMMIT: process.env.BUILD_COMMIT,
164183
NEXU_DESKTOP_BUILD_TIME: process.env.BUILT_AT,
165184
};
166185
if (process.env.UPDATE_FEED_URL) {
167-
config.NEXU_UPDATE_FEED_URL = process.env.UPDATE_FEED_URL;
186+
config.NEXU_UPDATE_FEED_URL = `${process.env.UPDATE_FEED_URL.replace(/\/$/, "")}/${process.env.BUILD_ARCH}`;
168187
}
169188
if (sentryDsn) {
170189
config.NEXU_DESKTOP_SENTRY_DSN = sentryDsn;
@@ -183,7 +202,10 @@ jobs:
183202
APPLE_ID: ${{ secrets.APPLE_ID }}
184203
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
185204
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
205+
NEXU_DESKTOP_TARGET_ARCH: ${{ matrix.arch }}
186206
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
207+
VITE_AMPLITUDE_API_KEY: ${{ secrets.AMPLITUDE_API_KEY }}
208+
AMPLITUDE_API_KEY: ${{ secrets.AMPLITUDE_API_KEY }}
187209
run: pnpm --filter @nexu/desktop dist:mac
188210

189211
- name: Prepare artifacts
@@ -192,6 +214,8 @@ jobs:
192214
env:
193215
CHANNEL: ${{ inputs.channel }}
194216
VERSION: ${{ steps.meta.outputs.desktop_version }}
217+
SHORT_SHA: ${{ steps.meta.outputs.short_sha }}
218+
ARCH: ${{ matrix.arch }}
195219
run: |
196220
set -euo pipefail
197221
shopt -s nullglob
@@ -207,11 +231,21 @@ jobs:
207231
exit 1
208232
fi
209233
210-
# New naming: nexu-{version}-{channel}-{arch}.{ext}
211-
versioned_dmg="nexu-${VERSION}-${CHANNEL}-arm64.dmg"
212-
versioned_zip="nexu-${VERSION}-${CHANNEL}-arm64.zip"
213-
latest_dmg="nexu-latest-${CHANNEL}-arm64.dmg"
214-
latest_zip="nexu-latest-${CHANNEL}-arm64.zip"
234+
artifact_version="$VERSION"
235+
if [ "$CHANNEL" != "stable" ]; then
236+
artifact_version="${VERSION}.${SHORT_SHA}"
237+
fi
238+
239+
latest_name_prefix="nexu-latest-mac-${ARCH}"
240+
if [ "$CHANNEL" != "stable" ]; then
241+
latest_name_prefix="nexu-latest-${CHANNEL}-mac-${ARCH}"
242+
fi
243+
244+
versioned_dmg="nexu-${artifact_version}-mac-${ARCH}.dmg"
245+
versioned_zip="nexu-${artifact_version}-mac-${ARCH}.zip"
246+
latest_dmg="${latest_name_prefix}.dmg"
247+
latest_zip="${latest_name_prefix}.zip"
248+
checksum_file="desktop-${ARCH}-sha256.txt"
215249
216250
cp "${dmg_files[0]}" "apps/desktop/channel-artifacts/${versioned_dmg}"
217251
cp "${zip_files[0]}" "apps/desktop/channel-artifacts/${versioned_zip}"
@@ -221,13 +255,14 @@ jobs:
221255
shasum -a 256 \
222256
"apps/desktop/channel-artifacts/${versioned_dmg}" \
223257
"apps/desktop/channel-artifacts/${versioned_zip}" \
224-
> apps/desktop/channel-artifacts/desktop-sha256.txt
258+
> "apps/desktop/channel-artifacts/${checksum_file}"
225259
226260
{
227261
echo "versioned_dmg=$versioned_dmg"
228262
echo "versioned_zip=$versioned_zip"
229263
echo "latest_dmg=$latest_dmg"
230264
echo "latest_zip=$latest_zip"
265+
echo "checksum_file=$checksum_file"
231266
} >> "$GITHUB_OUTPUT"
232267
233268
- name: Upload workflow artifacts
@@ -237,7 +272,7 @@ jobs:
237272
path: |
238273
apps/desktop/channel-artifacts/${{ steps.artifacts.outputs.versioned_dmg }}
239274
apps/desktop/channel-artifacts/${{ steps.artifacts.outputs.versioned_zip }}
240-
apps/desktop/channel-artifacts/desktop-sha256.txt
275+
apps/desktop/channel-artifacts/${{ steps.artifacts.outputs.checksum_file }}
241276
retention-days: 7
242277
if-no-files-found: error
243278

@@ -266,13 +301,14 @@ jobs:
266301
files: |
267302
apps/desktop/channel-artifacts/${{ steps.artifacts.outputs.versioned_dmg }}
268303
apps/desktop/channel-artifacts/${{ steps.artifacts.outputs.versioned_zip }}
269-
apps/desktop/channel-artifacts/desktop-sha256.txt
304+
apps/desktop/channel-artifacts/${{ steps.artifacts.outputs.checksum_file }}
270305
271306
- name: Patch latest-mac.yml for channel naming
272307
if: inputs.update_feed_url != ''
273308
env:
274309
VERSION: ${{ steps.meta.outputs.desktop_version }}
275310
CHANNEL: ${{ inputs.channel }}
311+
ARCH: ${{ matrix.arch }}
276312
VERSIONED_DMG: ${{ steps.artifacts.outputs.versioned_dmg }}
277313
VERSIONED_ZIP: ${{ steps.artifacts.outputs.versioned_zip }}
278314
shell: bash
@@ -284,11 +320,11 @@ jobs:
284320
exit 0
285321
fi
286322
287-
# electron-builder generates filenames without channel, we need to patch them
288-
# Original: nexu-0.1.3-arm64.dmg
289-
# Target: nexu-0.1.3-nightly-arm64.dmg
290-
original_dmg="nexu-${VERSION}-arm64.dmg"
291-
original_zip="nexu-${VERSION}-arm64.zip"
323+
# electron-builder generates filenames without the final public naming format.
324+
# Original: nexu-0.1.3-nightly.20260325-arm64.dmg
325+
# Target: nexu-0.1.3-nightly.20260325.ab12cd3-mac-arm64.dmg
326+
original_dmg="nexu-${VERSION}-${ARCH}.dmg"
327+
original_zip="nexu-${VERSION}-${ARCH}.zip"
292328
293329
echo "Patching latest-mac.yml:"
294330
echo " ${original_dmg} → ${VERSIONED_DMG}"
@@ -309,6 +345,7 @@ jobs:
309345
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
310346
UPDATE_FEED_URL: ${{ inputs.update_feed_url }}
311347
CHANNEL: ${{ inputs.channel }}
348+
ARCH: ${{ matrix.arch }}
312349
VERSIONED_DMG: ${{ steps.artifacts.outputs.versioned_dmg }}
313350
VERSIONED_ZIP: ${{ steps.artifacts.outputs.versioned_zip }}
314351
LATEST_DMG: ${{ steps.artifacts.outputs.latest_dmg }}
@@ -317,34 +354,42 @@ jobs:
317354
run: |
318355
set -euo pipefail
319356
320-
# Extract R2 prefix from feed URL path (e.g. https://desktop-releases.nexu.io/nightly → nightly)
321-
r2_prefix=$(node -e "process.stdout.write(new URL(process.env.UPDATE_FEED_URL).pathname.slice(1))")
357+
upload_prefix() {
358+
local prefix="$1"
322359
323-
echo "[r2] uploading to prefix: ${r2_prefix}/"
360+
echo "[r2] uploading to prefix: ${prefix}/"
324361
325-
# Upload patched latest-mac.yml for auto-update
326-
if [ -f "apps/desktop/release/latest-mac.yml" ]; then
327-
echo "Uploading latest-mac.yml → ${r2_prefix}/latest-mac.yml"
328-
npx wrangler r2 object put "nexu-desktop-releases/${r2_prefix}/latest-mac.yml" --file "apps/desktop/release/latest-mac.yml" --remote
329-
fi
362+
if [ -f "apps/desktop/release/latest-mac.yml" ]; then
363+
echo "Uploading latest-mac.yml → ${prefix}/latest-mac.yml"
364+
npx wrangler r2 object put "nexu-desktop-releases/${prefix}/latest-mac.yml" --file "apps/desktop/release/latest-mac.yml" --remote
365+
fi
366+
367+
echo "Uploading ${VERSIONED_DMG} → ${prefix}/${VERSIONED_DMG}"
368+
npx wrangler r2 object put "nexu-desktop-releases/${prefix}/${VERSIONED_DMG}" --file "apps/desktop/channel-artifacts/${VERSIONED_DMG}" --remote
369+
370+
echo "Uploading ${VERSIONED_ZIP} → ${prefix}/${VERSIONED_ZIP}"
371+
npx wrangler r2 object put "nexu-desktop-releases/${prefix}/${VERSIONED_ZIP}" --file "apps/desktop/channel-artifacts/${VERSIONED_ZIP}" --remote
372+
373+
echo "Uploading ${LATEST_DMG} → ${prefix}/${LATEST_DMG}"
374+
npx wrangler r2 object put "nexu-desktop-releases/${prefix}/${LATEST_DMG}" --file "apps/desktop/channel-artifacts/${LATEST_DMG}" --remote
330375
331-
# Upload versioned artifacts
332-
echo "Uploading ${VERSIONED_DMG} → ${r2_prefix}/${VERSIONED_DMG}"
333-
npx wrangler r2 object put "nexu-desktop-releases/${r2_prefix}/${VERSIONED_DMG}" --file "apps/desktop/channel-artifacts/${VERSIONED_DMG}" --remote
376+
echo "Uploading ${LATEST_ZIP} → ${prefix}/${LATEST_ZIP}"
377+
npx wrangler r2 object put "nexu-desktop-releases/${prefix}/${LATEST_ZIP}" --file "apps/desktop/channel-artifacts/${LATEST_ZIP}" --remote
334378
335-
echo "Uploading ${VERSIONED_ZIP} → ${r2_prefix}/${VERSIONED_ZIP}"
336-
npx wrangler r2 object put "nexu-desktop-releases/${r2_prefix}/${VERSIONED_ZIP}" --file "apps/desktop/channel-artifacts/${VERSIONED_ZIP}" --remote
379+
echo "Uploading ${{ steps.artifacts.outputs.checksum_file }} → ${prefix}/${{ steps.artifacts.outputs.checksum_file }}"
380+
npx wrangler r2 object put "nexu-desktop-releases/${prefix}/${{ steps.artifacts.outputs.checksum_file }}" --file "apps/desktop/channel-artifacts/${{ steps.artifacts.outputs.checksum_file }}" --remote
381+
}
337382
338-
# Upload latest artifacts (overwrite each time)
339-
echo "Uploading ${LATEST_DMG} → ${r2_prefix}/${LATEST_DMG}"
340-
npx wrangler r2 object put "nexu-desktop-releases/${r2_prefix}/${LATEST_DMG}" --file "apps/desktop/channel-artifacts/${LATEST_DMG}" --remote
383+
# Extract R2 prefix from feed URL path (e.g. https://desktop-releases.nexu.io/nightly → nightly)
384+
channel_prefix=$(node -e 'const path = new URL(process.env.UPDATE_FEED_URL).pathname.replace(/^\//, "").replace(/\/$/, ""); process.stdout.write(path);')
385+
r2_prefix="${channel_prefix}/${ARCH}"
341386
342-
echo "Uploading ${LATEST_ZIP} → ${r2_prefix}/${LATEST_ZIP}"
343-
npx wrangler r2 object put "nexu-desktop-releases/${r2_prefix}/${LATEST_ZIP}" --file "apps/desktop/channel-artifacts/${LATEST_ZIP}" --remote
387+
upload_prefix "$r2_prefix"
344388
345-
# Upload checksum
346-
echo "Uploading desktop-sha256.txt → ${r2_prefix}/desktop-sha256.txt"
347-
npx wrangler r2 object put "nexu-desktop-releases/${r2_prefix}/desktop-sha256.txt" --file "apps/desktop/channel-artifacts/desktop-sha256.txt" --remote
389+
if [ "${ARCH}" = "arm64" ]; then
390+
echo "[r2] uploading legacy arm64 compatibility feed to prefix: ${channel_prefix}/"
391+
upload_prefix "$channel_prefix"
392+
fi
348393
349394
- name: Purge Cloudflare CDN cache for latest artifacts
350395
if: inputs.update_feed_url != '' && env.CLOUDFLARE_ZONE_ID != ''
@@ -354,17 +399,27 @@ jobs:
354399
UPDATE_FEED_URL: ${{ inputs.update_feed_url }}
355400
LATEST_DMG: ${{ steps.artifacts.outputs.latest_dmg }}
356401
LATEST_ZIP: ${{ steps.artifacts.outputs.latest_zip }}
402+
ARCH: ${{ matrix.arch }}
357403
shell: bash
358404
run: |
359405
set -euo pipefail
360406
361-
base_url="${UPDATE_FEED_URL%/}"
407+
base_url="${UPDATE_FEED_URL%/}/${ARCH}"
362408
urls=(
363409
"${base_url}/${LATEST_DMG}"
364410
"${base_url}/${LATEST_ZIP}"
365411
"${base_url}/latest-mac.yml"
366412
)
367413
414+
if [ "${ARCH}" = "arm64" ]; then
415+
legacy_base_url="${UPDATE_FEED_URL%/}"
416+
urls+=(
417+
"${legacy_base_url}/${LATEST_DMG}"
418+
"${legacy_base_url}/${LATEST_ZIP}"
419+
"${legacy_base_url}/latest-mac.yml"
420+
)
421+
fi
422+
368423
files_json=$(printf '%s\n' "${urls[@]}" | jq -R . | jq -sc '.')
369424
echo "[cf-purge] Purging: ${files_json}"
370425
@@ -383,11 +438,12 @@ jobs:
383438
VERSIONED_ZIP: ${{ steps.artifacts.outputs.versioned_zip }}
384439
LATEST_DMG: ${{ steps.artifacts.outputs.latest_dmg }}
385440
LATEST_ZIP: ${{ steps.artifacts.outputs.latest_zip }}
441+
ARCH: ${{ matrix.arch }}
386442
shell: bash
387443
run: |
388444
set -euo pipefail
389445
390-
base_url="${UPDATE_FEED_URL%/}"
446+
base_url="${UPDATE_FEED_URL%/}/${ARCH}"
391447
latest_yml_url="${base_url}/latest-mac.yml"
392448
versioned_dmg_url="${base_url}/${VERSIONED_DMG}"
393449
versioned_zip_url="${base_url}/${VERSIONED_ZIP}"

.github/workflows/desktop-ci-dev.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ jobs:
9393
if: matrix.os == 'macos'
9494
env:
9595
NEXU_DESKTOP_CHECK_CAPTURE_DIR: ${{ runner.temp }}/desktop-ci
96+
NEXU_USE_LAUNCHD: "0"
9697
run: pnpm check:dev
9798

9899
- name: Verify Windows desktop build pipeline

.github/workflows/desktop-ci-dist.yml

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,15 @@ jobs:
5050
matrix:
5151
include:
5252
- os: macos
53-
runs_on: macos-14
53+
runner: macos-14
54+
arch: arm64
55+
- os: macos
56+
runner: macos-15-intel
57+
arch: x64
5458
- os: windows
55-
runs_on: windows-latest
56-
57-
runs-on: ${{ matrix.runs_on }}
59+
runner: windows-latest
60+
arch: x64
61+
runs-on: ${{ matrix.runner }}
5862
timeout-minutes: 45
5963

6064
steps:
@@ -81,7 +85,9 @@ jobs:
8185
run: pnpm exec electron --version
8286

8387
- name: Build unsigned desktop bundle
84-
run: ${{ matrix.os == 'macos' && 'pnpm dist:mac:unsigned' || 'pnpm --filter @nexu/desktop dist:win:unsigned' }}
88+
env:
89+
NEXU_DESKTOP_TARGET_ARCH: ${{ matrix.arch }}
90+
run: ${{ matrix.os == 'windows' && 'pnpm --filter @nexu/desktop dist:win:unsigned' || 'pnpm dist:mac:unsigned' }}
8591

8692
- name: Set packaged app paths
8793
shell: pwsh
@@ -93,7 +99,7 @@ jobs:
9399
94100
if ("${{ matrix.os }}" -eq "macos") {
95101
$packagedUserDataDir = Join-Path $packagedHome "Library/Application Support/@nexu/desktop"
96-
$packagedApp = "apps/desktop/release/mac-arm64/Nexu.app"
102+
$packagedApp = "apps/desktop/release/mac-${{ matrix.arch }}/Nexu.app"
97103
$packagedExecutable = "$packagedApp/Contents/MacOS/Nexu"
98104
} else {
99105
$packagedUserDataDir = Join-Path $packagedHome "nexu-desktop"
@@ -131,6 +137,17 @@ jobs:
131137
if (-not (Test-Path $env:PACKAGED_APP)) { throw "Packaged app path missing: $env:PACKAGED_APP" }
132138
if (-not (Test-Path $env:PACKAGED_EXECUTABLE)) { throw "Packaged executable missing: $env:PACKAGED_EXECUTABLE" }
133139
140+
- name: Upload desktop build artifacts
141+
if: success() && matrix.os == 'macos'
142+
uses: actions/upload-artifact@v4
143+
with:
144+
name: desktop-ci-dist-${{ matrix.os }}-${{ matrix.arch }}
145+
path: |
146+
apps/desktop/release/*.dmg
147+
apps/desktop/release/*.zip
148+
if-no-files-found: error
149+
retention-days: 3
150+
134151
- name: Verify packaged runtime unit health
135152
if: matrix.os == 'macos'
136153
env:
@@ -161,7 +178,7 @@ jobs:
161178
if: always()
162179
uses: actions/upload-artifact@v4
163180
with:
164-
name: desktop-ci-logs-${{ matrix.os }}
181+
name: desktop-ci-logs-${{ matrix.os }}-${{ matrix.arch }}
165182
path: ${{ runner.temp }}/desktop-ci
166183
if-no-files-found: warn
167-
retention-days: 7
184+
retention-days: 3

0 commit comments

Comments
 (0)