92 lines
2.9 KiB
JavaScript
92 lines
2.9 KiB
JavaScript
import fs from 'fs'
|
||
import path from 'path'
|
||
import { spawnSync } from 'child_process'
|
||
import { fileURLToPath } from 'url'
|
||
|
||
const __filename = fileURLToPath(import.meta.url)
|
||
const __dirname = path.dirname(__filename)
|
||
|
||
const electronRoot = path.resolve(__dirname, '..')
|
||
const backendRoot = path.resolve(electronRoot, '..')
|
||
|
||
const pythonCandidates = [
|
||
path.join(backendRoot, 'python', 'python.exe'),
|
||
path.join(backendRoot, 'python', 'Scripts', 'python.exe'),
|
||
]
|
||
const web2Py = path.join(backendRoot, 'web2.py')
|
||
const ffmpegCandidates = [
|
||
path.join(backendRoot, 'ffmpeg', 'ffmpeg.exe'),
|
||
path.join(backendRoot, 'ffmpeg', 'bin', 'ffmpeg.exe'),
|
||
path.join(backendRoot, 'tools', 'ffmpeg', 'ffmpeg.exe'),
|
||
path.join(backendRoot, 'tools', 'ffmpeg', 'bin', 'ffmpeg.exe'),
|
||
]
|
||
|
||
const errors = []
|
||
|
||
if (!fs.existsSync(web2Py)) {
|
||
errors.push(`缺少后端入口文件: ${web2Py}`)
|
||
}
|
||
const pythonExe = pythonCandidates.find((p) => fs.existsSync(p)) ?? null
|
||
if (!pythonExe) {
|
||
errors.push(`缺少内置 Python(任一路径即可):\n- ${pythonCandidates[0]}\n- ${pythonCandidates[1]}`)
|
||
}
|
||
// venv 结构在跨机器分发时可能依赖构建机本地 Python,不可作为懒人包运行时。
|
||
if (fs.existsSync(path.join(backendRoot, 'python', 'pyvenv.cfg'))) {
|
||
errors.push(
|
||
`检测到 venv 运行时(${path.join(
|
||
backendRoot,
|
||
'python',
|
||
'pyvenv.cfg',
|
||
)})。该结构不保证跨机器可用,请改为可分发的独立 Python Runtime(嵌入式/便携式)。`,
|
||
)
|
||
}
|
||
|
||
const ffmpegExe = ffmpegCandidates.find((p) => fs.existsSync(p)) ?? null
|
||
if (!ffmpegExe) {
|
||
errors.push(
|
||
`缺少 ffmpeg.exe(以下任一路径都可):\n${ffmpegCandidates.map((p) => `- ${p}`).join('\n')}`,
|
||
)
|
||
}
|
||
|
||
if (pythonExe) {
|
||
const check = spawnSync(
|
||
pythonExe,
|
||
[
|
||
'-c',
|
||
'import importlib.util,fastapi,uvicorn,requests,psutil,src; assert importlib.util.find_spec("web2"); print("runtime_ok")',
|
||
],
|
||
{
|
||
cwd: backendRoot,
|
||
encoding: 'utf8',
|
||
windowsHide: true,
|
||
timeout: 30000,
|
||
},
|
||
)
|
||
if (check.status !== 0) {
|
||
errors.push(
|
||
`内置 Python 缺少运行依赖或无法导入后端模块(fastapi/uvicorn/requests/psutil/web2/src)。\n请先在 ${path.join(
|
||
backendRoot,
|
||
'python',
|
||
)} 环境安装 requirements.txt。\n\nstdout:\n${check.stdout || '(empty)'}\n\nstderr:\n${check.stderr || '(empty)'}`,
|
||
)
|
||
}
|
||
}
|
||
|
||
if (errors.length) {
|
||
console.error('[verify:runtime] 运行时检查失败:\n')
|
||
for (const e of errors) console.error(`- ${e}\n`)
|
||
process.exit(1)
|
||
}
|
||
|
||
const st = spawnSync('node', [path.join(__dirname, 'stage-offline-runtime.mjs')], {
|
||
cwd: electronRoot,
|
||
stdio: 'inherit',
|
||
})
|
||
if (st.status !== 0) {
|
||
console.error('[verify:runtime] 生成 offline-runtime 镜像失败')
|
||
process.exit(1)
|
||
}
|
||
|
||
console.log('[verify:runtime] OK: 内置 Python/依赖/ffmpeg 已就绪,offline-runtime 已同步。')
|
||
|