🐛 Fix linux and windows packaging issues

This commit is contained in:
Tw93
2025-08-20 21:28:42 +08:00
parent a33519f398
commit 23c817606d
5 changed files with 182 additions and 40 deletions

31
bin/helpers/merge.ts vendored
View File

@@ -139,7 +139,38 @@ export async function mergeConfig(
// Processing targets are currently only open to Linux. // Processing targets are currently only open to Linux.
if (platform === 'linux') { if (platform === 'linux') {
// Remove hardcoded desktop files and regenerate with correct app name
delete tauriConf.bundle.linux.deb.files; 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']; const validTargets = ['deb', 'appimage', 'rpm'];
if (validTargets.includes(options.targets)) { if (validTargets.includes(options.targets)) {
tauriConf.bundle.targets = [options.targets]; tauriConf.bundle.targets = [options.targets];

68
bin/options/icon.ts vendored
View File

@@ -119,11 +119,49 @@ export async function handleIcon(options: PakeAppOptions, url?: string) {
} }
logger.info('✼ No icon provided, using default icon.'); logger.info('✼ No icon provided, using default icon.');
const iconPath = IS_WIN
? 'src-tauri/png/icon_256.ico' // For Windows, ensure we have proper fallback handling
: IS_LINUX if (IS_WIN) {
? 'src-tauri/png/icon_512.png' const defaultIcoPath = path.join(
: 'src-tauri/icons/icon.icns'; 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); return path.join(npmDirectory, iconPath);
} }
@@ -171,9 +209,18 @@ async function tryGetFavicon(
const serviceUrls = generateIconServiceUrls(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) { for (const serviceUrl of serviceUrls) {
try { try {
const faviconPath = await downloadIcon(serviceUrl, false); const faviconPath = await downloadIcon(
serviceUrl,
false,
downloadTimeout,
);
if (!faviconPath) continue; if (!faviconPath) continue;
const convertedPath = await convertIconFormat(faviconPath, appName); const convertedPath = await convertIconFormat(faviconPath, appName);
@@ -183,7 +230,11 @@ async function tryGetFavicon(
); );
return convertedPath; return convertedPath;
} }
} catch { } catch (error) {
// Log specific errors in CI for debugging
if (isCI) {
logger.debug(`Icon service ${serviceUrl} failed: ${error.message}`);
}
continue; continue;
} }
} }
@@ -202,11 +253,12 @@ async function tryGetFavicon(
export async function downloadIcon( export async function downloadIcon(
iconUrl: string, iconUrl: string,
showSpinner = true, showSpinner = true,
customTimeout?: number,
): Promise<string | null> { ): Promise<string | null> {
try { try {
const response = await axios.get(iconUrl, { const response = await axios.get(iconUrl, {
responseType: 'arraybuffer', responseType: 'arraybuffer',
timeout: ICON_CONFIG.downloadTimeout, timeout: customTimeout || ICON_CONFIG.downloadTimeout,
}); });
const iconData = response.data; const iconData = response.data;

78
dist/cli.js vendored
View File

@@ -531,7 +531,33 @@ async function mergeConfig(url, options, tauriConf) {
tauriConf.pake.system_tray[currentPlatform] = showSystemTray; tauriConf.pake.system_tray[currentPlatform] = showSystemTray;
// Processing targets are currently only open to Linux. // Processing targets are currently only open to Linux.
if (platform === 'linux') { if (platform === 'linux') {
// Remove hardcoded desktop files and regenerate with correct app name
delete tauriConf.bundle.linux.deb.files; 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']; const validTargets = ['deb', 'appimage', 'rpm'];
if (validTargets.includes(options.targets)) { if (validTargets.includes(options.targets)) {
tauriConf.bundle.targets = [options.targets]; tauriConf.bundle.targets = [options.targets];
@@ -1014,11 +1040,38 @@ async function handleIcon(options, url) {
return faviconPath; return faviconPath;
} }
logger.info('✼ No icon provided, using default icon.'); logger.info('✼ No icon provided, using default icon.');
const iconPath = IS_WIN // For Windows, ensure we have proper fallback handling
? 'src-tauri/png/icon_256.ico' if (IS_WIN) {
: IS_LINUX const defaultIcoPath = path.join(npmDirectory, 'src-tauri/png/icon_256.ico');
? 'src-tauri/png/icon_512.png' const defaultPngPath = path.join(npmDirectory, 'src-tauri/png/icon_512.png');
: 'src-tauri/icons/icon.icns'; // 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); return path.join(npmDirectory, iconPath);
} }
/** /**
@@ -1054,9 +1107,12 @@ async function tryGetFavicon(url, appName) {
const domain = new URL(url).hostname; const domain = new URL(url).hostname;
const spinner = getSpinner(`Fetching icon from ${domain}...`); const spinner = getSpinner(`Fetching icon from ${domain}...`);
const serviceUrls = generateIconServiceUrls(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) { for (const serviceUrl of serviceUrls) {
try { try {
const faviconPath = await downloadIcon(serviceUrl, false); const faviconPath = await downloadIcon(serviceUrl, false, downloadTimeout);
if (!faviconPath) if (!faviconPath)
continue; continue;
const convertedPath = await convertIconFormat(faviconPath, appName); const convertedPath = await convertIconFormat(faviconPath, appName);
@@ -1065,7 +1121,11 @@ async function tryGetFavicon(url, appName) {
return convertedPath; return convertedPath;
} }
} }
catch { catch (error) {
// Log specific errors in CI for debugging
if (isCI) {
logger.debug(`Icon service ${serviceUrl} failed: ${error.message}`);
}
continue; continue;
} }
} }
@@ -1080,11 +1140,11 @@ async function tryGetFavicon(url, appName) {
/** /**
* Downloads icon from URL * Downloads icon from URL
*/ */
async function downloadIcon(iconUrl, showSpinner = true) { async function downloadIcon(iconUrl, showSpinner = true, customTimeout) {
try { try {
const response = await axios.get(iconUrl, { const response = await axios.get(iconUrl, {
responseType: 'arraybuffer', responseType: 'arraybuffer',
timeout: ICON_CONFIG.downloadTimeout, timeout: customTimeout || ICON_CONFIG.downloadTimeout,
}); });
const iconData = response.data; const iconData = response.data;
if (!iconData || iconData.byteLength < ICON_CONFIG.minFileSize) if (!iconData || iconData.byteLength < ICON_CONFIG.minFileSize)

View File

@@ -30,7 +30,9 @@ const main = async () => {
// Check if pake-cli directory exists // Check if pake-cli directory exists
if (!fs.existsSync(cliPath)) { if (!fs.existsSync(cliPath)) {
console.error("Error: pake-cli not found at", 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.exit(1);
} }
@@ -42,17 +44,7 @@ const main = async () => {
// Remove .pake directory to force fresh config generation // Remove .pake directory to force fresh config generation
if (fs.existsSync(pakeDirPath)) { if (fs.existsSync(pakeDirPath)) {
fs.rmSync(pakeDirPath, { recursive: true, force: true }); fs.rmSync(pakeDirPath, { recursive: true, force: true });
console.log("Cleaned previous .pake directory"); console.log("Cleaned previous .pake directory for fresh build");
}
// 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}`);
} }
// Build CLI parameters // Build CLI parameters
@@ -95,7 +87,7 @@ const main = async () => {
params.push("--icon", process.env.ICON); params.push("--icon", process.env.ICON);
} else { } else {
console.log( 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,13 +103,20 @@ const main = async () => {
fs.mkdirSync("output"); 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 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; let foundFiles = false;
for (const file of files) { for (const file of files) {
if (namePattern.test(file)) { if (appFilePattern.test(file)) {
const destPath = path.join("output", file); const destPath = path.join("output", file);
fs.renameSync(file, destPath); fs.renameSync(file, destPath);
console.log(`Moved: ${file} -> output/${file}`); console.log(`Moved: ${file} -> output/${file}`);