diff --git a/automation/capitanodoc/capitanodoc.ts b/automation/capitanodoc/capitanodoc.ts index 2e40bf72..2dce82c4 100644 --- a/automation/capitanodoc/capitanodoc.ts +++ b/automation/capitanodoc/capitanodoc.ts @@ -85,7 +85,7 @@ const capitanoDoc = { }, { title: 'OS', - files: ['build/actions/os.js'], + files: ['build/actions/os.js', 'build/actions-oclif/os/configure.js'], }, { title: 'Config', diff --git a/automation/capitanodoc/markdown.ts b/automation/capitanodoc/markdown.ts index 8c0df619..600a0a64 100644 --- a/automation/capitanodoc/markdown.ts +++ b/automation/capitanodoc/markdown.ts @@ -118,6 +118,13 @@ function renderToc(categories: Category[]): string[] { const manualCategorySorting: { [category: string]: string[] } = { 'Environment Variables': ['envs', 'env rm', 'env add', 'env rename'], + OS: [ + 'os versions', + 'os download', + 'os build config', + 'os configure', + 'os initialize', + ], }; function sortCommands(doc: Document): void { diff --git a/doc/cli.markdown b/doc/cli.markdown index 10590ce2..3cb391c5 100644 --- a/doc/cli.markdown +++ b/doc/cli.markdown @@ -1132,23 +1132,22 @@ show advanced configuration options the path to the output JSON file -## os configure <image> +## os configure IMAGE -Use this command to configure a previously downloaded operating system image for -the specific device or for an application generally. +Configure a previously downloaded balenaOS image for a specific device type or +balena application. -This command will try to automatically determine the operating system version in order -to correctly configure the image. It may fail to do so however, in which case you'll -have to call this command again with the exact version number of the targeted image. +Configuration settings such as WiFi authentication will be taken from the +following sources, in precedence order: +1. Command-line options like `--config-wifi-ssid` +2. A given `config.json` file specified with the `--config` option. +3. User input through interactive prompts (text menus). -Note that device api keys are only supported on balenaOS 2.0.3+. +The --device-type option may be used to override the application's default +device type, in case of an application with mixed device types. -This command still supports the *deprecated* format where the UUID and optionally device key -are passed directly on the command line, but the recommended way is to pass either an --app or ---device argument. The deprecated format will be removed in a future release. - -In case that you want to configure an image for an application with mixed device types, -you can pass the --device-type argument along with --app to specify the target device type. +The --device-api-key option is deprecated and will be removed in a future release. +A suitable key is automatically generated or fetched if this option is omitted. Examples: @@ -1157,36 +1156,63 @@ Examples: $ balena os configure ../path/rpi3.img --app MyApp $ balena os configure ../path/rpi3.img --app MyApp --version 2.12.7 $ balena os configure ../path/rpi3.img --app MyFinApp --device-type raspberrypi3 + $ balena os configure ../path/rpi3.img --app MyFinApp --device-type raspberrypi3 --config myWifiConfig.json + +### Arguments + +#### IMAGE + +path to a balenaOS image file, e.g. "rpi3.img" ### Options -#### --advanced, -v +#### -v, --advanced -show advanced configuration options +ask advanced configuration questions (when in interactive mode) -#### --application, -a, --app <application> +#### --app APP + +same as '--application' + +#### -a, --application APPLICATION application name -#### --device, -d <device> +#### --config CONFIG -device uuid +path to a pre-generated config.json file to be injected in the OS image -#### --deviceApiKey, -k <device-api-key> +#### --config-app-update-poll-interval CONFIG-APP-UPDATE-POLL-INTERVAL -custom device key - note that this is only supported on balenaOS 2.0.3+ +interval (in minutes) for the on-device balena supervisor periodic app update check -#### --deviceType <device-type> +#### --config-network CONFIG-NETWORK -device type slug +device network type (non-interactive configuration) -#### --version <version> +#### --config-wifi-key CONFIG-WIFI-KEY -a balenaOS version +WiFi key (password) (non-interactive configuration) -#### --config <config> +#### --config-wifi-ssid CONFIG-WIFI-SSID -path to the config JSON file, see `balena os build-config` +WiFi SSID (network name) (non-interactive configuration) + +#### -d, --device DEVICE + +device UUID + +#### -k, --device-api-key DEVICE-API-KEY + +custom device API key (DEPRECATED and only supported with balenaOS 2.0.3+) + +#### --device-type DEVICE-TYPE + +device type slug (e.g. "raspberrypi3") to override the application device type + +#### --version VERSION + +balenaOS version, for example "2.32.0" or "2.44.0+rev1" ## os initialize <image> diff --git a/lib/actions-oclif/os/configure.ts b/lib/actions-oclif/os/configure.ts new file mode 100644 index 00000000..9092f73e --- /dev/null +++ b/lib/actions-oclif/os/configure.ts @@ -0,0 +1,431 @@ +/** + * @license + * Copyright 2019 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 { Command, flags } from '@oclif/command'; +import BalenaSdk = require('balena-sdk'); +import { stripIndent } from 'common-tags'; +import * as _ from 'lodash'; + +import { ExpectedError } from '../../errors'; +import * as cf from '../../utils/common-flags'; +import { CommandHelp } from '../../utils/oclif-utils'; + +interface FlagsDef { + advanced?: boolean; + app?: string; + application?: string; + config?: string; + 'config-app-update-poll-interval'?: number; + 'config-network'?: string; + 'config-wifi-key'?: string; + 'config-wifi-ssid'?: string; + device?: string; // device UUID + 'device-api-key'?: string; + 'device-type'?: string; + help?: void; + version?: string; +} + +interface ArgsDef { + image: string; +} + +interface DeferredDevice extends BalenaSdk.Device { + belongs_to__application: BalenaSdk.PineDeferred; +} + +interface Answers { + appUpdatePollInterval: number; // in minutes + deviceType: string; // e.g. "raspberrypi3" + network: 'ethernet' | 'wifi'; + version: string; // e.g. "2.32.0+rev1" + wifiSsid?: string; + wifiKey?: string; +} + +const deviceApiKeyDeprecationMsg = stripIndent` + The --device-api-key option is deprecated and will be removed in a future release. + A suitable key is automatically generated or fetched if this option is omitted.`; + +export default class OsConfigureCmd extends Command { + public static description = stripIndent` + Configure a previously downloaded balenaOS image. + + Configure a previously downloaded balenaOS image for a specific device type or + balena application. + + Configuration settings such as WiFi authentication will be taken from the + following sources, in precedence order: + 1. Command-line options like \`--config-wifi-ssid\` + 2. A given \`config.json\` file specified with the \`--config\` option. + 3. User input through interactive prompts (text menus). + + The --device-type option may be used to override the application's default + device type, in case of an application with mixed device types. + + ${deviceApiKeyDeprecationMsg.split('\n').join('\n\t\t')} + `; + public static examples = [ + '$ balena os configure ../path/rpi3.img --device 7cf02a6', + '$ balena os configure ../path/rpi3.img --device 7cf02a6 --device-api-key ', + '$ balena os configure ../path/rpi3.img --app MyApp', + '$ balena os configure ../path/rpi3.img --app MyApp --version 2.12.7', + '$ balena os configure ../path/rpi3.img --app MyFinApp --device-type raspberrypi3', + '$ balena os configure ../path/rpi3.img --app MyFinApp --device-type raspberrypi3 --config myWifiConfig.json', + ]; + + public static args = [ + { + name: 'image', + required: true, + description: 'path to a balenaOS image file, e.g. "rpi3.img"', + }, + ]; + + // hardcoded 'os configure' to avoid oclif's 'os:configure' topic syntax + public static usage = + 'os configure ' + + new CommandHelp({ args: OsConfigureCmd.args }).defaultUsage(); + + public static flags: flags.Input = { + advanced: flags.boolean({ + char: 'v', + description: + 'ask advanced configuration questions (when in interactive mode)', + }), + app: flags.string({ + description: "same as '--application'", + exclusive: ['application', 'device'], + }), + application: _.assign({ exclusive: ['app', 'device'] }, cf.application), + config: flags.string({ + description: + 'path to a pre-generated config.json file to be injected in the OS image', + }), + 'config-app-update-poll-interval': flags.integer({ + description: + 'interval (in minutes) for the on-device balena supervisor periodic app update check', + }), + 'config-network': flags.string({ + description: 'device network type (non-interactive configuration)', + options: ['ethernet', 'wifi'], + }), + 'config-wifi-key': flags.string({ + description: 'WiFi key (password) (non-interactive configuration)', + }), + 'config-wifi-ssid': flags.string({ + description: 'WiFi SSID (network name) (non-interactive configuration)', + }), + device: _.assign({ exclusive: ['app', 'application'] }, cf.device), + 'device-api-key': flags.string({ + char: 'k', + description: + 'custom device API key (DEPRECATED and only supported with balenaOS 2.0.3+)', + }), + 'device-type': flags.string({ + description: + 'device type slug (e.g. "raspberrypi3") to override the application device type', + }), + help: cf.help, + version: flags.string({ + description: 'balenaOS version, for example "2.32.0" or "2.44.0+rev1"', + }), + }; + + public async run() { + const { args: params, flags: options } = this.parse( + OsConfigureCmd, + ); + // Prefer options.application over options.app + options.application = options.application || options.app; + options.app = undefined; + + await validateOptions(options); + + const devInit = await import('balena-device-init'); + const balena = (await import('balena-sdk')).fromSharedOptions(); + const fs = await import('mz/fs'); + const { generateDeviceConfig, generateApplicationConfig } = await import( + '../../utils/config' + ); + const helpers = await import('../../utils/helpers'); + let app: BalenaSdk.Application | undefined; + let device: BalenaSdk.Device | undefined; + let deviceTypeSlug: string; + + if (options.device) { + device = await balena.models['device'].get(options.device); + deviceTypeSlug = device.device_type; + } else { + app = await balena.models['application'].get(options.application!); + await checkDeviceTypeCompatibility(balena, options, app); + deviceTypeSlug = options['device-type'] || app.device_type; + } + + const deviceTypeManifest = await helpers.getManifest( + params.image, + deviceTypeSlug, + ); + + let configJson: import('../../utils/config').ImgConfig | undefined; + if (options.config) { + const rawConfig = await fs.readFile(options.config, 'utf8'); + configJson = JSON.parse(rawConfig); + } + + const answers: Answers = await askQuestionsForDeviceType( + deviceTypeManifest, + options, + configJson, + ); + if (options.application) { + answers.deviceType = deviceTypeSlug; + } + answers.version = + options.version || + (await getOsVersionFromImage(params.image, deviceTypeManifest, devInit)); + + if (_.isEmpty(configJson)) { + if (device) { + configJson = await generateDeviceConfig( + device as DeferredDevice, + options['device-api-key'], + answers, + ); + } else { + configJson = await generateApplicationConfig(app!, answers); + } + } + + console.info('Configuring operating system image'); + + await helpers.osProgressHandler( + await devInit.configure( + params.image, + deviceTypeManifest, + configJson || {}, + answers, + ), + ); + } +} + +async function validateOptions(options: FlagsDef) { + if (process.platform === 'win32') { + throw new ExpectedError(stripIndent` + Unsupported platform error: the 'balena os configure' command currently requires + the Windows Subsystem for Linux in order to run on Windows. It was tested with + the Ubuntu 18.04 distribution from the Microsoft Store. With WSL, a balena CLI + release for Linux (rather than Windows) should be installed: for example, the + standalone zip package for Linux. (It is possible to have both a Windows CLI + release and a Linux CLI release installed simultaneously.) For more information + on WSL and the balena CLI installation options, please check: + - https://docs.microsoft.com/en-us/windows/wsl/about + - https://github.com/balena-io/balena-cli/blob/master/INSTALL.md + `); + } + // The 'device' and 'application' options are declared "exclusive" in the oclif + // flag definitions above, so oclif will enforce that they are not both used together. + if (!options.device && !options.application) { + throw new ExpectedError( + "Either the '--device' or the '--application' option must be provided", + ); + } + if (!options.application && options['device-type']) { + throw new ExpectedError( + "The '--device-type' option can only be used in conjunction with the '--application' option", + ); + } + if (options['device-api-key']) { + console.error(stripIndent` + ------------------------------------------------------------------------------------------- + Warning: ${deviceApiKeyDeprecationMsg.split('\n').join('\n\t\t\t')} + ------------------------------------------------------------------------------------------- + `); + } + const { checkLoggedIn } = await import('../../utils/patterns'); + await checkLoggedIn(); +} + +/** + * Wrapper around balena-device-init.getImageOsVersion(). Throws ExpectedError + * if the OS image could not be read or the OS version could not be extracted + * from it. + * @param imagePath Local filesystem path to a balenaOS image file + * @param deviceTypeManifest Device type manifest object + */ +async function getOsVersionFromImage( + imagePath: string, + deviceTypeManifest: BalenaSdk.DeviceType, + devInit: typeof import('balena-device-init'), +): Promise { + const osVersion = await devInit.getImageOsVersion( + imagePath, + deviceTypeManifest, + ); + if (!osVersion) { + throw new ExpectedError(stripIndent` + Could not read OS version from the image. Please specify the balenaOS + version manually with the --version command-line option.`); + } + return osVersion; +} + +/** + * Check that options['device-type'], e.g. 'raspberrypi3', is compatible with + * app.device_type, e.g. 'raspberry-pi2'. Throws ExpectedError if they are not + * compatible. + * @param sdk Balena Node SDK instance + * @param options oclif command-line options object + * @param app Balena SDK Application model object + */ +async function checkDeviceTypeCompatibility( + sdk: BalenaSdk.BalenaSDK, + options: FlagsDef, + app: BalenaSdk.Application, +) { + if (options['device-type']) { + const [appDeviceType, optionDeviceType] = await Promise.all([ + sdk.models.device.getManifestBySlug(app.device_type), + sdk.models.device.getManifestBySlug(options['device-type']), + ]); + const helpers = await import('../../utils/helpers'); + if (!helpers.areDeviceTypesCompatible(appDeviceType, optionDeviceType)) { + throw new ExpectedError( + `Device type ${ + options['device-type'] + } is incompatible with application ${options.application}`, + ); + } + } +} + +/** + * Check if the given options or configJson objects (in this order) contain + * the answers to some configuration questions, and interactively ask the + * user the questions for which answers are missing. Questions such as: + * + * ? Network Connection (Use arrow keys) + * ethernet + * ❯ wifi + * ? Network Connection wifi + * ? Wifi SSID i-ssid + * ? Wifi Passphrase [input is hidden] + * + * The questions are extracted from the given deviceType "manifest". + */ +async function askQuestionsForDeviceType( + deviceType: BalenaSdk.DeviceType, + options: FlagsDef, + configJson?: import('../../utils/config').ImgConfig, +): Promise { + const form = await import('resin-cli-form'); + const helpers = await import('../../utils/helpers'); + const answerSources: any[] = [camelifyConfigOptions(options)]; + const defaultAnswers: Partial = {}; + const questions: any = deviceType.options; + let extraOpts: { override: object } | undefined; + + if (!_.isEmpty(configJson)) { + answerSources.push(configJson); + } + + if (!options.advanced) { + const advancedGroup: any = _.find(questions, { + name: 'advanced', + isGroup: true, + }); + if (!_.isEmpty(advancedGroup)) { + answerSources.push(helpers.getGroupDefaults(advancedGroup)); + } + } + + for (const questionName of getQuestionNames(deviceType)) { + for (const answerSource of answerSources) { + if (answerSource[questionName] != null) { + defaultAnswers[questionName] = answerSource[questionName]; + break; + } + } + } + if ( + !defaultAnswers.network && + (defaultAnswers.wifiSsid || defaultAnswers.wifiKey) + ) { + defaultAnswers.network = 'wifi'; + } + + if (!_.isEmpty(defaultAnswers)) { + extraOpts = { override: defaultAnswers }; + } + + return form.run(questions, extraOpts); +} + +/** + * Given a deviceType "manifest" containing "options" properties, return an + * array of "question names" as in the following example. + * + * @param deviceType Device type "manifest", for example: + * { "slug": "raspberrypi3", + * "options": [{ + * "options": [ { + * "name": "network", + * "choices": ["ethernet", "wifi"], + * ... }, { + * "name": "wifiSsid", + * "type": "text", + * ... }, { + * "options": [ { + * "name": "appUpdatePollInterval", + * "default": 10, + * ... + * @return Array of question names, for example: + * [ 'network', 'wifiSsid', 'wifiKey', 'appUpdatePollInterval' ] + */ +function getQuestionNames( + deviceType: BalenaSdk.DeviceType, +): Array { + const questionNames: string[] = _.chain(deviceType.options) + .flatMap( + (group: BalenaSdk.DeviceTypeOptions) => + (group.isGroup && group.options) || [], + ) + .map((groupOption: BalenaSdk.DeviceTypeOptionsGroup) => groupOption.name) + .filter() + .value(); + return questionNames as Array; +} + +/** + * Create and return a new object with the key-value pairs from the input object, + * renaming keys that start with the 'config-' prefix as follows: + * Sample input: + * { app: 'foo', 'config-wifi-key': 'mykey', 'config-wifi-ssid': 'myssid' } + * Output: + * { app: 'foo', wifiKey: 'mykey', wifiSsid: 'myssid' } + */ +function camelifyConfigOptions(options: FlagsDef): { [key: string]: any } { + return _.mapKeys(options, (_value, key) => { + if (key.startsWith('config-')) { + return key + .substring('config-'.length) + .replace(/-[a-z]/g, match => match.substring(1).toUpperCase()); + } + return key; + }); +} diff --git a/lib/actions/os.coffee b/lib/actions/os.coffee index 0fe9a696..f2ea86f5 100644 --- a/lib/actions/os.coffee +++ b/lib/actions/os.coffee @@ -1,5 +1,5 @@ ### -Copyright 2016-2017 Balena +Copyright 2016-2019 Balena Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ limitations under the License. commandOptions = require('./command-options') _ = require('lodash') -{ normalizeUuidProp } = require('../utils/normalization') formatVersion = (v, isRecommended) -> result = "v#{v}" @@ -202,143 +201,6 @@ exports.buildConfig = writeFileAsync(options.output, JSON.stringify(answers, null, 4)) .nodeify(done) -exports.configure = - signature: 'os configure ' - description: 'configure an os image' - help: ''' - Use this command to configure a previously downloaded operating system image for - the specific device or for an application generally. - - This command will try to automatically determine the operating system version in order - to correctly configure the image. It may fail to do so however, in which case you'll - have to call this command again with the exact version number of the targeted image. - - Note that device api keys are only supported on balenaOS 2.0.3+. - - This command still supports the *deprecated* format where the UUID and optionally device key - are passed directly on the command line, but the recommended way is to pass either an --app or - --device argument. The deprecated format will be removed in a future release. - - In case that you want to configure an image for an application with mixed device types, - you can pass the --device-type argument along with --app to specify the target device type. - - Examples: - - $ balena os configure ../path/rpi3.img --device 7cf02a6 - $ balena os configure ../path/rpi3.img --device 7cf02a6 --device-api-key - $ balena os configure ../path/rpi3.img --app MyApp - $ balena os configure ../path/rpi3.img --app MyApp --version 2.12.7 - $ balena os configure ../path/rpi3.img --app MyFinApp --device-type raspberrypi3 - ''' - permission: 'user' - options: [ - commandOptions.advancedConfig - commandOptions.optionalApplication - commandOptions.optionalDevice - commandOptions.optionalDeviceApiKey - commandOptions.optionalDeviceType - commandOptions.optionalOsVersion - { - signature: 'config' - description: 'path to the config JSON file, see `balena os build-config`' - parameter: 'config' - } - ] - action: (params, options, done) -> - normalizeUuidProp(options, 'device') - fs = require('fs') - Promise = require('bluebird') - readFileAsync = Promise.promisify(fs.readFile) - balena = require('balena-sdk').fromSharedOptions() - init = require('balena-device-init') - helpers = require('../utils/helpers') - patterns = require('../utils/patterns') - { generateDeviceConfig, generateApplicationConfig } = require('../utils/config') - - if _.filter([ - options.device - options.application - ]).length != 1 - patterns.exitWithExpectedError ''' - To configure an image, you must provide exactly one of: - - * A device, with --device - * An application, with --app - - See the help page for examples: - - $ balena help os configure - ''' - - if !options.application and options.deviceType - patterns.exitWithExpectedError ''' - Specifying a different device type is only supported when - configuring an image using an application as a parameter: - - * An application, with --app - * A specific device type, with --device-type - - See the help page for examples: - - $ balena help os configure - ''' - - uuid = options.device - deviceApiKey = options.deviceApiKey - - console.info('Configuring operating system image') - - configurationResourceType = if uuid then 'device' else 'application' - - balena.models[configurationResourceType].get(uuid || options.application) - .then (appOrDevice) -> - deviceType = options.deviceType || appOrDevice.device_type - manifestPromise = helpers.getManifest(params.image, deviceType) - - if options.application && options.deviceType - app = appOrDevice - appManifestPromise = balena.models.device.getManifestBySlug(app.device_type) - paramManifestPromise = balena.models.device.getManifestBySlug(options.deviceType) - manifestPromise = Promise.resolve(manifestPromise).tap -> - Promise.join appManifestPromise, paramManifestPromise, (appDeviceType, paramDeviceType) -> - if not helpers.areDeviceTypesCompatible(appDeviceType, paramDeviceType) - throw new balena.errors.BalenaInvalidDeviceType( - "Device type #{options.deviceType} is incompatible with application #{options.application}" - ) - - answersPromise = Promise.try -> - if options.config - return readFileAsync(options.config, 'utf8') - .then(JSON.parse) - return manifestPromise.then (deviceTypeManifest) -> - buildConfigForDeviceType(deviceTypeManifest, options.advanced) - - Promise.join answersPromise, manifestPromise, (answers, manifest) -> - answers.version = options.version - - if configurationResourceType == 'application' - answers.deviceType = deviceType - - if not answers.version? - answers.version = Promise.resolve(helpers.getOsVersion(params.image, manifest)).tap (version) -> - if not version? - throw new Error( - 'Could not read OS version from the image. ' + - 'Please specify the version manually with the ' + - '--version argument to this command.' - ) - - Promise.props(answers).then (answers) -> - (if configurationResourceType == 'device' - generateDeviceConfig(appOrDevice, deviceApiKey, answers) - else - generateApplicationConfig(appOrDevice, answers) - ) - .then (config) -> - init.configure(params.image, manifest, config, answers) - .then(helpers.osProgressHandler) - .nodeify(done) - INIT_WARNING_MESSAGE = ''' Note: Initializing the device may ask for administrative permissions because we need to access the raw devices directly. diff --git a/lib/app-capitano.coffee b/lib/app-capitano.coffee index 52ae5ddc..85e2b59c 100644 --- a/lib/app-capitano.coffee +++ b/lib/app-capitano.coffee @@ -93,7 +93,6 @@ capitano.command(actions.tags.remove) capitano.command(actions.os.versions) capitano.command(actions.os.download) capitano.command(actions.os.buildConfig) -capitano.command(actions.os.configure) capitano.command(actions.os.initialize) # ---------- Config Module ---------- diff --git a/lib/errors.ts b/lib/errors.ts index 7e1a4c0c..d8c8daf4 100644 --- a/lib/errors.ts +++ b/lib/errors.ts @@ -1,5 +1,5 @@ /* -Copyright 2016-2017 Balena +Copyright 2016-2019 Balena Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/lib/preparser.ts b/lib/preparser.ts index 09eac4d9..42fb6d87 100644 --- a/lib/preparser.ts +++ b/lib/preparser.ts @@ -132,6 +132,7 @@ export const convertedCommands = [ 'env:add', 'env:rename', 'env:rm', + 'os:configure', 'version', ]; diff --git a/lib/utils/config.ts b/lib/utils/config.ts index eaf895e7..d5ff3f5f 100644 --- a/lib/utils/config.ts +++ b/lib/utils/config.ts @@ -1,5 +1,5 @@ /* -Copyright 2016-2017 Balena +Copyright 2016-2019 Balena Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ import * as semver from 'resin-semver'; const balena = BalenaSdk.fromSharedOptions(); -interface ImgConfig { +export interface ImgConfig { applicationName: string; applicationId: number; deviceType: string; @@ -103,7 +103,7 @@ export function generateDeviceConfig( device: BalenaSdk.Device & { belongs_to__application: BalenaSdk.PineDeferred; }, - deviceApiKey: string | true | null, + deviceApiKey: string | true | undefined, options: { version: string }, ) { return balena.models.application diff --git a/lib/utils/helpers.ts b/lib/utils/helpers.ts index 86c506b0..1fbee686 100644 --- a/lib/utils/helpers.ts +++ b/lib/utils/helpers.ts @@ -28,7 +28,7 @@ const balena = BalenaSdk.fromSharedOptions(); export function getGroupDefaults(group: { options: Array<{ name: string; default?: string }>; -}): { [name: string]: string | undefined } { +}): { [name: string]: string | number | undefined } { return _.chain(group) .get('options') .map(question => [question.name, question.default]) @@ -122,14 +122,6 @@ export const areDeviceTypesCompatible = ( deviceTypeA.arch === deviceTypeB.arch && !!deviceTypeA.isDependent === !!deviceTypeB.isDependent; -export async function getOsVersion( - image: string, - manifest: BalenaSdk.DeviceType, -): Promise { - const init = await import('balena-device-init'); - return init.getImageOsVersion(image, manifest); -} - export async function osProgressHandler(step: InitializeEmitter) { step.on('stdout', process.stdout.write.bind(process.stdout)); step.on('stderr', process.stderr.write.bind(process.stderr)); diff --git a/lib/utils/patterns.ts b/lib/utils/patterns.ts index 235e1b81..ed53afd4 100644 --- a/lib/utils/patterns.ts +++ b/lib/utils/patterns.ts @@ -1,5 +1,5 @@ /* -Copyright 2016-2017 Balena +Copyright 2016-2019 Balena Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/tests/commands/help.spec.ts b/tests/commands/help.spec.ts index fef2f584..7ee43082 100644 --- a/tests/commands/help.spec.ts +++ b/tests/commands/help.spec.ts @@ -71,7 +71,7 @@ Additional commands: logout logout from balena note <|note> set a device note os build-config build the OS config and save it to the JSON file - os configure configure an os image + os configure configure a previously downloaded balenaOS image os download download an unconfigured os image os initialize initialize an os image os versions show the available balenaOS versions for the given device type