From 23c817606d1009dca68284b8ed33121b16101ace Mon Sep 17 00:00:00 2001 From: Tw93 Date: Wed, 20 Aug 2025 21:28:42 +0800 Subject: [PATCH] :bug: Fix linux and windows packaging issues --- .github/workflows/pake-cli.yaml | 4 +- bin/helpers/merge.ts | 31 +++++++++++++ bin/options/icon.ts | 68 ++++++++++++++++++++++++---- dist/cli.js | 78 +++++++++++++++++++++++++++++---- script/build_with_pake_cli.js | 41 +++++++++-------- 5 files changed, 182 insertions(+), 40 deletions(-) diff --git a/.github/workflows/pake-cli.yaml b/.github/workflows/pake-cli.yaml index c75ccd9..f18e437 100644 --- a/.github/workflows/pake-cli.yaml +++ b/.github/workflows/pake-cli.yaml @@ -125,13 +125,13 @@ jobs: run: | echo "Installing pake-cli..." npm install pake-cli --no-package-lock - + # Verify installation if [ ! -d "node_modules/pake-cli" ]; then echo "Error: Failed to install pake-cli" exit 1 fi - + echo "Listing pake-cli contents:" ls -la node_modules/pake-cli/ | head -5 echo "pake-cli installation verified" diff --git a/bin/helpers/merge.ts b/bin/helpers/merge.ts index 81de207..8b78a88 100644 --- a/bin/helpers/merge.ts +++ b/bin/helpers/merge.ts @@ -139,7 +139,38 @@ export async function mergeConfig( // Processing targets are currently only open to Linux. if (platform === 'linux') { + // Remove hardcoded desktop files and regenerate with correct app name delete tauriConf.bundle.linux.deb.files; + + // Generate correct desktop file configuration + const appNameLower = name.toLowerCase(); + const identifier = `com.pake.${appNameLower}`; + const desktopFileName = `${identifier}.desktop`; + + // Create desktop file content + const desktopContent = `[Desktop Entry] +Version=1.0 +Type=Application +Name=${name} +Comment=${name} +Exec=${appNameLower} +Icon=${appNameLower} +Categories=Network;WebBrowser; +MimeType=text/html;text/xml;application/xhtml_xml; +StartupNotify=true +`; + + // Write desktop file to assets directory + const assetsDir = path.join(npmDirectory, 'src-tauri/assets'); + const desktopFilePath = path.join(assetsDir, desktopFileName); + await fsExtra.ensureDir(assetsDir); + await fsExtra.writeFile(desktopFilePath, desktopContent); + + // Set up desktop file in bundle configuration + tauriConf.bundle.linux.deb.files = { + [`/usr/share/applications/${desktopFileName}`]: `assets/${desktopFileName}`, + }; + const validTargets = ['deb', 'appimage', 'rpm']; if (validTargets.includes(options.targets)) { tauriConf.bundle.targets = [options.targets]; diff --git a/bin/options/icon.ts b/bin/options/icon.ts index e02de5c..66d315c 100644 --- a/bin/options/icon.ts +++ b/bin/options/icon.ts @@ -119,11 +119,49 @@ export async function handleIcon(options: PakeAppOptions, url?: string) { } logger.info('✼ No icon provided, using default icon.'); - const iconPath = IS_WIN - ? 'src-tauri/png/icon_256.ico' - : IS_LINUX - ? 'src-tauri/png/icon_512.png' - : 'src-tauri/icons/icon.icns'; + + // For Windows, ensure we have proper fallback handling + if (IS_WIN) { + const defaultIcoPath = path.join( + npmDirectory, + 'src-tauri/png/icon_256.ico', + ); + const defaultPngPath = path.join( + npmDirectory, + 'src-tauri/png/icon_512.png', + ); + + // First try default ico + if (await fsExtra.pathExists(defaultIcoPath)) { + return defaultIcoPath; + } + + // If ico doesn't exist, try to convert from png + if (await fsExtra.pathExists(defaultPngPath)) { + logger.info('✼ Default ico not found, converting from png...'); + try { + const convertedPath = await convertIconFormat(defaultPngPath, 'icon'); + if (convertedPath && (await fsExtra.pathExists(convertedPath))) { + return convertedPath; + } + } catch (error) { + logger.warn(`Failed to convert default png to ico: ${error.message}`); + } + } + + // Last resort: return png path if it exists (Windows can handle png in some cases) + if (await fsExtra.pathExists(defaultPngPath)) { + logger.warn('✼ Using png as fallback for Windows (may cause issues).'); + return defaultPngPath; + } + + // If nothing exists, let the error bubble up + throw new Error('No default icon found for Windows build'); + } + + const iconPath = IS_LINUX + ? 'src-tauri/png/icon_512.png' + : 'src-tauri/icons/icon.icns'; return path.join(npmDirectory, iconPath); } @@ -171,9 +209,18 @@ async function tryGetFavicon( const serviceUrls = generateIconServiceUrls(domain); + // Use shorter timeout for CI environments + const isCI = + process.env.CI === 'true' || process.env.GITHUB_ACTIONS === 'true'; + const downloadTimeout = isCI ? 5000 : ICON_CONFIG.downloadTimeout; + for (const serviceUrl of serviceUrls) { try { - const faviconPath = await downloadIcon(serviceUrl, false); + const faviconPath = await downloadIcon( + serviceUrl, + false, + downloadTimeout, + ); if (!faviconPath) continue; const convertedPath = await convertIconFormat(faviconPath, appName); @@ -183,7 +230,11 @@ async function tryGetFavicon( ); return convertedPath; } - } catch { + } catch (error) { + // Log specific errors in CI for debugging + if (isCI) { + logger.debug(`Icon service ${serviceUrl} failed: ${error.message}`); + } continue; } } @@ -202,11 +253,12 @@ async function tryGetFavicon( export async function downloadIcon( iconUrl: string, showSpinner = true, + customTimeout?: number, ): Promise { try { const response = await axios.get(iconUrl, { responseType: 'arraybuffer', - timeout: ICON_CONFIG.downloadTimeout, + timeout: customTimeout || ICON_CONFIG.downloadTimeout, }); const iconData = response.data; diff --git a/dist/cli.js b/dist/cli.js index 509d8ab..c894d59 100644 --- a/dist/cli.js +++ b/dist/cli.js @@ -531,7 +531,33 @@ async function mergeConfig(url, options, tauriConf) { tauriConf.pake.system_tray[currentPlatform] = showSystemTray; // Processing targets are currently only open to Linux. if (platform === 'linux') { + // Remove hardcoded desktop files and regenerate with correct app name delete tauriConf.bundle.linux.deb.files; + // Generate correct desktop file configuration + const appNameLower = name.toLowerCase(); + const identifier = `com.pake.${appNameLower}`; + const desktopFileName = `${identifier}.desktop`; + // Create desktop file content + const desktopContent = `[Desktop Entry] +Version=1.0 +Type=Application +Name=${name} +Comment=${name} +Exec=${appNameLower} +Icon=${appNameLower} +Categories=Network;WebBrowser; +MimeType=text/html;text/xml;application/xhtml_xml; +StartupNotify=true +`; + // Write desktop file to assets directory + const assetsDir = path.join(npmDirectory, 'src-tauri/assets'); + const desktopFilePath = path.join(assetsDir, desktopFileName); + await fsExtra.ensureDir(assetsDir); + await fsExtra.writeFile(desktopFilePath, desktopContent); + // Set up desktop file in bundle configuration + tauriConf.bundle.linux.deb.files = { + [`/usr/share/applications/${desktopFileName}`]: `assets/${desktopFileName}` + }; const validTargets = ['deb', 'appimage', 'rpm']; if (validTargets.includes(options.targets)) { tauriConf.bundle.targets = [options.targets]; @@ -1014,11 +1040,38 @@ async function handleIcon(options, url) { return faviconPath; } logger.info('✼ No icon provided, using default icon.'); - const iconPath = IS_WIN - ? 'src-tauri/png/icon_256.ico' - : IS_LINUX - ? 'src-tauri/png/icon_512.png' - : 'src-tauri/icons/icon.icns'; + // For Windows, ensure we have proper fallback handling + if (IS_WIN) { + const defaultIcoPath = path.join(npmDirectory, 'src-tauri/png/icon_256.ico'); + const defaultPngPath = path.join(npmDirectory, 'src-tauri/png/icon_512.png'); + // First try default ico + if (await fsExtra.pathExists(defaultIcoPath)) { + return defaultIcoPath; + } + // If ico doesn't exist, try to convert from png + if (await fsExtra.pathExists(defaultPngPath)) { + logger.info('✼ Default ico not found, converting from png...'); + try { + const convertedPath = await convertIconFormat(defaultPngPath, 'icon'); + if (convertedPath && await fsExtra.pathExists(convertedPath)) { + return convertedPath; + } + } + catch (error) { + logger.warn(`Failed to convert default png to ico: ${error.message}`); + } + } + // Last resort: return png path if it exists (Windows can handle png in some cases) + if (await fsExtra.pathExists(defaultPngPath)) { + logger.warn('✼ Using png as fallback for Windows (may cause issues).'); + return defaultPngPath; + } + // If nothing exists, let the error bubble up + throw new Error('No default icon found for Windows build'); + } + const iconPath = IS_LINUX + ? 'src-tauri/png/icon_512.png' + : 'src-tauri/icons/icon.icns'; return path.join(npmDirectory, iconPath); } /** @@ -1054,9 +1107,12 @@ async function tryGetFavicon(url, appName) { const domain = new URL(url).hostname; const spinner = getSpinner(`Fetching icon from ${domain}...`); const serviceUrls = generateIconServiceUrls(domain); + // Use shorter timeout for CI environments + const isCI = process.env.CI === 'true' || process.env.GITHUB_ACTIONS === 'true'; + const downloadTimeout = isCI ? 5000 : ICON_CONFIG.downloadTimeout; for (const serviceUrl of serviceUrls) { try { - const faviconPath = await downloadIcon(serviceUrl, false); + const faviconPath = await downloadIcon(serviceUrl, false, downloadTimeout); if (!faviconPath) continue; const convertedPath = await convertIconFormat(faviconPath, appName); @@ -1065,7 +1121,11 @@ async function tryGetFavicon(url, appName) { return convertedPath; } } - catch { + catch (error) { + // Log specific errors in CI for debugging + if (isCI) { + logger.debug(`Icon service ${serviceUrl} failed: ${error.message}`); + } continue; } } @@ -1080,11 +1140,11 @@ async function tryGetFavicon(url, appName) { /** * Downloads icon from URL */ -async function downloadIcon(iconUrl, showSpinner = true) { +async function downloadIcon(iconUrl, showSpinner = true, customTimeout) { try { const response = await axios.get(iconUrl, { responseType: 'arraybuffer', - timeout: ICON_CONFIG.downloadTimeout, + timeout: customTimeout || ICON_CONFIG.downloadTimeout, }); const iconData = response.data; if (!iconData || iconData.byteLength < ICON_CONFIG.minFileSize) diff --git a/script/build_with_pake_cli.js b/script/build_with_pake_cli.js index 7925bd1..f08c6f1 100644 --- a/script/build_with_pake_cli.js +++ b/script/build_with_pake_cli.js @@ -26,33 +26,25 @@ const main = async () => { logConfiguration(); const cliPath = path.join(process.cwd(), "node_modules/pake-cli"); - + // Check if pake-cli directory exists if (!fs.existsSync(cliPath)) { console.error("Error: pake-cli not found at", cliPath); - console.error("Please make sure pake-cli is installed: npm install pake-cli"); + console.error( + "Please make sure pake-cli is installed: npm install pake-cli", + ); process.exit(1); } - + process.chdir(cliPath); // Clean up any previous configuration to ensure fresh build const pakeDirPath = path.join("src-tauri", ".pake"); - + // Remove .pake directory to force fresh config generation if (fs.existsSync(pakeDirPath)) { fs.rmSync(pakeDirPath, { recursive: true, force: true }); - console.log("Cleaned previous .pake directory"); - } - - // Fix hardcoded default config in npm package - const defaultConfigPath = path.join("src-tauri", "tauri.conf.json"); - if (fs.existsSync(defaultConfigPath)) { - const defaultConfig = JSON.parse(fs.readFileSync(defaultConfigPath, 'utf8')); - defaultConfig.productName = process.env.NAME; - defaultConfig.identifier = `com.pake.${process.env.NAME.toLowerCase()}`; - fs.writeFileSync(defaultConfigPath, JSON.stringify(defaultConfig, null, 2)); - console.log(`Fixed default config: productName -> ${process.env.NAME}`); + console.log("Cleaned previous .pake directory for fresh build"); } // Build CLI parameters @@ -95,7 +87,7 @@ const main = async () => { params.push("--icon", process.env.ICON); } else { console.log( - "Won't use icon as ICON environment variable is not defined!", + "No icon provided, pake-cli will attempt to fetch favicon or use default", ); } @@ -111,20 +103,27 @@ const main = async () => { fs.mkdirSync("output"); } - // pake-cli outputs files to current directory with pattern: {name}.{extension} + // pake-cli outputs files to current directory with various naming patterns const files = fs.readdirSync("."); - const namePattern = new RegExp(`^${process.env.NAME}\\..*$`); + const appName = process.env.NAME; + const escapedAppName = appName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // Escape regex special chars + + // Create comprehensive pattern for app files + const appFilePattern = new RegExp( + `^(${escapedAppName}|${escapedAppName.toLowerCase()})(_.*|\\..*)$`, + "i", + ); let foundFiles = false; - + for (const file of files) { - if (namePattern.test(file)) { + if (appFilePattern.test(file)) { const destPath = path.join("output", file); fs.renameSync(file, destPath); console.log(`Moved: ${file} -> output/${file}`); foundFiles = true; } } - + if (!foundFiles) { console.log("Warning: No output files found matching pattern"); }