Dependency acquisition during packaging left native wheels and packager inputs outside the pre-build cache save. Compose PM and existing providers into a preparation phase, then require builds to consume admitted inputs. Share native preparation with PM Bundle. Keep path-bound environments and signing outputs separate from reusable caches. Use read-only cache tokens for commit builds and preserve the one-command local build path. Verify pinned tools through PM, probe PTYs under the prepared Electron, and supply dmgbuild through a build-only PM package. Resolve bundled tool stores from their payload manifest so relocation preserves discovery. Validation: focused Python and JS tests, checkJs, Ruff, Windows checks, anti-slop, cache relocation, and network-denied Linux AppImage builds. Relocated runtime smoke passed with NixOS host libraries supplied. Native Windows/macOS signing and live GitHub cache behavior remain untested.
63 lines
2.1 KiB
JavaScript
63 lines
2.1 KiB
JavaScript
'use strict'
|
|
|
|
const childProcess = require('node:child_process')
|
|
const { syncBuiltinESMExports } = require('node:module')
|
|
const path = require('node:path')
|
|
const { promisify } = require('node:util')
|
|
|
|
/** @param {typeof import("node:child_process").execFile} execFile @param {(chunk: Buffer) => void} [write] @returns {typeof import("node:child_process").execFile} */
|
|
function wrapDmgbuildExecFile(execFile, write = chunk => process.stderr.write(chunk)) {
|
|
function wrapped(file, args, options, callback) {
|
|
if (
|
|
typeof file !== 'string' ||
|
|
path.basename(file) !== 'dmgbuild' ||
|
|
!Array.isArray(args) ||
|
|
typeof options !== 'object' ||
|
|
((options?.env?.CUSTOM_DMGBUILD_PATH || process.env.CUSTOM_DMGBUILD_PATH)?.trim() &&
|
|
!(options?.env?.HERMES_PREPARED_PACKAGING || process.env.HERMES_PREPARED_PACKAGING))
|
|
) {
|
|
return execFile.apply(this, arguments)
|
|
}
|
|
|
|
// Use the interpreter and module path from the supplier's launcher.
|
|
// A PATH shim cannot intercept dmgbuild's absolute /usr/bin/hdiutil call.
|
|
const vendor = path.dirname(file)
|
|
const diagnostic = path.resolve(__dirname, '../../../scripts/bundles/dmgbuild_diagnostics.py')
|
|
const child = execFile.call(
|
|
this,
|
|
path.join(vendor, 'python', 'bin', 'python3'),
|
|
[diagnostic, ...args],
|
|
{
|
|
...options,
|
|
env: { ...(options?.env || process.env), PYTHONPATH: path.join(vendor, 'python', 'lib') }
|
|
},
|
|
callback
|
|
)
|
|
// execFile buffers stderr. Tee it so a later successful retry cannot hide it.
|
|
child.stderr?.on('data', write)
|
|
return child
|
|
}
|
|
|
|
wrapped[promisify.custom] = (...args) => {
|
|
const { promise, resolve, reject } = Promise.withResolvers()
|
|
promise.child = wrapped(...args, (error, stdout, stderr) => {
|
|
if (error) {
|
|
error.stdout = stdout
|
|
error.stderr = stderr
|
|
reject(error)
|
|
} else {
|
|
resolve({ stdout, stderr })
|
|
}
|
|
})
|
|
return promise
|
|
}
|
|
return wrapped
|
|
}
|
|
|
|
module.exports = { wrapDmgbuildExecFile }
|
|
|
|
if (process.platform === 'darwin') {
|
|
childProcess.execFile = wrapDmgbuildExecFile(childProcess.execFile)
|
|
syncBuiltinESMExports()
|
|
}
|