/** * @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, Args, Command } from '@oclif/core'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; export default class OsDownloadCmd extends Command { public static description = stripIndent` Download an unconfigured OS image. Download an unconfigured OS image for the specified device type. Check available device types with 'balena device-type list'. Note: Currently this command only works with balenaCloud, not openBalena. If using openBalena, please download the OS from: https://www.balena.io/os/ 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). Use '--type' to specify the type of OS download If omitted, the default type for the specified device type-version combination is used. Some OS download types may not be available for certain device types and 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/ Development images can be selected by appending \`.dev\` to the version. `; public static examples = [ '$ balena os download raspberrypi3 -o ../foo/bar/raspberrypi3.img', '$ balena os download raspberrypi3 -o ../foo/bar/raspberrypi3.img --version 2.101.7', '$ balena os download raspberrypi3 -o ../foo/bar/raspberrypi3.img --version 2022.7.0', '$ balena os download raspberrypi3 -o ../foo/bar/raspberrypi3.img --version ^2.90.0', '$ balena os download raspberrypi3 -o ../foo/bar/raspberrypi3.img --version 2.60.1+rev1', '$ balena os download raspberrypi3 -o ../foo/bar/raspberrypi3.img --version 2.60.1+rev1.dev', '$ balena os download raspberrypi3 -o ../foo/bar/raspberrypi3.img --version 2021.10.2.prod', '$ balena os download raspberrypi3 -o ../foo/bar/raspberrypi3.img --version latest', '$ balena os download raspberrypi3 -o ../foo/bar/raspberrypi3.img --version menu', '$ balena os download raspberrypi3 -o ../foo/bar/raspberrypi3.img --version menu-esr', '$ balena os download generic-amd64 -o ../foo/bar/generic-amd64.img --type installation-media', ]; public static args = { type: Args.string({ description: 'the device type', required: true, }), }; public static flags = { output: Flags.string({ description: 'output path', char: 'o', required: true, }), version: Flags.string({ description: stripIndent` version number (ESR or non-ESR versions), or semver range (non-ESR versions only), or 'latest' (excludes invalidated & pre-releases), or 'menu' (interactive menu, non-ESR versions), or 'menu-esr' (interactive menu, ESR versions) `, }), type: Flags.string({ options: ['installation-media', 'disk-image'], description: stripIndent` 'disk-image' (for flashing onto device system disk/storage) or 'installation-media' (for creating installation media to automatically erase, format, and install balenaOS on a device) `, }), }; public async run() { const { args: params, flags: options } = await this.parse(OsDownloadCmd); const { downloadOSImage, isESR } = await import('../../utils/os'); const balena = getBalenaSdk(); // TODO: Check the OS release's assets for a file that would start with `balena-image-flasher-${DEVICE_TYPE_SLUG}.manifest` once they have been backfilled const dtManifest = await balena.models.config.getDeviceTypeManifestBySlug( params.type, ); const defaultImageType = /^(resin|balena)-image-flasher\b/.test( dtManifest.yocto.deployArtifact, ) ? 'installation-media' : 'disk-image'; if ( options.type === 'installation-media' || (defaultImageType === 'installation-media' && !options.type) ) { // If the user is downloading an installation-media image, warn them that an installation-media image will wipe the target device's storage // This is because some users may not understand the difference between an installation-media and disk-image, and may be surprised that the downloaded image will wipe their device when flashed // Additionally, when no type is specified, the type of image downloaded depends on the device type, and some device types default to installation-media images, so we want to warn in that case as well console.warn( "WARNING: balenaOS installation media automatically and without confirmation erases and formats the target device's internal storage when booted.", ); } // balenaOS ESR versions require user authentication if (options.version) { if (options.version === 'menu-esr' || isESR(options.version)) { try { const { checkLoggedIn } = await import('../../utils/patterns'); await checkLoggedIn(); } catch (e) { const { ExpectedError, NotLoggedInError } = await import('../../errors'); if (e instanceof NotLoggedInError) { throw new ExpectedError(stripIndent` ${e.message} User authentication is required to download balenaOS ESR versions.`); } throw e; } } } try { await downloadOSImage( params.type, options.output, options.version, defaultImageType === options.type ? undefined : (options.type as 'installation-media' | 'disk-image' | undefined), ); } catch (e) { e.deviceTypeSlug = params.type; e.message ??= ''; const { OSVersionNotFoundError } = await import('../../errors'); if (e instanceof OSVersionNotFoundError) { const version = options.version ?? ''; if ( !version.endsWith('.dev') && !version.endsWith('.prod') && /^v?\d+\.\d+\.\d+/.test(version) ) { e.message += ` ** Hint: some OS releases require specifying the full OS version including ** the '.prod' or '.dev' suffix, e.g. '--version 2021.10.2.prod'`; } } throw e; } } }