From ef2a6522b88ae8212f7f9a9a37389a82eb6b2b20 Mon Sep 17 00:00:00 2001 From: Andrey Lushnikov Date: Fri, 17 Jul 2020 16:39:27 -0700 Subject: [PATCH] feat: support atomic browser installation - attempt 2 (#3008) Currently, Ctrl-C while extracting browser might yield users in a bad place. This patch adds a marker file inside browser directory to make sure that browser extraction completed. Note: this was already attempted in #2489, but was eventually reverted in #2534. References #2660 --- src/install/browserPaths.ts | 4 ++++ src/install/installer.ts | 21 ++++++++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/install/browserPaths.ts b/src/install/browserPaths.ts index 233a137038..6de762ac23 100644 --- a/src/install/browserPaths.ts +++ b/src/install/browserPaths.ts @@ -118,6 +118,10 @@ export function browserDirectory(browsersPath: string, browser: BrowserDescripto return path.join(browsersPath, `${browser.name}-${browser.revision}`); } +export function markerFilePath(browsersPath: string, browser: BrowserDescriptor): string { + return path.join(browserDirectory(browsersPath, browser), 'INSTALLATION_COMPLETE'); +} + export function isBrowserDirectory(browserPath: string): boolean { const baseName = path.basename(browserPath); return baseName.startsWith('chromium-') || baseName.startsWith('firefox-') || baseName.startsWith('webkit-'); diff --git a/src/install/installer.ts b/src/install/installer.ts index 939fc48f12..561e3cde23 100644 --- a/src/install/installer.ts +++ b/src/install/installer.ts @@ -26,6 +26,7 @@ import * as browserFetcher from './browserFetcher'; const fsMkdirAsync = util.promisify(fs.mkdir.bind(fs)); const fsReaddirAsync = util.promisify(fs.readdir.bind(fs)); const fsReadFileAsync = util.promisify(fs.readFile.bind(fs)); +const fsExistsAsync = (filePath: string) => fsReadFileAsync(filePath).then(() => true).catch(e => false); const fsUnlinkAsync = util.promisify(fs.unlink.bind(fs)); const fsWriteFileAsync = util.promisify(fs.writeFile.bind(fs)); const removeFolderAsync = util.promisify(removeFolder); @@ -44,15 +45,24 @@ export async function installBrowsersWithProgressBar(packagePath: string) { } async function validateCache(packagePath: string, browsersPath: string, linksDir: string) { - // 1. Collect unused downloads and package descriptors. - const allBrowsers: browserPaths.BrowserDescriptor[] = []; + // 1. Collect used downloads and package descriptors. + const usedBrowserPaths: Set = new Set(); for (const fileName of await fsReaddirAsync(linksDir)) { const linkPath = path.join(linksDir, fileName); let linkTarget = ''; try { linkTarget = (await fsReadFileAsync(linkPath)).toString(); const browsers = JSON.parse((await fsReadFileAsync(path.join(linkTarget, 'browsers.json'))).toString())['browsers']; - allBrowsers.push(...browsers); + for (const browser of browsers) { + const usedBrowserPath = browserPaths.browserDirectory(browsersPath, browser); + const browserRevision = parseInt(browser.revision, 10); + // Old browser installations don't have marker file. + const shouldHaveMarkerFile = (browser.name === 'chromium' && browserRevision >= 786218) || + (browser.name === 'firefox' && browserRevision >= 1128) || + (browser.name === 'webkit' && browserRevision >= 1307); + if (!shouldHaveMarkerFile || (await fsExistsAsync(browserPaths.markerFilePath(browsersPath, browser)))) + usedBrowserPaths.add(usedBrowserPath); + } } catch (e) { if (linkTarget) logPolitely('Failed to process descriptor at ' + linkTarget); @@ -64,8 +74,8 @@ async function validateCache(packagePath: string, browsersPath: string, linksDir let downloadedBrowsers = (await fsReaddirAsync(browsersPath)).map(file => path.join(browsersPath, file)); downloadedBrowsers = downloadedBrowsers.filter(file => browserPaths.isBrowserDirectory(file)); const directories = new Set(downloadedBrowsers); - for (const browser of allBrowsers) - directories.delete(browserPaths.browserDirectory(browsersPath, browser)); + for (const browserPath of usedBrowserPaths) + directories.delete(browserPath); for (const directory of directories) { logPolitely('Removing unused browser at ' + directory); await removeFolderAsync(directory).catch(e => {}); @@ -76,6 +86,7 @@ async function validateCache(packagePath: string, browsersPath: string, linksDir for (const browser of myBrowsers) { const browserPath = browserPaths.browserDirectory(browsersPath, browser); await browserFetcher.downloadBrowserWithProgressBar(browserPath, browser); + await fsWriteFileAsync(browserPaths.markerFilePath(browsersPath, browser), ''); } }