fix: skip optional native rebuild in release packaging
This commit is contained in:
parent
f450813a21
commit
ce0cd79ec1
|
|
@ -1,6 +1,7 @@
|
|||
const { chmodSync, existsSync, readdirSync } = require('node:fs')
|
||||
const { execFileSync } = require('node:child_process')
|
||||
const { join, resolve } = require('node:path')
|
||||
const electronBuilderNativeRebuild = require('./scripts/electron-builder-native-rebuild.cjs')
|
||||
|
||||
const isMacRelease = process.env.ORCA_MAC_RELEASE === '1'
|
||||
const featureWallResources = {
|
||||
|
|
@ -230,11 +231,13 @@ module.exports = {
|
|||
artifactName: 'orca-ide-${version}.${arch}.${ext}',
|
||||
depends: ['python3', 'python3-gobject', 'at-spi2-core', 'xdotool', 'xclip']
|
||||
},
|
||||
beforeBuild: electronBuilderNativeRebuild,
|
||||
// Why: must be true so that electron-builder rebuilds native modules
|
||||
// (node-pty) for each target architecture when producing dual-arch macOS
|
||||
// builds (x64 + arm64). With npmRebuild disabled, CI on an arm64 runner
|
||||
// packages arm64 binaries into the x64 DMG, causing "posix_spawnp failed"
|
||||
// on Intel Macs.
|
||||
// on Intel Macs. The beforeBuild hook performs Orca's targeted rebuild and
|
||||
// returns false so electron-builder does not rebuild optional cpu-features.
|
||||
npmRebuild: true,
|
||||
publish: {
|
||||
provider: 'github',
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { describe, expect, it } from 'vitest'
|
|||
|
||||
const require = createRequire(import.meta.url)
|
||||
const electronBuilderConfig = require('../electron-builder.config.cjs')
|
||||
const electronBuilderNativeRebuild = require('./electron-builder-native-rebuild.cjs')
|
||||
|
||||
describe('electron-builder config', () => {
|
||||
it('uses the multi-size icon source for Linux packages', () => {
|
||||
|
|
@ -18,4 +19,9 @@ describe('electron-builder config', () => {
|
|||
artifactName: 'orca-ide-${version}.${arch}.${ext}'
|
||||
})
|
||||
})
|
||||
|
||||
it('uses Orca native rebuild hook instead of electron-builder default rebuild', () => {
|
||||
expect(electronBuilderConfig.beforeBuild).toBe(electronBuilderNativeRebuild)
|
||||
expect(electronBuilderConfig.npmRebuild).toBe(true)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
const { execFileSync } = require('node:child_process')
|
||||
const { resolve } = require('node:path')
|
||||
|
||||
const projectDir = resolve(__dirname, '../..')
|
||||
|
||||
function electronBuilderNativeRebuild(context) {
|
||||
return runElectronBuilderNativeRebuild(context)
|
||||
}
|
||||
|
||||
function runElectronBuilderNativeRebuild(context, runner = execFileSync) {
|
||||
const args = buildNativeRebuildArgs(context)
|
||||
runner(process.execPath, args, {
|
||||
cwd: projectDir,
|
||||
stdio: 'inherit'
|
||||
})
|
||||
|
||||
// Why: returning false tells electron-builder that native deps were handled
|
||||
// externally, avoiding its all-module rebuild of optional cpu-features.
|
||||
return false
|
||||
}
|
||||
|
||||
function buildNativeRebuildArgs(context) {
|
||||
const platform = readPlatformName(context?.platform)
|
||||
const arch = readArchName(context?.arch)
|
||||
|
||||
return [
|
||||
'config/scripts/rebuild-native-deps.mjs',
|
||||
`--platform=${platform}`,
|
||||
`--arch=${arch}`,
|
||||
'--force'
|
||||
]
|
||||
}
|
||||
|
||||
function readPlatformName(platform) {
|
||||
const name = typeof platform === 'string' ? platform : platform?.nodeName
|
||||
if (!name) {
|
||||
throw new Error('electron-builder native rebuild context is missing platform.nodeName')
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
function readArchName(arch) {
|
||||
if (!arch || typeof arch !== 'string') {
|
||||
throw new Error('electron-builder native rebuild context is missing arch')
|
||||
}
|
||||
return arch
|
||||
}
|
||||
|
||||
module.exports = electronBuilderNativeRebuild
|
||||
module.exports.default = electronBuilderNativeRebuild
|
||||
module.exports.buildNativeRebuildArgs = buildNativeRebuildArgs
|
||||
module.exports.runElectronBuilderNativeRebuild = runElectronBuilderNativeRebuild
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
import { createRequire } from 'node:module'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
const {
|
||||
buildNativeRebuildArgs,
|
||||
runElectronBuilderNativeRebuild
|
||||
} = require('./electron-builder-native-rebuild.cjs')
|
||||
|
||||
describe('electron-builder native rebuild hook', () => {
|
||||
it('passes the target platform and arch to Orca native rebuild script', () => {
|
||||
expect(
|
||||
buildNativeRebuildArgs({
|
||||
platform: { nodeName: 'darwin' },
|
||||
arch: 'x64'
|
||||
})
|
||||
).toEqual([
|
||||
'config/scripts/rebuild-native-deps.mjs',
|
||||
'--platform=darwin',
|
||||
'--arch=x64',
|
||||
'--force'
|
||||
])
|
||||
})
|
||||
|
||||
it('returns false so electron-builder skips its optional module rebuild pass', () => {
|
||||
const calls = []
|
||||
const result = runElectronBuilderNativeRebuild(
|
||||
{
|
||||
platform: { nodeName: 'linux' },
|
||||
arch: 'arm64'
|
||||
},
|
||||
(...args) => calls.push(args)
|
||||
)
|
||||
|
||||
expect(result).toBe(false)
|
||||
expect(calls).toEqual([
|
||||
[
|
||||
process.execPath,
|
||||
['config/scripts/rebuild-native-deps.mjs', '--platform=linux', '--arch=arm64', '--force'],
|
||||
expect.objectContaining({ stdio: 'inherit' })
|
||||
]
|
||||
])
|
||||
})
|
||||
|
||||
it('rejects incomplete electron-builder contexts', () => {
|
||||
expect(() => buildNativeRebuildArgs({ arch: 'x64' })).toThrow(/platform/)
|
||||
expect(() => buildNativeRebuildArgs({ platform: { nodeName: 'linux' } })).toThrow(/arch/)
|
||||
})
|
||||
})
|
||||
|
|
@ -11,8 +11,9 @@
|
|||
* `pnpm install` from completing.
|
||||
*
|
||||
* This script replaces `electron-builder install-app-deps` in the postinstall
|
||||
* lifecycle. It calls @electron/rebuild's JS API directly so that we can skip
|
||||
* `cpu-features` when rebuilding modules against Electron. Skipping
|
||||
* lifecycle and the electron-builder beforeBuild hook. It calls
|
||||
* @electron/rebuild's JS API directly so that we can skip `cpu-features` when
|
||||
* rebuilding modules against Electron. Skipping
|
||||
* cpu-features is safe: ssh2 detects the missing native module and falls back
|
||||
* to pure-JS CPU feature detection automatically.
|
||||
*/
|
||||
|
|
@ -24,6 +25,15 @@ import { platform as osPlatform } from 'node:os'
|
|||
import { resolve } from 'node:path'
|
||||
|
||||
const projectDir = process.cwd()
|
||||
let cliOptions
|
||||
try {
|
||||
cliOptions = readCliOptions(process.argv.slice(2))
|
||||
} catch (error) {
|
||||
console.error(`[rebuild] ${formatError(error)}`)
|
||||
process.exit(2)
|
||||
}
|
||||
const rebuildPlatform = cliOptions.platform ?? osPlatform()
|
||||
const rebuildArch = cliOptions.arch ?? process.arch
|
||||
const electronPackageDir = resolve(projectDir, 'node_modules/electron')
|
||||
const electronVersion = JSON.parse(
|
||||
readFileSync(resolve(electronPackageDir, 'package.json'), 'utf8')
|
||||
|
|
@ -41,7 +51,11 @@ if (ignoreModules.length > 0) {
|
|||
// ABI regardless of the package manager's store layout.
|
||||
const NATIVE_MODULES = ['node-pty', 'cpu-features']
|
||||
const onlyModules = NATIVE_MODULES.filter((m) => !ignoreModules.includes(m))
|
||||
const forceRebuild = process.env.ORCA_FORCE_NATIVE_REBUILD === '1'
|
||||
const forceRebuild =
|
||||
process.env.ORCA_FORCE_NATIVE_REBUILD === '1' ||
|
||||
cliOptions.force ||
|
||||
rebuildPlatform !== osPlatform() ||
|
||||
rebuildArch !== process.arch
|
||||
|
||||
ensureElectronPackageInstalled()
|
||||
|
||||
|
|
@ -58,7 +72,7 @@ if (!forceRebuild) {
|
|||
console.log(probe.stderr.trim())
|
||||
}
|
||||
} else {
|
||||
console.log('[rebuild] ORCA_FORCE_NATIVE_REBUILD=1 set; forcing native rebuild.')
|
||||
console.log(`[rebuild] Forcing native rebuild for ${rebuildPlatform}-${rebuildArch}.`)
|
||||
}
|
||||
|
||||
// Why: cpu-features ships without `buildcheck.gypi`; its own `install` script
|
||||
|
|
@ -95,6 +109,8 @@ try {
|
|||
await rebuild({
|
||||
buildPath: projectDir,
|
||||
electronVersion,
|
||||
platform: rebuildPlatform,
|
||||
arch: rebuildArch,
|
||||
ignoreModules,
|
||||
onlyModules,
|
||||
// Why: without force, @electron/rebuild skips modules it considers
|
||||
|
|
@ -253,7 +269,7 @@ function safeReaddir(targetPath) {
|
|||
|
||||
function getElectronPlatformPath() {
|
||||
const targetPlatform =
|
||||
process.env.ELECTRON_INSTALL_PLATFORM || process.env.npm_config_platform || osPlatform()
|
||||
process.env.ELECTRON_INSTALL_PLATFORM || process.env.npm_config_platform || rebuildPlatform
|
||||
switch (targetPlatform) {
|
||||
case 'mas':
|
||||
case 'darwin':
|
||||
|
|
@ -269,6 +285,51 @@ function getElectronPlatformPath() {
|
|||
}
|
||||
}
|
||||
|
||||
function readCliOptions(args) {
|
||||
const options = { force: false }
|
||||
for (let index = 0; index < args.length; index += 1) {
|
||||
const arg = args[index]
|
||||
if (arg === '--force') {
|
||||
options.force = true
|
||||
continue
|
||||
}
|
||||
if (arg === '--platform') {
|
||||
options.platform = readRequiredArgValue(args, (index += 1), '--platform')
|
||||
continue
|
||||
}
|
||||
if (arg.startsWith('--platform=')) {
|
||||
options.platform = readInlineArgValue(arg, '--platform')
|
||||
continue
|
||||
}
|
||||
if (arg === '--arch') {
|
||||
options.arch = readRequiredArgValue(args, (index += 1), '--arch')
|
||||
continue
|
||||
}
|
||||
if (arg.startsWith('--arch=')) {
|
||||
options.arch = readInlineArgValue(arg, '--arch')
|
||||
continue
|
||||
}
|
||||
throw new Error(`Unknown argument: ${arg}`)
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
function readRequiredArgValue(args, index, flag) {
|
||||
const value = args[index]
|
||||
if (!value || value.startsWith('--')) {
|
||||
throw new Error(`Missing value for ${flag}`)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
function readInlineArgValue(arg, flag) {
|
||||
const value = arg.slice(`${flag}=`.length)
|
||||
if (!value) {
|
||||
throw new Error(`Missing value for ${flag}`)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
function getElectronExecutablePath() {
|
||||
const platformPath = getElectronPlatformPath()
|
||||
return process.env.ELECTRON_OVERRIDE_DIST_PATH
|
||||
|
|
|
|||
Loading…
Reference in New Issue