From 32e5ef96fd89216d4032c571f5d6bb21fb67785c Mon Sep 17 00:00:00 2001 From: scorpion Date: Sun, 8 Feb 2026 18:06:31 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat(=E5=8F=91=E5=B8=83):=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E7=89=88=E6=9C=AC=E8=87=B30.1.14=E5=B9=B6=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=B9=B3=E5=8F=B0=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增强生成清单脚本以自动发现签名目录 --- .../workflows/scripts/generate-manifest.js | 40 ++++++++++++++++++- latest-update.json | 21 ++++++++-- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/.github/workflows/scripts/generate-manifest.js b/.github/workflows/scripts/generate-manifest.js index fc126f9..301f679 100644 --- a/.github/workflows/scripts/generate-manifest.js +++ b/.github/workflows/scripts/generate-manifest.js @@ -199,19 +199,55 @@ async function generateManifest() { allSignatureFiles.forEach(sig => console.log(` - ${sig.relativePath}`)); console.log(''); + // 自动发现 signatures 目录下的所有子目录 + console.log('自动发现 signatures 子目录...'); + const discoveredDirs = []; + if (fs.existsSync('signatures')) { + const entries = fs.readdirSync('signatures', { withFileTypes: true }); + for (const entry of entries) { + if (entry.isDirectory()) { + discoveredDirs.push(`signatures/${entry.name}`); + } + } + } + console.log(`发现的子目录: ${discoveredDirs.join(', ') || '无'}`); + console.log(''); + const platforms = []; for (const config of platformConfigs) { console.log(`处理平台配置: ${config.key}`); console.log(` 文件名: ${config.fileName}`); - console.log(` 尝试的签名目录: ${config.signatureDirs.join(', ')}`); + + // 合并预定义目录和自动发现的目录 + const allDirs = [...config.signatureDirs]; + // 根据平台关键词从发现的目录中筛选 + const platformKeywords = { + 'darwin-x86_64': ['macos', 'x64', 'x86_64'], + 'darwin-aarch64': ['macos', 'aarch64', 'arm64'], + 'windows-x86_64-webview2': ['windows', 'webview2'], + 'windows-x86_64': ['windows', 'x64'] + }; + const keywords = platformKeywords[config.key] || []; + for (const dir of discoveredDirs) { + const dirLower = dir.toLowerCase(); + // 检查目录名是否包含平台关键词 + if (keywords.some(kw => dirLower.includes(kw.toLowerCase()))) { + // 避免重复添加 + if (!allDirs.includes(dir)) { + allDirs.push(dir); + } + } + } + + console.log(` 尝试的签名目录: ${allDirs.join(', ')}`); // 查找签名文件(支持直接文件路径或目录搜索) let foundSigPath = null; let foundSigDir = null; // 遍历所有可能的签名目录 - for (const sigDir of config.signatureDirs) { + for (const sigDir of allDirs) { console.log(` 检查目录: ${sigDir}`); // 首先尝试直接查找签名文件 diff --git a/latest-update.json b/latest-update.json index 9799843..821f252 100644 --- a/latest-update.json +++ b/latest-update.json @@ -1,6 +1,19 @@ { - "version": "0.0.0-test-", - "notes": "版本 0.0.0-test- 的发布说明", - "pub_date": "2026-01-09T11:17:23.502Z", - "platforms": {} + "version": "0.1.14", + "notes": "版本 0.1.14 的发布说明", + "pub_date": "2026-02-08T10:05:48.176Z", + "platforms": { + "darwin-x86_64": { + "signature": "test-signature-x64", + "url": "https://github.com/scorpionfree98/json_reader/releases/download/0.1.14/JSONFormatter_0.1.14-macos-x64.dmg" + }, + "darwin-aarch64": { + "signature": "test-signature-aarch64", + "url": "https://github.com/scorpionfree98/json_reader/releases/download/0.1.14/JSONFormatter_0.1.14-macos-aarch64.dmg" + }, + "windows-x86_64": { + "signature": "test-signature-x64", + "url": "https://github.com/scorpionfree98/json_reader/releases/download/0.1.14/JSONFormatter_0.1.14-windows-x64.exe" + } + } } \ No newline at end of file From b49277041070a7551e3cf8562340c693cc9f476b Mon Sep 17 00:00:00 2001 From: scorpion Date: Sun, 8 Feb 2026 18:16:04 +0800 Subject: [PATCH 2/4] =?UTF-8?q?ci(workflows):=20=E6=94=B9=E8=BF=9B?= =?UTF-8?q?=E7=AD=BE=E5=90=8D=E6=96=87=E4=BB=B6=E6=89=AB=E6=8F=8F=E5=92=8C?= =?UTF-8?q?=E5=8C=B9=E9=85=8D=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复签名文件路径通配符问题并增强签名文件扫描功能 处理双层 signatures 目录结构 优化平台特定签名文件的匹配逻辑 添加备选签名文件匹配机制 --- .github/workflows/release.yml | 4 +- .../workflows/scripts/generate-manifest.js | 100 +++++++++++++++--- 2 files changed, 88 insertions(+), 16 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a8de197..8e31d3c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -363,8 +363,8 @@ jobs: with: name: ${{ matrix.platform }}-${{ matrix.arch }}${{ matrix.webview2 && format('-{0}webview2', matrix.webview2) || '' }}-signature path: | - src-tauri/target/${{ matrix.target }}/release/signatures/ - src-tauri/target/${{ matrix.target }}/release/bundle/update/ + src-tauri/target/${{ matrix.target }}/release/signatures/* + src-tauri/target/${{ matrix.target }}/release/bundle/update/* if-no-files-found: warn retention-days: 1 diff --git a/.github/workflows/scripts/generate-manifest.js b/.github/workflows/scripts/generate-manifest.js index 301f679..582c44f 100644 --- a/.github/workflows/scripts/generate-manifest.js +++ b/.github/workflows/scripts/generate-manifest.js @@ -194,19 +194,37 @@ async function generateManifest() { return allSigs; } - const allSignatureFiles = scanSignaturesDir(); + let allSignatureFiles = scanSignaturesDir(); console.log(`找到 ${allSignatureFiles.length} 个签名文件:`); allSignatureFiles.forEach(sig => console.log(` - ${sig.relativePath}`)); console.log(''); + // 处理双层 signatures 目录情况 (signatures/signatures/) + if (allSignatureFiles.length === 0 && fs.existsSync('signatures/signatures')) { + console.log('检测到双层 signatures 目录结构,重新扫描...'); + allSignatureFiles = scanSignaturesDir('signatures/signatures'); + console.log(`在 signatures/signatures/ 中找到 ${allSignatureFiles.length} 个签名文件:`); + allSignatureFiles.forEach(sig => console.log(` - ${sig.relativePath}`)); + console.log(''); + } + // 自动发现 signatures 目录下的所有子目录 console.log('自动发现 signatures 子目录...'); const discoveredDirs = []; - if (fs.existsSync('signatures')) { - const entries = fs.readdirSync('signatures', { withFileTypes: true }); - for (const entry of entries) { - if (entry.isDirectory()) { - discoveredDirs.push(`signatures/${entry.name}`); + const baseDirs = ['signatures']; + + // 也检查双层结构 + if (fs.existsSync('signatures/signatures')) { + baseDirs.push('signatures/signatures'); + } + + for (const baseDir of baseDirs) { + if (fs.existsSync(baseDir)) { + const entries = fs.readdirSync(baseDir, { withFileTypes: true }); + for (const entry of entries) { + if (entry.isDirectory()) { + discoveredDirs.push(`${baseDir}/${entry.name}`); + } } } } @@ -288,29 +306,83 @@ async function generateManifest() { // 兜底:如果标准路径找不到,尝试从所有签名文件中匹配 if (!foundSigPath && allSignatureFiles.length > 0) { console.log(` -> 尝试从所有签名文件中匹配...`); - const targetBaseName = config.fileName.replace(/\.[^.]+$/, ''); + const targetExt = path.extname(config.fileName).toLowerCase(); // .dmg, .exe, .msi const platformKeywords = { - 'darwin-x86_64': ['macos', 'x64', 'x86_64', 'darwin'], - 'darwin-aarch64': ['macos', 'aarch64', 'arm64', 'darwin'], - 'windows-x86_64-webview2': ['windows', 'x64', 'webview2'], - 'windows-x86_64': ['windows', 'x64'] + 'darwin-x86_64': ['macos', 'x64', 'x86_64', 'darwin', 'app.tar.gz'], + 'darwin-aarch64': ['macos', 'aarch64', 'arm64', 'darwin', 'app.tar.gz'], + 'windows-x86_64-webview2': ['windows', 'x64', 'webview2', 'setup.exe', '.msi'], + 'windows-x86_64': ['windows', 'x64', 'setup.exe', '.msi'] }; const keywords = platformKeywords[config.key] || []; for (const sig of allSignatureFiles) { const sigName = sig.name.toLowerCase(); + const sigExt = path.extname(sigName).toLowerCase(); // .sig + // 检查签名文件名是否包含平台关键词 const matchesPlatform = keywords.some(kw => sigName.includes(kw.toLowerCase())); // 避免将 webview2 版本匹配到非 webview2 版本 const isWebview2 = sigName.includes('webview2'); const shouldBeWebview2 = config.key.includes('webview2'); + // macOS 平台匹配逻辑 + const isMacPlatform = config.key.startsWith('darwin'); + const isMacSig = sigName.includes('.app.tar.gz') || sigName.includes('.dmg'); + + // Windows 平台匹配逻辑 + const isWinPlatform = config.key.startsWith('windows'); + const isWinSig = sigName.includes('.exe') || sigName.includes('.msi'); + + let isMatch = false; + if (matchesPlatform && isWebview2 === shouldBeWebview2) { - // 进一步检查是否匹配版本号 - if (sigName.includes(version) || sigName.includes(productName.toLowerCase())) { + // 对于 macOS,优先匹配 .app.tar.gz.sig + if (isMacPlatform && isMacSig) { + isMatch = true; + } + // 对于 Windows,优先匹配 .exe.sig 或 .msi.sig + else if (isWinPlatform && isWinSig) { + isMatch = true; + } + // 通用匹配:检查产品名 + else if (sigName.includes(productName.toLowerCase().replace(/[^a-z0-9]/g, ''))) { + isMatch = true; + } + } + + if (isMatch) { + foundSigPath = sig.path; + foundSigDir = path.dirname(sig.path); + console.log(` -> 从全局扫描找到匹配签名: ${sig.relativePath}`); + break; + } + } + + // 如果还是没找到,使用第一个可用的签名文件(最后手段) + if (!foundSigPath && allSignatureFiles.length > 0) { + // 根据平台类型选择最合适的签名文件 + const platformType = config.key.split('-')[0]; // darwin, windows + const arch = config.key.includes('aarch64') || config.key.includes('arm64') ? 'aarch64' : 'x64'; + + for (const sig of allSignatureFiles) { + const sigName = sig.name.toLowerCase(); + + // macOS 平台选择 .app.tar.gz.sig + if (platformType === 'darwin' && sigName.includes('.app.tar.gz')) { + // 检查架构 + if ((arch === 'aarch64' && (sigName.includes('aarch64') || sigName.includes('arm64'))) || + (arch === 'x64' && (sigName.includes('x64') || sigName.includes('x86_64')))) { + foundSigPath = sig.path; + foundSigDir = path.dirname(sig.path); + console.log(` -> 使用备选签名文件 (按平台类型匹配): ${sig.relativePath}`); + break; + } + } + // Windows 平台选择 .exe.sig + else if (platformType === 'windows' && sigName.includes('.exe')) { foundSigPath = sig.path; foundSigDir = path.dirname(sig.path); - console.log(` -> 从全局扫描找到匹配签名: ${sig.relativePath}`); + console.log(` -> 使用备选签名文件 (按平台类型匹配): ${sig.relativePath}`); break; } } From 7a1ea377823511e6b09d6860236aae94584829d1 Mon Sep 17 00:00:00 2001 From: scorpion Date: Sun, 8 Feb 2026 18:35:25 +0800 Subject: [PATCH 3/4] =?UTF-8?q?ci(release):=20=E4=BF=AE=E6=94=B9=E7=AD=BE?= =?UTF-8?q?=E5=90=8D=E6=96=87=E4=BB=B6=E4=B8=8B=E8=BD=BD=E6=96=B9=E5=BC=8F?= =?UTF-8?q?=E5=B9=B6=E7=AE=80=E5=8C=96manifest=E7=94=9F=E6=88=90=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改release workflow不合并签名文件,确保每个artifact下载到独立目录 简化generate-manifest.js脚本,移除冗余代码和复杂逻辑 --- .github/workflows/release.yml | 3 +- .../workflows/scripts/generate-manifest.js | 483 ++---------------- 2 files changed, 56 insertions(+), 430 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8e31d3c..34e8af9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -390,7 +390,8 @@ jobs: with: path: signatures pattern: "*signature" - merge-multiple: true + # 注意:不要设置 merge-multiple: true,让每个 artifact 下载到独立的子目录 + # 这样可以保留平台信息,避免签名文件混淆 - name: Display structure of downloaded files run: | diff --git a/.github/workflows/scripts/generate-manifest.js b/.github/workflows/scripts/generate-manifest.js index 582c44f..c831f79 100644 --- a/.github/workflows/scripts/generate-manifest.js +++ b/.github/workflows/scripts/generate-manifest.js @@ -3,19 +3,16 @@ import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; -// ES模块中没有 __dirname 和 __filename,需要自己创建 const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); async function generateManifest() { - // 从环境变量获取版本号 const manualVersion = process.env.INPUT_VERSION || ''; const githubRef = process.env.GITHUB_REF || ''; const runNumber = process.env.GITHUB_RUN_NUMBER || ''; const repoOwner = process.env.GITHUB_REPOSITORY_OWNER || 'scorpionfree98'; const repoName = process.env.GITHUB_REPOSITORY?.split('/')[1] || 'json_reader'; - // 确定版本号 let version; if (manualVersion) { version = manualVersion.replace(/^v/, ''); @@ -28,71 +25,39 @@ async function generateManifest() { console.log(`使用测试版本: ${version}`); } - // 从 tauri.conf.json 读取产品名称 let productName = 'JSON格式化工具'; try { const tauriConfigPath = path.join(__dirname, '../../../src-tauri/tauri.conf.json'); - console.log('尝试读取 tauri.conf.json:', tauriConfigPath); const tauriConfig = JSON.parse(fs.readFileSync(tauriConfigPath, 'utf8')); productName = tauriConfig.productName || productName; - console.log('读取到的产品名称:', productName); + console.log('产品名称:', productName); } catch (error) { - console.warn('无法读取 tauri.conf.json,使用默认产品名称:', error.message); + console.warn('无法读取 tauri.conf.json,使用默认产品名称'); } - // 创建符合 Tauri 升级要求的基础 manifest const manifest = { version, notes: `版本 ${version} 的发布说明`, pub_date: new Date().toISOString(), platforms: {} }; - - console.log('=== 创建 Tauri 更新清单 ===') + + console.log('\n=== 创建 Tauri 更新清单 ==='); console.log('版本:', version); - console.log('产品名称:', productName); - console.log('发布日期:', manifest.pub_date); - // 递归查找签名文件的函数 - // targetFileName: 目标文件名,用于匹配正确的签名文件(如 .dmg.sig 而不是 .tar.gz.sig) - function findSignatureFile(basePath, targetFileName = null) { - // 首先检查 basePath 是否存在 + // 递归查找签名文件 + function findSignatureFile(basePath) { if (!fs.existsSync(basePath)) { - console.log(` -> 路径不存在: ${basePath}`); return null; } - + const queue = [basePath]; - let foundSig = null; - let allSigs = []; - while (queue.length > 0) { const currentPath = queue.shift(); - try { const stats = fs.statSync(currentPath); if (stats.isFile() && currentPath.endsWith('.sig')) { - allSigs.push(currentPath); - - // 如果有目标文件名,优先匹配包含目标文件名的签名文件 - if (targetFileName) { - const sigFileName = path.basename(currentPath); - const targetExt = path.extname(targetFileName); // 如 .dmg - const targetBase = path.basename(targetFileName, targetExt); // 如 JSONFormatter_0.1.14-macos-x64 - - // 检查签名文件名是否匹配目标文件 - // 签名文件名格式通常是: .sig 或 ..sig - if (sigFileName.includes(targetBase) && sigFileName.includes(targetExt.replace('.', ''))) { - console.log(` -> 精确匹配: ${sigFileName} 匹配 ${targetFileName}`); - return currentPath; - } - // 如果没有找到匹配的,记录第一个找到的作为备选 - if (!foundSig) { - foundSig = currentPath; - } - } else { - return currentPath; - } + return currentPath; } else if (stats.isDirectory()) { const files = fs.readdirSync(currentPath); for (const file of files) { @@ -100,444 +65,104 @@ async function generateManifest() { } } } catch (error) { - console.log(` -> 无法访问 ${currentPath}: ${error.message}`); + // 忽略访问错误 } } - - // 如果没有找到精确匹配的,但有找到签名文件,返回第一个找到的 - if (foundSig) { - console.log(` -> 使用备选签名文件: ${path.basename(foundSig)}`); - } else if (allSigs.length > 0) { - // 如果没有任何匹配,返回第一个找到的签名文件 - console.log(` -> 未找到精确匹配,使用第一个签名文件: ${path.basename(allSigs[0])}`); - return allSigs[0]; - } - - return foundSig; + return null; } - // 定义平台配置 - 支持多种可能的artifact命名模式 - // 注意:GitHub Actions download-artifact 会将 artifact 名称作为子目录 + // 平台配置 - artifact 下载后会创建对应的子目录 const platformConfigs = [ { key: 'darwin-x86_64', - signatureDirs: [ - 'signatures/macos-latest-x64-signature', - 'signatures/macos-13-x64-signature', - 'signatures/macos-latest-x86_64-signature', - 'signatures/x64-signature', // 备选:如果artifact名称简化 - 'signatures/darwin-x86_64-signature' - ], - fileName: `${productName}_${version}-macos-x64.dmg` + signatureDir: 'signatures/macos-latest-x64-signature', + fileName: `${productName}_${version}-macos-x64.app.tar.gz` }, { key: 'darwin-aarch64', - signatureDirs: [ - 'signatures/macos-latest-aarch64-signature', - 'signatures/macos-14-aarch64-signature', - 'signatures/macos-latest-arm64-signature', - 'signatures/aarch64-signature', // 备选:如果artifact名称简化 - 'signatures/arm64-signature', - 'signatures/darwin-aarch64-signature' - ], - fileName: `${productName}_${version}-macos-aarch64.dmg` + signatureDir: 'signatures/macos-latest-aarch64-signature', + fileName: `${productName}_${version}-macos-aarch64.app.tar.gz` }, { key: 'windows-x86_64-webview2', - signatureDirs: [ - 'signatures/windows-latest-x64-with-webview2-signature', - 'signatures/windows-2022-x64-with-webview2-signature', - 'signatures/x64-with-webview2-signature', - 'signatures/windows-x86_64-webview2-signature' - ], + signatureDir: 'signatures/windows-latest-x64-with-webview2-signature', fileName: `${productName}_${version}-windows-x64-webview2.exe` }, { key: 'windows-x86_64', - signatureDirs: [ - 'signatures/windows-latest-x64-without-webview2-signature', - 'signatures/windows-latest-x64-signature', - 'signatures/windows-2022-x64-signature', - 'signatures/x64-signature', - 'signatures/x64-without-webview2-signature', - 'signatures/windows-x86_64-signature' - ], + signatureDir: 'signatures/windows-latest-x64-without-webview2-signature', fileName: `${productName}_${version}-windows-x64.exe` } ]; - console.log('\n=== 检查签名文件 ==='); - - // 首先扫描整个 signatures 目录,了解实际结构 - console.log('扫描 signatures 目录结构...'); - function scanSignaturesDir(baseDir = 'signatures') { - const allSigs = []; - if (!fs.existsSync(baseDir)) { - console.warn(` -> signatures 目录不存在: ${baseDir}`); - return allSigs; - } - - function scanDir(dir, relativePath = '') { - const items = fs.readdirSync(dir); - for (const item of items) { - const fullPath = path.join(dir, item); - const relPath = path.join(relativePath, item); - const stat = fs.statSync(fullPath); - if (stat.isDirectory()) { - scanDir(fullPath, relPath); - } else if (item.endsWith('.sig')) { - allSigs.push({ path: fullPath, relativePath: relPath, name: item }); - } - } - } - scanDir(baseDir); - return allSigs; - } - - let allSignatureFiles = scanSignaturesDir(); - console.log(`找到 ${allSignatureFiles.length} 个签名文件:`); - allSignatureFiles.forEach(sig => console.log(` - ${sig.relativePath}`)); - console.log(''); - - // 处理双层 signatures 目录情况 (signatures/signatures/) - if (allSignatureFiles.length === 0 && fs.existsSync('signatures/signatures')) { - console.log('检测到双层 signatures 目录结构,重新扫描...'); - allSignatureFiles = scanSignaturesDir('signatures/signatures'); - console.log(`在 signatures/signatures/ 中找到 ${allSignatureFiles.length} 个签名文件:`); - allSignatureFiles.forEach(sig => console.log(` - ${sig.relativePath}`)); - console.log(''); - } - - // 自动发现 signatures 目录下的所有子目录 - console.log('自动发现 signatures 子目录...'); - const discoveredDirs = []; - const baseDirs = ['signatures']; - - // 也检查双层结构 - if (fs.existsSync('signatures/signatures')) { - baseDirs.push('signatures/signatures'); - } - - for (const baseDir of baseDirs) { - if (fs.existsSync(baseDir)) { - const entries = fs.readdirSync(baseDir, { withFileTypes: true }); - for (const entry of entries) { - if (entry.isDirectory()) { - discoveredDirs.push(`${baseDir}/${entry.name}`); - } - } - } - } - console.log(`发现的子目录: ${discoveredDirs.join(', ') || '无'}`); - console.log(''); - - const platforms = []; + console.log('\n=== 处理平台配置 ==='); for (const config of platformConfigs) { - console.log(`处理平台配置: ${config.key}`); - console.log(` 文件名: ${config.fileName}`); - - // 合并预定义目录和自动发现的目录 - const allDirs = [...config.signatureDirs]; - // 根据平台关键词从发现的目录中筛选 - const platformKeywords = { - 'darwin-x86_64': ['macos', 'x64', 'x86_64'], - 'darwin-aarch64': ['macos', 'aarch64', 'arm64'], - 'windows-x86_64-webview2': ['windows', 'webview2'], - 'windows-x86_64': ['windows', 'x64'] - }; - const keywords = platformKeywords[config.key] || []; - for (const dir of discoveredDirs) { - const dirLower = dir.toLowerCase(); - // 检查目录名是否包含平台关键词 - if (keywords.some(kw => dirLower.includes(kw.toLowerCase()))) { - // 避免重复添加 - if (!allDirs.includes(dir)) { - allDirs.push(dir); - } - } - } - - console.log(` 尝试的签名目录: ${allDirs.join(', ')}`); - - // 查找签名文件(支持直接文件路径或目录搜索) - let foundSigPath = null; - let foundSigDir = null; - - // 遍历所有可能的签名目录 - for (const sigDir of allDirs) { - console.log(` 检查目录: ${sigDir}`); - - // 首先尝试直接查找签名文件 - const directSigPath = `${sigDir}/signature.sig`; - if (fs.existsSync(directSigPath)) { - foundSigPath = directSigPath; - foundSigDir = sigDir; - console.log(` -> 找到直接签名文件: ${foundSigPath}`); - break; - } - - // 尝试在签名目录的 update 子目录中查找 - const updateSigPath = `${sigDir}/update/signature.sig`; - if (fs.existsSync(updateSigPath)) { - foundSigPath = updateSigPath; - foundSigDir = sigDir; - console.log(` -> 在 update 目录找到签名文件: ${foundSigPath}`); - break; - } - - // 尝试递归查找 - foundSigPath = findSignatureFile(sigDir, config.fileName); - if (foundSigPath) { - foundSigDir = sigDir; - console.log(` -> 递归找到签名文件: ${foundSigPath}`); - // 验证签名文件是否匹配目标文件 - const sigFileName = path.basename(foundSigPath); - const targetBaseName = config.fileName.replace(/\.[^.]+$/, ''); - if (sigFileName.includes(targetBaseName)) { - console.log(` -> 签名文件与目标文件匹配: ${sigFileName} ≈ ${config.fileName}`); - } else { - console.warn(` -> 警告: 签名文件可能与目标文件不匹配: ${sigFileName} vs ${config.fileName}`); - } - break; - } - } - - // 兜底:如果标准路径找不到,尝试从所有签名文件中匹配 - if (!foundSigPath && allSignatureFiles.length > 0) { - console.log(` -> 尝试从所有签名文件中匹配...`); - const targetExt = path.extname(config.fileName).toLowerCase(); // .dmg, .exe, .msi - const platformKeywords = { - 'darwin-x86_64': ['macos', 'x64', 'x86_64', 'darwin', 'app.tar.gz'], - 'darwin-aarch64': ['macos', 'aarch64', 'arm64', 'darwin', 'app.tar.gz'], - 'windows-x86_64-webview2': ['windows', 'x64', 'webview2', 'setup.exe', '.msi'], - 'windows-x86_64': ['windows', 'x64', 'setup.exe', '.msi'] - }; - const keywords = platformKeywords[config.key] || []; - - for (const sig of allSignatureFiles) { - const sigName = sig.name.toLowerCase(); - const sigExt = path.extname(sigName).toLowerCase(); // .sig - - // 检查签名文件名是否包含平台关键词 - const matchesPlatform = keywords.some(kw => sigName.includes(kw.toLowerCase())); - // 避免将 webview2 版本匹配到非 webview2 版本 - const isWebview2 = sigName.includes('webview2'); - const shouldBeWebview2 = config.key.includes('webview2'); - - // macOS 平台匹配逻辑 - const isMacPlatform = config.key.startsWith('darwin'); - const isMacSig = sigName.includes('.app.tar.gz') || sigName.includes('.dmg'); - - // Windows 平台匹配逻辑 - const isWinPlatform = config.key.startsWith('windows'); - const isWinSig = sigName.includes('.exe') || sigName.includes('.msi'); - - let isMatch = false; - - if (matchesPlatform && isWebview2 === shouldBeWebview2) { - // 对于 macOS,优先匹配 .app.tar.gz.sig - if (isMacPlatform && isMacSig) { - isMatch = true; - } - // 对于 Windows,优先匹配 .exe.sig 或 .msi.sig - else if (isWinPlatform && isWinSig) { - isMatch = true; - } - // 通用匹配:检查产品名 - else if (sigName.includes(productName.toLowerCase().replace(/[^a-z0-9]/g, ''))) { - isMatch = true; - } - } - - if (isMatch) { - foundSigPath = sig.path; - foundSigDir = path.dirname(sig.path); - console.log(` -> 从全局扫描找到匹配签名: ${sig.relativePath}`); - break; - } - } - - // 如果还是没找到,使用第一个可用的签名文件(最后手段) - if (!foundSigPath && allSignatureFiles.length > 0) { - // 根据平台类型选择最合适的签名文件 - const platformType = config.key.split('-')[0]; // darwin, windows - const arch = config.key.includes('aarch64') || config.key.includes('arm64') ? 'aarch64' : 'x64'; - - for (const sig of allSignatureFiles) { - const sigName = sig.name.toLowerCase(); - - // macOS 平台选择 .app.tar.gz.sig - if (platformType === 'darwin' && sigName.includes('.app.tar.gz')) { - // 检查架构 - if ((arch === 'aarch64' && (sigName.includes('aarch64') || sigName.includes('arm64'))) || - (arch === 'x64' && (sigName.includes('x64') || sigName.includes('x86_64')))) { - foundSigPath = sig.path; - foundSigDir = path.dirname(sig.path); - console.log(` -> 使用备选签名文件 (按平台类型匹配): ${sig.relativePath}`); - break; - } - } - // Windows 平台选择 .exe.sig - else if (platformType === 'windows' && sigName.includes('.exe')) { - foundSigPath = sig.path; - foundSigDir = path.dirname(sig.path); - console.log(` -> 使用备选签名文件 (按平台类型匹配): ${sig.relativePath}`); - break; - } - } - } - } - - if (!foundSigPath) { - console.log(` -> 未找到签名文件,跳过该平台配置`); - continue; - } - - // 添加到平台列表 - platforms.push({ - key: config.key, - sigPath: foundSigPath, - fileName: config.fileName - }); - console.log(` -> 已添加到平台列表 (目录: ${foundSigDir})`); - } + console.log(`\n处理平台: ${config.key}`); + console.log(` 签名目录: ${config.signatureDir}`); + + const sigPath = findSignatureFile(config.signatureDir); - // 为每个平台生成 manifest 条目 - console.log('\n=== 生成平台 manifest 条目 ==='); - - // 用于跟踪已添加的平台,避免重复 - const addedPlatforms = new Set(); - - for (const platform of platforms) { - const platformKey = platform.key; - - // 跳过已经处理过的平台 - if (addedPlatforms.has(platformKey)) { - console.log(`平台 ${platformKey} 已处理,跳过重复配置`); + if (!sigPath) { + console.log(` -> 未找到签名文件,跳过`); continue; } - - console.log(`处理平台: ${platformKey}`); - console.log(` 签名路径: ${platform.sigPath}`); - console.log(` 文件名: ${platform.fileName}`); - + + console.log(` -> 找到签名文件: ${path.basename(sigPath)}`); + try { - // 读取签名文件内容 - if (!fs.existsSync(platform.sigPath)) { - console.warn(` -> 签名文件不存在: ${platform.sigPath}`); - continue; - } - - const signature = fs.readFileSync(platform.sigPath, 'utf8').trim(); - - // 验证签名内容 - if (!signature || signature.length === 0) { + const signature = fs.readFileSync(sigPath, 'utf8').trim(); + + if (!signature) { console.warn(` -> 签名内容为空,跳过`); continue; } - - // 生成下载 URL - const fileNameEncoded = encodeURIComponent(platform.fileName); + + const fileNameEncoded = encodeURIComponent(config.fileName); const url = `https://github.com/${repoOwner}/${repoName}/releases/download/${version}/${fileNameEncoded}`; - - // 添加到 manifest - manifest.platforms[platformKey] = { + + manifest.platforms[config.key] = { signature, url }; - - addedPlatforms.add(platformKey); - + console.log(` -> 成功添加到 manifest`); - console.log(` 签名长度: ${signature.length}`); - console.log(` URL: ${url}`); - console.log(` 平台: ${platformKey}`); - + } catch (error) { - console.error(` -> 处理平台 ${platformKey} 失败:`, error.message); - console.error(` 错误详情:`, error); + console.error(` -> 处理失败:`, error.message); } } - // 验证 manifest 完整性 - console.log('\n=== 验证 Manifest 完整性 ==='); - if (Object.keys(manifest.platforms).length === 0) { - console.error('❌ 错误: 没有找到任何平台!生成的 manifest 将不包含任何平台信息。'); - console.error('这会导致 Tauri 应用无法检测到更新。'); - console.error(''); - console.error('可能的原因:'); - console.error('1. 签名文件没有正确生成(检查 TAURI_SIGNING_PRIVATE_KEY 是否设置)'); - console.error('2. Artifact 名称不匹配(检查上传和下载的 artifact 名称)'); - console.error('3. 签名文件路径不正确(检查 signatures 目录结构)'); - console.error(''); - console.error('请检查 GitHub Actions 日志中的 "Display structure of downloaded files" 步骤'); + // 验证 manifest + console.log('\n=== 验证 Manifest ==='); + const platformCount = Object.keys(manifest.platforms).length; + + if (platformCount === 0) { + console.error('❌ 错误: 没有找到任何平台!'); + console.error('请检查:'); + console.error('1. TAURI_SIGNING_PRIVATE_KEY 是否正确设置'); + console.error('2. Artifact 是否正确上传和下载'); + console.error('3. 签名文件是否在预期的目录中'); process.exit(1); - } else { - console.log(`✅ 成功: 找到 ${Object.keys(manifest.platforms).length} 个平台`); - console.log('平台列表:', Object.keys(manifest.platforms).join(', ')); } + console.log(`✅ 成功: 找到 ${platformCount} 个平台`); + console.log('平台列表:', Object.keys(manifest.platforms).join(', ')); + // 写入文件 const outputPath = 'latest-update.json'; fs.writeFileSync(outputPath, JSON.stringify(manifest, null, 2), 'utf8'); - console.log(`\n=== Manifest 生成完成 ===`); - console.log(`Manifest 已写入: ${outputPath}`); - console.log(`文件大小: ${fs.statSync(outputPath).size} 字节`); + console.log(`\n✅ Manifest 已写入: ${outputPath}`); - // 输出 version 到 GitHub Actions + // 输出到 GitHub Actions if (process.env.GITHUB_OUTPUT) { fs.appendFileSync(process.env.GITHUB_OUTPUT, `version=${version}\n`); - console.log(`版本号已输出到 GitHub Actions: version=${version}`); - } else { - // 本地测试时输出 - console.log(`版本号: ${version}`); } - // 打印生成的 manifest(简化版) - console.log('\n生成的 Manifest 摘要:'); - console.log(`- 版本: ${manifest.version}`); - console.log(`- 发布日期: ${manifest.pub_date}`); - console.log(`- 发布说明: ${manifest.notes}`); - console.log(`- 支持平台: ${Object.keys(manifest.platforms).join(', ')}`); - console.log('\n完整 Manifest 内容:'); + console.log('\n生成的 Manifest:'); console.log(JSON.stringify(manifest, null, 2)); - - // 验证生成的 manifest 符合 Tauri 要求 - console.log('\n=== Tauri Manifest 验证 ==='); - const requiredFields = ['version', 'pub_date', 'platforms']; - const missingFields = requiredFields.filter(field => !manifest.hasOwnProperty(field)); - - if (missingFields.length > 0) { - console.error('错误: 生成的 manifest 缺少必要字段:', missingFields.join(', ')); - console.error('这将导致 Tauri 应用无法正确处理更新!'); - process.exit(1); - } else { - console.log('成功: 生成的 manifest 包含所有必要字段'); - console.log('✅ version 字段存在'); - console.log('✅ pub_date 字段存在'); - console.log('✅ platforms 字段存在'); - - // 验证平台字段格式 - let platformValid = true; - for (const [platform, data] of Object.entries(manifest.platforms)) { - if (!data.hasOwnProperty('signature') || !data.hasOwnProperty('url')) { - console.error(`错误: 平台 ${platform} 缺少必要字段 signature 或 url`); - platformValid = false; - } - } - - if (platformValid) { - console.log('✅ 所有平台包含必要的 signature 和 url 字段'); - console.log('✅ 生成的 manifest 符合 Tauri 升级要求!'); - } else { - console.error('错误: 部分平台缺少必要字段'); - process.exit(1); - } - } } -// 处理可能的错误 if (process.argv[1] === fileURLToPath(import.meta.url)) { generateManifest().catch(error => { console.error('生成 manifest 时出错:', error); @@ -545,4 +170,4 @@ if (process.argv[1] === fileURLToPath(import.meta.url)) { }); } -export { generateManifest }; \ No newline at end of file +export { generateManifest }; From 998cb4e14c648a402ebdbb9014c86e1c8e14dab9 Mon Sep 17 00:00:00 2001 From: scorpion Date: Sun, 8 Feb 2026 18:47:51 +0800 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20=E4=BF=AE=E6=AD=A3Windows=E7=AD=BE?= =?UTF-8?q?=E5=90=8D=E7=9B=AE=E5=BD=95=E5=90=8D=E7=A7=B0=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E8=BF=9E=E5=AD=97=E7=AC=A6=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/scripts/generate-manifest.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/scripts/generate-manifest.js b/.github/workflows/scripts/generate-manifest.js index c831f79..0d9cee9 100644 --- a/.github/workflows/scripts/generate-manifest.js +++ b/.github/workflows/scripts/generate-manifest.js @@ -85,12 +85,12 @@ async function generateManifest() { }, { key: 'windows-x86_64-webview2', - signatureDir: 'signatures/windows-latest-x64-with-webview2-signature', + signatureDir: 'signatures/windows-latest-x64-withwebview2-signature', fileName: `${productName}_${version}-windows-x64-webview2.exe` }, { key: 'windows-x86_64', - signatureDir: 'signatures/windows-latest-x64-without-webview2-signature', + signatureDir: 'signatures/windows-latest-x64-withoutwebview2-signature', fileName: `${productName}_${version}-windows-x64.exe` } ];