From df7cdfacc7410899143bd699d18f05ccfe1b9ed1 Mon Sep 17 00:00:00 2001 From: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com> Date: Fri, 4 Mar 2022 11:59:47 -0800 Subject: [PATCH] Don't mkdir restore dir in updater --- ts/services/updateListener.ts | 4 +-- ts/shims/updateIpc.ts | 4 +-- ts/state/ducks/updates.ts | 41 ++++++++++++++++++++--------- ts/test-node/updater/common_test.ts | 34 ++++++++++++++++++++++++ ts/updater/common.ts | 34 ++++++++++++++++++++---- 5 files changed, 96 insertions(+), 21 deletions(-) diff --git a/ts/services/updateListener.ts b/ts/services/updateListener.ts index 8536361d3..97cb18abf 100644 --- a/ts/services/updateListener.ts +++ b/ts/services/updateListener.ts @@ -5,14 +5,14 @@ import { ipcRenderer } from 'electron'; import type { DialogType } from '../types/Dialogs'; import type { UpdateDialogOptionsType, - ShowUpdateDialogAction, + ShowUpdateDialogActionType, } from '../state/ducks/updates'; type UpdatesActions = { showUpdateDialog: ( x: DialogType, options: UpdateDialogOptionsType - ) => ShowUpdateDialogAction; + ) => ShowUpdateDialogActionType; }; export function initializeUpdateListener(updatesActions: UpdatesActions): void { diff --git a/ts/shims/updateIpc.ts b/ts/shims/updateIpc.ts index 113ca046e..608f5ccea 100644 --- a/ts/shims/updateIpc.ts +++ b/ts/shims/updateIpc.ts @@ -3,6 +3,6 @@ import { ipcRenderer } from 'electron'; -export function startUpdate(): void { - ipcRenderer.invoke('start-update'); +export function startUpdate(): Promise { + return ipcRenderer.invoke('start-update'); } diff --git a/ts/state/ducks/updates.ts b/ts/state/ducks/updates.ts index 4a360c5e9..2413ffcc0 100644 --- a/ts/state/ducks/updates.ts +++ b/ts/state/ducks/updates.ts @@ -32,11 +32,11 @@ export type UpdateDialogOptionsType = { version?: string; }; -type DismissDialogAction = { +type DismissDialogActionType = { type: typeof DISMISS_DIALOG; }; -export type ShowUpdateDialogAction = { +export type ShowUpdateDialogActionType = { type: typeof SHOW_UPDATE_DIALOG; payload: { dialogType: DialogType; @@ -48,7 +48,7 @@ type SnoozeUpdateActionType = { type: typeof SNOOZE_UPDATE; }; -type StartUpdateAction = { +type StartUpdateActionType = { type: typeof START_UPDATE; }; @@ -58,15 +58,15 @@ type UnsnoozeUpdateActionType = { }; export type UpdatesActionType = - | DismissDialogAction - | ShowUpdateDialogAction + | DismissDialogActionType + | ShowUpdateDialogActionType | SnoozeUpdateActionType - | StartUpdateAction + | StartUpdateActionType | UnsnoozeUpdateActionType; // Action Creators -function dismissDialog(): DismissDialogAction { +function dismissDialog(): DismissDialogActionType { return { type: DISMISS_DIALOG, }; @@ -75,7 +75,7 @@ function dismissDialog(): DismissDialogAction { function showUpdateDialog( dialogType: DialogType, updateDialogOptions: UpdateDialogOptionsType = {} -): ShowUpdateDialogAction { +): ShowUpdateDialogActionType { return { type: SHOW_UPDATE_DIALOG, payload: { @@ -106,11 +106,28 @@ function snoozeUpdate(): ThunkAction< }; } -function startUpdate(): StartUpdateAction { - updateIpc.startUpdate(); +function startUpdate(): ThunkAction< + void, + RootStateType, + unknown, + StartUpdateActionType | ShowUpdateDialogActionType +> { + return async dispatch => { + dispatch({ + type: START_UPDATE, + }); - return { - type: START_UPDATE, + try { + await updateIpc.startUpdate(); + } catch (_) { + dispatch({ + type: SHOW_UPDATE_DIALOG, + payload: { + dialogType: DialogType.Cannot_Update, + otherState: {}, + }, + }); + } }; } diff --git a/ts/test-node/updater/common_test.ts b/ts/test-node/updater/common_test.ts index 6e6af95ab..3453048af 100644 --- a/ts/test-node/updater/common_test.ts +++ b/ts/test-node/updater/common_test.ts @@ -2,6 +2,9 @@ // SPDX-License-Identifier: AGPL-3.0-only import { assert } from 'chai'; +import { pathExists } from 'fs-extra'; +import { stat, mkdir } from 'fs/promises'; +import { join } from 'path'; import { createUpdateCacheDirIfNeeded, @@ -10,6 +13,9 @@ import { isUpdateFileNameValid, validatePath, parseYaml, + createTempDir, + getTempDir, + deleteTempDir, } from '../../updater/common'; 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'); + }); + }); }); diff --git a/ts/updater/common.ts b/ts/updater/common.ts index 221c0b42b..2f877499c 100644 --- a/ts/updater/common.ts +++ b/ts/updater/common.ts @@ -446,7 +446,6 @@ export abstract class Updater { const targetUpdatePath = join(cacheDir, fileName); const tempDir = await createTempDir(); - const restoreDir = await createTempDir(); const tempUpdatePath = join(tempDir, fileName); const tempBlockMapPath = join(tempDir, blockMapFileName); @@ -556,7 +555,12 @@ export abstract class Updater { return undefined; } + this.logger.info( + 'downloadUpdate: Downloaded update, moving into cache dir' + ); + // Backup old files + const restoreDir = await getTempDir(); await rename(cacheDir, restoreDir); // Move the files into the final position @@ -569,9 +573,18 @@ export abstract class Updater { 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 }; } finally { - await Promise.all([deleteTempDir(tempDir), deleteTempDir(restoreDir)]); + await deleteTempDir(tempDir); } } @@ -781,14 +794,25 @@ function getBaseTempDir() { } export async function createTempDir(): Promise { - const baseTempDir = getBaseTempDir(); - const uniqueName = getGuid(); - const targetDir = join(baseTempDir, uniqueName); + const targetDir = await getTempDir(); + await mkdirpPromise(targetDir); return targetDir; } +export async function getTempDir(): Promise { + 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() { // We only use tmpdir() when this code is run outside of an Electron app (as in: tests) return app ? getUpdateCachePath(app.getPath('userData')) : tmpdir();