Convert local flash to oclif

Change-type: patch
Signed-off-by: Scott Lowe <scott@balena.io>
This commit is contained in:
Scott Lowe 2020-07-09 15:38:04 +02:00
parent 92d37ffcb7
commit 7ebc94c1e5
8 changed files with 186 additions and 147 deletions

View File

@ -137,7 +137,10 @@ const capitanoDoc = {
},
{
title: 'Local',
files: ['build/actions/local/index.js'],
files: [
'build/actions-oclif/local/flash.js',
'build/actions/local/index.js',
],
},
{
title: 'Deploy',

View File

@ -250,8 +250,8 @@ Users are encouraged to regularly update the balena CLI to the latest version.
- Local
- [local configure &#60;target&#62;](#local-configure-target)
- [local flash &#60;image&#62;](#local-flash-image)
- [local configure &#60;target&#62;](#local-configure-target)
- Deploy
@ -2161,6 +2161,36 @@ Examples:
# Local
## local flash &#60;image&#62;
Flash a balenaOS image to a drive.
Image file may be one of: .img|.zip|.gz|.bz2|.xz
If --drive is not specified, then it will interactively
show a list of available drives for selection.
Examples:
$ balena local flash path/to/balenaos.img
$ balena local flash path/to/balenaos.img --drive /dev/disk2
$ balena local flash path/to/balenaos.img --drive /dev/disk2 --yes
### Arguments
#### IMAGE
path to OS image
### Options
#### -d, --drive DRIVE
drive to flash
#### -y, --yes
answer "yes" to all questions (non interactive use)
## local configure &#60;target&#62;
Use this command to configure or reconfigure a balenaOS drive or image.
@ -2170,26 +2200,6 @@ Examples:
$ balena local configure /dev/sdc
$ balena local configure path/to/image.img
## local flash &#60;image&#62;
Use this command to flash a balenaOS image to a drive.
Examples:
$ balena local flash path/to/balenaos.img[.zip|.gz|.bz2|.xz]
$ balena local flash path/to/balenaos.img --drive /dev/disk2
$ balena local flash path/to/balenaos.img --drive /dev/disk2 --yes
### Options
#### --yes, -y
confirm non-interactively
#### --drive, -d &#60;drive&#62;
drive
# Deploy
## build [source]

View File

@ -0,0 +1,149 @@
/**
* @license
* Copyright 2016-2020 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { flags } from '@oclif/command';
import Command from '../../command';
import { ExpectedError } from '../../errors';
import * as cf from '../../utils/common-flags';
import {
getChalk,
getCliForm,
getVisuals,
stripIndent,
} from '../../utils/lazy';
import type * as SDK from 'etcher-sdk';
interface FlagsDef {
yes: boolean;
drive?: string;
help: void;
}
interface ArgsDef {
image: string;
}
export default class LocalFlashCmd extends Command {
public static description = stripIndent`
Flash an image to a drive.
Flash a balenaOS image to a drive.
Image file may be one of: .img|.zip|.gz|.bz2|.xz
If --drive is not specified, then it will interactively
show a list of available drives for selection.
`;
public static examples = [
'$ balena local flash path/to/balenaos.img',
'$ balena local flash path/to/balenaos.img --drive /dev/disk2',
'$ balena local flash path/to/balenaos.img --drive /dev/disk2 --yes',
];
public static args = [
{
name: 'image',
description: 'path to OS image',
required: true,
},
];
public static usage = 'local flash <image>';
public static flags: flags.Input<FlagsDef> = {
drive: flags.string({
description: 'drive to flash',
char: 'd',
}),
yes: cf.yes,
help: cf.help,
};
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
LocalFlashCmd,
);
const { sourceDestination, multiWrite } = await import('etcher-sdk');
const drive = await this.getDrive(options);
const yes =
options.yes ||
(await getCliForm().ask({
message: 'This will erase the selected drive. Are you sure?',
type: 'confirm',
name: 'yes',
default: false,
}));
if (!yes) {
console.log(getChalk().red.bold('Aborted image flash'));
process.exit(0);
}
const file = new sourceDestination.File(
params.image,
sourceDestination.File.OpenFlags.Read,
);
const source = await file.getInnerSource();
const visuals = getVisuals();
const progressBars: { [key: string]: any } = {
flashing: new visuals.Progress('Flashing'),
verifying: new visuals.Progress('Validating'),
};
await multiWrite.pipeSourceToDestinations(
source,
[drive],
(_, error) => {
// onFail
console.log(getChalk().red.bold(error.message));
},
(progress: SDK.multiWrite.MultiDestinationProgress) => {
// onProgress
progressBars[progress.type].update(progress);
},
true, // verify
);
}
async getDrive(options: {
drive?: string;
}): Promise<SDK.sourceDestination.BlockDevice> {
const drive = options.drive || (await getVisuals().drive('Select a drive'));
const sdk = await import('etcher-sdk');
const adapter = new sdk.scanner.adapters.BlockDeviceAdapter(() => false);
const scanner = new sdk.scanner.Scanner([adapter]);
await scanner.start();
try {
const d = scanner.getBy('device', drive);
if (
d === undefined ||
!(d instanceof sdk.sourceDestination.BlockDevice)
) {
throw new ExpectedError(`Drive not found: ${options.drive}`);
}
return d;
} finally {
scanner.stop();
}
}
}

View File

@ -1,122 +0,0 @@
/*
Copyright 2017-2020 Balena Ltd.
Licensed under the Apache License, Version 2.0 (the 'License');
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an 'AS IS' BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import type { CommandDefinition } from 'capitano';
import type * as SDK from 'etcher-sdk';
import {
getChalk,
getVisuals,
stripIndent,
getCliForm,
} from '../../utils/lazy';
import { ExpectedError } from '../../errors';
async function getDrive(options: {
drive?: string;
}): Promise<SDK.sourceDestination.BlockDevice> {
const drive = options.drive || (await getVisuals().drive('Select a drive'));
const sdk = await import('etcher-sdk');
const adapter = new sdk.scanner.adapters.BlockDeviceAdapter(() => false);
const scanner = new sdk.scanner.Scanner([adapter]);
await scanner.start();
try {
const d = scanner.getBy('device', drive);
if (d === undefined || !(d instanceof sdk.sourceDestination.BlockDevice)) {
throw new ExpectedError(`Drive not found: ${options.drive}`);
}
return d;
} finally {
scanner.stop();
}
}
export const flash: CommandDefinition<
{ image: string },
{ drive: string; yes: boolean }
> = {
signature: 'local flash <image>',
description: 'Flash an image to a drive',
help: stripIndent`
Use this command to flash a balenaOS image to a drive.
Examples:
$ balena local flash path/to/balenaos.img[.zip|.gz|.bz2|.xz]
$ balena local flash path/to/balenaos.img --drive /dev/disk2
$ balena local flash path/to/balenaos.img --drive /dev/disk2 --yes
`,
options: [
{
signature: 'yes',
boolean: true,
description: 'confirm non-interactively',
alias: 'y',
},
{
signature: 'drive',
parameter: 'drive',
description: 'drive',
alias: 'd',
},
],
async action(params, options) {
const { sourceDestination, multiWrite } = await import('etcher-sdk');
const drive = await getDrive(options);
const yes =
options.yes ||
(await getCliForm().ask({
message: 'This will erase the selected drive. Are you sure?',
type: 'confirm',
name: 'yes',
default: false,
}));
if (yes !== true) {
console.log(getChalk().red.bold('Aborted image flash'));
process.exit(0);
}
const file = new sourceDestination.File(
params.image,
sourceDestination.File.OpenFlags.Read,
);
const source = await file.getInnerSource();
const visuals = getVisuals();
const progressBars: { [key: string]: any } = {
flashing: new visuals.Progress('Flashing'),
verifying: new visuals.Progress('Validating'),
};
await multiWrite.pipeSourceToDestinations(
source,
[drive],
(_, error) => {
// onFail
console.log(getChalk().red.bold(error.message));
},
(progress: SDK.multiWrite.MultiDestinationProgress) => {
// onProgress
progressBars[progress.type].update(progress);
},
true, // verify
);
},
};

View File

@ -15,4 +15,3 @@ limitations under the License.
*/
export { configure } from './configure';
export { flash } from './flash';

View File

@ -64,7 +64,6 @@ capitano.command(actions.preload);
// ---------- Local balenaOS Module ----------
capitano.command(actions.local.configure);
capitano.command(actions.local.flash);
// ------------ Local build and deploy -------
capitano.command(actions.build);

View File

@ -173,6 +173,7 @@ export const convertedCommands = [
'key:add',
'key:rm',
'leave',
'local:flash',
'login',
'logout',
'logs',

View File

@ -76,7 +76,7 @@ Additional commands:
key rm <id> remove an SSH key from balenaCloud
keys list the SSH keys in balenaCloud
local configure <target> (Re)configure a balenaOS drive or image
local flash <image> Flash an image to a drive
local flash <image> flash an image to a drive
logout logout from balena
note <|note> set a device note
os build-config <image> <device-type> build the OS config and save it to the JSON file