From a814535aa0a2048098c7cd35f05a0fa3b245c8a1 Mon Sep 17 00:00:00 2001 From: mkdir700 Date: Fri, 12 Sep 2025 23:10:11 +0800 Subject: [PATCH 1/3] fix(build): fix FFmpeg cross-platform build on macOS for Windows targets - Add cross-env dependency for cross-platform environment variables - Update Windows build scripts to set BUILD_TARGET_PLATFORM and BUILD_TARGET_ARCH - Modify download-ffmpeg.ts to prioritize environment variables over command arguments - Update vite plugin to avoid duplicate FFmpeg downloads - Fix release scripts to download all platform FFmpeg binaries before building - Ensure correct FFmpeg binaries are included for each target platform Resolves issue where building Windows packages on macOS would include macOS FFmpeg binaries instead of Windows ones. --- electron.vite.config.ts | 22 +++++++++++++++++----- package.json | 19 +++++++++++-------- pnpm-lock.yaml | 18 ++++++++++++++++++ scripts/download-ffmpeg.ts | 23 ++++++++++++++++++++++- 4 files changed, 68 insertions(+), 14 deletions(-) diff --git a/electron.vite.config.ts b/electron.vite.config.ts index da28b75f..1526bcb9 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -18,13 +18,25 @@ function ffmpegDownloadPlugin() { // 只在生产构建时下载 FFmpeg if (!isProd) return - console.log('Downloading FFmpeg...') + // 根据构建目标决定下载哪个平台 + const targetPlatform = process.env.BUILD_TARGET_PLATFORM || process.platform + const targetArch = process.env.BUILD_TARGET_ARCH || process.arch + + // 检查是否已存在,避免重复下载 + const ffmpegPath = path.resolve( + 'resources/ffmpeg', + `${targetPlatform}-${targetArch}`, + targetPlatform === 'win32' ? 'ffmpeg.exe' : 'ffmpeg' + ) + + if (fs.existsSync(ffmpegPath)) { + console.log(`FFmpeg already exists for ${targetPlatform}-${targetArch}`) + return + } - try { - // 根据构建目标决定下载哪个平台 - const targetPlatform = process.env.BUILD_TARGET_PLATFORM || process.platform - const targetArch = process.env.BUILD_TARGET_ARCH || process.arch + console.log(`Downloading FFmpeg for ${targetPlatform}-${targetArch}...`) + try { await new Promise((resolve, reject) => { const downloadScript = spawn( 'tsx', diff --git a/package.json b/package.json index 92a27f60..ff4da17c 100644 --- a/package.json +++ b/package.json @@ -20,11 +20,12 @@ "start": "electron-vite preview", "dev": "electron-vite dev", "build": "npm run typecheck && electron-vite build", + "build:release": "npm run typecheck && electron-vite build", "postinstall": "electron-builder install-app-deps", "build:unpack": "npm run build && electron-builder --dir --publish never", - "build:win": "npm run build && electron-builder --win --publish never", - "build:win:x64": "npm run build && electron-builder --win --x64 --publish never", - "build:win:arm64": "npm run build && electron-builder --win --arm64 --publish never", + "build:win": "cross-env BUILD_TARGET_PLATFORM=win32 npm run build && electron-builder --win --publish never", + "build:win:x64": "cross-env BUILD_TARGET_PLATFORM=win32 BUILD_TARGET_ARCH=x64 npm run build && electron-builder --win --x64 --publish never", + "build:win:arm64": "cross-env BUILD_TARGET_PLATFORM=win32 BUILD_TARGET_ARCH=arm64 npm run build && electron-builder --win --arm64 --publish never", "build:mac": "electron-vite build && electron-builder --mac --publish never", "build:mac:x64": "npm run build && electron-builder --mac --x64 --publish never", "build:mac:arm64": "npm run build && electron-builder --mac --arm64 --publish never", @@ -47,10 +48,10 @@ "version:prerelease": "tsx scripts/version-manager.ts prerelease", "version:beta": "tsx scripts/version-manager.ts minor beta", "version:beta-patch": "tsx scripts/version-manager.ts patch beta", - "release": "npm run build && electron-builder --publish onTagOrDraft", - "release:all": "npm run build && electron-builder --publish always", - "release:never": "npm run build && electron-builder --publish never", - "release:draft": "npm run build && electron-builder --publish onTagOrDraft", + "release": "npm run ffmpeg:download-all && npm run build:release && electron-builder --publish onTagOrDraft", + "release:all": "npm run ffmpeg:download-all && npm run build:release && electron-builder --publish always", + "release:never": "npm run ffmpeg:download-all && npm run build:release && electron-builder --publish never", + "release:draft": "npm run ffmpeg:download-all && npm run build:release && electron-builder --publish onTagOrDraft", "migrate": "tsx src/main/db/migration-cli.ts", "migrate:up": "npm run migrate up", "migrate:down": "npm run migrate down", @@ -71,7 +72,8 @@ "ffmpeg:download-all": "tsx scripts/download-ffmpeg.ts all", "ffmpeg:clean": "tsx scripts/download-ffmpeg.ts clean", "ffmpeg:test": "tsx scripts/test-ffmpeg-integration.ts", - "prebuild": "npm run ffmpeg:download" + "prebuild": "npm run ffmpeg:download", + "prebuild:release": "echo 'FFmpeg already downloaded by release script'" }, "dependencies": { "@ant-design/icons": "^6.0.1", @@ -128,6 +130,7 @@ "@welldone-software/why-did-you-render": "^10.0.1", "cli-progress": "^3.12.0", "code-inspector-plugin": "^1.2.7", + "cross-env": "^10.0.0", "electron": "37.2.4", "electron-builder": "26.0.19", "electron-devtools-installer": "^4.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eac64848..bed42240 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -171,6 +171,9 @@ importers: code-inspector-plugin: specifier: ^1.2.7 version: 1.2.7 + cross-env: + specifier: ^10.0.0 + version: 10.0.0 electron: specifier: 37.2.4 version: 37.2.4 @@ -685,6 +688,9 @@ packages: '@emotion/unitless@0.8.1': resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==} + '@epic-web/invariant@1.0.0': + resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} + '@esbuild/aix-ppc64@0.25.8': resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==} engines: {node: '>=18'} @@ -2584,6 +2590,11 @@ packages: cross-dirname@0.1.0: resolution: {integrity: sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==} + cross-env@10.0.0: + resolution: {integrity: sha512-aU8qlEK/nHYtVuN4p7UQgAwVljzMg8hB4YK5ThRqD2l/ziSnryncPNn7bMLt5cFYsKVKBh8HqLqyCoTupEUu7Q==} + engines: {node: '>=20'} + hasBin: true + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -7433,6 +7444,8 @@ snapshots: '@emotion/unitless@0.8.1': {} + '@epic-web/invariant@1.0.0': {} + '@esbuild/aix-ppc64@0.25.8': optional: true @@ -9622,6 +9635,11 @@ snapshots: cross-dirname@0.1.0: optional: true + cross-env@10.0.0: + dependencies: + '@epic-web/invariant': 1.0.0 + cross-spawn: 7.0.6 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 diff --git a/scripts/download-ffmpeg.ts b/scripts/download-ffmpeg.ts index f4017661..442d5034 100644 --- a/scripts/download-ffmpeg.ts +++ b/scripts/download-ffmpeg.ts @@ -376,11 +376,28 @@ class FFmpegDownloader { // CLI 入口 async function main() { const args = process.argv.slice(2) - const command = args[0] || 'current' + let command = args[0] const downloader = new FFmpegDownloader() try { + // 优先检查环境变量,如果设置了构建目标则使用目标平台 + if (process.env.BUILD_TARGET_PLATFORM) { + console.log( + `检测到构建目标平台: ${process.env.BUILD_TARGET_PLATFORM}-${process.env.BUILD_TARGET_ARCH || process.arch}` + ) + await downloader.downloadFFmpeg( + process.env.BUILD_TARGET_PLATFORM, + process.env.BUILD_TARGET_ARCH || process.arch + ) + return + } + + // 如果没有环境变量,按原逻辑处理命令参数 + if (!command) { + command = 'current' + } + switch (command) { case 'all': await downloader.downloadAllPlatforms() @@ -416,6 +433,10 @@ async function main() { win32: x64, arm64 darwin: x64, arm64 linux: x64, arm64 + +环境变量: + BUILD_TARGET_PLATFORM - 构建目标平台 (win32, darwin, linux) + BUILD_TARGET_ARCH - 构建目标架构 (x64, arm64) `) } } catch (error) { From df42d9b56c1997e42ff72d4a89dbf82624c191f8 Mon Sep 17 00:00:00 2001 From: mkdir700 Date: Fri, 12 Sep 2025 23:15:35 +0800 Subject: [PATCH 2/3] fix(build): improve FFmpeg download in CI environments - Replace direct tsx command with npm run for better CI compatibility - Add CI-specific handling with proper environment variable passing - Convert hard failures to warnings to prevent CI build blocking - Improve error messages and debugging information Fixes Windows CI build failure: spawn tsx ENOENT --- electron.vite.config.ts | 64 +++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/electron.vite.config.ts b/electron.vite.config.ts index 1526bcb9..d22725ed 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -34,33 +34,49 @@ function ffmpegDownloadPlugin() { return } - console.log(`Downloading FFmpeg for ${targetPlatform}-${targetArch}...`) + console.log( + `FFmpeg missing for ${targetPlatform}-${targetArch}, should be downloaded via prebuild` + ) - try { - await new Promise((resolve, reject) => { - const downloadScript = spawn( - 'tsx', - ['scripts/download-ffmpeg.ts', 'platform', targetPlatform, targetArch], - { - stdio: 'inherit' - } - ) - - downloadScript.on('close', (code) => { - if (code === 0) { - console.log('FFmpeg Downloaded successfully') - resolve() - } else { - reject(new Error(`FFmpeg Download failed with exit code: ${code}`)) - } - }) + // 在生产构建时,FFmpeg 应该已经通过 prebuild 阶段下载 + // 如果这里仍然缺失,说明 prebuild 没有正确执行,发出警告但不阻止构建 + console.warn(`⚠️ FFmpeg not found for ${targetPlatform}-${targetArch}`) + console.warn(` This may cause runtime issues. Please ensure prebuild stage runs correctly.`) + console.warn(` You can manually download with: npm run ffmpeg:download`) + + // 对于 CI 环境,我们可以尝试通过 npm run 来下载,这更可靠 + if (process.env.CI) { + console.log(`CI environment detected, attempting to download via npm script...`) + try { + await new Promise((resolve) => { + const downloadScript = spawn('npm', ['run', 'ffmpeg:download'], { + stdio: 'inherit', + shell: true, + env: { + ...process.env, + BUILD_TARGET_PLATFORM: targetPlatform, + BUILD_TARGET_ARCH: targetArch + } + }) + + downloadScript.on('close', (code) => { + if (code === 0) { + console.log('FFmpeg Downloaded successfully via npm script') + resolve() + } else { + console.warn(`FFmpeg download failed with exit code: ${code}, continuing build...`) + resolve() // 不阻止构建 + } + }) - downloadScript.on('error', (error) => { - reject(error) + downloadScript.on('error', (error) => { + console.warn('FFmpeg download failed:', error.message, 'continuing build...') + resolve() // 不阻止构建 + }) }) - }) - } catch (error) { - console.warn('FFmpeg Download failed', error) + } catch (error) { + console.warn('FFmpeg Download failed in CI:', error) + } } } } From 5e609535362cb9242fba18fd54cd88924240fe20 Mon Sep 17 00:00:00 2001 From: mkdir700 Date: Fri, 12 Sep 2025 23:17:55 +0800 Subject: [PATCH 3/3] fix(build): ensure FFmpeg is properly downloaded in Windows CI - Use npm run ffmpeg:download on Windows for better compatibility - Pass environment variables to ensure correct platform detection - Throw errors on download failure to prevent incomplete builds - Keep direct tsx usage on Unix systems for performance This ensures CI builds never skip FFmpeg download and always include the correct FFmpeg binaries for the target platform. --- electron.vite.config.ts | 81 +++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/electron.vite.config.ts b/electron.vite.config.ts index d22725ed..7b77040e 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -34,49 +34,50 @@ function ffmpegDownloadPlugin() { return } - console.log( - `FFmpeg missing for ${targetPlatform}-${targetArch}, should be downloaded via prebuild` - ) + console.log(`Downloading FFmpeg for ${targetPlatform}-${targetArch}...`) + + try { + await new Promise((resolve, reject) => { + // 在不同环境中使用不同的命令来确保兼容性 + let command: string + let args: string[] + + if (process.platform === 'win32') { + // Windows 环境:使用 npm run 调用脚本,更可靠 + command = 'npm' + args = ['run', 'ffmpeg:download'] + } else { + // Unix 环境:直接使用 tsx + command = 'tsx' + args = ['scripts/download-ffmpeg.ts', 'platform', targetPlatform, targetArch] + } - // 在生产构建时,FFmpeg 应该已经通过 prebuild 阶段下载 - // 如果这里仍然缺失,说明 prebuild 没有正确执行,发出警告但不阻止构建 - console.warn(`⚠️ FFmpeg not found for ${targetPlatform}-${targetArch}`) - console.warn(` This may cause runtime issues. Please ensure prebuild stage runs correctly.`) - console.warn(` You can manually download with: npm run ffmpeg:download`) - - // 对于 CI 环境,我们可以尝试通过 npm run 来下载,这更可靠 - if (process.env.CI) { - console.log(`CI environment detected, attempting to download via npm script...`) - try { - await new Promise((resolve) => { - const downloadScript = spawn('npm', ['run', 'ffmpeg:download'], { - stdio: 'inherit', - shell: true, - env: { - ...process.env, - BUILD_TARGET_PLATFORM: targetPlatform, - BUILD_TARGET_ARCH: targetArch - } - }) - - downloadScript.on('close', (code) => { - if (code === 0) { - console.log('FFmpeg Downloaded successfully via npm script') - resolve() - } else { - console.warn(`FFmpeg download failed with exit code: ${code}, continuing build...`) - resolve() // 不阻止构建 - } - }) + const downloadScript = spawn(command, args, { + stdio: 'inherit', + shell: process.platform === 'win32', + env: { + ...process.env, + BUILD_TARGET_PLATFORM: targetPlatform, + BUILD_TARGET_ARCH: targetArch + } + }) - downloadScript.on('error', (error) => { - console.warn('FFmpeg download failed:', error.message, 'continuing build...') - resolve() // 不阻止构建 - }) + downloadScript.on('close', (code) => { + if (code === 0) { + console.log('FFmpeg Downloaded successfully') + resolve() + } else { + reject(new Error(`FFmpeg Download failed with exit code: ${code}`)) + } }) - } catch (error) { - console.warn('FFmpeg Download failed in CI:', error) - } + + downloadScript.on('error', (error) => { + reject(error) + }) + }) + } catch (error) { + console.error('FFmpeg Download failed:', error) + throw new Error(`Failed to download FFmpeg for ${targetPlatform}-${targetArch}: ${error}`) } } }