mirror of
https://github.com/balena-io/balena-cli.git
synced 2024-12-18 13:26:24 +00:00
Convert os download
to oclif, typescript
Change-type: patch Signed-off-by: Scott Lowe <scott@balena.io>
This commit is contained in:
parent
c65dafd2ff
commit
1c17572db0
@ -121,6 +121,7 @@ const capitanoDoc = {
|
||||
'build/actions/os.js',
|
||||
'build/actions-oclif/os/configure.js',
|
||||
'build/actions-oclif/os/versions.js',
|
||||
'build/actions-oclif/os/download.js',
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -1538,14 +1538,14 @@ device type
|
||||
|
||||
## os download <type>
|
||||
|
||||
Use this command to download an unconfigured os image for a certain device type.
|
||||
Download an unconfigured OS image for a certain device type.
|
||||
Check available types with `balena devices supported`
|
||||
|
||||
> Note: Currently this command only works with balenaCloud, not openBalena.
|
||||
> If using openBalena, please download the OS from: https://www.balena.io/os/
|
||||
Note: Currently this command only works with balenaCloud, not openBalena.
|
||||
If using openBalena, please download the OS from: https://www.balena.io/os/
|
||||
|
||||
If version is not specified the newest stable (non-pre-release) version of OS
|
||||
is downloaded if available, or the newest version otherwise (if all existing
|
||||
is downloaded (if available), otherwise the newest version (if all existing
|
||||
versions for the given device type are pre-release).
|
||||
|
||||
You can pass `--version menu` to pick the OS version from the interactive menu
|
||||
@ -1560,13 +1560,19 @@ Examples:
|
||||
$ balena os download raspberrypi3 -o ../foo/bar/raspberry-pi.img --version default
|
||||
$ balena os download raspberrypi3 -o ../foo/bar/raspberry-pi.img --version menu
|
||||
|
||||
### Arguments
|
||||
|
||||
#### TYPE
|
||||
|
||||
the device type
|
||||
|
||||
### Options
|
||||
|
||||
#### --output, -o <output>
|
||||
#### -o, --output OUTPUT
|
||||
|
||||
output path
|
||||
|
||||
#### --version <version>
|
||||
#### --version VERSION
|
||||
|
||||
exact version number, or a valid semver range,
|
||||
or 'latest' (includes pre-releases),
|
||||
|
96
lib/actions-oclif/os/download.ts
Normal file
96
lib/actions-oclif/os/download.ts
Normal file
@ -0,0 +1,96 @@
|
||||
/**
|
||||
* @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 * as cf from '../../utils/common-flags';
|
||||
import { stripIndent } from '../../utils/lazy';
|
||||
|
||||
interface FlagsDef {
|
||||
output: string;
|
||||
version?: string;
|
||||
help: void;
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
type: string;
|
||||
}
|
||||
|
||||
export default class OsDownloadCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Download an unconfigured OS image.
|
||||
|
||||
Download an unconfigured OS image for a certain device type.
|
||||
Check available types with \`balena devices supported\`
|
||||
|
||||
Note: Currently this command only works with balenaCloud, not openBalena.
|
||||
If using openBalena, please download the OS from: https://www.balena.io/os/
|
||||
|
||||
If version is not specified the newest stable (non-pre-release) version of OS
|
||||
is downloaded (if available), otherwise the newest version (if all existing
|
||||
versions for the given device type are pre-release).
|
||||
|
||||
You can pass \`--version menu\` to pick the OS version from the interactive menu
|
||||
of all available versions.
|
||||
`;
|
||||
public static examples = [
|
||||
'$ balena os download raspberrypi3 -o ../foo/bar/raspberry-pi.img',
|
||||
'$ balena os download raspberrypi3 -o ../foo/bar/raspberry-pi.img --version 1.24.1',
|
||||
'$ balena os download raspberrypi3 -o ../foo/bar/raspberry-pi.img --version ^1.20.0',
|
||||
'$ balena os download raspberrypi3 -o ../foo/bar/raspberry-pi.img --version latest',
|
||||
'$ balena os download raspberrypi3 -o ../foo/bar/raspberry-pi.img --version default',
|
||||
'$ balena os download raspberrypi3 -o ../foo/bar/raspberry-pi.img --version menu',
|
||||
];
|
||||
|
||||
public static args = [
|
||||
{
|
||||
name: 'type',
|
||||
description: 'the device type',
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
|
||||
public static usage = 'os download <type>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
output: flags.string({
|
||||
description: 'output path',
|
||||
char: 'o',
|
||||
required: true,
|
||||
}),
|
||||
version: flags.string({
|
||||
description: stripIndent`
|
||||
exact version number, or a valid semver range,
|
||||
or 'latest' (includes pre-releases),
|
||||
or 'default' (excludes pre-releases if at least one stable version is available),
|
||||
or 'recommended' (excludes pre-releases, will fail if only pre-release versions are available),
|
||||
or 'menu' (will show the interactive menu)
|
||||
`,
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public async run() {
|
||||
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
|
||||
OsDownloadCmd,
|
||||
);
|
||||
|
||||
const { downloadOSImage } = await import('../../utils/cloud');
|
||||
|
||||
await downloadOSImage(params.type, options.output, options.version);
|
||||
}
|
||||
}
|
@ -15,146 +15,8 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import * as commandOptions from './command-options';
|
||||
|
||||
import * as _ from 'lodash';
|
||||
import { getBalenaSdk, getVisuals, getCliForm } from '../utils/lazy';
|
||||
|
||||
const formatVersion = function (v, isRecommended) {
|
||||
let result = `v${v}`;
|
||||
if (isRecommended) {
|
||||
result += ' (recommended)';
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const resolveVersion = function (deviceType, version) {
|
||||
if (version !== 'menu') {
|
||||
if (version[0] === 'v') {
|
||||
version = version.slice(1);
|
||||
}
|
||||
return Promise.resolve(version);
|
||||
}
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
return balena.models.os
|
||||
.getSupportedVersions(deviceType)
|
||||
.then(function ({ versions: vs, recommended }) {
|
||||
const choices = vs.map((v) => ({
|
||||
value: v,
|
||||
name: formatVersion(v, v === recommended),
|
||||
}));
|
||||
|
||||
return getCliForm().ask({
|
||||
message: 'Select the OS version:',
|
||||
type: 'list',
|
||||
choices,
|
||||
default: recommended,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const download = {
|
||||
signature: 'os download <type>',
|
||||
description: 'download an unconfigured os image',
|
||||
help: `\
|
||||
Use this command to download an unconfigured os image for a certain device type.
|
||||
Check available types with \`balena devices supported\`
|
||||
|
||||
> Note: Currently this command only works with balenaCloud, not openBalena.
|
||||
> If using openBalena, please download the OS from: https://www.balena.io/os/
|
||||
|
||||
If version is not specified the newest stable (non-pre-release) version of OS
|
||||
is downloaded if available, or the newest version otherwise (if all existing
|
||||
versions for the given device type are pre-release).
|
||||
|
||||
You can pass \`--version menu\` to pick the OS version from the interactive menu
|
||||
of all available versions.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena os download raspberrypi3 -o ../foo/bar/raspberry-pi.img
|
||||
$ balena os download raspberrypi3 -o ../foo/bar/raspberry-pi.img --version 1.24.1
|
||||
$ balena os download raspberrypi3 -o ../foo/bar/raspberry-pi.img --version ^1.20.0
|
||||
$ balena os download raspberrypi3 -o ../foo/bar/raspberry-pi.img --version latest
|
||||
$ balena os download raspberrypi3 -o ../foo/bar/raspberry-pi.img --version default
|
||||
$ balena os download raspberrypi3 -o ../foo/bar/raspberry-pi.img --version menu\
|
||||
`,
|
||||
options: [
|
||||
{
|
||||
signature: 'output',
|
||||
description: 'output path',
|
||||
parameter: 'output',
|
||||
alias: 'o',
|
||||
required: 'You have to specify the output location',
|
||||
},
|
||||
commandOptions.osVersionOrSemver,
|
||||
],
|
||||
action(params, options) {
|
||||
const Bluebird = require('bluebird');
|
||||
const unzip = require('node-unzip-2');
|
||||
const fs = require('fs');
|
||||
const manager = require('balena-image-manager');
|
||||
|
||||
console.info(`Getting device operating system for ${params.type}`);
|
||||
|
||||
let displayVersion = '';
|
||||
return Bluebird.try(function () {
|
||||
if (!options.version) {
|
||||
console.warn(`OS version is not specified, using the default version: \
|
||||
the newest stable (non-pre-release) version if available, \
|
||||
or the newest version otherwise (if all existing \
|
||||
versions for the given device type are pre-release).`);
|
||||
return 'default';
|
||||
}
|
||||
return resolveVersion(params.type, options.version);
|
||||
})
|
||||
.then(function (version) {
|
||||
if (version !== 'default') {
|
||||
displayVersion = ` ${version}`;
|
||||
}
|
||||
return manager.get(params.type, version);
|
||||
})
|
||||
.then(async function (stream) {
|
||||
const visuals = getVisuals();
|
||||
const bar = new visuals.Progress(
|
||||
`Downloading Device OS${displayVersion}`,
|
||||
);
|
||||
const spinner = new visuals.Spinner(
|
||||
`Downloading Device OS${displayVersion} (size unknown)`,
|
||||
);
|
||||
|
||||
stream.on('progress', function (state) {
|
||||
if (state != null) {
|
||||
return bar.update(state);
|
||||
} else {
|
||||
return spinner.start();
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('end', () => {
|
||||
spinner.stop();
|
||||
});
|
||||
|
||||
// We completely rely on the `mime` custom property
|
||||
// to make this decision.
|
||||
// The actual stream should be checked instead.
|
||||
let output;
|
||||
if (stream.mime === 'application/zip') {
|
||||
output = unzip.Extract({ path: options.output });
|
||||
} else {
|
||||
output = fs.createWriteStream(options.output);
|
||||
}
|
||||
|
||||
const streamToPromise = await import('stream-to-promise');
|
||||
await streamToPromise(stream.pipe(output));
|
||||
return options.output;
|
||||
})
|
||||
.tap(() => {
|
||||
console.info('The image was downloaded successfully');
|
||||
});
|
||||
},
|
||||
};
|
||||
import { getCliForm } from '../utils/lazy';
|
||||
|
||||
const buildConfigForDeviceType = function (deviceType, advanced) {
|
||||
if (advanced == null) {
|
||||
|
@ -47,7 +47,6 @@ capitano.globalOption({
|
||||
capitano.command(actions.help.help);
|
||||
|
||||
// ---------- OS Module ----------
|
||||
capitano.command(actions.os.download);
|
||||
capitano.command(actions.os.buildConfig);
|
||||
capitano.command(actions.os.initialize);
|
||||
|
||||
|
@ -181,6 +181,7 @@ export const convertedCommands = [
|
||||
'note',
|
||||
'os:configure',
|
||||
'os:versions',
|
||||
'os:download',
|
||||
'scan',
|
||||
'settings',
|
||||
'ssh',
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
import type * as SDK from 'balena-sdk';
|
||||
import * as _ from 'lodash';
|
||||
import { stripIndent } from './lazy';
|
||||
import { getBalenaSdk, getCliForm, getVisuals, stripIndent } from './lazy';
|
||||
|
||||
import { ExpectedError } from '../errors';
|
||||
|
||||
@ -106,3 +106,104 @@ export const getDeviceAndMaybeAppFromUUID = _.memoize(
|
||||
// Memoize the call based on UUID
|
||||
(_sdk, deviceUUID) => deviceUUID,
|
||||
);
|
||||
|
||||
/**
|
||||
* Download balenaOS image for the specified `deviceType`.
|
||||
* `OSVersion` may be one of:
|
||||
* - exact version number,
|
||||
* - valid semver range,
|
||||
* - `latest` (includes pre-releases),
|
||||
* - `default` (excludes pre-releases if at least one stable version is available),
|
||||
* - `recommended` (excludes pre-releases, will fail if only pre-release versions are available),
|
||||
* - `menu` (will show the interactive menu )
|
||||
* If not provided, OSVersion will be set to `default`
|
||||
*
|
||||
* @param deviceType
|
||||
* @param outputPath
|
||||
* @param OSVersion
|
||||
*/
|
||||
export async function downloadOSImage(
|
||||
deviceType: string,
|
||||
outputPath: string,
|
||||
OSVersion?: string,
|
||||
) {
|
||||
console.info(`Getting device operating system for ${deviceType}`);
|
||||
|
||||
if (!OSVersion) {
|
||||
console.warn(
|
||||
'Using default OS version: the latest stable version, or the latest version for pre-release devices',
|
||||
);
|
||||
}
|
||||
|
||||
OSVersion = OSVersion
|
||||
? await resolveOSVersion(deviceType, OSVersion)
|
||||
: 'default';
|
||||
|
||||
const displayVersion = OSVersion === 'default' ? '' : ` ${OSVersion}`;
|
||||
|
||||
const manager = await import('balena-image-manager');
|
||||
const stream = await manager.get(deviceType, OSVersion);
|
||||
|
||||
const visuals = getVisuals();
|
||||
const bar = new visuals.Progress(`Downloading Device OS${displayVersion}`);
|
||||
const spinner = new visuals.Spinner(
|
||||
`Downloading Device OS${displayVersion} (size unknown)`,
|
||||
);
|
||||
|
||||
stream.on('progress', (state: any) => {
|
||||
if (state != null) {
|
||||
return bar.update(state);
|
||||
} else {
|
||||
return spinner.start();
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('end', () => {
|
||||
spinner.stop();
|
||||
});
|
||||
|
||||
// We completely rely on the `mime` custom property
|
||||
// to make this decision.
|
||||
// The actual stream should be checked instead.
|
||||
let output;
|
||||
if (stream.mime === 'application/zip') {
|
||||
const unzip = await import('node-unzip-2');
|
||||
output = unzip.Extract({ path: outputPath });
|
||||
} else {
|
||||
const fs = await import('fs');
|
||||
output = fs.createWriteStream(outputPath);
|
||||
}
|
||||
|
||||
const streamToPromise = await import('stream-to-promise');
|
||||
await streamToPromise(stream.pipe(output));
|
||||
|
||||
console.info('The image was downloaded successfully');
|
||||
|
||||
return outputPath;
|
||||
}
|
||||
|
||||
async function resolveOSVersion(deviceType: string, version: string) {
|
||||
if (version !== 'menu') {
|
||||
if (version[0] === 'v') {
|
||||
version = version.slice(1);
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
const {
|
||||
versions: vs,
|
||||
recommended,
|
||||
} = await getBalenaSdk().models.os.getSupportedVersions(deviceType);
|
||||
|
||||
const choices = vs.map((v) => ({
|
||||
value: v,
|
||||
name: `v${v}` + (v === recommended ? ' (recommended)' : ''),
|
||||
}));
|
||||
|
||||
return getCliForm().ask({
|
||||
message: 'Select the OS version:',
|
||||
type: 'list',
|
||||
choices,
|
||||
default: recommended,
|
||||
});
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ Additional commands:
|
||||
note <|note> set a device note
|
||||
os build-config <image> <device-type> build the OS config and save it to the JSON file
|
||||
os configure <image> configure a previously downloaded balenaOS image
|
||||
os download <type> download an unconfigured os image
|
||||
os download <type> download an unconfigured OS image
|
||||
os initialize <image> initialize an os image
|
||||
os versions <type> show available balenaOS versions for the given device type
|
||||
settings print current settings
|
||||
|
25
typings/balena-image-manager/index.d.ts
vendored
Normal file
25
typings/balena-image-manager/index.d.ts
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 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.
|
||||
*/
|
||||
|
||||
declare module 'balena-image-manager' {
|
||||
export function cleanCache(): Promise<void>;
|
||||
|
||||
export function get(
|
||||
deviceType: string,
|
||||
versionOrRange: string,
|
||||
): Promise<NodeJS.ReadableStream & { mime: string }>;
|
||||
}
|
18
typings/node-unzip-2/index.d.ts
vendored
Normal file
18
typings/node-unzip-2/index.d.ts
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 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.
|
||||
*/
|
||||
|
||||
declare module 'node-unzip-2';
|
Loading…
Reference in New Issue
Block a user