Don't mkdir restore dir in updater

This commit is contained in:
Fedor Indutny 2022-03-04 11:59:47 -08:00 committed by GitHub
parent effe5aae6f
commit df7cdfacc7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 96 additions and 21 deletions

View file

@ -5,14 +5,14 @@ import { ipcRenderer } from 'electron';
import type { DialogType } from '../types/Dialogs'; import type { DialogType } from '../types/Dialogs';
import type { import type {
UpdateDialogOptionsType, UpdateDialogOptionsType,
ShowUpdateDialogAction, ShowUpdateDialogActionType,
} from '../state/ducks/updates'; } from '../state/ducks/updates';
type UpdatesActions = { type UpdatesActions = {
showUpdateDialog: ( showUpdateDialog: (
x: DialogType, x: DialogType,
options: UpdateDialogOptionsType options: UpdateDialogOptionsType
) => ShowUpdateDialogAction; ) => ShowUpdateDialogActionType;
}; };
export function initializeUpdateListener(updatesActions: UpdatesActions): void { export function initializeUpdateListener(updatesActions: UpdatesActions): void {

View file

@ -3,6 +3,6 @@
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
export function startUpdate(): void { export function startUpdate(): Promise<void> {
ipcRenderer.invoke('start-update'); return ipcRenderer.invoke('start-update');
} }

View file

@ -32,11 +32,11 @@ export type UpdateDialogOptionsType = {
version?: string; version?: string;
}; };
type DismissDialogAction = { type DismissDialogActionType = {
type: typeof DISMISS_DIALOG; type: typeof DISMISS_DIALOG;
}; };
export type ShowUpdateDialogAction = { export type ShowUpdateDialogActionType = {
type: typeof SHOW_UPDATE_DIALOG; type: typeof SHOW_UPDATE_DIALOG;
payload: { payload: {
dialogType: DialogType; dialogType: DialogType;
@ -48,7 +48,7 @@ type SnoozeUpdateActionType = {
type: typeof SNOOZE_UPDATE; type: typeof SNOOZE_UPDATE;
}; };
type StartUpdateAction = { type StartUpdateActionType = {
type: typeof START_UPDATE; type: typeof START_UPDATE;
}; };
@ -58,15 +58,15 @@ type UnsnoozeUpdateActionType = {
}; };
export type UpdatesActionType = export type UpdatesActionType =
| DismissDialogAction | DismissDialogActionType
| ShowUpdateDialogAction | ShowUpdateDialogActionType
| SnoozeUpdateActionType | SnoozeUpdateActionType
| StartUpdateAction | StartUpdateActionType
| UnsnoozeUpdateActionType; | UnsnoozeUpdateActionType;
// Action Creators // Action Creators
function dismissDialog(): DismissDialogAction { function dismissDialog(): DismissDialogActionType {
return { return {
type: DISMISS_DIALOG, type: DISMISS_DIALOG,
}; };
@ -75,7 +75,7 @@ function dismissDialog(): DismissDialogAction {
function showUpdateDialog( function showUpdateDialog(
dialogType: DialogType, dialogType: DialogType,
updateDialogOptions: UpdateDialogOptionsType = {} updateDialogOptions: UpdateDialogOptionsType = {}
): ShowUpdateDialogAction { ): ShowUpdateDialogActionType {
return { return {
type: SHOW_UPDATE_DIALOG, type: SHOW_UPDATE_DIALOG,
payload: { payload: {
@ -106,11 +106,28 @@ function snoozeUpdate(): ThunkAction<
}; };
} }
function startUpdate(): StartUpdateAction { function startUpdate(): ThunkAction<
updateIpc.startUpdate(); void,
RootStateType,
return { unknown,
StartUpdateActionType | ShowUpdateDialogActionType
> {
return async dispatch => {
dispatch({
type: START_UPDATE, type: START_UPDATE,
});
try {
await updateIpc.startUpdate();
} catch (_) {
dispatch({
type: SHOW_UPDATE_DIALOG,
payload: {
dialogType: DialogType.Cannot_Update,
otherState: {},
},
});
}
}; };
} }

View file

@ -2,6 +2,9 @@
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { assert } from 'chai'; import { assert } from 'chai';
import { pathExists } from 'fs-extra';
import { stat, mkdir } from 'fs/promises';
import { join } from 'path';
import { import {
createUpdateCacheDirIfNeeded, createUpdateCacheDirIfNeeded,
@ -10,6 +13,9 @@ import {
isUpdateFileNameValid, isUpdateFileNameValid,
validatePath, validatePath,
parseYaml, parseYaml,
createTempDir,
getTempDir,
deleteTempDir,
} from '../../updater/common'; } from '../../updater/common';
describe('updater/signatures', () => { describe('updater/signatures', () => {
@ -152,4 +158,32 @@ releaseDate: '2021-12-03T19:00:23.754Z'
}); });
}); });
}); });
describe('createTempDir', () => {
it('creates a temporary directory', async () => {
const dir = await createTempDir();
assert.isTrue((await stat(dir)).isDirectory());
await deleteTempDir(dir);
assert.isFalse(await pathExists(dir), 'Directory should be deleted');
});
});
describe('getTempDir', () => {
it('reserves a temporary directory', async () => {
const dir = await getTempDir();
assert.isTrue(
(await stat(join(dir, '..'))).isDirectory(),
'Parent folder should exist'
);
assert.isFalse(await pathExists(dir), 'Reserved folder should not exist');
await mkdir(dir);
await deleteTempDir(dir);
assert.isFalse(await pathExists(dir), 'Directory should be deleted');
});
});
}); });

