feat: add ability to change electron download binary path

This commit is contained in:
vhashimotoo
2020-10-02 19:18:05 +03:00
parent e3e148d1e8
commit 89d60e51fc
7 changed files with 126 additions and 32 deletions
+16 -14
View File
@@ -4,7 +4,6 @@ import * as path from 'path';
import { VersionState } from '../interfaces';
import { fancyImport } from '../utils/import';
import { normalizeVersion } from '../utils/normalize-version';
import { USER_DATA_PATH } from './constants';
import { removeTypeDefsForVersion } from './fetch-types';
import { AppState } from './state';
@@ -22,7 +21,7 @@ export async function setupBinary(
const version = normalizeVersion(iVersion);
const fs = await fancyImport<typeof fsType>('fs-extra');
await fs.mkdirp(getDownloadPath(version));
await fs.mkdirp(getDownloadPath(appState.downloadBinaryPath, version));
const { state } = appState.versions[version];
if (state === VersionState.downloading || state === VersionState.unzipping) {
@@ -30,7 +29,7 @@ export async function setupBinary(
return;
}
if (await getIsDownloaded(version)) {
if (await getIsDownloaded(appState.downloadBinaryPath, version)) {
console.log(`Binary: Electron ${version} already downloaded.`);
appState.versions[version].state = VersionState.ready;
return;
@@ -40,7 +39,7 @@ export async function setupBinary(
appState.versions[version].state = VersionState.downloading;
const zipPath = await download(appState, version);
const extractPath = getDownloadPath(version);
const extractPath = getDownloadPath(appState.downloadBinaryPath, version);
console.log(
`Binary: Electron ${version} downloaded, now unpacking to ${extractPath}`,
);
@@ -70,7 +69,7 @@ export async function setupBinary(
* @param {string} iVersion
* @returns {Promise<void>}
*/
export async function removeBinary(iVersion: string) {
export async function removeBinary(downloadPath: string, iVersion: string) {
const version = normalizeVersion(iVersion);
const fs = await fancyImport<typeof fsType>('fs-extra');
let isDeleted = false;
@@ -93,12 +92,12 @@ export async function removeBinary(iVersion: string) {
};
const binaryCleaner = async () => {
if (await getIsDownloaded(version)) {
if (await getIsDownloaded(downloadPath, version)) {
// This is necessary since we're messing with .asar files inside
// the Electron binaries. Electron, powering Fiddle, will try to
// "correct" our calls, but we don't want that right here.
process.noAsar = true;
await fs.remove(getDownloadPath(version));
await fs.remove(getDownloadPath(downloadPath, version));
process.noAsar = false;
isDeleted = true;
@@ -123,10 +122,11 @@ export async function removeBinary(iVersion: string) {
* @returns {boolean}
*/
export async function getIsDownloaded(
downloadPath: string,
version: string,
dir?: string,
): Promise<boolean> {
const expectedPath = getElectronBinaryPath(version, dir);
const expectedPath = getElectronBinaryPath(downloadPath, version, dir);
const fs = await fancyImport<typeof fsType>('fs-extra');
return fs.existsSync(expectedPath);
@@ -140,8 +140,9 @@ export async function getIsDownloaded(
* @returns {string}
*/
export function getElectronBinaryPath(
downloadPath: string,
version: string,
dir: string = getDownloadPath(version),
dir: string = getDownloadPath(downloadPath, version),
): string {
switch (process.platform) {
case 'darwin':
@@ -169,9 +170,10 @@ export function getDownloadingVersions(appState: AppState) {
*
* @returns {Promise<Array<string>>}
*/
export async function getDownloadedVersions(): Promise<Array<string>> {
export async function getDownloadedVersions(
downloadPath: string,
): Promise<Array<string>> {
const fs = await fancyImport<typeof fsType>('fs-extra');
const downloadPath = path.join(USER_DATA_PATH, 'electron-bin');
console.log(`Binary: Checking for downloaded versions`);
try {
@@ -179,7 +181,7 @@ export async function getDownloadedVersions(): Promise<Array<string>> {
const knownVersions: Array<string> = [];
for (const directory of directories) {
if (await getIsDownloaded(directory)) {
if (await getIsDownloaded(downloadPath, directory)) {
knownVersions.push(directory);
}
}
@@ -233,8 +235,8 @@ async function download(appState: AppState, version: string): Promise<string> {
* @param {string} version
* @returns {string}
*/
function getDownloadPath(version: string): string {
return path.join(USER_DATA_PATH, 'electron-bin', version);
function getDownloadPath(downloadPath: string, version: string): string {
return path.join(downloadPath, version);
}
/**
@@ -8,9 +8,12 @@ import {
IButtonProps,
Icon,
IconName,
InputGroup,
Tooltip,
} from '@blueprintjs/core';
import { observer } from 'mobx-react';
import { remote } from 'electron';
import * as path from 'path';
import * as React from 'react';
import { RunnableVersion, VersionSource, VersionState } from '../../interfaces';
@@ -144,6 +147,8 @@ export class ElectronSettings extends React.Component<
{this.renderVersionStateOptions()}
</Callout>
<br />
<Callout>{this.renderDownloadBinaryOptions()}</Callout>
<br />
<Callout>
{this.renderAdvancedButtons()}
{this.renderTable()}
@@ -152,6 +157,43 @@ export class ElectronSettings extends React.Component<
);
}
private renderDownloadBinaryOptions = () => {
const { downloadBinaryPath } = this.props.appState;
return (
<FormGroup>
<p>You can setup the download directory for Electron binary files.</p>
<InputGroup
readOnly={true}
value={downloadBinaryPath}
rightElement={
<Button
icon="folder-open"
onClick={this.handleDownloadBinaryChanging}
>
Select
</Button>
}
/>
</FormGroup>
);
};
private handleDownloadBinaryChanging = async () => {
const { filePaths } = await remote.dialog.showOpenDialog({
title: 'Select Folder',
properties: ['openDirectory'],
});
if (!filePaths || filePaths.length < 1 || filePaths.length > 1) {
return;
}
this.props.appState.downloadBinaryPath = path.join(filePaths[0]);
this.props.appState.updateDownloadedVersionState();
this.props.appState.updateElectronVersions();
};
/**
* Renders the various buttons for advanced operations
*
+16 -4
View File
@@ -49,7 +49,7 @@ export class Runner {
public async run(): Promise<boolean> {
const { fileManager, getEditorValues } = window.ElectronFiddle.app;
const options = { includeDependencies: false, includeElectron: false };
const { currentElectronVersion } = this.appState;
const { currentElectronVersion, downloadBinaryPath } = this.appState;
const { version, localPath } = currentElectronVersion;
if (this.appState.isClearingConsoleOnRun) {
@@ -71,7 +71,11 @@ export class Runner {
return false;
}
const isReady = await getIsDownloaded(version, localPath);
const isReady = await getIsDownloaded(
downloadBinaryPath,
version,
localPath,
);
if (!isReady) {
console.warn(`Runner: Binary ${version} not ready`);
@@ -209,9 +213,17 @@ export class Runner {
* @memberof Runner
*/
public async execute(dir: string): Promise<void> {
const { currentElectronVersion, pushOutput } = this.appState;
const {
currentElectronVersion,
pushOutput,
downloadBinaryPath,
} = this.appState;
const { version, localPath } = currentElectronVersion;
const binaryPath = getElectronBinaryPath(version, localPath);
const binaryPath = getElectronBinaryPath(
downloadBinaryPath,
version,
localPath,
);
console.log(`Runner: Binary ${binaryPath} ready, launching`);
const env = { ...process.env };
+17 -2
View File
@@ -1,6 +1,7 @@
import * as fsType from 'fs-extra';
import { action, autorun, computed, observable, when } from 'mobx';
import { MosaicNode } from 'react-mosaic-component';
import * as path from 'path';
import {
ALL_MOSAICS,
@@ -19,6 +20,7 @@ import {
} from '../interfaces';
import { IpcEvents } from '../ipc-events';
import { arrayToStringMap } from '../utils/array-to-stringmap';
import { USER_DATA_PATH } from './constants';
import { EditorBackup, getEditorBackup } from '../utils/editor-backup';
import {
createMosaicArrangement,
@@ -174,6 +176,11 @@ export class AppState {
Record<MosaicId, EditorBackup | true>
> = DEFAULT_CLOSED_PANELS;
@observable public downloadBinaryPath =
(localStorage.getItem('download-binary-path') &&
path.join(localStorage.getItem('download-binary-path') as string)) ||
path.join(USER_DATA_PATH, 'electron-bin');
private outputBuffer = '';
private name: string;
public appData: string;
@@ -237,6 +244,12 @@ export class AppState {
autorun(() => this.save('channelsToShow', this.channelsToShow));
autorun(() => this.save('statesToShow', this.statesToShow));
autorun(() => this.save('packageManager', this.packageManager ?? 'npm'));
autorun(() =>
this.save(
'download-binary-path',
this.downloadBinaryPath ?? path.join(USER_DATA_PATH, 'electron-bin'),
),
);
autorun(() => {
if (typeof this.isUnsaved === 'undefined') return;
@@ -472,7 +485,7 @@ export class AppState {
saveLocalVersions(versionsAsArray);
} else {
await removeBinary(version);
await removeBinary(this.downloadBinaryPath, version);
updatedVersions[version].state = VersionState.unknown;
}
@@ -588,7 +601,9 @@ export class AppState {
}
});
const downloadedVersions = await getDownloadedVersions();
const downloadedVersions = await getDownloadedVersions(
this.downloadBinaryPath,
);
(downloadedVersions || []).forEach((version) => {
if (updatedVersions[version]) {
updatedVersions[version].state = VersionState.ready;
+15 -11
View File
@@ -9,6 +9,7 @@ import { removeTypeDefsForVersion } from '../../src/renderer/fetch-types';
import { overridePlatform, resetPlatform } from '../utils';
import * as path from 'path';
import { USER_DATA_PATH } from '../../src/renderer/constants';
jest.mock('fs-extra');
jest.mock('../../src/renderer/ipc', () => ({}));
@@ -35,11 +36,14 @@ jest.mock('@electron/get', () => ({
download: jest.fn(),
}));
const downloadPath = path.join(USER_DATA_PATH, 'electron-bin');
describe('binary', () => {
let mockState: any = {};
beforeEach(() => {
mockState = {
downloadBinaryPath: downloadPath,
versions: {
'3.0.0': {
state: 'downloading',
@@ -58,7 +62,7 @@ describe('binary', () => {
(fs.existsSync as jest.Mock<any>).mockReturnValue(true);
await removeBinary('v3.0.0');
await removeBinary(downloadPath, 'v3.0.0');
expect(fs.remove).toHaveBeenCalled();
});
@@ -70,7 +74,7 @@ describe('binary', () => {
throw new Error('Bwap bwap');
});
await removeBinary('v3.0.0');
await removeBinary(downloadPath, 'v3.0.0');
expect(fs.remove).toHaveBeenCalledTimes(4);
});
@@ -79,7 +83,7 @@ describe('binary', () => {
(fs.existsSync as jest.Mock<any>).mockReturnValue(true);
await removeBinary('v3.0.0');
await removeBinary(downloadPath, 'v3.0.0');
expect(fs.remove).toHaveBeenCalled();
expect(removeTypeDefsForVersion).toHaveBeenCalled();
});
@@ -92,7 +96,7 @@ describe('binary', () => {
(fs.existsSync as jest.Mock<any>).mockReturnValue(true);
await removeBinary('v3.0.0');
await removeBinary(downloadPath, 'v3.0.0');
expect(removeTypeDefsForVersion).toHaveBeenCalledTimes(4);
});
});
@@ -104,7 +108,7 @@ describe('binary', () => {
(fs.readdir as jest.Mock<any>).mockReturnValue(['v3.0.0']);
(fs.existsSync as jest.Mock<any>).mockReturnValue(true);
const result = await getDownloadedVersions();
const result = await getDownloadedVersions(downloadPath);
expect(result).toEqual(['v3.0.0']);
});
@@ -113,7 +117,7 @@ describe('binary', () => {
const fs = require('fs-extra');
(fs.readdir as jest.Mock<any>).mockReturnValue([]);
const result = await getDownloadedVersions();
const result = await getDownloadedVersions(downloadPath);
expect(result).toEqual([]);
});
@@ -123,7 +127,7 @@ describe('binary', () => {
(fs.readdir as jest.Mock<any>).mockImplementationOnce(() => {
throw new Error('💩');
});
const result = await getDownloadedVersions();
const result = await getDownloadedVersions(downloadPath);
expect(result).toEqual([]);
});
});
@@ -139,7 +143,7 @@ describe('binary', () => {
it('returns the correct path on Windows', () => {
overridePlatform('win32');
const result = getElectronBinaryPath('v3.0.0');
const result = getElectronBinaryPath(downloadPath, 'v3.0.0');
expect(result).toBe(
path.join('user/data/electron-bin/v3.0.0/electron.exe'),
);
@@ -148,14 +152,14 @@ describe('binary', () => {
it('returns the correct path on Linux', () => {
overridePlatform('linux');
const result = getElectronBinaryPath('v3.0.0');
const result = getElectronBinaryPath(downloadPath, 'v3.0.0');
expect(result).toBe(path.join('user/data/electron-bin/v3.0.0/electron'));
});
it('returns the correct path on macOS', () => {
overridePlatform('darwin');
const result = getElectronBinaryPath('v3.0.0');
const result = getElectronBinaryPath(downloadPath, 'v3.0.0');
const expected =
'user/data/electron-bin/v3.0.0/Electron.app/Contents/MacOS/Electron';
expect(result).toBe(path.join(expected));
@@ -164,7 +168,7 @@ describe('binary', () => {
it('throws on other platforms', () => {
overridePlatform('bleepbloop');
expect(() => getElectronBinaryPath('v3.0.0')).toThrow();
expect(() => getElectronBinaryPath(downloadPath, 'v3.0.0')).toThrow();
});
});
@@ -125,6 +125,25 @@ exports[`ElectronSettings component renders 1`] = `
</Blueprint3.FormGroup>
</Blueprint3.Callout>
<br />
<Blueprint3.Callout>
<Blueprint3.FormGroup>
<p>
You can setup the download directory for Electron binary files.
</p>
<Blueprint3.InputGroup
readOnly={true}
rightElement={
<Blueprint3.Button
icon="folder-open"
onClick={[Function]}
>
Select
</Blueprint3.Button>
}
/>
</Blueprint3.FormGroup>
</Blueprint3.Callout>
<br />
<Blueprint3.Callout>
<Blueprint3.ButtonGroup
fill={true}
+1 -1
View File
@@ -340,7 +340,7 @@ describe('AppState', () => {
appState.versions['2.0.2'].state = VersionState.ready;
await appState.removeVersion('v2.0.2');
expect(removeBinary).toHaveBeenCalledWith('2.0.2');
expect(removeBinary).toHaveBeenCalledWith(expect.any(String), '2.0.2');
});
it('does not remove it if not necessary', async () => {