mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
Electron's sidecar runs outside app.asar, so H5 static files must be available as normal unpacked files. Point the sidecar at the unpacked renderer dist and keep a server fallback for stale app.asar-style paths. Constraint: Packaged Bun sidecars cannot read app.asar paths with ordinary fs stat calls. Rejected: Serve H5 from app.asar directly | the external sidecar is not Electron and does not get asar filesystem support. Confidence: high Scope-risk: narrow Directive: Keep package-smoke checking app.asar.unpacked/dist/index.html before changing asarUnpack or H5 dist paths. Tested: bun test src/server/__tests__/h5-access-auth.test.ts src/server/__tests__/h5-access-policy.test.ts Tested: bun test desktop/electron/services/sidecarManager.test.ts scripts/quality-gate/package-smoke/index.test.ts Tested: bun run check:server Tested: cd desktop && bun run check:electron Tested: git diff --check Tested: SKIP_INSTALL=1 SIGN_BUILD=0 MAC_TARGETS=dmg desktop/scripts/build-macos-arm64.sh Tested: packaged sidecar curl /?serverUrl=...&h5Token=... returned HTTP 200 Not-tested: Gatekeeper notarization for the local ad-hoc DMG
434 lines
27 KiB
TypeScript
434 lines
27 KiB
TypeScript
import { afterEach, describe, expect, test } from 'bun:test'
|
|
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import { dirname, join } from 'node:path'
|
|
import {
|
|
currentPackageSmokePlatform,
|
|
} from './current'
|
|
import {
|
|
inspectPackagedArtifacts,
|
|
parsePackageSmokeArgs,
|
|
} from './index'
|
|
|
|
function createRepoRoot() {
|
|
const rootDir = mkdtempSync(join(tmpdir(), 'package-smoke-'))
|
|
mkdirSync(join(rootDir, 'desktop'), { recursive: true })
|
|
writeFileSync(
|
|
join(rootDir, 'desktop', 'package.json'),
|
|
JSON.stringify({
|
|
name: 'claude-code-desktop',
|
|
version: '0.3.1',
|
|
build: {
|
|
productName: 'Claude Code Haha',
|
|
},
|
|
}, null, 2),
|
|
)
|
|
return rootDir
|
|
}
|
|
|
|
function writeFile(rootDir: string, relativePath: string, content = 'ok') {
|
|
const fullPath = join(rootDir, relativePath)
|
|
mkdirSync(dirname(fullPath), { recursive: true })
|
|
writeFileSync(fullPath, content)
|
|
}
|
|
|
|
const tempDirs: string[] = []
|
|
|
|
afterEach(() => {
|
|
for (const dir of tempDirs.splice(0)) {
|
|
rmSync(dir, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
describe('package smoke args', () => {
|
|
test('requires a supported platform value', () => {
|
|
expect(() => parsePackageSmokeArgs([])).toThrow('--platform')
|
|
expect(() => parsePackageSmokeArgs(['--platform', 'android'])).toThrow('macos|windows|linux')
|
|
expect(() => parsePackageSmokeArgs(['--platform', 'macos', '--package-kind', 'installer'])).toThrow('auto|dir|release')
|
|
expect(parsePackageSmokeArgs(['--platform', 'macos']).platform).toBe('macos')
|
|
expect(parsePackageSmokeArgs(['--platform', 'macos']).packageKind).toBe('auto')
|
|
expect(parsePackageSmokeArgs(['--platform', 'macos', '--package-kind', 'dir']).packageKind).toBe('dir')
|
|
expect(parsePackageSmokeArgs(['--platform', 'macos', '--require-macos-gatekeeper']).requireMacosGatekeeper).toBe(true)
|
|
})
|
|
|
|
test('maps host platforms to current package-smoke platforms', () => {
|
|
expect(currentPackageSmokePlatform('darwin')).toBe('macos')
|
|
expect(currentPackageSmokePlatform('win32')).toBe('windows')
|
|
expect(currentPackageSmokePlatform('linux')).toBe('linux')
|
|
expect(currentPackageSmokePlatform('freebsd')).toBeNull()
|
|
})
|
|
})
|
|
|
|
describe('packaged artifact inspection', () => {
|
|
test('passes macOS bundle structure checks and records optional archive artifacts', async () => {
|
|
const rootDir = createRepoRoot()
|
|
tempDirs.push(rootDir)
|
|
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Info.plist')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/MacOS/Claude Code Haha')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app-update.yml')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/dist/index.html')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/src-tauri/binaries/claude-sidecar-aarch64-apple-darwin')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/package.json')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/prebuilds/darwin-arm64/pty.node')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/Claude-Code-Haha-0.3.1-arm64.zip')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/Claude-Code-Haha-0.3.1-arm64.zip.blockmap')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/Claude-Code-Haha-0.3.1-arm64.dmg')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/Claude-Code-Haha-0.3.1-arm64.dmg.blockmap')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/latest-mac.yml', [
|
|
'version: 0.3.1',
|
|
'files:',
|
|
' - url: Claude-Code-Haha-0.3.1-arm64.zip',
|
|
' - url: Claude-Code-Haha-0.3.1-arm64.dmg',
|
|
'path: Claude-Code-Haha-0.3.1-arm64.zip',
|
|
].join('\n'))
|
|
|
|
const report = await inspectPackagedArtifacts(rootDir, { platform: 'macos' })
|
|
|
|
expect(report.passed).toBe(true)
|
|
expect(report.verificationMode).toBe('bundle-structure')
|
|
expect(report.notes.join('\n')).toContain('No GUI launch was attempted.')
|
|
expect(report.optionalArtifacts.some((artifact) => artifact.path.endsWith('.zip'))).toBe(true)
|
|
expect(report.optionalArtifacts.some((artifact) => artifact.path.endsWith('.dmg'))).toBe(true)
|
|
expect(report.optionalArtifacts.some((artifact) => artifact.path.endsWith('latest-mac.yml'))).toBe(true)
|
|
expect(report.passedChecks.some((check) => check.label.includes('update metadata referenced artifact'))).toBe(true)
|
|
expect(report.passedChecks.some((check) => check.label.includes('macOS update artifact blockmap'))).toBe(true)
|
|
expect(report.passedChecks.some((check) => check.label === 'macOS unpacked H5 shell')).toBe(true)
|
|
})
|
|
|
|
test('fails macOS inspection when the H5 shell is not unpacked for the sidecar', async () => {
|
|
const rootDir = createRepoRoot()
|
|
tempDirs.push(rootDir)
|
|
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Info.plist')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/MacOS/Claude Code Haha')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app-update.yml')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/src-tauri/binaries/claude-sidecar-aarch64-apple-darwin')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/package.json')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/prebuilds/darwin-arm64/pty.node')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper')
|
|
|
|
const report = await inspectPackagedArtifacts(rootDir, { platform: 'macos', packageKind: 'dir' })
|
|
|
|
expect(report.passed).toBe(false)
|
|
expect(report.missingChecks.some((check) => check.label === 'macOS unpacked H5 shell')).toBe(true)
|
|
})
|
|
|
|
test('fails macOS archive checks when latest-mac.yml points at missing assets', async () => {
|
|
const rootDir = createRepoRoot()
|
|
tempDirs.push(rootDir)
|
|
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Info.plist')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/MacOS/Claude Code Haha')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app-update.yml')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/src-tauri/binaries/claude-sidecar-aarch64-apple-darwin')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/package.json')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/prebuilds/darwin-arm64/pty.node')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/Claude Code Haha-0.3.1-arm64-mac.zip')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/latest-mac.yml', 'path: Claude-Code-Haha-0.3.1-arm64-mac.zip\n')
|
|
|
|
const report = await inspectPackagedArtifacts(rootDir, { platform: 'macos' })
|
|
|
|
expect(report.passed).toBe(false)
|
|
expect(report.missingChecks.some((check) => check.label.includes('update metadata referenced artifact'))).toBe(true)
|
|
})
|
|
|
|
test('fails macOS inspection when the installed updater metadata is missing from Resources', async () => {
|
|
const rootDir = createRepoRoot()
|
|
tempDirs.push(rootDir)
|
|
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Info.plist')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/MacOS/Claude Code Haha')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/src-tauri/binaries/claude-sidecar-aarch64-apple-darwin')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/package.json')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/prebuilds/darwin-arm64/pty.node')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/Claude-Code-Haha-0.3.1-arm64.zip')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/latest-mac.yml', [
|
|
'version: 0.3.1',
|
|
'files:',
|
|
' - url: Claude-Code-Haha-0.3.1-arm64.zip',
|
|
'path: Claude-Code-Haha-0.3.1-arm64.zip',
|
|
].join('\n'))
|
|
|
|
const report = await inspectPackagedArtifacts(rootDir, { platform: 'macos' })
|
|
|
|
expect(report.passed).toBe(false)
|
|
expect(report.missingChecks.some((check) => check.label === 'macOS app-update.yml')).toBe(true)
|
|
})
|
|
|
|
test('fails release inspection when an update artifact blockmap is missing', async () => {
|
|
const rootDir = createRepoRoot()
|
|
tempDirs.push(rootDir)
|
|
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Info.plist')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/MacOS/Claude Code Haha')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app-update.yml')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/src-tauri/binaries/claude-sidecar-aarch64-apple-darwin')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/package.json')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/prebuilds/darwin-arm64/pty.node')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/Claude-Code-Haha-0.3.1-arm64.zip')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/latest-mac.yml', 'path: Claude-Code-Haha-0.3.1-arm64.zip\n')
|
|
|
|
const report = await inspectPackagedArtifacts(rootDir, { platform: 'macos', packageKind: 'release' })
|
|
|
|
expect(report.passed).toBe(false)
|
|
expect(report.missingChecks.some((check) => check.label.includes('macOS update artifact blockmap'))).toBe(true)
|
|
})
|
|
|
|
test('adds codesign diagnostics when macOS Gatekeeper assessment fails', async () => {
|
|
const rootDir = createRepoRoot()
|
|
tempDirs.push(rootDir)
|
|
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Info.plist')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/MacOS/Claude Code Haha')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/src-tauri/binaries/claude-sidecar-aarch64-apple-darwin')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/package.json')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/prebuilds/darwin-arm64/pty.node')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper')
|
|
|
|
const report = await inspectPackagedArtifacts(rootDir, {
|
|
platform: 'macos',
|
|
packageKind: 'dir',
|
|
requireMacosGatekeeper: true,
|
|
hostPlatform: 'macos',
|
|
commandRunner: (command, args) => {
|
|
if (command.endsWith('/spctl')) {
|
|
return { status: 1, stdout: '', stderr: 'rejected\nsource=Unnotarized Developer ID\n' }
|
|
}
|
|
if (command.endsWith('/codesign')) {
|
|
return { status: 1, stdout: '', stderr: 'code object is not signed at all\n' }
|
|
}
|
|
if (command.endsWith('/xcrun') && args[0] === 'stapler') {
|
|
return { status: 65, stdout: '', stderr: 'The validate action failed!\n' }
|
|
}
|
|
return { status: 0, stdout: '', stderr: '' }
|
|
},
|
|
})
|
|
|
|
expect(report.passed).toBe(false)
|
|
expect(report.missingChecks.some((check) => check.label.includes('macOS Gatekeeper launch approval'))).toBe(true)
|
|
expect(report.notes.join('\n')).toContain('spctl Gatekeeper assessment exited with status 1')
|
|
expect(report.notes.join('\n')).toContain('codesign verification exited with status 1')
|
|
expect(report.notes.join('\n')).toContain('codesign signature details exited with status 1')
|
|
expect(report.notes.join('\n')).toContain('notarization ticket validation exited with status 65')
|
|
})
|
|
|
|
test('retries macOS Gatekeeper assessment with a raised file limit when spctl hits open-file limits', async () => {
|
|
const rootDir = createRepoRoot()
|
|
tempDirs.push(rootDir)
|
|
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Info.plist')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/MacOS/Claude Code Haha')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/src-tauri/binaries/claude-sidecar-aarch64-apple-darwin')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/package.json')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/prebuilds/darwin-arm64/pty.node')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper')
|
|
|
|
const commands: string[] = []
|
|
const report = await inspectPackagedArtifacts(rootDir, {
|
|
platform: 'macos',
|
|
packageKind: 'dir',
|
|
requireMacosGatekeeper: true,
|
|
hostPlatform: 'macos',
|
|
commandRunner: (command, args) => {
|
|
commands.push(`${command} ${args.join(' ')}`)
|
|
if (command.endsWith('/spctl')) {
|
|
return { status: 1, stdout: '', stderr: 'Too many open files\n' }
|
|
}
|
|
if (command.endsWith('/zsh')) {
|
|
return { status: 1, stdout: '', stderr: 'bundle format unrecognized, invalid, or unsuitable\n' }
|
|
}
|
|
if (command.endsWith('/codesign')) {
|
|
return { status: 0, stdout: '', stderr: 'valid on disk\n' }
|
|
}
|
|
if (command.endsWith('/xcrun') && args[0] === 'stapler') {
|
|
return { status: 65, stdout: '', stderr: 'does not have a ticket stapled to it\n' }
|
|
}
|
|
return { status: 0, stdout: '', stderr: '' }
|
|
},
|
|
})
|
|
|
|
expect(report.passed).toBe(false)
|
|
expect(commands.some(command => command.startsWith('/bin/zsh -lc ulimit -n'))).toBe(true)
|
|
expect(report.missingChecks.some((check) => check.label.includes('bundle format unrecognized'))).toBe(true)
|
|
expect(report.notes.join('\n')).toContain('retried with a raised file descriptor limit')
|
|
expect(report.notes.join('\n')).toContain('spctl Gatekeeper assessment exited with status 1: bundle format unrecognized')
|
|
})
|
|
|
|
test('treats Windows checks as static inspection on non-Windows hosts', async () => {
|
|
const rootDir = createRepoRoot()
|
|
tempDirs.push(rootDir)
|
|
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/Claude Code Haha Setup 0.3.1.exe')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/Claude Code Haha Setup 0.3.1.exe.blockmap')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/win-unpacked/resources/app.asar')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/win-unpacked/resources/app-update.yml')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/win-unpacked/resources/app.asar.unpacked/src-tauri/binaries/claude-sidecar-x86_64-pc-windows-msvc.exe')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/win-unpacked/resources/app.asar.unpacked/node_modules/node-pty/package.json')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/win-unpacked/resources/app.asar.unpacked/node_modules/node-pty/prebuilds/win32-x64/pty.node')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/latest.yml', 'path: Claude Code Haha Setup 0.3.1.exe\n')
|
|
|
|
const report = await inspectPackagedArtifacts(rootDir, { platform: 'windows' })
|
|
|
|
expect(report.passed).toBe(true)
|
|
expect(report.verificationMode).toBe('static-artifact')
|
|
expect(report.notes.join('\n')).toContain('not claim installer execution or app launch success')
|
|
})
|
|
|
|
test('passes Windows checks against the canonical build script output directory', async () => {
|
|
const rootDir = createRepoRoot()
|
|
tempDirs.push(rootDir)
|
|
|
|
writeFile(rootDir, 'desktop/build-artifacts/windows-x64/Claude-Code-Haha-0.3.1-x64.exe')
|
|
writeFile(rootDir, 'desktop/build-artifacts/windows-x64/Claude-Code-Haha-0.3.1-x64.exe.blockmap')
|
|
writeFile(rootDir, 'desktop/build-artifacts/windows-x64/win-unpacked/resources/app.asar')
|
|
writeFile(rootDir, 'desktop/build-artifacts/windows-x64/win-unpacked/resources/app-update.yml')
|
|
writeFile(rootDir, 'desktop/build-artifacts/windows-x64/win-unpacked/resources/app.asar.unpacked/src-tauri/binaries/claude-sidecar-x86_64-pc-windows-msvc.exe')
|
|
writeFile(rootDir, 'desktop/build-artifacts/windows-x64/win-unpacked/resources/app.asar.unpacked/node_modules/node-pty/package.json')
|
|
writeFile(rootDir, 'desktop/build-artifacts/windows-x64/win-unpacked/resources/app.asar.unpacked/node_modules/node-pty/prebuilds/win32-x64/pty.node')
|
|
writeFile(rootDir, 'desktop/build-artifacts/windows-x64/latest.yml', 'path: Claude-Code-Haha-0.3.1-x64.exe\n')
|
|
|
|
const report = await inspectPackagedArtifacts(rootDir, {
|
|
platform: 'windows',
|
|
packageKind: 'release',
|
|
artifactsDir: 'desktop/build-artifacts/windows-x64',
|
|
})
|
|
|
|
expect(report.passed).toBe(true)
|
|
expect(report.artifactsDir.endsWith('desktop/build-artifacts/windows-x64')).toBe(true)
|
|
})
|
|
|
|
test('passes Windows directory-only checks for electron-builder --dir output', async () => {
|
|
const rootDir = createRepoRoot()
|
|
tempDirs.push(rootDir)
|
|
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/win-unpacked/Claude Code Haha.exe')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/win-unpacked/resources/app.asar')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/win-unpacked/resources/app.asar.unpacked/src-tauri/binaries/claude-sidecar-x86_64-pc-windows-msvc.exe')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/win-unpacked/resources/app.asar.unpacked/node_modules/node-pty/package.json')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/win-unpacked/resources/app.asar.unpacked/node_modules/node-pty/prebuilds/win32-x64/pty.node')
|
|
|
|
const report = await inspectPackagedArtifacts(rootDir, { platform: 'windows', packageKind: 'dir' })
|
|
|
|
expect(report.passed).toBe(true)
|
|
expect(report.notes.join('\n')).toContain('directory-only development package')
|
|
expect(report.notes.join('\n')).toContain('Windows app-update.yml was not required')
|
|
})
|
|
|
|
test('does not treat the win-unpacked app executable as a Windows release installer', async () => {
|
|
const rootDir = createRepoRoot()
|
|
tempDirs.push(rootDir)
|
|
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/win-unpacked/Claude Code Haha.exe')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/win-unpacked/resources/app.asar')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/win-unpacked/resources/app-update.yml')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/win-unpacked/resources/app.asar.unpacked/src-tauri/binaries/claude-sidecar-x86_64-pc-windows-msvc.exe')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/win-unpacked/resources/app.asar.unpacked/node_modules/node-pty/package.json')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/win-unpacked/resources/app.asar.unpacked/node_modules/node-pty/prebuilds/win32-x64/pty.node')
|
|
|
|
const report = await inspectPackagedArtifacts(rootDir, { platform: 'windows', packageKind: 'release' })
|
|
|
|
expect(report.passed).toBe(false)
|
|
expect(report.missingChecks.some((check) => check.label.includes('.exe installer'))).toBe(true)
|
|
})
|
|
|
|
test('passes Linux checks against the canonical build script output directory', async () => {
|
|
const rootDir = createRepoRoot()
|
|
tempDirs.push(rootDir)
|
|
|
|
writeFile(rootDir, 'desktop/build-artifacts/linux-x64/Claude-Code-Haha-0.3.1-x64.AppImage')
|
|
writeFile(rootDir, 'desktop/build-artifacts/linux-x64/Claude-Code-Haha-0.3.1-x64.AppImage.blockmap')
|
|
writeFile(rootDir, 'desktop/build-artifacts/linux-x64/claude-code-desktop_0.3.1_amd64.deb')
|
|
writeFile(rootDir, 'desktop/build-artifacts/linux-x64/linux-unpacked/resources/app.asar')
|
|
writeFile(rootDir, 'desktop/build-artifacts/linux-x64/linux-unpacked/resources/app-update.yml')
|
|
writeFile(rootDir, 'desktop/build-artifacts/linux-x64/linux-unpacked/resources/app.asar.unpacked/src-tauri/binaries/claude-sidecar-x86_64-unknown-linux-gnu')
|
|
writeFile(rootDir, 'desktop/build-artifacts/linux-x64/linux-unpacked/resources/app.asar.unpacked/node_modules/node-pty/package.json')
|
|
writeFile(rootDir, 'desktop/build-artifacts/linux-x64/linux-unpacked/resources/app.asar.unpacked/node_modules/node-pty/prebuilds/linux-x64/pty.node')
|
|
writeFile(rootDir, 'desktop/build-artifacts/linux-x64/latest-linux.yml', 'path: Claude-Code-Haha-0.3.1-x64.AppImage\n')
|
|
|
|
const report = await inspectPackagedArtifacts(rootDir, {
|
|
platform: 'linux',
|
|
packageKind: 'release',
|
|
artifactsDir: 'desktop/build-artifacts/linux-x64',
|
|
})
|
|
|
|
expect(report.passed).toBe(true)
|
|
expect(report.artifactsDir.endsWith('desktop/build-artifacts/linux-x64')).toBe(true)
|
|
})
|
|
|
|
test('accepts Linux architecture-specific update metadata from arm64 builds', async () => {
|
|
const rootDir = createRepoRoot()
|
|
tempDirs.push(rootDir)
|
|
|
|
writeFile(rootDir, 'desktop/build-artifacts/linux-arm64/Claude-Code-Haha-0.3.1-arm64.AppImage')
|
|
writeFile(rootDir, 'desktop/build-artifacts/linux-arm64/Claude-Code-Haha-0.3.1-arm64.AppImage.blockmap')
|
|
writeFile(rootDir, 'desktop/build-artifacts/linux-arm64/claude-code-desktop_0.3.1_arm64.deb')
|
|
writeFile(rootDir, 'desktop/build-artifacts/linux-arm64/linux-unpacked/resources/app.asar')
|
|
writeFile(rootDir, 'desktop/build-artifacts/linux-arm64/linux-unpacked/resources/app-update.yml')
|
|
writeFile(rootDir, 'desktop/build-artifacts/linux-arm64/linux-unpacked/resources/app.asar.unpacked/src-tauri/binaries/claude-sidecar-aarch64-unknown-linux-gnu')
|
|
writeFile(rootDir, 'desktop/build-artifacts/linux-arm64/linux-unpacked/resources/app.asar.unpacked/node_modules/node-pty/package.json')
|
|
writeFile(rootDir, 'desktop/build-artifacts/linux-arm64/linux-unpacked/resources/app.asar.unpacked/node_modules/node-pty/prebuilds/linux-arm64/pty.node')
|
|
writeFile(rootDir, 'desktop/build-artifacts/linux-arm64/latest-linux-arm64.yml', 'path: Claude-Code-Haha-0.3.1-arm64.AppImage\n')
|
|
|
|
const report = await inspectPackagedArtifacts(rootDir, {
|
|
platform: 'linux',
|
|
packageKind: 'release',
|
|
artifactsDir: 'desktop/build-artifacts/linux-arm64',
|
|
})
|
|
|
|
expect(report.passed).toBe(true)
|
|
expect(report.optionalArtifacts.some((artifact) => artifact.path.endsWith('latest-linux-arm64.yml'))).toBe(true)
|
|
expect(report.passedChecks.some((check) => check.label.includes('update metadata referenced artifact'))).toBe(true)
|
|
})
|
|
|
|
test('passes Linux directory-only checks for electron-builder --dir output', async () => {
|
|
const rootDir = createRepoRoot()
|
|
tempDirs.push(rootDir)
|
|
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/linux-unpacked/resources/app.asar')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/linux-unpacked/resources/app.asar.unpacked/src-tauri/binaries/claude-sidecar-x86_64-unknown-linux-gnu')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/linux-unpacked/resources/app.asar.unpacked/node_modules/node-pty/package.json')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/linux-unpacked/resources/app.asar.unpacked/node_modules/node-pty/prebuilds/linux-x64/pty.node')
|
|
|
|
const report = await inspectPackagedArtifacts(rootDir, { platform: 'linux', packageKind: 'dir' })
|
|
|
|
expect(report.passed).toBe(true)
|
|
expect(report.notes.join('\n')).toContain('directory-only development package')
|
|
expect(report.notes.join('\n')).toContain('Linux app-update.yml was not required')
|
|
})
|
|
|
|
test('fails Linux inspection when no packaged artifact is present', async () => {
|
|
const rootDir = createRepoRoot()
|
|
tempDirs.push(rootDir)
|
|
|
|
const report = await inspectPackagedArtifacts(rootDir, { platform: 'linux' })
|
|
|
|
expect(report.passed).toBe(false)
|
|
expect(report.missingChecks.some((check) => check.label.includes('linux packaged artifact'))).toBe(true)
|
|
})
|
|
|
|
test('fails Linux release inspection without linux-unpacked static resources', async () => {
|
|
const rootDir = createRepoRoot()
|
|
tempDirs.push(rootDir)
|
|
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/Claude-Code-Haha-0.3.1-x64.AppImage')
|
|
writeFile(rootDir, 'desktop/build-artifacts/electron/latest-linux.yml', 'path: Claude-Code-Haha-0.3.1-x64.AppImage\n')
|
|
|
|
const report = await inspectPackagedArtifacts(rootDir, { platform: 'linux', packageKind: 'release' })
|
|
|
|
expect(report.passed).toBe(false)
|
|
expect(report.missingChecks.some((check) => check.label.includes('linux-unpacked'))).toBe(true)
|
|
})
|
|
})
|