Skip to content

Commit 5793180

Browse files
authored
feat: add ARM64 Linux build target and download detection (#427)
- CI: add ubuntu-24.04-arm matrix entry with aarch64-unknown-linux-gnu - Node sidecar: download linux-arm64 Node.js bundle for ARM runners - Download API: add linux-appimage-arm64 pattern for aarch64 AppImage - Download banner: show both x64 and ARM64 Linux options (UA can't distinguish) - Desktop updater: map Linux aarch64 to linux-appimage-arm64 for in-app updates - Smoke test: fix AppImage search path for cross-target builds
1 parent 334d320 commit 5793180

5 files changed

Lines changed: 39 additions & 7 deletions

File tree

.github/workflows/build-desktop.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ jobs:
5555
node_target: 'x86_64-unknown-linux-gnu'
5656
label: 'Linux-x64'
5757
timeout: 120
58+
- platform: 'ubuntu-24.04-arm'
59+
args: '--target aarch64-unknown-linux-gnu'
60+
node_target: 'aarch64-unknown-linux-gnu'
61+
label: 'Linux-ARM64'
62+
timeout: 120
5863

5964
runs-on: ${{ matrix.platform }}
6065
name: Build (${{ matrix.label }})
@@ -77,7 +82,7 @@ jobs:
7782
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7
7883
with:
7984
toolchain: stable
80-
targets: ${{ contains(matrix.platform, 'macos') && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
85+
targets: ${{ contains(matrix.platform, 'macos') && 'aarch64-apple-darwin,x86_64-apple-darwin' || (matrix.label == 'Linux-ARM64' && 'aarch64-unknown-linux-gnu' || '') }}
8186

8287
- name: Rust cache
8388
uses: swatinem/rust-cache@ad397744b0d591a723ab90405b7247fac0e6b8db
@@ -298,7 +303,7 @@ jobs:
298303
shell: bash
299304
run: |
300305
sudo apt-get install -y xvfb imagemagick
301-
APPIMAGE=$(find src-tauri/target/release/bundle/appimage -name '*.AppImage' | head -1)
306+
APPIMAGE=$(find src-tauri/target -path '*/bundle/appimage/*.AppImage' | head -1)
302307
if [ -z "$APPIMAGE" ]; then
303308
echo "::error::No AppImage found after build"
304309
exit 1

api/download.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const PLATFORM_PATTERNS = {
1010
'macos-arm64': (name) => name.endsWith('_aarch64.dmg'),
1111
'macos-x64': (name) => name.endsWith('_x64.dmg') && !name.includes('setup'),
1212
'linux-appimage': (name) => name.endsWith('_amd64.AppImage'),
13+
'linux-appimage-arm64': (name) => name.endsWith('_aarch64.AppImage'),
1314
};
1415

1516
const VARIANT_IDENTIFIERS = {

scripts/download-node.sh

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Supported targets:
1515
- x86_64-apple-darwin
1616
- aarch64-apple-darwin
1717
- x86_64-unknown-linux-gnu
18+
- aarch64-unknown-linux-gnu
1819
1920
Environment:
2021
NODE_VERSION Node.js version to bundle (default: 22.14.0)
@@ -72,7 +73,14 @@ if [[ -z "${TARGET}" ]]; then
7273
esac
7374
;;
7475
Linux)
75-
TARGET="x86_64-unknown-linux-gnu"
76+
case "${RUNNER_ARCH:-}" in
77+
ARM64|arm64)
78+
TARGET="aarch64-unknown-linux-gnu"
79+
;;
80+
*)
81+
TARGET="x86_64-unknown-linux-gnu"
82+
;;
83+
esac
7684
;;
7785
*)
7886
echo "Unsupported RUNNER_OS: ${RUNNER_OS}" >&2
@@ -96,7 +104,14 @@ if [[ -z "${TARGET}" ]]; then
96104
esac
97105
;;
98106
Linux)
99-
TARGET="x86_64-unknown-linux-gnu"
107+
case "$(uname -m)" in
108+
aarch64|arm64)
109+
TARGET="aarch64-unknown-linux-gnu"
110+
;;
111+
*)
112+
TARGET="x86_64-unknown-linux-gnu"
113+
;;
114+
esac
100115
;;
101116
MINGW*|MSYS*|CYGWIN*|Windows_NT)
102117
TARGET="x86_64-pc-windows-msvc"
@@ -135,6 +150,12 @@ case "${TARGET}" in
135150
NODE_RELATIVE_PATH="bin/node"
136151
OUTPUT_NAME="node"
137152
;;
153+
aarch64-unknown-linux-gnu)
154+
DIST_NAME="node-v${NODE_VERSION}-linux-arm64"
155+
ARCHIVE_NAME="${DIST_NAME}.tar.gz"
156+
NODE_RELATIVE_PATH="bin/node"
157+
OUTPUT_NAME="node"
158+
;;
138159
*)
139160
echo "Unsupported target: ${TARGET}" >&2
140161
exit 1

