mirror of
https://gitea.shironeko-all.duckdns.org/shironeko/Hytale-F2P-2.git
synced 2026-02-26 10:41:46 -03:00
Fix Linux metadata files in workflow and improve error handling
This commit is contained in:
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
||||
dist/*.deb
|
||||
dist/*.rpm
|
||||
dist/*.pacman
|
||||
dist/latest.yml
|
||||
dist/latest*.yml
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
|
||||
@@ -23,6 +23,10 @@ class ClientUpdateManager {
|
||||
this.showUpdateDownloaded(updateInfo);
|
||||
});
|
||||
|
||||
window.electronAPI.onUpdateError((errorInfo) => {
|
||||
this.handleUpdateError(errorInfo);
|
||||
});
|
||||
|
||||
this.checkForUpdatesOnDemand();
|
||||
}
|
||||
|
||||
@@ -57,6 +61,10 @@ class ClientUpdateManager {
|
||||
<div class="update-popup-message">
|
||||
A new version of Hytale F2P Launcher is available.<br>
|
||||
<span id="update-status-text">Downloading update automatically...</span>
|
||||
<div id="update-error-message" style="display: none; margin-top: 0.75rem; padding: 0.75rem; background: rgba(239, 68, 68, 0.1); border: 1px solid rgba(239, 68, 68, 0.3); border-radius: 0.5rem; color: #fca5a5; font-size: 0.875rem;">
|
||||
<i class="fas fa-exclamation-triangle" style="margin-right: 0.5rem;"></i>
|
||||
<span id="update-error-text"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="update-progress-container" style="display: none; margin-bottom: 1rem;">
|
||||
@@ -161,7 +169,6 @@ class ClientUpdateManager {
|
||||
const progressPercent = document.getElementById('update-progress-percent');
|
||||
const progressSpeed = document.getElementById('update-progress-speed');
|
||||
const progressSize = document.getElementById('update-progress-size');
|
||||
const statusText = document.getElementById('update-status-text');
|
||||
|
||||
if (progressBar && progress) {
|
||||
const percent = Math.round(progress.percent || 0);
|
||||
@@ -182,9 +189,7 @@ class ClientUpdateManager {
|
||||
progressSize.textContent = `${transferredMB} MB / ${totalMB} MB`;
|
||||
}
|
||||
|
||||
if (statusText) {
|
||||
statusText.textContent = `Downloading update... ${percent}%`;
|
||||
}
|
||||
// Don't update status text here - it's already set and the progress bar shows the percentage
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,6 +213,83 @@ class ClientUpdateManager {
|
||||
console.log('✅ Update downloaded, ready to install');
|
||||
}
|
||||
|
||||
handleUpdateError(errorInfo) {
|
||||
console.error('Update error:', errorInfo);
|
||||
|
||||
// If manual download is required, update the UI (this will handle status text)
|
||||
if (errorInfo.requiresManualDownload) {
|
||||
this.showManualDownloadRequired(errorInfo);
|
||||
return; // Don't do anything else, showManualDownloadRequired handles everything
|
||||
}
|
||||
|
||||
// For non-critical errors, just show error message without changing status
|
||||
const errorMessage = document.getElementById('update-error-message');
|
||||
const errorText = document.getElementById('update-error-text');
|
||||
|
||||
if (errorMessage && errorText) {
|
||||
let message = errorInfo.message || 'An error occurred during the update process.';
|
||||
if (errorInfo.isMacSigningError) {
|
||||
message = 'Auto-update requires code signing. Please download manually.';
|
||||
}
|
||||
errorText.textContent = message;
|
||||
errorMessage.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
showManualDownloadRequired(errorInfo) {
|
||||
const statusText = document.getElementById('update-status-text');
|
||||
const progressContainer = document.getElementById('update-progress-container');
|
||||
const buttonsContainer = document.getElementById('update-buttons-container');
|
||||
const installBtn = document.getElementById('update-install-btn');
|
||||
const downloadBtn = document.getElementById('update-download-btn');
|
||||
const errorMessage = document.getElementById('update-error-message');
|
||||
const errorText = document.getElementById('update-error-text');
|
||||
|
||||
// Hide progress and install button
|
||||
if (progressContainer) {
|
||||
progressContainer.style.display = 'none';
|
||||
}
|
||||
|
||||
if (installBtn) {
|
||||
installBtn.style.display = 'none';
|
||||
}
|
||||
|
||||
// Update status message (only once, don't change it again)
|
||||
if (statusText && !statusText.dataset.manualMode) {
|
||||
statusText.textContent = 'Please download and install the update manually.';
|
||||
statusText.dataset.manualMode = 'true'; // Mark that we've set manual mode
|
||||
}
|
||||
|
||||
// Show error message with details
|
||||
if (errorMessage && errorText) {
|
||||
let message = 'Auto-update is not available. ';
|
||||
if (errorInfo.isMacSigningError) {
|
||||
message = 'This app requires code signing for automatic updates.';
|
||||
} else if (errorInfo.message) {
|
||||
message = errorInfo.message;
|
||||
} else {
|
||||
message = 'An error occurred during the update process.';
|
||||
}
|
||||
errorText.textContent = message;
|
||||
errorMessage.style.display = 'block';
|
||||
}
|
||||
|
||||
// Show and enable the manual download button (make it primary since it's the only option)
|
||||
if (downloadBtn) {
|
||||
downloadBtn.style.display = 'block';
|
||||
downloadBtn.disabled = false;
|
||||
downloadBtn.classList.remove('update-download-btn-secondary');
|
||||
downloadBtn.innerHTML = '<i class="fas fa-external-link-alt" style="margin-right: 0.5rem;"></i>Download Update Manually';
|
||||
}
|
||||
|
||||
// Show buttons container if not already visible
|
||||
if (buttonsContainer) {
|
||||
buttonsContainer.style.display = 'block';
|
||||
}
|
||||
|
||||
console.log('⚠️ Manual download required due to update error');
|
||||
}
|
||||
|
||||
blockInterface() {
|
||||
const mainContent = document.querySelector('.flex.w-full.h-screen');
|
||||
if (mainContent) {
|
||||
|
||||
@@ -5,6 +5,9 @@ const logger = require('./logger');
|
||||
class AppUpdater {
|
||||
constructor(mainWindow) {
|
||||
this.mainWindow = mainWindow;
|
||||
this.autoUpdateAvailable = true; // Track if auto-update is possible
|
||||
this.updateAvailable = false; // Track if an update was detected
|
||||
this.updateVersion = null; // Store the available update version
|
||||
this.setupAutoUpdater();
|
||||
}
|
||||
|
||||
@@ -40,6 +43,10 @@ class AppUpdater {
|
||||
|
||||
autoUpdater.on('update-available', (info) => {
|
||||
console.log('Update available:', info.version);
|
||||
this.updateAvailable = true;
|
||||
this.updateVersion = info.version;
|
||||
this.autoUpdateAvailable = true; // Reset flag when new update is available
|
||||
|
||||
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
|
||||
this.mainWindow.webContents.send('update-available', {
|
||||
version: info.version,
|
||||
@@ -68,9 +75,68 @@ class AppUpdater {
|
||||
|
||||
autoUpdater.on('error', (err) => {
|
||||
console.error('Error in auto-updater:', err);
|
||||
|
||||
// Check if this is a network error (not critical, don't show UI)
|
||||
const errorMessage = err.message?.toLowerCase() || '';
|
||||
const isNetworkError = errorMessage.includes('err_name_not_resolved') ||
|
||||
errorMessage.includes('network') ||
|
||||
errorMessage.includes('connection') ||
|
||||
errorMessage.includes('timeout') ||
|
||||
errorMessage.includes('enotfound');
|
||||
|
||||
if (isNetworkError) {
|
||||
console.warn('Network error in auto-updater - will retry later. Not showing error UI.');
|
||||
return; // Don't show error UI for network issues
|
||||
}
|
||||
|
||||
// Determine if this is a critical error that prevents auto-update
|
||||
const isCriticalError = this.isCriticalUpdateError(err);
|
||||
|
||||
if (isCriticalError) {
|
||||
this.autoUpdateAvailable = false;
|
||||
console.warn('Auto-update failed. Manual download required.');
|
||||
}
|
||||
|
||||
// Handle missing metadata files (platform-specific builds)
|
||||
if (err.code === 'ERR_UPDATER_CHANNEL_FILE_NOT_FOUND') {
|
||||
const platform = process.platform === 'darwin' ? 'macOS' :
|
||||
process.platform === 'win32' ? 'Windows' : 'Linux';
|
||||
const missingFile = process.platform === 'darwin' ? 'latest-mac.yml' :
|
||||
process.platform === 'win32' ? 'latest.yml' : 'latest-linux.yml';
|
||||
console.warn(`${platform} update metadata file (${missingFile}) not found in release.`);
|
||||
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
|
||||
this.mainWindow.webContents.send('update-error', {
|
||||
message: err.message
|
||||
message: `Update metadata file for ${platform} not found in release. Please download manually.`,
|
||||
code: err.code,
|
||||
requiresManualDownload: true,
|
||||
updateVersion: this.updateVersion,
|
||||
isMissingMetadata: true
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// macOS-specific: Handle unsigned app errors gracefully
|
||||
if (process.platform === 'darwin' && err.code === 2) {
|
||||
console.warn('macOS update error: App may not be code-signed. Auto-update requires code signing.');
|
||||
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
|
||||
this.mainWindow.webContents.send('update-error', {
|
||||
message: 'Auto-update requires code signing. Please download manually from GitHub.',
|
||||
code: err.code,
|
||||
isMacSigningError: true,
|
||||
requiresManualDownload: true,
|
||||
updateVersion: this.updateVersion
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
|
||||
this.mainWindow.webContents.send('update-error', {
|
||||
message: err.message,
|
||||
code: err.code,
|
||||
requiresManualDownload: isCriticalError,
|
||||
updateVersion: this.updateVersion
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -104,12 +170,55 @@ class AppUpdater {
|
||||
// Check for updates and notify if available
|
||||
autoUpdater.checkForUpdatesAndNotify().catch(err => {
|
||||
console.error('Failed to check for updates:', err);
|
||||
|
||||
// Network errors are not critical - just log and continue
|
||||
const errorMessage = err.message?.toLowerCase() || '';
|
||||
const isNetworkError = errorMessage.includes('err_name_not_resolved') ||
|
||||
errorMessage.includes('network') ||
|
||||
errorMessage.includes('connection') ||
|
||||
errorMessage.includes('timeout') ||
|
||||
errorMessage.includes('enotfound');
|
||||
|
||||
if (isNetworkError) {
|
||||
console.warn('Network error checking for updates - will retry later. This is not critical.');
|
||||
return; // Don't show error UI for network issues
|
||||
}
|
||||
|
||||
const isCritical = this.isCriticalUpdateError(err);
|
||||
if (this.mainWindow && !this.mainWindow.isDestroyed() && isCritical) {
|
||||
this.mainWindow.webContents.send('update-error', {
|
||||
message: err.message || 'Failed to check for updates',
|
||||
code: err.code,
|
||||
requiresManualDownload: true
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
checkForUpdates() {
|
||||
// Manual check for updates (returns promise)
|
||||
return autoUpdater.checkForUpdates();
|
||||
return autoUpdater.checkForUpdates().catch(err => {
|
||||
console.error('Failed to check for updates:', err);
|
||||
|
||||
// Network errors are not critical - just return no update available
|
||||
const errorMessage = err.message?.toLowerCase() || '';
|
||||
const isNetworkError = errorMessage.includes('err_name_not_resolved') ||
|
||||
errorMessage.includes('network') ||
|
||||
errorMessage.includes('connection') ||
|
||||
errorMessage.includes('timeout') ||
|
||||
errorMessage.includes('enotfound');
|
||||
|
||||
if (isNetworkError) {
|
||||
console.warn('Network error - update check unavailable');
|
||||
return { updateInfo: null }; // Return empty result for network errors
|
||||
}
|
||||
|
||||
const isCritical = this.isCriticalUpdateError(err);
|
||||
if (isCritical) {
|
||||
this.autoUpdateAvailable = false;
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
quitAndInstall() {
|
||||
@@ -123,6 +232,59 @@ class AppUpdater {
|
||||
updateAvailable: false
|
||||
};
|
||||
}
|
||||
|
||||
isCriticalUpdateError(err) {
|
||||
// Check for errors that prevent auto-update
|
||||
const errorMessage = err.message?.toLowerCase() || '';
|
||||
const errorCode = err.code;
|
||||
|
||||
// Missing update metadata files (platform-specific)
|
||||
if (errorCode === 'ERR_UPDATER_CHANNEL_FILE_NOT_FOUND' ||
|
||||
errorMessage.includes('cannot find latest') ||
|
||||
errorMessage.includes('latest-linux.yml') ||
|
||||
errorMessage.includes('latest-mac.yml') ||
|
||||
errorMessage.includes('latest.yml')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// macOS code signing errors
|
||||
if (process.platform === 'darwin' && (errorCode === 2 || errorMessage.includes('shipit'))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Download failures
|
||||
if (errorMessage.includes('download') && errorMessage.includes('fail')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Network errors that prevent download (but we handle these separately as non-critical)
|
||||
// Installation errors
|
||||
if (errorMessage.includes('install') && errorMessage.includes('fail')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Permission errors
|
||||
if (errorMessage.includes('permission') || errorMessage.includes('access denied')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// File system errors (but not "not found" for metadata files - handled above)
|
||||
if (errorMessage.includes('enoent') || errorMessage.includes('cannot find')) {
|
||||
// Only if it's not about metadata files
|
||||
if (!errorMessage.includes('latest') && !errorMessage.includes('.yml')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Generic critical error codes
|
||||
if (errorCode && (errorCode >= 100 ||
|
||||
errorCode === 'ERR_UPDATER_INVALID_RELEASE_FEED' ||
|
||||
errorCode === 'ERR_UPDATER_CHANNEL_FILE_NOT_FOUND')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AppUpdater;
|
||||
|
||||
@@ -60,6 +60,9 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||
onUpdateDownloaded: (callback) => {
|
||||
ipcRenderer.on('update-downloaded', (event, data) => callback(data));
|
||||
},
|
||||
onUpdateError: (callback) => {
|
||||
ipcRenderer.on('update-error', (event, data) => callback(data));
|
||||
},
|
||||
quitAndInstallUpdate: () => ipcRenderer.invoke('quit-and-install-update'),
|
||||
|
||||
getGpuInfo: () => ipcRenderer.invoke('get-gpu-info'),
|
||||
|
||||
Reference in New Issue
Block a user