mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-23 11:16:39 +00:00
commit
d814202424
2
FAQ.md
2
FAQ.md
@ -43,4 +43,4 @@ Etcher requires an available [polkit authentication agent](https://wiki.archlinu
|
|||||||
|
|
||||||
## May I run Etcher in older macOS versions?
|
## May I run Etcher in older macOS versions?
|
||||||
|
|
||||||
Etcher GUI is based on the [Electron](http://electron.atom.io/) framework, [which only supports macOS 10.9 and newer versions](https://github.com/electron/electron/blob/master/docs/tutorial/support.md#supported-platforms).
|
Etcher GUI is based on the [Electron](http://electron.atom.io/) framework, [which only supports macOS 10.10 and newer versions](https://github.com/electron/electron/blob/master/docs/tutorial/support.md#supported-platforms).
|
||||||
|
2
Makefile
2
Makefile
@ -3,7 +3,7 @@
|
|||||||
# ---------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
|
|
||||||
RESIN_SCRIPTS ?= ./scripts/resin
|
RESIN_SCRIPTS ?= ./scripts/resin
|
||||||
export NPM_VERSION ?= 6.14.5
|
export NPM_VERSION ?= 6.14.8
|
||||||
S3_BUCKET = artifacts.ci.balena-cloud.com
|
S3_BUCKET = artifacts.ci.balena-cloud.com
|
||||||
|
|
||||||
# This directory will be completely deleted by the `clean` rule
|
# This directory will be completely deleted by the `clean` rule
|
||||||
|
@ -4,6 +4,13 @@ Getting help with Etcher
|
|||||||
There are various ways to get support for Etcher if you experience an issue or
|
There are various ways to get support for Etcher if you experience an issue or
|
||||||
have an idea you'd like to share with us.
|
have an idea you'd like to share with us.
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
------
|
||||||
|
|
||||||
|
We have answers to a variety of frequently asked questions in the [user
|
||||||
|
documentation][documentation] and also in the [FAQs][faq] on the Etcher website.
|
||||||
|
|
||||||
|
|
||||||
Forums
|
Forums
|
||||||
------
|
------
|
||||||
|
|
||||||
@ -32,3 +39,5 @@ one][new-issue].
|
|||||||
[discourse]: https://forums.balena.io/c/etcher
|
[discourse]: https://forums.balena.io/c/etcher
|
||||||
[issues]: https://github.com/balena-io/etcher/issues
|
[issues]: https://github.com/balena-io/etcher/issues
|
||||||
[new-issue]: https://github.com/balena-io/etcher/issues/new
|
[new-issue]: https://github.com/balena-io/etcher/issues/new
|
||||||
|
[documentation]: https://github.com/balena-io/etcher/blob/master/docs/USER-DOCUMENTATION.md
|
||||||
|
[faq]: https://etcher.io
|
||||||
|
@ -74,6 +74,8 @@ function isDrivelistDrive(drive: Drive): drive is DrivelistDrive {
|
|||||||
const DrivesTable = styled((props: GenericTableProps<Drive>) => (
|
const DrivesTable = styled((props: GenericTableProps<Drive>) => (
|
||||||
<Table<Drive> {...props} />
|
<Table<Drive> {...props} />
|
||||||
))`
|
))`
|
||||||
|
border-bottom: none;
|
||||||
|
|
||||||
[data-display='table-head'],
|
[data-display='table-head'],
|
||||||
[data-display='table-body'] {
|
[data-display='table-body'] {
|
||||||
> [data-display='table-row'] > [data-display='table-cell'] {
|
> [data-display='table-row'] > [data-display='table-cell'] {
|
||||||
@ -303,9 +305,9 @@ export class DriveSelector extends React.Component<
|
|||||||
case compatibility.system():
|
case compatibility.system():
|
||||||
return warning.systemDrive();
|
return warning.systemDrive();
|
||||||
case compatibility.tooSmall():
|
case compatibility.tooSmall():
|
||||||
const recommendedDriveSize =
|
const size =
|
||||||
this.state.image?.recommendedDriveSize || this.state.image?.size || 0;
|
this.state.image?.recommendedDriveSize || this.state.image?.size || 0;
|
||||||
return warning.unrecommendedDriveSize({ recommendedDriveSize }, drive);
|
return warning.tooSmall({ size }, drive);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
import CircleSvg from '@fortawesome/fontawesome-free/svgs/solid/circle.svg';
|
import CircleSvg from '@fortawesome/fontawesome-free/svgs/solid/circle.svg';
|
||||||
import CheckCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/check-circle.svg';
|
import CheckCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/check-circle.svg';
|
||||||
import TimesCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/times-circle.svg';
|
import TimesCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/times-circle.svg';
|
||||||
import * as _ from 'lodash';
|
|
||||||
import outdent from 'outdent';
|
import outdent from 'outdent';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Flex, FlexProps, Link, TableColumn, Txt } from 'rendition';
|
import { Flex, FlexProps, Link, TableColumn, Txt } from 'rendition';
|
||||||
@ -104,6 +103,19 @@ const columns: Array<TableColumn<FlashError>> = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function getEffectiveSpeed(results: {
|
||||||
|
sourceMetadata: {
|
||||||
|
size: number;
|
||||||
|
blockmappedSize?: number;
|
||||||
|
};
|
||||||
|
averageFlashingSpeed: number;
|
||||||
|
}) {
|
||||||
|
const flashedSize =
|
||||||
|
results.sourceMetadata.blockmappedSize ?? results.sourceMetadata.size;
|
||||||
|
const timeSpent = flashedSize / results.averageFlashingSpeed;
|
||||||
|
return results.sourceMetadata.size / timeSpent;
|
||||||
|
}
|
||||||
|
|
||||||
export function FlashResults({
|
export function FlashResults({
|
||||||
goToMain,
|
goToMain,
|
||||||
image = '',
|
image = '',
|
||||||
@ -117,10 +129,9 @@ export function FlashResults({
|
|||||||
errors: FlashError[];
|
errors: FlashError[];
|
||||||
skip: boolean;
|
skip: boolean;
|
||||||
results: {
|
results: {
|
||||||
bytesWritten: number;
|
|
||||||
sourceMetadata: {
|
sourceMetadata: {
|
||||||
size: number;
|
size: number;
|
||||||
blockmappedSize: number;
|
blockmappedSize?: number;
|
||||||
};
|
};
|
||||||
averageFlashingSpeed: number;
|
averageFlashingSpeed: number;
|
||||||
devices: { failed: number; successful: number };
|
devices: { failed: number; successful: number };
|
||||||
@ -129,11 +140,7 @@ export function FlashResults({
|
|||||||
const [showErrorsInfo, setShowErrorsInfo] = React.useState(false);
|
const [showErrorsInfo, setShowErrorsInfo] = React.useState(false);
|
||||||
const allFailed = !skip && results.devices.successful === 0;
|
const allFailed = !skip && results.devices.successful === 0;
|
||||||
const someFailed = results.devices.failed !== 0 || errors.length !== 0;
|
const someFailed = results.devices.failed !== 0 || errors.length !== 0;
|
||||||
const effectiveSpeed = _.round(
|
const effectiveSpeed = bytesToMegabytes(getEffectiveSpeed(results)).toFixed(
|
||||||
bytesToMegabytes(
|
|
||||||
results.sourceMetadata.size /
|
|
||||||
(results.sourceMetadata.blockmappedSize / results.averageFlashingSpeed),
|
|
||||||
),
|
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
import GithubSvg from '@fortawesome/fontawesome-free/svgs/brands/github.svg';
|
import GithubSvg from '@fortawesome/fontawesome-free/svgs/brands/github.svg';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import * as os from 'os';
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Flex, Checkbox, Txt } from 'rendition';
|
import { Flex, Checkbox, Txt } from 'rendition';
|
||||||
|
|
||||||
@ -26,40 +25,25 @@ import * as analytics from '../../modules/analytics';
|
|||||||
import { open as openExternal } from '../../os/open-external/services/open-external';
|
import { open as openExternal } from '../../os/open-external/services/open-external';
|
||||||
import { Modal } from '../../styled-components';
|
import { Modal } from '../../styled-components';
|
||||||
|
|
||||||
const platform = os.platform();
|
|
||||||
|
|
||||||
interface Setting {
|
interface Setting {
|
||||||
name: string;
|
name: string;
|
||||||
label: string | JSX.Element;
|
label: string | JSX.Element;
|
||||||
options?: {
|
|
||||||
description: string;
|
|
||||||
confirmLabel: string;
|
|
||||||
};
|
|
||||||
hide?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getSettingsList(): Promise<Setting[]> {
|
async function getSettingsList(): Promise<Setting[]> {
|
||||||
return [
|
const list: Setting[] = [
|
||||||
{
|
{
|
||||||
name: 'errorReporting',
|
name: 'errorReporting',
|
||||||
label: 'Anonymously report errors and usage statistics to balena.io',
|
label: 'Anonymously report errors and usage statistics to balena.io',
|
||||||
},
|
},
|
||||||
{
|
];
|
||||||
name: 'unmountOnSuccess',
|
if (['appimage', 'nsis', 'dmg'].includes(packageType)) {
|
||||||
/**
|
list.push({
|
||||||
* On Windows, "Unmounting" basically means "ejecting".
|
|
||||||
* On top of that, Windows users are usually not even
|
|
||||||
* familiar with the meaning of "unmount", which comes
|
|
||||||
* from the UNIX world.
|
|
||||||
*/
|
|
||||||
label: `${platform === 'win32' ? 'Eject' : 'Auto-unmount'} on success`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'updatesEnabled',
|
name: 'updatesEnabled',
|
||||||
label: 'Auto-updates enabled',
|
label: 'Auto-updates enabled',
|
||||||
hide: ['rpm', 'deb'].includes(packageType),
|
});
|
||||||
},
|
}
|
||||||
];
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SettingsModalProps {
|
interface SettingsModalProps {
|
||||||
@ -86,25 +70,14 @@ export function SettingsModal({ toggleModal }: SettingsModalProps) {
|
|||||||
})();
|
})();
|
||||||
});
|
});
|
||||||
|
|
||||||
const toggleSetting = async (
|
const toggleSetting = async (setting: string) => {
|
||||||
setting: string,
|
|
||||||
options?: Setting['options'],
|
|
||||||
) => {
|
|
||||||
const value = currentSettings[setting];
|
const value = currentSettings[setting];
|
||||||
const dangerous = options !== undefined;
|
analytics.logEvent('Toggle setting', { setting, value });
|
||||||
|
|
||||||
analytics.logEvent('Toggle setting', {
|
|
||||||
setting,
|
|
||||||
value,
|
|
||||||
dangerous,
|
|
||||||
});
|
|
||||||
|
|
||||||
await settings.set(setting, !value);
|
await settings.set(setting, !value);
|
||||||
setCurrentSettings({
|
setCurrentSettings({
|
||||||
...currentSettings,
|
...currentSettings,
|
||||||
[setting]: !value,
|
[setting]: !value,
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -118,14 +91,14 @@ export function SettingsModal({ toggleModal }: SettingsModalProps) {
|
|||||||
>
|
>
|
||||||
<Flex flexDirection="column">
|
<Flex flexDirection="column">
|
||||||
{settingsList.map((setting: Setting, i: number) => {
|
{settingsList.map((setting: Setting, i: number) => {
|
||||||
return setting.hide ? null : (
|
return (
|
||||||
<Flex key={setting.name} mb={14}>
|
<Flex key={setting.name} mb={14}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
toggle
|
toggle
|
||||||
tabIndex={6 + i}
|
tabIndex={6 + i}
|
||||||
label={setting.label}
|
label={setting.label}
|
||||||
checked={currentSettings[setting.name]}
|
checked={currentSettings[setting.name]}
|
||||||
onChange={() => toggleSetting(setting.name, setting.options)}
|
onChange={() => toggleSetting(setting.name)}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
@ -64,3 +64,19 @@ input[type="checkbox"] + div {
|
|||||||
#rendition-tooltip-root > div {
|
#rendition-tooltip-root > div {
|
||||||
font-family: "SourceSansPro", sans-serif;
|
font-family: "SourceSansPro", sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* HIGH-CONTRAST CHANGES */
|
||||||
|
input[type="text"],
|
||||||
|
input[type="checkbox"] ~ div,
|
||||||
|
input[type="checkbox"] ~ span {
|
||||||
|
border-color: #b5b5b5 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-display="table-head"]
|
||||||
|
> [data-display="table-row"]
|
||||||
|
> [data-display="table-cell"],
|
||||||
|
[data-display="table-body"]
|
||||||
|
> [data-display="table-row"]
|
||||||
|
> [data-display="table-cell"] {
|
||||||
|
border-bottom: 1px solid #b5b5b5 !important;
|
||||||
|
}
|
||||||
|
@ -85,6 +85,10 @@ export function addFailedDeviceError({
|
|||||||
const failedDeviceErrorsMap = new Map(
|
const failedDeviceErrorsMap = new Map(
|
||||||
store.getState().toJS().failedDeviceErrors,
|
store.getState().toJS().failedDeviceErrors,
|
||||||
);
|
);
|
||||||
|
if (failedDeviceErrorsMap.has(device.device)) {
|
||||||
|
// Only store the first error
|
||||||
|
return;
|
||||||
|
}
|
||||||
failedDeviceErrorsMap.set(device.device, {
|
failedDeviceErrorsMap.set(device.device, {
|
||||||
description: device.description,
|
description: device.description,
|
||||||
device: device.device,
|
device: device.device,
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import { AnimationFunction, Color, RGBLed } from 'sys-class-rgb-led';
|
import { Animator, AnimationFunction, Color, RGBLed } from 'sys-class-rgb-led';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
isSourceDrive,
|
isSourceDrive,
|
||||||
@ -25,30 +25,14 @@ import * as settings from './settings';
|
|||||||
import { DEFAULT_STATE, observe } from './store';
|
import { DEFAULT_STATE, observe } from './store';
|
||||||
|
|
||||||
const leds: Map<string, RGBLed> = new Map();
|
const leds: Map<string, RGBLed> = new Map();
|
||||||
|
const animator = new Animator([], 10);
|
||||||
|
|
||||||
function setLeds(
|
const red: Color = [0.59, 0, 0];
|
||||||
drivesPaths: Set<string>,
|
const green: Color = [0, 0.59, 0];
|
||||||
colorOrAnimation: Color | AnimationFunction,
|
const blue: Color = [0, 0, 0.59];
|
||||||
frequency?: number,
|
const white: Color = [0.04, 0.04, 0.04];
|
||||||
) {
|
|
||||||
for (const path of drivesPaths) {
|
|
||||||
const led = leds.get(path);
|
|
||||||
if (led) {
|
|
||||||
if (Array.isArray(colorOrAnimation)) {
|
|
||||||
led.setStaticColor(colorOrAnimation);
|
|
||||||
} else {
|
|
||||||
led.setAnimation(colorOrAnimation, frequency);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const red: Color = [1, 0, 0];
|
|
||||||
const green: Color = [0, 1, 0];
|
|
||||||
const blue: Color = [0, 0, 1];
|
|
||||||
const white: Color = [1, 1, 1];
|
|
||||||
const black: Color = [0, 0, 0];
|
const black: Color = [0, 0, 0];
|
||||||
const purple: Color = [0.5, 0, 0.5];
|
const purple: Color = [0.117, 0, 0.196];
|
||||||
|
|
||||||
function createAnimationFunction(
|
function createAnimationFunction(
|
||||||
intensityFunction: (t: number) => number,
|
intensityFunction: (t: number) => number,
|
||||||
@ -61,16 +45,20 @@ function createAnimationFunction(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function blink(t: number) {
|
function blink(t: number) {
|
||||||
return Math.floor(t / 1000) % 2;
|
return Math.floor(t) % 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
function breathe(t: number) {
|
function one(_t: number) {
|
||||||
return (1 + Math.sin(t / 1000)) / 2;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const breatheBlue = createAnimationFunction(breathe, blue);
|
|
||||||
const blinkGreen = createAnimationFunction(blink, green);
|
const blinkGreen = createAnimationFunction(blink, green);
|
||||||
const blinkPurple = createAnimationFunction(blink, purple);
|
const blinkPurple = createAnimationFunction(blink, purple);
|
||||||
|
const staticRed = createAnimationFunction(one, red);
|
||||||
|
const staticGreen = createAnimationFunction(one, green);
|
||||||
|
const staticBlue = createAnimationFunction(one, blue);
|
||||||
|
const staticWhite = createAnimationFunction(one, white);
|
||||||
|
const staticBlack = createAnimationFunction(one, black);
|
||||||
|
|
||||||
interface LedsState {
|
interface LedsState {
|
||||||
step: 'main' | 'flashing' | 'verifying' | 'finish';
|
step: 'main' | 'flashing' | 'verifying' | 'finish';
|
||||||
@ -80,6 +68,17 @@ interface LedsState {
|
|||||||
failedDrives: string[];
|
failedDrives: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setLeds(animation: AnimationFunction, drivesPaths: Set<string>) {
|
||||||
|
const rgbLeds: RGBLed[] = [];
|
||||||
|
for (const path of drivesPaths) {
|
||||||
|
const led = leds.get(path);
|
||||||
|
if (led) {
|
||||||
|
rgbLeds.push(led);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { animation, rgbLeds };
|
||||||
|
}
|
||||||
|
|
||||||
// Source slot (1st slot): behaves as a target unless it is chosen as source
|
// Source slot (1st slot): behaves as a target unless it is chosen as source
|
||||||
// No drive: black
|
// No drive: black
|
||||||
// Drive plugged: blue - on
|
// Drive plugged: blue - on
|
||||||
@ -110,6 +109,7 @@ export function updateLeds({
|
|||||||
// Remove selected devices from plugged set
|
// Remove selected devices from plugged set
|
||||||
for (const d of selectedOk) {
|
for (const d of selectedOk) {
|
||||||
plugged.delete(d);
|
plugged.delete(d);
|
||||||
|
unplugged.delete(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove plugged devices from unplugged set
|
// Remove plugged devices from unplugged set
|
||||||
@ -122,38 +122,42 @@ export function updateLeds({
|
|||||||
selectedOk.delete(d);
|
selectedOk.delete(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mapping: Array<{
|
||||||
|
animation: AnimationFunction;
|
||||||
|
rgbLeds: RGBLed[];
|
||||||
|
}> = [];
|
||||||
// Handle source slot
|
// Handle source slot
|
||||||
if (sourceDrive !== undefined) {
|
if (sourceDrive !== undefined) {
|
||||||
if (unplugged.has(sourceDrive)) {
|
if (plugged.has(sourceDrive)) {
|
||||||
unplugged.delete(sourceDrive);
|
|
||||||
// TODO
|
|
||||||
setLeds(new Set([sourceDrive]), breatheBlue, 2);
|
|
||||||
} else if (plugged.has(sourceDrive)) {
|
|
||||||
plugged.delete(sourceDrive);
|
plugged.delete(sourceDrive);
|
||||||
setLeds(new Set([sourceDrive]), blue);
|
mapping.push(setLeds(staticBlue, new Set([sourceDrive])));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (step === 'main') {
|
if (step === 'main') {
|
||||||
setLeds(unplugged, black);
|
mapping.push(
|
||||||
setLeds(plugged, black);
|
setLeds(staticBlack, new Set([...unplugged, ...plugged])),
|
||||||
setLeds(selectedOk, white);
|
setLeds(staticWhite, new Set([...selectedOk, ...selectedFailed])),
|
||||||
setLeds(selectedFailed, white);
|
);
|
||||||
} else if (step === 'flashing') {
|
} else if (step === 'flashing') {
|
||||||
setLeds(unplugged, black);
|
mapping.push(
|
||||||
setLeds(plugged, black);
|
setLeds(staticBlack, new Set([...unplugged, ...plugged])),
|
||||||
setLeds(selectedOk, blinkPurple, 2);
|
setLeds(blinkPurple, selectedOk),
|
||||||
setLeds(selectedFailed, red);
|
setLeds(staticRed, selectedFailed),
|
||||||
|
);
|
||||||
} else if (step === 'verifying') {
|
} else if (step === 'verifying') {
|
||||||
setLeds(unplugged, black);
|
mapping.push(
|
||||||
setLeds(plugged, black);
|
setLeds(staticBlack, new Set([...unplugged, ...plugged])),
|
||||||
setLeds(selectedOk, blinkGreen, 2);
|
setLeds(blinkGreen, selectedOk),
|
||||||
setLeds(selectedFailed, red);
|
setLeds(staticRed, selectedFailed),
|
||||||
|
);
|
||||||
} else if (step === 'finish') {
|
} else if (step === 'finish') {
|
||||||
setLeds(unplugged, black);
|
mapping.push(
|
||||||
setLeds(plugged, black);
|
setLeds(staticBlack, new Set([...unplugged, ...plugged])),
|
||||||
setLeds(selectedOk, green);
|
setLeds(staticGreen, selectedOk),
|
||||||
setLeds(selectedFailed, red);
|
setLeds(staticRed, selectedFailed),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
animator.mapping = mapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DeviceFromState {
|
interface DeviceFromState {
|
||||||
@ -189,7 +193,7 @@ function stateObserver(state: typeof DEFAULT_STATE) {
|
|||||||
selectedDrivesPaths = s.devicePaths;
|
selectedDrivesPaths = s.devicePaths;
|
||||||
}
|
}
|
||||||
const failedDevicePaths = s.failedDeviceErrors.map(
|
const failedDevicePaths = s.failedDeviceErrors.map(
|
||||||
([devicePath]: [string]) => devicePath,
|
([, { devicePath }]: [string, { devicePath: string }]) => devicePath,
|
||||||
);
|
);
|
||||||
const newLedsState = {
|
const newLedsState = {
|
||||||
step,
|
step,
|
||||||
|
@ -77,8 +77,7 @@ export async function writeConfigFile(
|
|||||||
|
|
||||||
const DEFAULT_SETTINGS: _.Dictionary<any> = {
|
const DEFAULT_SETTINGS: _.Dictionary<any> = {
|
||||||
errorReporting: true,
|
errorReporting: true,
|
||||||
unmountOnSuccess: true,
|
updatesEnabled: ['appimage', 'nsis', 'dmg'].includes(packageJSON.packageType),
|
||||||
updatesEnabled: !_.includes(['rpm', 'deb'], packageJSON.packageType),
|
|
||||||
desktopNotifications: true,
|
desktopNotifications: true,
|
||||||
autoBlockmapping: true,
|
autoBlockmapping: true,
|
||||||
decompressFirst: true,
|
decompressFirst: true,
|
||||||
|
@ -151,11 +151,7 @@ async function performWrite(
|
|||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
let skip = false;
|
let skip = false;
|
||||||
ipc.serve();
|
ipc.serve();
|
||||||
const {
|
const { autoBlockmapping, decompressFirst } = await settings.getAll();
|
||||||
unmountOnSuccess,
|
|
||||||
autoBlockmapping,
|
|
||||||
decompressFirst,
|
|
||||||
} = await settings.getAll();
|
|
||||||
return await new Promise((resolve, reject) => {
|
return await new Promise((resolve, reject) => {
|
||||||
ipc.server.on('error', (error) => {
|
ipc.server.on('error', (error) => {
|
||||||
terminateServer();
|
terminateServer();
|
||||||
@ -174,7 +170,6 @@ async function performWrite(
|
|||||||
driveCount: drives.length,
|
driveCount: drives.length,
|
||||||
uuid: flashState.getFlashUuid(),
|
uuid: flashState.getFlashUuid(),
|
||||||
flashInstanceUuid: flashState.getFlashUuid(),
|
flashInstanceUuid: flashState.getFlashUuid(),
|
||||||
unmountOnSuccess,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ipc.server.on('fail', ({ device, error }) => {
|
ipc.server.on('fail', ({ device, error }) => {
|
||||||
@ -211,7 +206,6 @@ async function performWrite(
|
|||||||
destinations: drives,
|
destinations: drives,
|
||||||
SourceType: image.SourceType.name,
|
SourceType: image.SourceType.name,
|
||||||
autoBlockmapping,
|
autoBlockmapping,
|
||||||
unmountOnSuccess,
|
|
||||||
decompressFirst,
|
decompressFirst,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -290,7 +284,6 @@ export async function flash(
|
|||||||
uuid: flashState.getFlashUuid(),
|
uuid: flashState.getFlashUuid(),
|
||||||
status: 'started',
|
status: 'started',
|
||||||
flashInstanceUuid: flashState.getFlashUuid(),
|
flashInstanceUuid: flashState.getFlashUuid(),
|
||||||
unmountOnSuccess: await settings.get('unmountOnSuccess'),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
analytics.logEvent('Flash', analyticsData);
|
analytics.logEvent('Flash', analyticsData);
|
||||||
@ -345,7 +338,6 @@ export async function cancel(type: string) {
|
|||||||
driveCount: drives.length,
|
driveCount: drives.length,
|
||||||
uuid: flashState.getFlashUuid(),
|
uuid: flashState.getFlashUuid(),
|
||||||
flashInstanceUuid: flashState.getFlashUuid(),
|
flashInstanceUuid: flashState.getFlashUuid(),
|
||||||
unmountOnSuccess: await settings.get('unmountOnSuccess'),
|
|
||||||
status,
|
status,
|
||||||
};
|
};
|
||||||
analytics.logEvent('Cancel', analyticsData);
|
analytics.logEvent('Cancel', analyticsData);
|
||||||
|
@ -167,7 +167,6 @@ async function writeAndValidate({
|
|||||||
interface WriteOptions {
|
interface WriteOptions {
|
||||||
image: SourceMetadata;
|
image: SourceMetadata;
|
||||||
destinations: DrivelistDrive[];
|
destinations: DrivelistDrive[];
|
||||||
unmountOnSuccess: boolean;
|
|
||||||
autoBlockmapping: boolean;
|
autoBlockmapping: boolean;
|
||||||
decompressFirst: boolean;
|
decompressFirst: boolean;
|
||||||
SourceType: string;
|
SourceType: string;
|
||||||
@ -257,13 +256,12 @@ ipc.connectTo(IPC_SERVER_ID, () => {
|
|||||||
const imagePath = options.image.path;
|
const imagePath = options.image.path;
|
||||||
log(`Image: ${imagePath}`);
|
log(`Image: ${imagePath}`);
|
||||||
log(`Devices: ${destinations.join(', ')}`);
|
log(`Devices: ${destinations.join(', ')}`);
|
||||||
log(`Umount on success: ${options.unmountOnSuccess}`);
|
|
||||||
log(`Auto blockmapping: ${options.autoBlockmapping}`);
|
log(`Auto blockmapping: ${options.autoBlockmapping}`);
|
||||||
log(`Decompress first: ${options.decompressFirst}`);
|
log(`Decompress first: ${options.decompressFirst}`);
|
||||||
const dests = options.destinations.map((destination) => {
|
const dests = options.destinations.map((destination) => {
|
||||||
return new BlockDevice({
|
return new BlockDevice({
|
||||||
drive: destination,
|
drive: destination,
|
||||||
unmountOnSuccess: options.unmountOnSuccess,
|
unmountOnSuccess: true,
|
||||||
write: true,
|
write: true,
|
||||||
direct: true,
|
direct: true,
|
||||||
});
|
});
|
||||||
|
@ -81,13 +81,10 @@ export const compatibility = {
|
|||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const warning = {
|
export const warning = {
|
||||||
unrecommendedDriveSize: (
|
tooSmall: (source: { size: number }, target: { size: number }) => {
|
||||||
image: { recommendedDriveSize: number },
|
|
||||||
drive: { device: string; size: number },
|
|
||||||
) => {
|
|
||||||
return outdent({ newline: ' ' })`
|
return outdent({ newline: ' ' })`
|
||||||
This image recommends a ${prettyBytes(image.recommendedDriveSize)}
|
The selected source is ${prettyBytes(source.size - target.size)}
|
||||||
drive, however ${drive.device} is only ${prettyBytes(drive.size)}.
|
larger than this drive.
|
||||||
`;
|
`;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
18
npm-shrinkwrap.json
generated
18
npm-shrinkwrap.json
generated
@ -5864,9 +5864,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"electron": {
|
"electron": {
|
||||||
"version": "9.3.3",
|
"version": "9.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/electron/-/electron-9.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/electron/-/electron-9.4.0.tgz",
|
||||||
"integrity": "sha512-xghKeUY1qgnEcJ5w2rXo/toH+8NT2Dktx2aAxBNPV7CIJr3mejJJAPwLbycwtddzr37tgKxHeHlc8ivfKtMkJQ==",
|
"integrity": "sha512-hOC4q0jkb+UDYZRy8vrZ1IANnq+jznZnbkD62OEo06nU+hIbp2IrwDRBNuSLmQ3cwZMVir0WSIA1qEVK0PkzGA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@electron/get": "^1.0.1",
|
"@electron/get": "^1.0.1",
|
||||||
@ -7357,9 +7357,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"etcher-sdk": {
|
"etcher-sdk": {
|
||||||
"version": "5.1.10",
|
"version": "5.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/etcher-sdk/-/etcher-sdk-5.1.10.tgz",
|
"resolved": "https://registry.npmjs.org/etcher-sdk/-/etcher-sdk-5.1.11.tgz",
|
||||||
"integrity": "sha512-tCHY6v4txJr6+3KCIYhaLem6U3ZTEiXRtchMZiuZO6X9t2w8U5ntn6RgHbs7YIixrr/17o1aRUnqPKsXrLbDHQ==",
|
"integrity": "sha512-aS5gbclUoBF+8NOV2R2MJN52BOYKJHc+gjlZF3xZh1hVoglVb0AtCZpFFfTk8/cDn2SvrfAv03/amXaJxJjaPQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@balena/udif": "^1.1.1",
|
"@balena/udif": "^1.1.1",
|
||||||
@ -15006,9 +15006,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"sys-class-rgb-led": {
|
"sys-class-rgb-led": {
|
||||||
"version": "2.1.1",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/sys-class-rgb-led/-/sys-class-rgb-led-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/sys-class-rgb-led/-/sys-class-rgb-led-3.0.0.tgz",
|
||||||
"integrity": "sha512-CPx01dR22xsqqgpGQ0BcKWf1hCJNTK/Y/gK/hvNEZX5PyuvUzrCYsBWgletzlaruc47RYGi/0be+ZbkIIiQjnA==",
|
"integrity": "sha512-e5vMYgWgDFfXMN67lbTW6niSxzm3eiD8A8hEciUtOUexfYGM6lpd6dH6bERq2LL99mmBYFSxYFZTMWHga4xe7Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"tapable": {
|
"tapable": {
|
||||||
|
10
package.json
10
package.json
@ -27,8 +27,8 @@
|
|||||||
"webpack": "webpack",
|
"webpack": "webpack",
|
||||||
"watch": "webpack --watch",
|
"watch": "webpack --watch",
|
||||||
"concourse-build-electron": "npm run webpack",
|
"concourse-build-electron": "npm run webpack",
|
||||||
"concourse-test": "npx npm@6.14.5 test",
|
"concourse-test": "npx npm@6.14.8 test",
|
||||||
"concourse-test-electron": "npx npm@6.14.5 test"
|
"concourse-test-electron": "npx npm@6.14.8 test"
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
"hooks": {
|
"hooks": {
|
||||||
@ -71,13 +71,13 @@
|
|||||||
"css-loader": "^4.2.1",
|
"css-loader": "^4.2.1",
|
||||||
"d3": "^4.13.0",
|
"d3": "^4.13.0",
|
||||||
"debug": "^4.2.0",
|
"debug": "^4.2.0",
|
||||||
"electron": "9.3.3",
|
"electron": "9.4.0",
|
||||||
"electron-builder": "^22.9.1",
|
"electron-builder": "^22.9.1",
|
||||||
"electron-mocha": "^9.3.2",
|
"electron-mocha": "^9.3.2",
|
||||||
"electron-notarize": "^1.0.0",
|
"electron-notarize": "^1.0.0",
|
||||||
"electron-rebuild": "^2.3.2",
|
"electron-rebuild": "^2.3.2",
|
||||||
"electron-updater": "^4.3.5",
|
"electron-updater": "^4.3.5",
|
||||||
"etcher-sdk": "^5.1.10",
|
"etcher-sdk": "^5.1.11",
|
||||||
"file-loader": "^6.0.0",
|
"file-loader": "^6.0.0",
|
||||||
"husky": "^4.2.5",
|
"husky": "^4.2.5",
|
||||||
"immutable": "^3.8.1",
|
"immutable": "^3.8.1",
|
||||||
@ -103,7 +103,7 @@
|
|||||||
"string-replace-loader": "^2.3.0",
|
"string-replace-loader": "^2.3.0",
|
||||||
"styled-components": "^5.1.0",
|
"styled-components": "^5.1.0",
|
||||||
"sudo-prompt": "github:zvin/sudo-prompt#7cdede2f0da28fbcc2db48402d7d935f3a825c91",
|
"sudo-prompt": "github:zvin/sudo-prompt#7cdede2f0da28fbcc2db48402d7d935f3a825c91",
|
||||||
"sys-class-rgb-led": "^2.1.1",
|
"sys-class-rgb-led": "^3.0.0",
|
||||||
"tmp": "^0.2.1",
|
"tmp": "^0.2.1",
|
||||||
"ts-loader": "^8.0.0",
|
"ts-loader": "^8.0.0",
|
||||||
"ts-node": "^9.0.0",
|
"ts-node": "^9.0.0",
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
|
|
||||||
import * as settings from '../../../lib/gui/app/models/settings';
|
|
||||||
import * as progressStatus from '../../../lib/gui/app/modules/progress-status';
|
import * as progressStatus from '../../../lib/gui/app/modules/progress-status';
|
||||||
|
|
||||||
describe('Browser: progressStatus', function () {
|
describe('Browser: progressStatus', function () {
|
||||||
@ -30,8 +29,6 @@ describe('Browser: progressStatus', function () {
|
|||||||
eta: 15,
|
eta: 15,
|
||||||
speed: 100000000000000,
|
speed: 100000000000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
settings.set('unmountOnSuccess', true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should report 0% if percentage == 0 but speed != 0', function () {
|
it('should report 0% if percentage == 0 but speed != 0', function () {
|
||||||
@ -40,22 +37,14 @@ describe('Browser: progressStatus', function () {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle percentage == 0, flashing, unmountOnSuccess', function () {
|
it('should handle percentage == 0, flashing', function () {
|
||||||
this.state.speed = 0;
|
this.state.speed = 0;
|
||||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
||||||
'0% Flashing...',
|
'0% Flashing...',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle percentage == 0, flashing, !unmountOnSuccess', function () {
|
it('should handle percentage == 0, verifying', function () {
|
||||||
this.state.speed = 0;
|
|
||||||
settings.set('unmountOnSuccess', false);
|
|
||||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
|
||||||
'0% Flashing...',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle percentage == 0, verifying, unmountOnSuccess', function () {
|
|
||||||
this.state.speed = 0;
|
this.state.speed = 0;
|
||||||
this.state.type = 'verifying';
|
this.state.type = 'verifying';
|
||||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
||||||
@ -63,31 +52,14 @@ describe('Browser: progressStatus', function () {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle percentage == 0, verifying, !unmountOnSuccess', function () {
|
it('should handle percentage == 50, flashing', function () {
|
||||||
this.state.speed = 0;
|
|
||||||
this.state.type = 'verifying';
|
|
||||||
settings.set('unmountOnSuccess', false);
|
|
||||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
|
||||||
'0% Validating...',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle percentage == 50, flashing, unmountOnSuccess', function () {
|
|
||||||
this.state.percentage = 50;
|
this.state.percentage = 50;
|
||||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
||||||
'50% Flashing...',
|
'50% Flashing...',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle percentage == 50, flashing, !unmountOnSuccess', function () {
|
it('should handle percentage == 50, verifying', function () {
|
||||||
this.state.percentage = 50;
|
|
||||||
settings.set('unmountOnSuccess', false);
|
|
||||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
|
||||||
'50% Flashing...',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle percentage == 50, verifying, unmountOnSuccess', function () {
|
|
||||||
this.state.percentage = 50;
|
this.state.percentage = 50;
|
||||||
this.state.type = 'verifying';
|
this.state.type = 'verifying';
|
||||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
||||||
@ -95,31 +67,14 @@ describe('Browser: progressStatus', function () {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle percentage == 50, verifying, !unmountOnSuccess', function () {
|
it('should handle percentage == 100, flashing', function () {
|
||||||
this.state.percentage = 50;
|
|
||||||
this.state.type = 'verifying';
|
|
||||||
settings.set('unmountOnSuccess', false);
|
|
||||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
|
||||||
'50% Validating...',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle percentage == 100, flashing, unmountOnSuccess', function () {
|
|
||||||
this.state.percentage = 100;
|
this.state.percentage = 100;
|
||||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
||||||
'Finishing...',
|
'Finishing...',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle percentage == 100, flashing, !unmountOnSuccess', function () {
|
it('should handle percentage == 100, verifying', function () {
|
||||||
this.state.percentage = 100;
|
|
||||||
settings.set('unmountOnSuccess', false);
|
|
||||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
|
||||||
'Finishing...',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle percentage == 100, verifying, unmountOnSuccess', function () {
|
|
||||||
this.state.percentage = 100;
|
this.state.percentage = 100;
|
||||||
this.state.type = 'verifying';
|
this.state.type = 'verifying';
|
||||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
||||||
@ -127,9 +82,8 @@ describe('Browser: progressStatus', function () {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle percentage == 100, validatinf, !unmountOnSuccess', function () {
|
it('should handle percentage == 100, validating', function () {
|
||||||
this.state.percentage = 100;
|
this.state.percentage = 100;
|
||||||
settings.set('unmountOnSuccess', false);
|
|
||||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
||||||
'Finishing...',
|
'Finishing...',
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user