build: publish linux arm64 release artifacts (#4755) · xhwgithub/orca@531bcc9 · GitHub
Skip to content

Commit 531bcc9

Browse files
authored
build: publish linux arm64 release artifacts (stablyai#4755)
1 parent 2400056 commit 531bcc9

11 files changed

Lines changed: 164 additions & 20 deletions

‎.github/workflows/release-cut.yml‎

Lines changed: 8 additions & 2 deletions

‎config/electron-builder.config.cjs‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const {
99
} = require('./packaged-runtime-node-modules.cjs')
1010

1111
const isMacRelease = process.env.ORCA_MAC_RELEASE === '1'
12+
const isLinuxArm64Release = process.env.ORCA_LINUX_ARM64_RELEASE === '1'
1213
const featureWallResources = {
1314
from: 'resources/onboarding/feature-wall',
1415
to: 'onboarding/feature-wall'
@@ -273,12 +274,12 @@ module.exports = {
273274
},
274275
featureWallResources
275276
],
276-
target: ['AppImage', 'deb', 'rpm'],
277+
target: ['AppImage', 'deb'],
277278
maintainer: 'stablyai',
278279
category: 'Utility'
279280
},
280281
appImage: {
281-
artifactName: 'orca-linux.${ext}'
282+
artifactName: isLinuxArm64Release ? 'orca-linux-arm64.${ext}' : 'orca-linux.${ext}'
282283
},
283284
deb: {
284285
packageName: 'orca-ide',

‎config/scripts/electron-builder-config.test.mjs‎

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ describe('electron-builder config', () => {
7171
expect(electronBuilderConfig.linux.desktop.entry.StartupWMClass).toBe('orca')
7272
})
7373

74-
it('builds RPMs without changing existing Linux artifact names', () => {
75-
expect(electronBuilderConfig.linux.target).toEqual(['AppImage', 'deb', 'rpm'])
74+
it('uses AppImage and deb as local Linux targets without changing existing artifact names', () => {
75+
expect(electronBuilderConfig.linux.target).toEqual(['AppImage', 'deb'])
7676
expect(electronBuilderConfig.appImage.artifactName).toBe('orca-linux.${ext}')
7777
expect(electronBuilderConfig.deb.artifactName).toBe('orca-ide_${version}_${arch}.${ext}')
7878
expect(electronBuilderConfig.rpm).toMatchObject({
@@ -81,6 +81,26 @@ describe('electron-builder config', () => {
8181
})
8282
})
8383

84+
it('uses a distinct AppImage name for Linux arm64 release uploads', () => {
85+
const configPath = require.resolve('../electron-builder.config.cjs')
86+
const original = process.env.ORCA_LINUX_ARM64_RELEASE
87+
try {
88+
delete require.cache[configPath]
89+
process.env.ORCA_LINUX_ARM64_RELEASE = '1'
90+
expect(require('../electron-builder.config.cjs').appImage.artifactName).toBe(
91+
'orca-linux-arm64.${ext}'
92+
)
93+
} finally {
94+
if (original === undefined) {
95+
delete process.env.ORCA_LINUX_ARM64_RELEASE
96+
} else {
97+
process.env.ORCA_LINUX_ARM64_RELEASE = original
98+
}
99+
delete require.cache[configPath]
100+
require('../electron-builder.config.cjs')
101+
}
102+
})
103+
84104
it('uses Orca native rebuild hook instead of electron-builder default rebuild', () => {
85105
expect(electronBuilderConfig.beforeBuild).toBe(electronBuilderNativeRebuild)
86106
expect(electronBuilderConfig.npmRebuild).toBe(true)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import os from 'node:os'
2+
3+
const maxOldSpaceSizeMb = 4096
4+
const minOldSpaceSizeMb = 2048
5+
const reservedSystemMemoryMb = 1024
6+
7+
export function getBuildOldSpaceSizeMb(totalMemoryBytes = os.totalmem()) {
8+
const totalMemoryMb = Math.floor(totalMemoryBytes / 1024 / 1024)
9+
const hostSizedLimitMb = Math.max(minOldSpaceSizeMb, totalMemoryMb - reservedSystemMemoryMb)
10+
11+
return Math.min(maxOldSpaceSizeMb, hostSizedLimitMb)
12+
}
13+
14+
export function appendBuildOldSpaceOption(existingNodeOptions, totalMemoryBytes = os.totalmem()) {
15+
const requestedNodeOptions = `--max-old-space-size=${getBuildOldSpaceSizeMb(totalMemoryBytes)}`
16+
const trimmedNodeOptions = existingNodeOptions?.trim()
17+
18+
return trimmedNodeOptions ? `${trimmedNodeOptions} ${requestedNodeOptions}` : requestedNodeOptions
19+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { appendBuildOldSpaceOption, getBuildOldSpaceSizeMb } from './node-old-space-limit.mjs'
3+
4+
const gib = 1024 * 1024 * 1024
5+
6+
describe('node old-space build limit', () => {
7+
it('caps larger hosts at the release-runner build heap', () => {
8+
expect(getBuildOldSpaceSizeMb(8 * gib)).toBe(4096)
9+
})
10+
11+
it('reserves memory for smaller arm64 hosts', () => {
12+
expect(getBuildOldSpaceSizeMb(4 * gib)).toBe(3072)
13+
})
14+
15+
it('keeps existing NODE_OPTIONS while appending the build heap limit last', () => {
16+
expect(appendBuildOldSpaceOption('--trace-warnings --max-old-space-size=8192', 4 * gib)).toBe(
17+
'--trace-warnings --max-old-space-size=8192 --max-old-space-size=3072'
18+
)
19+
})
20+
})

‎config/scripts/package-electron-runtime-contract.test.mjs‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ describe('Electron runtime package contract', () => {
5050
expect(scripts['build:release']).not.toContain('build:computer-macos')
5151
})
5252

53+
it('runs the web build through the heap-sized Vite wrapper', () => {
54+
expect(packageJson.scripts['build:web']).toContain('node config/scripts/run-vite-web-build.mjs')
55+
expect(packageJson.scripts['build:web']).toContain('node config/scripts/verify-web-build.mjs')
56+
})
57+
5358
it('guards release publishing before electron-builder runs', () => {
5459
const releaseWorkflow = readFileSync(
5560
join(projectDir, '.github/workflows/release-cut.yml'),
@@ -63,7 +68,7 @@ describe('Electron runtime package contract', () => {
6368
])
6469
)
6570

66-
expect([...releaseCommands.keys()].sort()).toEqual(['linux', 'mac', 'win'])
71+
expect([...releaseCommands.keys()].sort()).toEqual(['linux-arm64', 'linux-x64', 'mac', 'win'])
6772
for (const command of releaseCommands.values()) {
6873
expect(command).toContain('node config/scripts/ensure-native-runtime.mjs --runtime=electron')
6974
expect(command).toContain('electron-builder')
@@ -72,7 +77,10 @@ describe('Electron runtime package contract', () => {
7277
)
7378
}
7479
expect(releaseCommands.get('mac')).toContain(' && ORCA_MAC_RELEASE=1 ')
75-
expect(releaseCommands.get('linux')).toContain(' && pnpm exec electron-builder ')
80+
expect(releaseCommands.get('linux-x64')).toContain(' && pnpm exec electron-builder ')
81+
expect(releaseCommands.get('linux-x64')).toContain('--linux AppImage deb rpm --x64')
82+
expect(releaseCommands.get('linux-arm64')).toContain('ORCA_LINUX_ARM64_RELEASE=1')
83+
expect(releaseCommands.get('linux-arm64')).toContain('--linux AppImage deb rpm --arm64')
7684
expect(releaseCommands.get('win')).toContain(
7785
'; if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }; pnpm exec electron-builder '
7886
)

‎config/scripts/run-electron-vite-build.mjs‎

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
11
import { spawn } from 'node:child_process'
22
import { createRequire } from 'node:module'
33
import path from 'node:path'
4+
import { appendBuildOldSpaceOption } from './node-old-space-limit.mjs'
45

56
const require = createRequire(import.meta.url)
67
const electronVitePackageJson = require.resolve('electron-vite/package.json')
78
const electronViteCli = path.join(path.dirname(electronVitePackageJson), 'bin', 'electron-vite.js')
8-
const requestedNodeOptions = '--max-old-space-size=4096'
9-
const existingNodeOptions = process.env.NODE_OPTIONS?.trim()
109

1110
// Release builds have started OOMing on GitHub's macOS runners during the
12-
// renderer bundle, so we force a larger heap here in one shared entrypoint
13-
// instead of relying on shell-specific env syntax in individual scripts.
14-
const nodeOptions = existingNodeOptions
15-
? `${existingNodeOptions} ${requestedNodeOptions}`
16-
: requestedNodeOptions
11+
// renderer bundle. Reserve memory on smaller hosts so the OS does not kill Vite.
12+
const nodeOptions = appendBuildOldSpaceOption(process.env.NODE_OPTIONS)
1713

1814
const child = spawn(process.execPath, [electronViteCli, 'build', ...process.argv.slice(2)], {
1915
stdio: 'inherit',
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { spawn } from 'node:child_process'
2+
import { createRequire } from 'node:module'
3+
import path from 'node:path'
4+
import { appendBuildOldSpaceOption } from './node-old-space-limit.mjs'
5+
6+
const require = createRequire(import.meta.url)
7+
const vitePackageJson = require.resolve('vite/package.json')
8+
const viteCli = path.join(path.dirname(vitePackageJson), 'bin', 'vite.js')
9+
10+
// Why: Raspberry Pi and release runners can hit Node's default old-space
11+
// ceiling, but smaller hosts still need memory left for the OS and bundler.
12+
const nodeOptions = appendBuildOldSpaceOption(process.env.NODE_OPTIONS)
13+
14+
const child = spawn(
15+
process.execPath,
16+
[viteCli, 'build', '--config', 'vite.web.config.ts', ...process.argv.slice(2)],
17+
{
18+
stdio: 'inherit',
19+
env: {
20+
...process.env,
21+
NODE_OPTIONS: nodeOptions
22+
}
23+
}
24+
)
25+
26+
child.on('exit', (code, signal) => {
27+
if (signal) {
28+
process.kill(process.pid, signal)
29+
return
30+
}
31+
32+
process.exit(code ?? 1)
33+
})

‎config/scripts/verify-release-required-assets.mjs‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,15 @@ export function getRequiredReleaseAssetNames(tag) {
88
const version = tag.replace(/^v/i, '')
99
return [
1010
'latest-linux.yml',
11+
'latest-linux-arm64.yml',
1112
'latest-mac.yml',
1213
'latest.yml',
1314
'orca-linux.AppImage',
15+
'orca-linux-arm64.AppImage',
1416
`orca-ide_${version}_amd64.deb`,
17+
`orca-ide_${version}_arm64.deb`,
1518
`orca-ide-${version}.x86_64.rpm`,
19+
`orca-ide-${version}.aarch64.rpm`,
1620
'orca-windows-setup.exe',
1721
'orca-windows-setup.exe.blockmap',
1822
`Orca-${version}-mac.zip`,
@@ -86,7 +90,12 @@ export async function verifyRequiredReleaseAssets({ repo, tag, token }) {
8690
const assetsByName = new Map(release.assets.map((asset) => [asset.name, asset]))
8791

8892
const requiredNames = new Set(getRequiredReleaseAssetNames(tag))
89-
const manifestNames = ['latest-linux.yml', 'latest-mac.yml', 'latest.yml']
93+
const manifestNames = [
94+
'latest-linux.yml',
95+
'latest-linux-arm64.yml',
96+
'latest-mac.yml',
97+
'latest.yml'
98+
]
9099

91100
for (const manifestName of manifestNames) {
92101
const manifestAsset = assetsByName.get(manifestName)

‎config/scripts/verify-release-required-assets.test.mjs‎

Lines changed: 34 additions & 2 deletions

0 commit comments

Comments
 (0)