Files
hermes-agent/apps/desktop/scripts/dmgbuild-diagnostics.test.mjs
ethernet 2efa4ff94f refactor(desktop): prepare dependencies before saving build caches
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.
2026-09-13 14:28:31 -04:00

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)
})