From c166ec75979fcaa8bd123a1bf84f379279050fd4 Mon Sep 17 00:00:00 2001 From: Paulo Castro Date: Mon, 13 Sep 2021 18:26:08 +0100 Subject: [PATCH] os versions, os download: Add support for balenaOS ESR versions Change-type: minor --- doc/cli.markdown | 34 +++++++++++++++--------- lib/commands/os/download.ts | 27 ++++++++++--------- lib/commands/os/versions.ts | 22 ++++++++++----- lib/utils/cloud.ts | 53 ++++++++++++++++++++++++++++++------- npm-shrinkwrap.json | 6 ++--- package.json | 2 +- 6 files changed, 99 insertions(+), 45 deletions(-) diff --git a/doc/cli.markdown b/doc/cli.markdown index 9134f164..8129b570 100644 --- a/doc/cli.markdown +++ b/doc/cli.markdown @@ -2240,6 +2240,9 @@ device UUID Show the available balenaOS versions for the given device type. Check available types with `balena devices supported`. +balenaOS ESR versions can be listed with the '--esr' option. See also: +https://www.balena.io/docs/reference/OS/extended-support-release/ + Examples: $ balena os versions raspberrypi3 @@ -2252,23 +2255,28 @@ device type ### Options +#### --esr + +select balenaOS ESR versions + ## os download <type> -Download an unconfigured OS image for a certain device type. -Check available types with `balena devices supported` +Download an unconfigured OS image for the specified device type. +Check available device 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). +The '--version' option is used to select the balenaOS version. If omitted, +the latest released version is downloaded (and if only pre-release versions +exist, the latest pre-release version is downloaded). -You can pass `--version menu` to pick the OS version from the interactive menu -of all available versions. +Use '--version menu' or '--version menu-esr' to interactively select the +OS version. The latter lists ESR versions which are only available for +download on Production and Enterprise plans. See also: +https://www.balena.io/docs/reference/OS/extended-support-release/ -To download a development image append `.dev` to the version or select from -the interactive menu. +Development images can be selected by appending `.dev` to the version. Examples: @@ -2294,11 +2302,13 @@ output path #### --version VERSION -exact version number, or a valid semver range, +version number (ESR or non-ESR versions), +or semver range (non-ESR versions only), or 'latest' (includes pre-releases), -or 'default' (excludes pre-releases if at least one stable version is available), +or 'default' (excludes pre-releases if at least one released version is available), or 'recommended' (excludes pre-releases, will fail if only pre-release versions are available), -or 'menu' (will show the interactive menu) +or 'menu' (interactive menu, non-ESR versions), +or 'menu-esr' (interactive menu, ESR versions) ## os build-config <image> <device-type> diff --git a/lib/commands/os/download.ts b/lib/commands/os/download.ts index 52c2f628..b5e5855c 100644 --- a/lib/commands/os/download.ts +++ b/lib/commands/os/download.ts @@ -34,21 +34,22 @@ 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\` + Download an unconfigured OS image for the specified device type. + Check available device 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). + The '--version' option is used to select the balenaOS version. If omitted, + the latest released version is downloaded (and if only pre-release versions + exist, the latest pre-release version is downloaded). - You can pass \`--version menu\` to pick the OS version from the interactive menu - of all available versions. + Use '--version menu' or '--version menu-esr' to interactively select the + OS version. The latter lists ESR versions which are only available for + download on Production and Enterprise plans. See also: + https://www.balena.io/docs/reference/OS/extended-support-release/ - To download a development image append \`.dev\` to the version or select from - the interactive menu. + Development images can be selected by appending \`.dev\` to the version. `; public static examples = [ '$ balena os download raspberrypi3 -o ../foo/bar/raspberry-pi.img', @@ -78,11 +79,13 @@ export default class OsDownloadCmd extends Command { }), version: flags.string({ description: stripIndent` - exact version number, or a valid semver range, + version number (ESR or non-ESR versions), + or semver range (non-ESR versions only), or 'latest' (includes pre-releases), - or 'default' (excludes pre-releases if at least one stable version is available), + or 'default' (excludes pre-releases if at least one released version is available), or 'recommended' (excludes pre-releases, will fail if only pre-release versions are available), - or 'menu' (will show the interactive menu) + or 'menu' (interactive menu, non-ESR versions), + or 'menu-esr' (interactive menu, ESR versions) `, }), help: cf.help, diff --git a/lib/commands/os/versions.ts b/lib/commands/os/versions.ts index 86dff364..6e3a42ac 100644 --- a/lib/commands/os/versions.ts +++ b/lib/commands/os/versions.ts @@ -18,9 +18,10 @@ import { flags } from '@oclif/command'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; +import { stripIndent } from '../../utils/lazy'; interface FlagsDef { + esr?: boolean; help: void; } @@ -34,6 +35,9 @@ export default class OsVersionsCmd extends Command { Show the available balenaOS versions for the given device type. Check available types with \`balena devices supported\`. + + balenaOS ESR versions can be listed with the '--esr' option. See also: + https://www.balena.io/docs/reference/OS/extended-support-release/ `; public static examples = ['$ balena os versions raspberrypi3']; @@ -50,16 +54,20 @@ export default class OsVersionsCmd extends Command { public static flags: flags.Input = { help: cf.help, + esr: flags.boolean({ + description: 'select balenaOS ESR versions', + default: false, + }), }; public async run() { - const { args: params } = this.parse(OsVersionsCmd); + const { args: params, flags: options } = this.parse( + OsVersionsCmd, + ); - const { versions: vs, recommended } = - await getBalenaSdk().models.os.getSupportedVersions(params.type); + const { getFormattedOsVersions } = await import('../../utils/cloud'); + const vs = await getFormattedOsVersions(params.type, !!options.esr); - vs.forEach((v) => { - console.log(`v${v}` + (v === recommended ? ' (recommended)' : '')); - }); + console.log(vs.map((v) => v.formattedVersion).join('\n')); } } diff --git a/lib/utils/cloud.ts b/lib/utils/cloud.ts index b01c97c8..4e4d3df8 100644 --- a/lib/utils/cloud.ts +++ b/lib/utils/cloud.ts @@ -130,7 +130,7 @@ export async function downloadOSImage( console.info(`Getting device operating system for ${deviceType}`); if (!OSVersion) { - console.warn('OS version not specified: using latest stable version'); + console.warn('OS version not specified: using latest released version'); } OSVersion = OSVersion @@ -183,28 +183,29 @@ export async function downloadOSImage( const streamToPromise = await import('stream-to-promise'); await streamToPromise(stream.pipe(output)); - console.info('The image was downloaded successfully'); + console.info( + `balenaOS image version ${displayVersion} downloaded successfully`, + ); return outputPath; } -async function resolveOSVersion(deviceType: string, version: string) { - if (version !== 'menu') { +async function resolveOSVersion( + deviceType: string, + version: string, +): Promise { + if (!['menu', 'menu-esr'].includes(version)) { if (version[0] === 'v') { version = version.slice(1); } return version; } - const vs = ( - (await getBalenaSdk().models.hostapp.getAllOsVersions([deviceType]))[ - deviceType - ] ?? [] - ).filter((v) => v.osType === 'default'); + const vs = await getFormattedOsVersions(deviceType, version === 'menu-esr'); const choices = vs.map((v) => ({ value: v.rawVersion, - name: `v${v.rawVersion}` + (v.isRecommended ? ' (recommended)' : ''), + name: v.formattedVersion, })); return getCliForm().ask({ @@ -214,3 +215,35 @@ async function resolveOSVersion(deviceType: string, version: string) { default: (vs.find((v) => v.isRecommended) ?? vs[0])?.rawVersion, }); } + +/** + * Return the output of sdk.models.hostapp.getAvailableOsVersions(), filtered + * regarding ESR or non-ESR versions, and having the `formattedVersion` field + * reformatted for compatibility with the pre-existing output format of the + * `os versions` and `os download` commands. + */ +export async function getFormattedOsVersions( + deviceType: string, + esr: boolean, +): Promise { + const versions: SDK.OsVersion[] = ( + (await getBalenaSdk().models.hostapp.getAvailableOsVersions([deviceType]))[ + deviceType + ] ?? [] + ) + .filter((v: SDK.OsVersion) => v.osType === (esr ? 'esr' : 'default')) + .map((v: SDK.OsVersion) => { + const i = v.formattedVersion.indexOf(' '); + v.formattedVersion = + i < 0 + ? `v${v.rawVersion}` + : `v${v.rawVersion}${v.formattedVersion.substring(i)}`; + return v; + }); + if (!versions.length) { + throw new ExpectedError(stripIndent` + Error: No balenaOS versions found for device type '${deviceType}'. + Double-check the device type slug with 'balena devices supported'.`); + } + return versions; +} diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 008b78fe..2efe7f9e 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -3785,9 +3785,9 @@ } }, "balena-image-manager": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/balena-image-manager/-/balena-image-manager-7.0.3.tgz", - "integrity": "sha512-KLb/5JksYxCR7JClq+MAuCMYPNMAXB9MgqubUvzj4mI/Yec3XD4xZ2BMITwINrl4sMKui/G7t/R5tBNFZsFe9w==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/balena-image-manager/-/balena-image-manager-7.1.0.tgz", + "integrity": "sha512-W8Etn0PBQJUlTJ1m1rCuQQ8TTLdkBoNP2SRHQqez2HANQp+T38wFjQXVx9PhQ6J7eqOYuW87SkJiKLpnmlz79Q==", "requires": { "balena-sdk": "^15.2.1", "mime": "^2.4.6", diff --git a/package.json b/package.json index 50073ffa..ea4c99e1 100644 --- a/package.json +++ b/package.json @@ -205,7 +205,7 @@ "balena-device-init": "^6.0.0", "balena-errors": "^4.7.1", "balena-image-fs": "^7.0.6", - "balena-image-manager": "^7.0.3", + "balena-image-manager": "^7.1.0", "balena-preload": "^11.0.0", "balena-release": "^3.2.0", "balena-sdk": "^15.51.1",