src/app/desktop-updater.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,9 @@ export class DesktopUpdater implements AppModule {
134134
}
135135

136136
if (normalizedOs === 'linux') {
137-
return normalizedArch === 'x86_64' ? 'linux-appimage' : null;
137+
if (normalizedArch === 'x86_64') return 'linux-appimage';
138+
if (normalizedArch === 'aarch64') return 'linux-appimage-arm64';
139+
return null;
138140
}
139141

140142
return null;

src/components/DownloadBanner.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ function dismiss(panel: HTMLElement, fromDownload = false): void {
3131
panel.addEventListener('transitionend', () => panel.remove(), { once: true });
3232
}
3333

34-
type Platform = 'macos-arm64' | 'macos-x64' | 'macos' | 'windows' | 'linux' | 'unknown';
34+
type Platform = 'macos-arm64' | 'macos-x64' | 'macos' | 'windows' | 'linux' | 'linux-x64' | 'linux-arm64' | 'unknown';
3535

3636
function detectPlatform(): Platform {
3737
const ua = navigator.userAgent;
@@ -64,7 +64,8 @@ function allButtons(): DlButton[] {
6464
{ cls: 'mac', href: '/api/download?platform=macos-arm64', label: `\uF8FF ${t('modals.downloadBanner.macSilicon')}` },
6565
{ cls: 'mac', href: '/api/download?platform=macos-x64', label: `\uF8FF ${t('modals.downloadBanner.macIntel')}` },
6666
{ cls: 'win', href: '/api/download?platform=windows-exe', label: `\u229E ${t('modals.downloadBanner.windows')}` },
67-
{ cls: 'linux', href: '/api/download?platform=linux-appimage', label: `\u{1F427} ${t('modals.downloadBanner.linux')}` },
67+
{ cls: 'linux', href: '/api/download?platform=linux-appimage', label: `\u{1F427} ${t('modals.downloadBanner.linux')} (x64)` },
68+
{ cls: 'linux', href: '/api/download?platform=linux-appimage-arm64', label: `\u{1F427} ${t('modals.downloadBanner.linux')} (ARM64)` },
6869
];
6970
}
7071

@@ -76,6 +77,8 @@ function buttonsForPlatform(p: Platform): DlButton[] {
7677
case 'macos': return buttons.filter(b => b.cls === 'mac');
7778
case 'windows': return buttons.filter(b => b.cls === 'win');
7879
case 'linux': return buttons.filter(b => b.cls === 'linux');
80+
case 'linux-x64': return buttons.filter(b => b.href.includes('linux-appimage') && !b.href.includes('arm64'));
81+
case 'linux-arm64': return buttons.filter(b => b.href.includes('linux-appimage-arm64'));
7982
default: return buttons;
8083
}
8184
}

0 commit comments

Comments
 (0)