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.
81 lines
3.3 KiB
JavaScript
81 lines
3.3 KiB
JavaScript
import { execFile } from 'node:child_process'
|
|
import { EventEmitter } from 'node:events'
|
|
import { createRequire } from 'node:module'
|
|
import path from 'node:path'
|
|
import { promisify } from 'node:util'
|
|
|
|
import { expect, test } from 'vitest'
|
|
|
|
const require = createRequire(import.meta.url)
|
|
const { wrapDmgbuildExecFile } = require('./dmgbuild-diagnostics.cjs')
|
|
|
|
test('the resolved dmgbuild uses its paired Python and preserves args, results and live diagnostics', () => {
|
|
const invocations = []
|
|
const output = []
|
|
const child = { stderr: new EventEmitter() }
|
|
const original = (...args) => {
|
|
invocations.push(args)
|
|
return child
|
|
}
|
|
const wrapped = wrapDmgbuildExecFile(original, chunk => output.push(chunk))
|
|
const vendor = path.resolve('tool cache', 'dmgbuild bundle')
|
|
const args = ['-s', 'settings with spaces.json', 'Install Hermes Agent', 'output.dmg']
|
|
const options = { maxBuffer: 1024, env: { KEEP: 'value', PYTHONPATH: 'old' } }
|
|
const callback = () => {}
|
|
|
|
expect(wrapped(path.join(vendor, 'dmgbuild'), args, options, callback)).toBe(child)
|
|
const invocation = invocations[0]
|
|
expect(invocation[0]).toBe(path.join(vendor, 'python', 'bin', 'python3'))
|
|
expect(path.basename(invocation[1][0])).toBe('dmgbuild_diagnostics.py')
|
|
expect(invocation[1].slice(1)).toEqual(args)
|
|
expect(invocation[2]).toEqual({ ...options, env: { KEEP: 'value', PYTHONPATH: path.join(vendor, 'python', 'lib') } })
|
|
expect(invocation[3]).toBe(callback)
|
|
expect(options.env.PYTHONPATH).toBe('old')
|
|
child.stderr.emit('data', Buffer.from('[dmg-detach] actual child output'))
|
|
expect(output[0].toString()).toContain('[dmg-detach]')
|
|
})
|
|
|
|
test('prepared supplier overrides retain the paired-Python diagnostic route', () => {
|
|
const calls = []
|
|
const wrapped = wrapDmgbuildExecFile((...args) => { calls.push(args); return {} })
|
|
const binary = path.resolve('prepared', 'dmgbuild')
|
|
wrapped(binary, ['-s', 'settings.json', 'Volume', 'out.dmg'], { env: {
|
|
CUSTOM_DMGBUILD_PATH: binary, HERMES_PREPARED_PACKAGING: '/work/prepared.json',
|
|
} }, () => {})
|
|
expect(calls[0][0]).toBe(path.join(path.dirname(binary), 'python/bin/python3'))
|
|
expect(calls[0][2].env.PYTHONPATH).toBe(path.join(path.dirname(binary), 'python/lib'))
|
|
})
|
|
|
|
test('promisified callers retain stdout, stderr and the real child handle', async () => {
|
|
const wrapped = wrapDmgbuildExecFile(execFile)
|
|
const pending = promisify(wrapped)(process.execPath, [
|
|
'-e',
|
|
'process.stdout.write("out"); process.stderr.write("err")'
|
|
])
|
|
expect(pending.child).toBeDefined()
|
|
expect(await pending).toEqual({ stdout: 'out', stderr: 'err' })
|
|
await expect(
|
|
promisify(wrapped)(process.execPath, [
|
|
'-e',
|
|
'process.stdout.write("out"); process.stderr.write("err"); process.exit(23)'
|
|
])
|
|
).rejects.toMatchObject({ code: 23, stdout: 'out', stderr: 'err' })
|
|
})
|
|
|
|
test('other executables and explicit custom dmgbuild overrides pass through unchanged', () => {
|
|
const calls = []
|
|
const original = (...args) => calls.push(args)
|
|
const wrapped = wrapDmgbuildExecFile(original)
|
|
const callback = () => {}
|
|
wrapped('/usr/bin/true', callback)
|
|
expect(calls[0]).toEqual(['/usr/bin/true', callback])
|
|
const invocation = [
|
|
path.resolve('custom', 'dmgbuild'),
|
|
['--help'],
|
|
{ env: { CUSTOM_DMGBUILD_PATH: '/custom/dmgbuild' } },
|
|
callback
|
|
]
|
|
wrapped(...invocation)
|
|
expect(calls[1]).toEqual(invocation)
|
|
})
|