View file

@ -446,7 +446,6 @@ export abstract class Updater {
const targetUpdatePath = join(cacheDir, fileName); const targetUpdatePath = join(cacheDir, fileName);
const tempDir = await createTempDir(); const tempDir = await createTempDir();
const restoreDir = await createTempDir();
const tempUpdatePath = join(tempDir, fileName); const tempUpdatePath = join(tempDir, fileName);
const tempBlockMapPath = join(tempDir, blockMapFileName); const tempBlockMapPath = join(tempDir, blockMapFileName);
@ -556,7 +555,12 @@ export abstract class Updater {
return undefined; return undefined;
} }
this.logger.info(
'downloadUpdate: Downloaded update, moving into cache dir'
);
// Backup old files // Backup old files
const restoreDir = await getTempDir();
await rename(cacheDir, restoreDir); await rename(cacheDir, restoreDir);
// Move the files into the final position // Move the files into the final position
@ -569,9 +573,18 @@ export abstract class Updater {
throw error; throw error;
} }
try {
await deleteTempDir(restoreDir);
} catch (error) {
this.logger.warn(
'downloadUpdate: Failed to remove backup folder, ignoring',
Errors.toLogFormat(error)
);
}
return { updateFilePath: targetUpdatePath, signature }; return { updateFilePath: targetUpdatePath, signature };
} finally { } finally {
await Promise.all([deleteTempDir(tempDir), deleteTempDir(restoreDir)]); await deleteTempDir(tempDir);
} }
} }
@ -781,14 +794,25 @@ function getBaseTempDir() {
} }
export async function createTempDir(): Promise<string> { export async function createTempDir(): Promise<string> {
const baseTempDir = getBaseTempDir(); const targetDir = await getTempDir();
const uniqueName = getGuid();
const targetDir = join(baseTempDir, uniqueName);
await mkdirpPromise(targetDir); await mkdirpPromise(targetDir);
return targetDir; return targetDir;
} }
export async function getTempDir(): Promise<string> {
const baseTempDir = getBaseTempDir();
const uniqueName = getGuid();
// Create parent folder if not already present
if (!(await pathExists(baseTempDir))) {
await mkdirpPromise(baseTempDir);
}
return join(baseTempDir, uniqueName);
}
function getUpdateCacheDir() { function getUpdateCacheDir() {
// We only use tmpdir() when this code is run outside of an Electron app (as in: tests) // We only use tmpdir() when this code is run outside of an Electron app (as in: tests)
return app ? getUpdateCachePath(app.getPath('userData')) : tmpdir(); return app ? getUpdateCachePath(app.getPath('userData')) : tmpdir();