Graceful renames, better errors in updater

This commit is contained in:
Fedor Indutny 2022-03-28 12:05:44 -07:00 committed by GitHub
parent a0ae7c1aa2
commit acda5b2cb3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 137 additions and 42 deletions

View file

@ -4,7 +4,7 @@
/* eslint-disable no-console */
import { createWriteStream } from 'fs';
import { pathExists } from 'fs-extra';
import { readdir, rename, stat, writeFile } from 'fs/promises';
import { readdir, stat, writeFile } from 'fs/promises';
import { promisify } from 'util';
import { execFile } from 'child_process';
import { join, normalize, extname } from 'path';
@ -42,7 +42,7 @@ import type { SettingsChannel } from '../main/settingsChannel';
import type { LoggerType } from '../types/Logging';
import { getGotOptions } from './got';
import { checkIntegrity } from './util';
import { checkIntegrity, gracefulRename } from './util';
import type { PrepareDownloadResultType as DifferentialDownloadDataType } from './differential';
import {
prepareDownload as prepareDifferentialDownload,
@ -99,6 +99,8 @@ export abstract class Updater {
private activeDownload: Promise<boolean> | undefined;
private markedCannotUpdate = false;
constructor(
protected readonly logger: LoggerType,
private readonly settingsChannel: SettingsChannel,
@ -156,6 +158,29 @@ export abstract class Updater {
ipcMain.handleOnce('start-update', performUpdateCallback);
}
protected markCannotUpdate(
error: Error,
dialogType = DialogType.Cannot_Update
): void {
if (this.markedCannotUpdate) {
this.logger.warn(
'updater/markCannotUpdate: already marked',
Errors.toLogFormat(error)
);
return;
}
this.markedCannotUpdate = true;
this.logger.error(
'updater/markCannotUpdate: marking due to error: ' +
`${Errors.toLogFormat(error)}, ` +
`dialogType: ${dialogType}`
);
const mainWindow = this.getMainWindow();
mainWindow?.webContents.send('show-update-dialog', dialogType);
}
//
// Private methods
//
@ -243,6 +268,7 @@ export abstract class Updater {
return true;
} catch (error) {
logger.error(`downloadAndInstall: ${Errors.toLogFormat(error)}`);
this.markCannotUpdate(error);
throw error;
}
}
@ -450,6 +476,9 @@ export abstract class Updater {
const tempUpdatePath = join(tempDir, fileName);
const tempBlockMapPath = join(tempDir, blockMapFileName);
// If true - we will attempt to install from a temporary directory.
let tempPathFailover = false;
try {
validatePath(cacheDir, targetUpdatePath);
@ -491,7 +520,7 @@ export abstract class Updater {
// Move file into downloads directory
try {
await rename(targetUpdatePath, tempUpdatePath);
await gracefulRename(this.logger, targetUpdatePath, tempUpdatePath);
gotUpdate = true;
} catch (error) {
this.logger.error(
@ -561,16 +590,28 @@ export abstract class Updater {
// Backup old files
const restoreDir = await getTempDir();
await rename(cacheDir, restoreDir);
await gracefulRename(this.logger, cacheDir, restoreDir);
// Move the files into the final position
try {
await rename(tempDir, cacheDir);
await gracefulRename(this.logger, tempDir, cacheDir);
} catch (error) {
// Attempt to restore old files
await rename(restoreDir, cacheDir);
try {
// Attempt to restore old files
await gracefulRename(this.logger, restoreDir, cacheDir);
} catch (restoreError) {
this.logger.warn(
'downloadUpdate: Failed to restore from backup folder, ignoring',
Errors.toLogFormat(restoreError)
);
}
throw error;
this.logger.warn(
'downloadUpdate: running update from a temporary folder due to error',
Errors.toLogFormat(error)
);
tempPathFailover = true;
return { updateFilePath: tempUpdatePath, signature };
}
try {
@ -584,7 +625,9 @@ export abstract class Updater {
return { updateFilePath: targetUpdatePath, signature };
} finally {
await deleteTempDir(tempDir);
if (!tempPathFailover) {
await deleteTempDir(tempDir);
}
}
}