Skip to content

Commit

Permalink
feat: support npmmirror(China) mirror and switch mirror (#991)
Browse files Browse the repository at this point in the history
  • Loading branch information
BlackHole1 authored Mar 22, 2022
1 parent f19b774 commit 4737544
Show file tree
Hide file tree
Showing 12 changed files with 317 additions and 19 deletions.
30 changes: 22 additions & 8 deletions src/renderer/binary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from '../interfaces';
import { USER_DATA_PATH } from './constants';
import { download as electronDownload } from '@electron/get';
import { Mirrors } from './mirror-constants';

// versions that are currently being downloaded
const downloading: Map<string, Promise<void>> = new Map();
Expand Down Expand Up @@ -44,9 +45,13 @@ export function getVersionState(ver: Version): VersionState {
* to ensure that we always have or download at least one version.
*
* @param {RunnableVersion} ver
* @param {Mirrors} mirror
* @returns {Promise<void>}
*/
export function setupBinary(ver: RunnableVersion): Promise<void> {
export function setupBinary(
ver: RunnableVersion,
mirror: Mirrors,
): Promise<void> {
const { version } = ver;

// Only remote versions can be downloaded
Expand All @@ -57,21 +62,24 @@ export function setupBinary(ver: RunnableVersion): Promise<void> {
// Return a promise that resolves when the download completes
let pending = downloading.get(version);
if (!pending) {
pending = downloadBinary(ver);
pending = downloadBinary(ver, mirror);
downloading.set(version, pending);
}
return pending;
}

async function downloadBinary(ver: RunnableVersion): Promise<void> {
async function downloadBinary(
ver: RunnableVersion,
mirror: Mirrors,
): Promise<void> {
const { version } = ver;

ver.state = VersionState.downloading;
console.log(`Binary: Downloading Electron ${version}`);

let zipPath;
try {
zipPath = await download(ver);
zipPath = await download(ver, mirror);
} catch (error) {
console.warn(`Binary: Failure to download ${version}`, error);
ver.state = VersionState.unknown;
Expand Down Expand Up @@ -204,9 +212,13 @@ export function getElectronBinaryPath(
* Download an Electron version.
*
* @param {RunnableVersion} ver
* @param {Mirrors} mirror
* @returns {Promise<string>} path to the downloaded file
*/
async function download(ver: RunnableVersion): Promise<string> {
async function download(
ver: RunnableVersion,
mirror: Mirrors,
): Promise<string> {
const { version } = ver;

const getProgressCallback = (progress: Progress) => {
Expand All @@ -217,7 +229,11 @@ async function download(ver: RunnableVersion): Promise<string> {
}
};

const zipFilePath = await electronDownload(version, {
return await electronDownload(version, {
mirrorOptions: {
mirror: mirror.electronMirror,
nightlyMirror: mirror.electronNightlyMirror,
},
downloadOptions: {
quiet: true,
getProgressCallback,
Expand All @@ -229,8 +245,6 @@ async function download(ver: RunnableVersion): Promise<string> {
},
},
});

return zipFilePath;
}

/**
Expand Down
82 changes: 82 additions & 0 deletions src/renderer/components/settings-general-mirror.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { observer } from 'mobx-react';
import * as React from 'react';
import { AppState } from '../state';
import { Callout, InputGroup, Radio, RadioGroup } from '@blueprintjs/core';
import { FormEvent } from 'react';
import { Mirrors, Sources } from '../mirror-constants';

interface MirrorSettingsProps {
appState: AppState;
}

type IMirrorSettingsState = Mirrors;

/**
* Settings electron mirror
*
* @class MirrorSettings
* @extends {React.Component<MirrorSettingsProps, IMirrorSettingsState>}
*/
@observer
export class MirrorSettings extends React.Component<
MirrorSettingsProps,
IMirrorSettingsState
> {
constructor(props: MirrorSettingsProps) {
super(props);

this.changeSourceType = this.changeSourceType.bind(this);
}

private modifyMirror(isNightly: boolean, value: string) {
this.props.appState.electronMirror.sources.CUSTOM[
isNightly ? 'electronNightlyMirror' : 'electronMirror'
] = value;
}

private changeSourceType(e: FormEvent<HTMLInputElement>) {
this.props.appState.electronMirror.sourceType = (e.target as HTMLInputElement)
.value as Sources;
}

private get notCustomSource() {
return this.props.appState.electronMirror.sourceType !== 'CUSTOM';
}

public render() {
const { sourceType, sources } = this.props.appState.electronMirror;
const electronMirrorLabel = `If you don't have access to Electron's GitHub releases, you can tell Fiddle to download Electron binaries from an alternate source.`;

return (
<div>
<h4>Electron Mirrors</h4>
<Callout>
<RadioGroup
label={electronMirrorLabel}
inline={true}
onChange={this.changeSourceType}
selectedValue={sourceType}
>
<Radio label="Default" value="DEFAULT" />
<Radio label="China" value="CHINA" />
<Radio label="Custom" value="CUSTOM" />
</RadioGroup>
<InputGroup
value={sources[sourceType].electronMirror}
disabled={this.notCustomSource}
onChange={(e) => {
this.modifyMirror(false, e.target.value);
}}
/>
<InputGroup
value={sources[sourceType].electronNightlyMirror}
disabled={this.notCustomSource}
onChange={(e) => {
this.modifyMirror(true, e.target.value);
}}
/>
</Callout>
</div>
);
}
}
3 changes: 3 additions & 0 deletions src/renderer/components/settings-general.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { BlockAcceleratorsSettings } from './settings-general-block-accelerators
import { ConsoleSettings } from './settings-general-console';
import { GitHubSettings } from './settings-general-github';
import { PackageAuthorSettings } from './settings-general-package-author';
import { MirrorSettings } from './settings-general-mirror';

interface GeneralSettingsProps {
appState: AppState;
Expand Down Expand Up @@ -38,6 +39,8 @@ export class GeneralSettings extends React.Component<GeneralSettingsProps> {
<BlockAcceleratorsSettings appState={this.props.appState} />
<Divider />
<PackageAuthorSettings appState={this.props.appState} />
<Divider />
<MirrorSettings appState={this.props.appState} />
</div>
);
}
Expand Down
26 changes: 26 additions & 0 deletions src/renderer/mirror-constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const sources = {
DEFAULT: {
electronMirror: 'https://github.com/electron/electron/releases/download/',
electronNightlyMirror:
'https://github.com/electron/nightlies/releases/download/',
},
CHINA: {
electronMirror: 'https://npmmirror.com/mirrors/electron/',
electronNightlyMirror: 'https://npmmirror.com/mirrors/electron-nightly/',
},
CUSTOM: {
electronMirror: '',
electronNightlyMirror: '',
},
};

export const ELECTRON_MIRROR = {
sourceType: 'DEFAULT' as keyof typeof sources,
sources,
};

export type Sources = keyof typeof sources;
export type Mirrors = {
electronMirror: string;
electronNightlyMirror: string;
};
17 changes: 15 additions & 2 deletions src/renderer/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
saveLocalVersions,
} from './versions';
import { getUsername } from '../utils/get-username';
import { ELECTRON_MIRROR } from './mirror-constants';

/**
* The application's state. Exported as a singleton below.
Expand Down Expand Up @@ -104,6 +105,14 @@ export class AppState {
(this.retrieve('acceleratorsToBlock') as Array<BlockableAccelerator>) || [];
@observable public packageAuthor =
(localStorage.getItem('packageAuthor') as string) ?? getUsername();
@observable public electronMirror: typeof ELECTRON_MIRROR =
(this.retrieve('electronMirror') as typeof ELECTRON_MIRROR) === null
? {
...ELECTRON_MIRROR,
sourceType: navigator.language === 'zh-CN' ? 'CHINA' : 'DEFAULT',
}
: (this.retrieve('electronMirror') as typeof ELECTRON_MIRROR);

// -- Various session-only state ------------------
@observable public gistId: string | undefined;
@observable public readonly versions: Record<string, RunnableVersion>;
Expand Down Expand Up @@ -225,6 +234,7 @@ export class AppState {
autorun(() => this.save('packageManager', this.packageManager ?? 'npm'));
autorun(() => this.save('acceleratorsToBlock', this.acceleratorsToBlock));
autorun(() => this.save('packageAuthor', this.packageAuthor));
autorun(() => this.save('electronMirror', this.electronMirror as any));

// Update our known versions
this.updateElectronVersions();
Expand Down Expand Up @@ -445,7 +455,7 @@ export class AppState {
/**
* Download a version of Electron.
*
* @param {string} input
* @param {RunnableVersion} ver
* @returns {Promise<void>}
*/
@action public async downloadVersion(ver: RunnableVersion) {
Expand All @@ -459,7 +469,10 @@ export class AppState {
}

console.log(`State: Downloading Electron ${version}`);
await setupBinary(ver);
await setupBinary(
ver,
this.electronMirror.sources[this.electronMirror.sourceType],
);
}

public hasVersion(input: string): boolean {
Expand Down
2 changes: 2 additions & 0 deletions tests/mocks/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { EditorMosaic } from '../../src/renderer/editor-mosaic';
import { objectDifference } from '../utils';
import { BisectorMock } from './bisector';
import { VersionsMock } from './electron-versions';
import { ELECTRON_MIRROR } from '../../src/renderer/mirror-constants';

export class StateMock {
@observable public acceleratorsToBlock: BlockableAccelerator[] = [];
Expand Down Expand Up @@ -55,6 +56,7 @@ export class StateMock {
@observable public versions: Record<string, RunnableVersion>;
@observable public versionsToShow: RunnableVersion[] = [];
@observable public packageAuthor = 'electron<[email protected]>';
@observable public electronMirror = ELECTRON_MIRROR;
@observable public isBisectCommandShowing = false;

public Bisector = new BisectorMock();
Expand Down
17 changes: 9 additions & 8 deletions tests/renderer/binary-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import * as path from 'path';
import * as semver from 'semver';
import extract from 'extract-zip';
import { download as electronDownload } from '@electron/get';
import { ELECTRON_MIRROR } from '../../src/renderer/mirror-constants';

jest.mock('fs-extra');
jest.mock('extract-zip');
Expand Down Expand Up @@ -92,7 +93,7 @@ describe('binary', () => {
new Promise((r) => (downloadResolve = r)),
);

setupBinary(ver);
setupBinary(ver, ELECTRON_MIRROR.sources.DEFAULT);

expect(getVersionState(ver)).toBe(VersionState.downloading);
downloadResolve();
Expand All @@ -105,7 +106,7 @@ describe('binary', () => {
const unzipPromise = new Promise((r) => (unzipResolve = r));
(extract as jest.Mock).mockReturnValueOnce(unzipPromise);

setupBinary(ver);
setupBinary(ver, ELECTRON_MIRROR.sources.DEFAULT);
await waitFor(() => getVersionState(ver) === VersionState.unzipping);

expect(getVersionState(ver)).toBe(VersionState.unzipping);
Expand Down Expand Up @@ -181,7 +182,7 @@ describe('binary', () => {

ver.source = VersionSource.local;
ver.state = VersionState.unknown;
await setupBinary(ver);
await setupBinary(ver, ELECTRON_MIRROR.sources.DEFAULT);

expect(electronDownload).not.toHaveBeenCalled();
expect(ver.state).toBe(VersionState.unknown);
Expand All @@ -194,7 +195,7 @@ describe('binary', () => {

ver.source = VersionSource.remote;
ver.state = VersionState.unknown;
await setupBinary(ver);
await setupBinary(ver, ELECTRON_MIRROR.sources.DEFAULT);

expect(electronDownload).toHaveBeenCalled();
expect(ver.state).toBe(VersionState.ready);
Expand All @@ -215,7 +216,7 @@ describe('binary', () => {

ver.source = VersionSource.remote;
ver.state = VersionState.unknown;
setupBinary(ver);
setupBinary(ver, ELECTRON_MIRROR.sources.DEFAULT);
await waitFor(() => ver.state === VersionState.downloading);

expect(ver.downloadProgress).not.toBe(percent);
Expand All @@ -232,8 +233,8 @@ describe('binary', () => {
it(`returns the same promise if called twice for the same version`, async () => {
(fs.existsSync as jest.Mock).mockReturnValue(false);

const prom1 = setupBinary(ver);
const prom2 = setupBinary(ver);
const prom1 = setupBinary(ver, ELECTRON_MIRROR.sources.DEFAULT);
const prom2 = setupBinary(ver, ELECTRON_MIRROR.sources.DEFAULT);

expect(prom1).toBe(prom2);
});
Expand All @@ -244,7 +245,7 @@ describe('binary', () => {

ver.source = VersionSource.remote;
ver.state = VersionState.unknown;
await setupBinary(ver);
await setupBinary(ver, ELECTRON_MIRROR.sources.DEFAULT);

expect(extract as jest.Mock).toHaveBeenCalledTimes(1);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`MirrorSettings component renders 1`] = `
<div>
<h4>
Electron Mirrors
</h4>
<Blueprint3.Callout>
<Blueprint3.RadioGroup
inline={true}
label="If you don't have access to Electron's GitHub releases, you can tell Fiddle to download Electron binaries from an alternate source."
onChange={[Function]}
selectedValue="DEFAULT"
>
<Blueprint3.Radio
label="Default"
value="DEFAULT"
/>
<Blueprint3.Radio
label="China"
value="CHINA"
/>
<Blueprint3.Radio
label="Custom"
value="CUSTOM"
/>
</Blueprint3.RadioGroup>
<Blueprint3.InputGroup
disabled={true}
onChange={[Function]}
value="https://github.com/electron/electron/releases/download/"
/>
<Blueprint3.InputGroup
disabled={true}
onChange={[Function]}
value="https://github.com/electron/nightlies/releases/download/"
/>
</Blueprint3.Callout>
</div>
`;
Loading

0 comments on commit 4737544

Please sign in to comment.