diff --git a/automation/capitanodoc/capitanodoc.ts b/automation/capitanodoc/capitanodoc.ts index 6b7dffbf..1f3327bb 100644 --- a/automation/capitanodoc/capitanodoc.ts +++ b/automation/capitanodoc/capitanodoc.ts @@ -61,6 +61,7 @@ const capitanoDoc = { 'build/actions-oclif/device/shutdown.js', 'build/actions-oclif/devices/index.js', 'build/actions-oclif/devices/supported.js', + 'build/actions-oclif/device/os-update.js', 'build/actions-oclif/device/public-url.js', ], }, diff --git a/doc/cli.markdown b/doc/cli.markdown index 04f2dc3a..0d46b3d7 100644 --- a/doc/cli.markdown +++ b/doc/cli.markdown @@ -168,7 +168,6 @@ Users are encouraged to regularly update the balena CLI to the latest version. - Device - [device init](#device-init) - - [device os-update <uuid>](#device-os-update-uuid) - [device identify <uuid>](#device-identify-uuid) - [device <uuid>](#device-uuid) - [device move <uuid>](#device-move-uuid) @@ -179,6 +178,7 @@ Users are encouraged to regularly update the balena CLI to the latest version. - [device shutdown <uuid>](#device-shutdown-uuid) - [devices](#devices) - [devices supported](#devices-supported) + - [device os-update <uuid>](#device-os-update-uuid) - [device public-url <uuid>](#device-public-url-uuid) - Environment Variables @@ -496,30 +496,6 @@ the drive to write the image to, like `/dev/sdb` or `/dev/mmcblk0`. Careful with path to the config JSON file, see `balena os build-config` -## device os-update <uuid> - -Use this command to trigger a Host OS update for a device. - -Notice this command will ask for confirmation interactively. -You can avoid this by passing the `--yes` boolean option. - -Requires balenaCloud; will not work with openBalena or standalone balenaOS. - -Examples: - - $ balena device os-update 23c73a1 - $ balena device os-update 23c73a1 --version 2.31.0+rev1.prod - -### Options - -#### --version <version> - -a balenaOS version - -#### --yes, -y - -confirm non interactively - ## device identify <uuid> Identify a device by making the ACT LED blink (Raspberry Pi). @@ -746,6 +722,36 @@ produce JSON output instead of tabular output add extra columns in the tabular output (ALIASES, ARCH, STATE) +## device os-update <uuid> + +Start a Host OS update for a device. + +Note this command will ask for confirmation interactively. +This can be avoided by passing the `--yes` option. + +Requires balenaCloud; will not work with openBalena or standalone balenaOS. + +Examples: + + $ balena device os-update 23c73a1 + $ balena device os-update 23c73a1 --version 2.31.0+rev1.prod + +### Arguments + +#### UUID + +the uuid of the device to update + +### Options + +#### --version VERSION + +a balenaOS version + +#### -y, --yes + +answer "yes" to all questions (non interactive use) + ## device public-url <uuid> This command will output the current public URL for the diff --git a/lib/actions-oclif/device/os-update.ts b/lib/actions-oclif/device/os-update.ts new file mode 100644 index 00000000..1ab2f921 --- /dev/null +++ b/lib/actions-oclif/device/os-update.ts @@ -0,0 +1,148 @@ +/** + * @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 { IArg } from '@oclif/parser/lib/args'; +import { stripIndent } from 'common-tags'; +import Command from '../../command'; +import * as cf from '../../utils/common-flags'; +import { getBalenaSdk } from '../../utils/lazy'; +import { tryAsInteger } from '../../utils/validation'; +import { Device } from 'balena-sdk'; +import { ExpectedError } from '../../errors'; + +interface FlagsDef { + version?: string; + yes: boolean; + help: void; +} + +interface ArgsDef { + uuid: string; +} + +export default class DeviceOsUpdateCmd extends Command { + public static description = stripIndent` + Start a Host OS update for a device. + + Start a Host OS update for a device. + + Note this command will ask for confirmation interactively. + This can be avoided by passing the \`--yes\` option. + + Requires balenaCloud; will not work with openBalena or standalone balenaOS. + `; + public static examples = [ + '$ balena device os-update 23c73a1', + '$ balena device os-update 23c73a1 --version 2.31.0+rev1.prod', + ]; + + public static args: Array> = [ + { + name: 'uuid', + description: 'the uuid of the device to update', + parse: (dev) => tryAsInteger(dev), + required: true, + }, + ]; + + public static usage = 'device os-update '; + + public static flags: flags.Input = { + version: flags.string({ + description: 'a balenaOS version', + }), + yes: cf.yes, + help: cf.help, + }; + + public static authenticated = true; + + public async run() { + const { args: params, flags: options } = this.parse( + DeviceOsUpdateCmd, + ); + + const _ = await import('lodash'); + const sdk = getBalenaSdk(); + const patterns = await import('../../utils/patterns'); + const form = await import('resin-cli-form'); + + // Get device info + const { + uuid, + device_type, + os_version, + os_variant, + } = await sdk.models.device.get(params.uuid, { + $select: ['uuid', 'device_type', 'os_version', 'os_variant'], + }); + + // Get current device OS version + const currentOsVersion = sdk.models.device.getOsVersion({ + os_version, + os_variant, + } as Device); + if (!currentOsVersion) { + throw new ExpectedError( + 'The current os version of the device is not available', + ); + } + + // Get supported OS update versions + const hupVersionInfo = await sdk.models.os.getSupportedOsUpdateVersions( + device_type, + currentOsVersion, + ); + if (hupVersionInfo.versions.length === 0) { + throw new ExpectedError( + 'There are no available Host OS update targets for this device', + ); + } + + // Get target OS version + let targetOsVersion = options.version; + if (targetOsVersion != null) { + if (!_.includes(hupVersionInfo.versions, targetOsVersion)) { + throw new ExpectedError( + `The provided version ${targetOsVersion} is not in the Host OS update targets for this device`, + ); + } + } else { + targetOsVersion = await form.ask({ + message: 'Target OS version', + type: 'list', + choices: hupVersionInfo.versions.map((version) => ({ + name: + hupVersionInfo.recommended === version + ? `${version} (recommended)` + : version, + value: version, + })), + }); + } + + // Confirm and start update + await patterns.confirm( + options.yes || false, + 'Host OS updates require a device restart when they complete. Are you sure you want to proceed?', + ); + + await sdk.models.device.startOsUpdate(uuid, targetOsVersion); + await patterns.awaitDeviceOsUpdate(uuid, targetOsVersion); + } +} diff --git a/lib/actions/device.js b/lib/actions/device.js index 95d4211d..471453bc 100644 --- a/lib/actions/device.js +++ b/lib/actions/device.js @@ -112,5 +112,3 @@ Examples: }); }, }; - -export { osUpdate } from './device_ts'; diff --git a/lib/actions/device_ts.ts b/lib/actions/device_ts.ts deleted file mode 100644 index fd94b1f5..00000000 --- a/lib/actions/device_ts.ts +++ /dev/null @@ -1,122 +0,0 @@ -/* -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 { Device } from 'balena-sdk'; -import { CommandDefinition } from 'capitano'; -import { stripIndent } from 'common-tags'; -import { ExpectedError } from '../errors'; -import { getBalenaSdk } from '../utils/lazy'; -import { normalizeUuidProp } from '../utils/normalization'; -import * as commandOptions from './command-options'; - -// tslint:disable-next-line:no-namespace -namespace OsUpdate { - export interface Args { - uuid: string; - } - - export type Options = commandOptions.OptionalOsVersionOption & - commandOptions.YesOption; -} - -export const osUpdate: CommandDefinition = { - signature: 'device os-update ', - description: 'Start a Host OS update for a device', - help: stripIndent` - Use this command to trigger a Host OS update for a device. - - Notice this command will ask for confirmation interactively. - You can avoid this by passing the \`--yes\` boolean option. - - Requires balenaCloud; will not work with openBalena or standalone balenaOS. - - Examples: - - $ balena device os-update 23c73a1 - $ balena device os-update 23c73a1 --version 2.31.0+rev1.prod - `, - options: [commandOptions.optionalOsVersion, commandOptions.yes], - permission: 'user', - async action(params, options) { - normalizeUuidProp(params); - const _ = await import('lodash'); - const sdk = getBalenaSdk(); - const patterns = await import('../utils/patterns'); - const form = await import('resin-cli-form'); - - // Get device info - const { - uuid, - device_type, - os_version, - os_variant, - } = await sdk.models.device.get(params.uuid, { - $select: ['uuid', 'device_type', 'os_version', 'os_variant'], - }); - - // Get current device OS version - const currentOsVersion = sdk.models.device.getOsVersion({ - os_version, - os_variant, - } as Device); - if (!currentOsVersion) { - throw new ExpectedError( - 'The current os version of the device is not available', - ); - } - - // Get supported OS update versions - const hupVersionInfo = await sdk.models.os.getSupportedOsUpdateVersions( - device_type, - currentOsVersion, - ); - if (hupVersionInfo.versions.length === 0) { - throw new ExpectedError( - 'There are no available Host OS update targets for this device', - ); - } - - // Get target OS version - let targetOsVersion = options.version; - if (targetOsVersion != null) { - if (!_.includes(hupVersionInfo.versions, targetOsVersion)) { - throw new ExpectedError( - `The provided version ${targetOsVersion} is not in the Host OS update targets for this device`, - ); - } - } else { - targetOsVersion = await form.ask({ - message: 'Target OS version', - type: 'list', - choices: hupVersionInfo.versions.map((version) => ({ - name: - hupVersionInfo.recommended === version - ? `${version} (recommended)` - : version, - value: version, - })), - }); - } - - // Confirm and start update - await patterns.confirm( - options.yes || false, - 'Host OS updates require a device restart when they complete. Are you sure you want to proceed?', - ); - - await sdk.models.device.startOsUpdate(uuid, targetOsVersion); - await patterns.awaitDeviceOsUpdate(uuid, targetOsVersion); - }, -}; diff --git a/lib/app-capitano.js b/lib/app-capitano.js index 557f113b..62d8d95c 100644 --- a/lib/app-capitano.js +++ b/lib/app-capitano.js @@ -54,7 +54,6 @@ capitano.command(actions.auth.whoami); // ---------- Device Module ---------- capitano.command(actions.device.init); -capitano.command(actions.device.osUpdate); // ---------- OS Module ---------- capitano.command(actions.os.versions); diff --git a/lib/preparser.ts b/lib/preparser.ts index ae0af587..5130dd76 100644 --- a/lib/preparser.ts +++ b/lib/preparser.ts @@ -144,6 +144,7 @@ export const convertedCommands = [ 'device', 'device:identify', 'device:move', + 'device:os-update', 'device:public-url', 'device:reboot', 'device:register', diff --git a/tests/commands/help.spec.ts b/tests/commands/help.spec.ts index 24fec796..54b6d6d1 100644 --- a/tests/commands/help.spec.ts +++ b/tests/commands/help.spec.ts @@ -40,7 +40,7 @@ Additional commands: device identify identify a device device init initialise a device with balenaOS device move move a device to another application - device os-update Start a Host OS update for a device + device os-update start a Host OS update for a device device public-url get or manage the public URL for a device device reboot restart a device device register register a device