import fs from 'fs' import path from 'path' import { fileURLToPath } from 'url' const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) const electronRoot = path.resolve(__dirname, '..') const distRoot = path.join(electronRoot, 'dist') const unpackedRoot = path.join(distRoot, 'win-unpacked') const pkg = JSON.parse(fs.readFileSync(path.join(electronRoot, 'package.json'), 'utf8')) const productName = pkg.build?.productName || pkg.productName || pkg.name const requiredFiles = [ path.join(unpackedRoot, `${productName}.exe`), path.join(unpackedRoot, 'ffmpeg.dll'), path.join(unpackedRoot, 'icudtl.dat'), path.join(unpackedRoot, 'resources', 'app.asar'), path.join(unpackedRoot, 'resources', 'backend', 'web2.py'), ] const pythonCandidates = [ path.join(unpackedRoot, 'resources', 'backend', 'python', 'python.exe'), path.join(unpackedRoot, 'resources', 'backend', 'python', 'Scripts', 'python.exe'), ] const ffmpegCandidates = [ path.join(unpackedRoot, 'resources', 'backend', 'ffmpeg', 'ffmpeg.exe'), path.join(unpackedRoot, 'resources', 'backend', 'ffmpeg', 'bin', 'ffmpeg.exe'), path.join(unpackedRoot, 'resources', 'backend', 'tools', 'ffmpeg', 'ffmpeg.exe'), path.join(unpackedRoot, 'resources', 'backend', 'tools', 'ffmpeg', 'bin', 'ffmpeg.exe'), ] const errors = [] if (!fs.existsSync(unpackedRoot)) { errors.push(`missing unpacked app directory: ${unpackedRoot}`) } for (const file of requiredFiles) { if (!fs.existsSync(file)) errors.push(`missing required file: ${file}`) } if (!pythonCandidates.some((p) => fs.existsSync(p))) { errors.push(`missing bundled Python executable:\n${pythonCandidates.map((p) => `- ${p}`).join('\n')}`) } if (!ffmpegCandidates.some((p) => fs.existsSync(p))) { errors.push(`missing bundled backend ffmpeg.exe:\n${ffmpegCandidates.map((p) => `- ${p}`).join('\n')}`) } const distEntries = fs.existsSync(distRoot) ? fs.readdirSync(distRoot, { withFileTypes: true }) : [] const rootExeArtifacts = distEntries .filter((e) => e.isFile() && e.name.toLowerCase().endsWith('.exe')) .map((e) => e.name) const rootZipArtifacts = distEntries .filter((e) => e.isFile() && e.name.toLowerCase().endsWith('.zip')) .map((e) => e.name) if (!rootExeArtifacts.length) { errors.push(`missing single-file portable exe in dist root: ${distRoot}`) } if (!rootZipArtifacts.length) { errors.push(`missing complete lazy-package zip in dist root: ${distRoot}`) } if (errors.length) { console.error('[verify:win-package] Windows package check failed:\n') for (const error of errors) console.error(`- ${error}\n`) process.exit(1) } console.log('[verify:win-package] OK: Windows portable exe, zip package, Electron DLLs, Python, and ffmpeg are present.') console.log(`[verify:win-package] portable exe: ${rootExeArtifacts.join(', ')}`) console.log(`[verify:win-package] lazy zip: ${rootZipArtifacts.join(', ')}`)