mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-28 13:46:33 +00:00
Add skip function to validation
Change-type: patch Changelog-entry: Add skip function to validation Signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
This commit is contained in:
parent
8ff8b02f37
commit
74a78076cf
@ -23,7 +23,7 @@ import * as selectionState from '../../models/selection-state';
|
|||||||
import { Actions, store } from '../../models/store';
|
import { Actions, store } from '../../models/store';
|
||||||
import * as analytics from '../../modules/analytics';
|
import * as analytics from '../../modules/analytics';
|
||||||
import { FlashAnother } from '../flash-another/flash-another';
|
import { FlashAnother } from '../flash-another/flash-another';
|
||||||
import { FlashResults } from '../flash-results/flash-results';
|
import { FlashResults, FlashError } from '../flash-results/flash-results';
|
||||||
import { SafeWebview } from '../safe-webview/safe-webview';
|
import { SafeWebview } from '../safe-webview/safe-webview';
|
||||||
|
|
||||||
function restart(goToMain: () => void) {
|
function restart(goToMain: () => void) {
|
||||||
@ -41,8 +41,33 @@ function restart(goToMain: () => void) {
|
|||||||
|
|
||||||
function FinishPage({ goToMain }: { goToMain: () => void }) {
|
function FinishPage({ goToMain }: { goToMain: () => void }) {
|
||||||
const [webviewShowing, setWebviewShowing] = React.useState(false);
|
const [webviewShowing, setWebviewShowing] = React.useState(false);
|
||||||
const errors = flashState.getFlashResults().results?.errors;
|
let errors = flashState.getFlashResults().results?.errors;
|
||||||
const results = flashState.getFlashResults().results || {};
|
if (errors === undefined) {
|
||||||
|
errors = (store.getState().toJS().failedDevicePaths || []).map(
|
||||||
|
([, error]: [string, FlashError]) => ({
|
||||||
|
...error,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const {
|
||||||
|
averageSpeed,
|
||||||
|
blockmappedSize,
|
||||||
|
bytesWritten,
|
||||||
|
failed,
|
||||||
|
size,
|
||||||
|
} = flashState.getFlashState();
|
||||||
|
const {
|
||||||
|
skip,
|
||||||
|
results = {
|
||||||
|
bytesWritten,
|
||||||
|
sourceMetadata: {
|
||||||
|
size,
|
||||||
|
blockmappedSize,
|
||||||
|
},
|
||||||
|
averageFlashingSpeed: averageSpeed,
|
||||||
|
devices: { failed, successful: 0 },
|
||||||
|
},
|
||||||
|
} = flashState.getFlashResults();
|
||||||
return (
|
return (
|
||||||
<Flex height="100%" justifyContent="space-between">
|
<Flex height="100%" justifyContent="space-between">
|
||||||
<Flex
|
<Flex
|
||||||
@ -61,6 +86,7 @@ function FinishPage({ goToMain }: { goToMain: () => void }) {
|
|||||||
<FlashResults
|
<FlashResults
|
||||||
image={selectionState.getImageName()}
|
image={selectionState.getImageName()}
|
||||||
results={results}
|
results={results}
|
||||||
|
skip={skip}
|
||||||
errors={errors}
|
errors={errors}
|
||||||
mb="32px"
|
mb="32px"
|
||||||
/>
|
/>
|
||||||
|
@ -57,7 +57,7 @@ const ErrorsTable = styled(({ refFn, ...props }) => {
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface FlashError extends Error {
|
export interface FlashError extends Error {
|
||||||
description: string;
|
description: string;
|
||||||
device: string;
|
device: string;
|
||||||
code: string;
|
code: string;
|
||||||
@ -91,10 +91,12 @@ export function FlashResults({
|
|||||||
image = '',
|
image = '',
|
||||||
errors,
|
errors,
|
||||||
results,
|
results,
|
||||||
|
skip,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
image?: string;
|
image?: string;
|
||||||
errors: FlashError[];
|
errors: FlashError[];
|
||||||
|
skip: boolean;
|
||||||
results: {
|
results: {
|
||||||
bytesWritten: number;
|
bytesWritten: number;
|
||||||
sourceMetadata: {
|
sourceMetadata: {
|
||||||
@ -142,6 +144,7 @@ export function FlashResults({
|
|||||||
<Txt fontSize={24} color="#fff" mb="17px">
|
<Txt fontSize={24} color="#fff" mb="17px">
|
||||||
Flash Complete!
|
Flash Complete!
|
||||||
</Txt>
|
</Txt>
|
||||||
|
{skip ? <Flex color="#7e8085">Validation has been skipped</Flex> : null}
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex flexDirection="column" color="#7e8085">
|
<Flex flexDirection="column" color="#7e8085">
|
||||||
{Object.entries(results.devices).map(([type, quantity]) => {
|
{Object.entries(results.devices).map(([type, quantity]) => {
|
||||||
|
@ -49,7 +49,7 @@ interface ProgressButtonProps {
|
|||||||
percentage: number;
|
percentage: number;
|
||||||
position: number;
|
position: number;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
cancel: () => void;
|
cancel: (type: string) => void;
|
||||||
callback: () => void;
|
callback: () => void;
|
||||||
warning?: boolean;
|
warning?: boolean;
|
||||||
}
|
}
|
||||||
@ -60,11 +60,14 @@ const colors = {
|
|||||||
verifying: '#1ac135',
|
verifying: '#1ac135',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const CancelButton = styled((props) => (
|
const CancelButton = styled(({ type, onClick, ...props }) => {
|
||||||
<Button plain {...props}>
|
const status = type === 'verifying' ? 'Skip' : 'Cancel';
|
||||||
Cancel
|
return (
|
||||||
|
<Button plain onClick={() => onClick(status)} {...props}>
|
||||||
|
{status}
|
||||||
</Button>
|
</Button>
|
||||||
))`
|
);
|
||||||
|
})`
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
&&& {
|
&&& {
|
||||||
width: auto;
|
width: auto;
|
||||||
@ -75,10 +78,13 @@ const CancelButton = styled((props) => (
|
|||||||
|
|
||||||
export class ProgressButton extends React.PureComponent<ProgressButtonProps> {
|
export class ProgressButton extends React.PureComponent<ProgressButtonProps> {
|
||||||
public render() {
|
public render() {
|
||||||
|
const type = this.props.type;
|
||||||
|
const percentage = this.props.percentage;
|
||||||
|
const warning = this.props.warning;
|
||||||
const { status, position } = fromFlashState({
|
const { status, position } = fromFlashState({
|
||||||
type: this.props.type,
|
type,
|
||||||
|
percentage,
|
||||||
position: this.props.position,
|
position: this.props.position,
|
||||||
percentage: this.props.percentage,
|
|
||||||
});
|
});
|
||||||
if (this.props.active) {
|
if (this.props.active) {
|
||||||
return (
|
return (
|
||||||
@ -96,21 +102,24 @@ export class ProgressButton extends React.PureComponent<ProgressButtonProps> {
|
|||||||
>
|
>
|
||||||
<Flex>
|
<Flex>
|
||||||
<Txt color="#fff">{status} </Txt>
|
<Txt color="#fff">{status} </Txt>
|
||||||
<Txt color={colors[this.props.type]}>{position}</Txt>
|
<Txt color={colors[type]}>{position}</Txt>
|
||||||
</Flex>
|
</Flex>
|
||||||
<CancelButton onClick={this.props.cancel} color="#00aeef" />
|
{type && (
|
||||||
</Flex>
|
<CancelButton
|
||||||
<FlashProgressBar
|
type={type}
|
||||||
background={colors[this.props.type]}
|
onClick={this.props.cancel}
|
||||||
value={this.props.percentage}
|
color="#00aeef"
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
<FlashProgressBar background={colors[type]} value={percentage} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<StepButton
|
<StepButton
|
||||||
primary={!this.props.warning}
|
primary={!warning}
|
||||||
warning={this.props.warning}
|
warning={warning}
|
||||||
onClick={this.props.callback}
|
onClick={this.props.callback}
|
||||||
disabled={this.props.disabled}
|
disabled={this.props.disabled}
|
||||||
style={{
|
style={{
|
||||||
|
@ -75,14 +75,25 @@ export function setDevicePaths(devicePaths: string[]) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addFailedDevicePath(devicePath: string) {
|
export function addFailedDevicePath({
|
||||||
const failedDevicePathsSet = new Set(
|
device,
|
||||||
|
error,
|
||||||
|
}: {
|
||||||
|
device: sdk.scanner.adapters.DrivelistDrive;
|
||||||
|
error: Error;
|
||||||
|
}) {
|
||||||
|
const failedDevicePathsMap = new Map(
|
||||||
store.getState().toJS().failedDevicePaths,
|
store.getState().toJS().failedDevicePaths,
|
||||||
);
|
);
|
||||||
failedDevicePathsSet.add(devicePath);
|
failedDevicePathsMap.set(device.device, {
|
||||||
|
description: device.description,
|
||||||
|
device: device.device,
|
||||||
|
devicePath: device.devicePath,
|
||||||
|
...error,
|
||||||
|
});
|
||||||
store.dispatch({
|
store.dispatch({
|
||||||
type: Actions.SET_FAILED_DEVICE_PATHS,
|
type: Actions.SET_FAILED_DEVICE_PATHS,
|
||||||
data: Array.from(failedDevicePathsSet),
|
data: Array.from(failedDevicePathsMap),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,12 +188,15 @@ function stateObserver(state: typeof DEFAULT_STATE) {
|
|||||||
} else {
|
} else {
|
||||||
selectedDrivesPaths = s.devicePaths;
|
selectedDrivesPaths = s.devicePaths;
|
||||||
}
|
}
|
||||||
|
const failedDevicePaths = s.failedDevicePaths.map(
|
||||||
|
([devicePath]: [string]) => devicePath,
|
||||||
|
);
|
||||||
const newLedsState = {
|
const newLedsState = {
|
||||||
step,
|
step,
|
||||||
sourceDrive: sourceDrivePath,
|
sourceDrive: sourceDrivePath,
|
||||||
availableDrives: availableDrivesPaths,
|
availableDrives: availableDrivesPaths,
|
||||||
selectedDrives: selectedDrivesPaths,
|
selectedDrives: selectedDrivesPaths,
|
||||||
failedDrives: s.failedDevicePaths,
|
failedDrives: failedDevicePaths,
|
||||||
};
|
};
|
||||||
if (!_.isEqual(newLedsState, ledsState)) {
|
if (!_.isEqual(newLedsState, ledsState)) {
|
||||||
updateLeds(newLedsState);
|
updateLeds(newLedsState);
|
||||||
|
@ -295,6 +295,7 @@ function storeReducer(
|
|||||||
|
|
||||||
_.defaults(action.data, {
|
_.defaults(action.data, {
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
|
skip: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!_.isBoolean(action.data.cancelled)) {
|
if (!_.isBoolean(action.data.cancelled)) {
|
||||||
@ -337,8 +338,7 @@ function storeReducer(
|
|||||||
|
|
||||||
return state
|
return state
|
||||||
.set('isFlashing', false)
|
.set('isFlashing', false)
|
||||||
.set('flashResults', Immutable.fromJS(action.data))
|
.set('flashResults', Immutable.fromJS(action.data));
|
||||||
.set('flashState', DEFAULT_STATE.get('flashState'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.SELECT_TARGET: {
|
case Actions.SELECT_TARGET: {
|
||||||
|
@ -131,6 +131,7 @@ function writerEnv() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface FlashResults {
|
interface FlashResults {
|
||||||
|
skip?: boolean;
|
||||||
cancelled?: boolean;
|
cancelled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,6 +141,7 @@ async function performWrite(
|
|||||||
onProgress: sdk.multiWrite.OnProgressFunction,
|
onProgress: sdk.multiWrite.OnProgressFunction,
|
||||||
): Promise<{ cancelled?: boolean }> {
|
): Promise<{ cancelled?: boolean }> {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
|
let skip = false;
|
||||||
ipc.serve();
|
ipc.serve();
|
||||||
const {
|
const {
|
||||||
unmountOnSuccess,
|
unmountOnSuccess,
|
||||||
@ -171,7 +173,7 @@ async function performWrite(
|
|||||||
|
|
||||||
ipc.server.on('fail', ({ device, error }) => {
|
ipc.server.on('fail', ({ device, error }) => {
|
||||||
if (device.devicePath) {
|
if (device.devicePath) {
|
||||||
flashState.addFailedDevicePath(device.devicePath);
|
flashState.addFailedDevicePath({ device, error });
|
||||||
}
|
}
|
||||||
handleErrorLogging(error, analyticsData);
|
handleErrorLogging(error, analyticsData);
|
||||||
});
|
});
|
||||||
@ -188,6 +190,11 @@ async function performWrite(
|
|||||||
cancelled = true;
|
cancelled = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipc.server.on('skip', () => {
|
||||||
|
terminateServer();
|
||||||
|
skip = true;
|
||||||
|
});
|
||||||
|
|
||||||
ipc.server.on('state', onProgress);
|
ipc.server.on('state', onProgress);
|
||||||
|
|
||||||
ipc.server.on('ready', (_data, socket) => {
|
ipc.server.on('ready', (_data, socket) => {
|
||||||
@ -213,6 +220,7 @@ async function performWrite(
|
|||||||
environment: env,
|
environment: env,
|
||||||
});
|
});
|
||||||
flashResults.cancelled = cancelled || results.cancelled;
|
flashResults.cancelled = cancelled || results.cancelled;
|
||||||
|
flashResults.skip = skip;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// This happens when the child is killed using SIGKILL
|
// This happens when the child is killed using SIGKILL
|
||||||
const SIGKILL_EXIT_CODE = 137;
|
const SIGKILL_EXIT_CODE = 137;
|
||||||
@ -229,6 +237,7 @@ async function performWrite(
|
|||||||
// This likely means the child died halfway through
|
// This likely means the child died halfway through
|
||||||
if (
|
if (
|
||||||
!flashResults.cancelled &&
|
!flashResults.cancelled &&
|
||||||
|
!flashResults.skip &&
|
||||||
!_.get(flashResults, ['results', 'bytesWritten'])
|
!_.get(flashResults, ['results', 'bytesWritten'])
|
||||||
) {
|
) {
|
||||||
reject(
|
reject(
|
||||||
@ -286,8 +295,7 @@ export async function flash(
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
flashState.unsetFlashingFlag({ cancelled: false, errorCode: error.code });
|
flashState.unsetFlashingFlag({ cancelled: false, errorCode: error.code });
|
||||||
windowProgress.clear();
|
windowProgress.clear();
|
||||||
let { results } = flashState.getFlashResults();
|
const { results = {} } = flashState.getFlashResults();
|
||||||
results = results || {};
|
|
||||||
const eventData = {
|
const eventData = {
|
||||||
...analyticsData,
|
...analyticsData,
|
||||||
errors: results.errors,
|
errors: results.errors,
|
||||||
@ -306,7 +314,7 @@ export async function flash(
|
|||||||
};
|
};
|
||||||
analytics.logEvent('Elevation cancelled', eventData);
|
analytics.logEvent('Elevation cancelled', eventData);
|
||||||
} else {
|
} else {
|
||||||
const { results } = flashState.getFlashResults();
|
const { results = {} } = flashState.getFlashResults();
|
||||||
const eventData = {
|
const eventData = {
|
||||||
...analyticsData,
|
...analyticsData,
|
||||||
errors: results.errors,
|
errors: results.errors,
|
||||||
@ -322,7 +330,8 @@ export async function flash(
|
|||||||
/**
|
/**
|
||||||
* @summary Cancel write operation
|
* @summary Cancel write operation
|
||||||
*/
|
*/
|
||||||
export async function cancel() {
|
export async function cancel(type: string) {
|
||||||
|
const status = type.toLowerCase();
|
||||||
const drives = selectionState.getSelectedDevices();
|
const drives = selectionState.getSelectedDevices();
|
||||||
const analyticsData = {
|
const analyticsData = {
|
||||||
image: selectionState.getImagePath(),
|
image: selectionState.getImagePath(),
|
||||||
@ -332,7 +341,7 @@ export async function cancel() {
|
|||||||
flashInstanceUuid: flashState.getFlashUuid(),
|
flashInstanceUuid: flashState.getFlashUuid(),
|
||||||
unmountOnSuccess: await settings.get('unmountOnSuccess'),
|
unmountOnSuccess: await settings.get('unmountOnSuccess'),
|
||||||
validateWriteOnSuccess: await settings.get('validateWriteOnSuccess'),
|
validateWriteOnSuccess: await settings.get('validateWriteOnSuccess'),
|
||||||
status: 'cancel',
|
status,
|
||||||
};
|
};
|
||||||
analytics.logEvent('Cancel', analyticsData);
|
analytics.logEvent('Cancel', analyticsData);
|
||||||
|
|
||||||
@ -342,7 +351,7 @@ export async function cancel() {
|
|||||||
// @ts-ignore (no Server.sockets in @types/node-ipc)
|
// @ts-ignore (no Server.sockets in @types/node-ipc)
|
||||||
const [socket] = ipc.server.sockets;
|
const [socket] = ipc.server.sockets;
|
||||||
if (socket !== undefined) {
|
if (socket !== undefined) {
|
||||||
ipc.server.emit(socket, 'cancel');
|
ipc.server.emit(socket, status);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
analytics.logException(error);
|
analytics.logException(error);
|
||||||
|
@ -82,14 +82,12 @@ async function flashImageToDrive(
|
|||||||
try {
|
try {
|
||||||
await imageWriter.flash(image, drives);
|
await imageWriter.flash(image, drives);
|
||||||
if (!flashState.wasLastFlashCancelled()) {
|
if (!flashState.wasLastFlashCancelled()) {
|
||||||
const flashResults: any = flashState.getFlashResults();
|
const {
|
||||||
|
results = { devices: { successful: 0, failed: 0 } },
|
||||||
|
} = flashState.getFlashResults();
|
||||||
notification.send(
|
notification.send(
|
||||||
'Flash complete!',
|
'Flash complete!',
|
||||||
messages.info.flashComplete(
|
messages.info.flashComplete(basename, drives as any, results.devices),
|
||||||
basename,
|
|
||||||
drives as any,
|
|
||||||
flashResults.results.devices,
|
|
||||||
),
|
|
||||||
iconPath,
|
iconPath,
|
||||||
);
|
);
|
||||||
goToSuccess();
|
goToSuccess();
|
||||||
|
@ -208,8 +208,17 @@ ipc.connectTo(IPC_SERVER_ID, () => {
|
|||||||
terminate(exitCode);
|
terminate(exitCode);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onSkip = async () => {
|
||||||
|
log('Skip validation');
|
||||||
|
ipc.of[IPC_SERVER_ID].emit('skip');
|
||||||
|
await delay(DISCONNECT_DELAY);
|
||||||
|
terminate(exitCode);
|
||||||
|
};
|
||||||
|
|
||||||
ipc.of[IPC_SERVER_ID].on('cancel', onAbort);
|
ipc.of[IPC_SERVER_ID].on('cancel', onAbort);
|
||||||
|
|
||||||
|
ipc.of[IPC_SERVER_ID].on('skip', onSkip);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Failure handler (non-fatal errors)
|
* @summary Failure handler (non-fatal errors)
|
||||||
* @param {SourceDestination} destination - destination
|
* @param {SourceDestination} destination - destination
|
||||||
|
Loading…
x
Reference in New Issue
Block a user