diff --git a/doc/cli.markdown b/doc/cli.markdown index aa4353ec..0c45b3c5 100644 --- a/doc/cli.markdown +++ b/doc/cli.markdown @@ -164,10 +164,10 @@ Users are encouraged to regularly update the balena CLI to the latest version. - [apps](#apps) - [app <nameorslug>](#app-nameorslug) - [app create <name>](#app-create-name) - - [app purge <name>](#app-purge-name) - - [app rename <nameorslug> [newname]](#app-rename-nameorslug-newname) - - [app restart <name>](#app-restart-name) - - [app rm <name>](#app-rm-name) + - [app purge <application>](#app-purge-application) + - [app rename <application> [newname]](#app-rename-application-newname) + - [app restart <application>](#app-restart-application) + - [app rm <application>](#app-rm-application) - Authentication @@ -312,7 +312,7 @@ the API key name list all your balena applications. For detailed information on a particular application, -use `balena app instead`. +use `balena app ` instead. Examples: @@ -328,6 +328,19 @@ No-op since release v12.0.0 Display detailed information about a single balena application. +Applications may be specified by app name, slug, or numeric ID. App slugs +are the recommended option, as they are unique and unambiguous. Slugs +can be listed with the `balena apps` command. Note that slugs may change +if the application is renamed. +App names are not unique and may result in "Application is ambiguous" errors +at any time (even if it "used to work in the past"), for example if the name +clashes with a newly created public application, or with apps from other balena +accounts that you may have been invited to as a member. For this reason, app +names are especially discouraged in scripts (e.g. CI environments). +Numeric app IDs are deprecated because they consist of an implementation detail +of the balena backend. We intend to remove support for numeric IDs at some point +in the future. + Examples: $ balena app MyApp @@ -335,9 +348,9 @@ Examples: ### Arguments -#### NAMEORSLUG +#### APPLICATION -application name or org/name slug +application name, slug (preferred), or numeric ID (deprecated) ### Options @@ -382,30 +395,57 @@ handle of the organization the application should belong to application device type (Check available types with `balena devices supported`) -## app purge <name> +## app purge <application> Purge data from all devices belonging to an application. This will clear the application's /data directory. +Applications may be specified by app name, slug, or numeric ID. App slugs +are the recommended option, as they are unique and unambiguous. Slugs +can be listed with the `balena apps` command. Note that slugs may change +if the application is renamed. +App names are not unique and may result in "Application is ambiguous" errors +at any time (even if it "used to work in the past"), for example if the name +clashes with a newly created public application, or with apps from other balena +accounts that you may have been invited to as a member. For this reason, app +names are especially discouraged in scripts (e.g. CI environments). +Numeric app IDs are deprecated because they consist of an implementation detail +of the balena backend. We intend to remove support for numeric IDs at some point +in the future. + Examples: $ balena app purge MyApp + $ balena app purge myorg/myapp ### Arguments -#### NAME +#### APPLICATION -application name or numeric ID +application name, slug (preferred), or numeric ID (deprecated) ### Options -## app rename <nameOrSlug> [newName] +## app rename <application> [newName] Rename an application. Note, if the `newName` parameter is omitted, it will be prompted for interactively. +Applications may be specified by app name, slug, or numeric ID. App slugs +are the recommended option, as they are unique and unambiguous. Slugs +can be listed with the `balena apps` command. Note that slugs may change +if the application is renamed. +App names are not unique and may result in "Application is ambiguous" errors +at any time (even if it "used to work in the past"), for example if the name +clashes with a newly created public application, or with apps from other balena +accounts that you may have been invited to as a member. For this reason, app +names are especially discouraged in scripts (e.g. CI environments). +Numeric app IDs are deprecated because they consist of an implementation detail +of the balena backend. We intend to remove support for numeric IDs at some point +in the future. + Examples: $ balena app rename OldName @@ -414,9 +454,9 @@ Examples: ### Arguments -#### NAMEORSLUG +#### APPLICATION -application name or org/name slug +application name, slug (preferred), or numeric ID (deprecated) #### NEWNAME @@ -424,38 +464,66 @@ the new name for the application ### Options -## app restart <name> +## app restart <application> Restart all devices belonging to an application. +Applications may be specified by app name, slug, or numeric ID. App slugs +are the recommended option, as they are unique and unambiguous. Slugs +can be listed with the `balena apps` command. Note that slugs may change +if the application is renamed. +App names are not unique and may result in "Application is ambiguous" errors +at any time (even if it "used to work in the past"), for example if the name +clashes with a newly created public application, or with apps from other balena +accounts that you may have been invited to as a member. For this reason, app +names are especially discouraged in scripts (e.g. CI environments). +Numeric app IDs are deprecated because they consist of an implementation detail +of the balena backend. We intend to remove support for numeric IDs at some point +in the future. + Examples: $ balena app restart MyApp + $ balena app restart myorg/myapp ### Arguments -#### NAME +#### APPLICATION -application name or numeric ID +application name, slug (preferred), or numeric ID (deprecated) ### Options -## app rm <name> +## app rm <application> Permanently remove a balena application. The --yes option may be used to avoid interactive confirmation. +Applications may be specified by app name, slug, or numeric ID. App slugs +are the recommended option, as they are unique and unambiguous. Slugs +can be listed with the `balena apps` command. Note that slugs may change +if the application is renamed. +App names are not unique and may result in "Application is ambiguous" errors +at any time (even if it "used to work in the past"), for example if the name +clashes with a newly created public application, or with apps from other balena +accounts that you may have been invited to as a member. For this reason, app +names are especially discouraged in scripts (e.g. CI environments). +Numeric app IDs are deprecated because they consist of an implementation detail +of the balena backend. We intend to remove support for numeric IDs at some point +in the future. + Examples: $ balena app rm MyApp $ balena app rm MyApp --yes + $ balena app rm myorg/myapp ### Arguments -#### NAME +#### APPLICATION -application name or numeric ID +application name, slug (preferred), or numeric ID (deprecated) ### Options @@ -546,6 +614,19 @@ list all devices that belong to you. You can filter the devices by application by using the `--application` option. +Applications may be specified by app name, slug, or numeric ID. App slugs +are the recommended option, as they are unique and unambiguous. Slugs +can be listed with the `balena apps` command. Note that slugs may change +if the application is renamed. +App names are not unique and may result in "Application is ambiguous" errors +at any time (even if it "used to work in the past"), for example if the name +clashes with a newly created public application, or with apps from other balena +accounts that you may have been invited to as a member. For this reason, app +names are especially discouraged in scripts (e.g. CI environments). +Numeric app IDs are deprecated because they consist of an implementation detail +of the balena backend. We intend to remove support for numeric IDs at some point +in the future. + The --json option is recommended when scripting the output of this command, because field names are less likely to change in JSON format and because it better represents data types like arrays, empty strings and null values. @@ -558,12 +639,13 @@ Examples: $ balena devices --application MyApp $ balena devices --app MyApp $ balena devices -a MyApp + $ balena devices -a myorg/myapp ### Options #### -a, --application APPLICATION -application name +application name, slug (preferred), or numeric ID (deprecated) #### --app APP @@ -642,22 +724,36 @@ the uuid of the device to identify ## device init -Initialise a device by downloading the OS image of a certain application +Initialize a device by downloading the OS image of a certain application and writing it to an SD Card. Note, if the application option is omitted it will be prompted for interactively. +Applications may be specified by app name, slug, or numeric ID. App slugs +are the recommended option, as they are unique and unambiguous. Slugs +can be listed with the `balena apps` command. Note that slugs may change +if the application is renamed. +App names are not unique and may result in "Application is ambiguous" errors +at any time (even if it "used to work in the past"), for example if the name +clashes with a newly created public application, or with apps from other balena +accounts that you may have been invited to as a member. For this reason, app +names are especially discouraged in scripts (e.g. CI environments). +Numeric app IDs are deprecated because they consist of an implementation detail +of the balena backend. We intend to remove support for numeric IDs at some point +in the future. + Examples: $ balena device init $ balena device init --application MyApp + $ balena device init -a myorg/myapp ### Options #### -a, --application APPLICATION -application name +application name, slug (preferred), or numeric ID (deprecated) #### --app APP @@ -696,11 +792,25 @@ Move one or more devices to another application. Note, if the application option is omitted it will be prompted for interactively. +Applications may be specified by app name, slug, or numeric ID. App slugs +are the recommended option, as they are unique and unambiguous. Slugs +can be listed with the `balena apps` command. Note that slugs may change +if the application is renamed. +App names are not unique and may result in "Application is ambiguous" errors +at any time (even if it "used to work in the past"), for example if the name +clashes with a newly created public application, or with apps from other balena +accounts that you may have been invited to as a member. For this reason, app +names are especially discouraged in scripts (e.g. CI environments). +Numeric app IDs are deprecated because they consist of an implementation detail +of the balena backend. We intend to remove support for numeric IDs at some point +in the future. + Examples: $ balena device move 7cf02a6 $ balena device move 7cf02a6,dc39e52 $ balena device move 7cf02a6 --application MyNewApp + $ balena device move 7cf02a6 -a myorg/mynewapp ### Arguments @@ -712,7 +822,7 @@ comma-separated list (no blank spaces) of device UUIDs to be moved #### -a, --application APPLICATION -application name +application name, slug (preferred), or numeric ID (deprecated) #### --app APP @@ -833,16 +943,30 @@ force action if the update lock is set Register a device to an application. +Applications may be specified by app name, slug, or numeric ID. App slugs +are the recommended option, as they are unique and unambiguous. Slugs +can be listed with the `balena apps` command. Note that slugs may change +if the application is renamed. +App names are not unique and may result in "Application is ambiguous" errors +at any time (even if it "used to work in the past"), for example if the name +clashes with a newly created public application, or with apps from other balena +accounts that you may have been invited to as a member. For this reason, app +names are especially discouraged in scripts (e.g. CI environments). +Numeric app IDs are deprecated because they consist of an implementation detail +of the balena backend. We intend to remove support for numeric IDs at some point +in the future. + Examples: $ balena device register MyApp $ balena device register MyApp --uuid + $ balena device register myorg/myapp --uuid ### Arguments #### APPLICATION -the name or id of application to register device with +application name, slug (preferred), or numeric ID (deprecated) ### Options @@ -983,9 +1107,23 @@ application linked to the device is no longer accessible by the current user (for example, in case the current user has been removed from the application by its owner). +Applications may be specified by app name, slug, or numeric ID. App slugs +are the recommended option, as they are unique and unambiguous. Slugs +can be listed with the `balena apps` command. Note that slugs may change +if the application is renamed. +App names are not unique and may result in "Application is ambiguous" errors +at any time (even if it "used to work in the past"), for example if the name +clashes with a newly created public application, or with apps from other balena +accounts that you may have been invited to as a member. For this reason, app +names are especially discouraged in scripts (e.g. CI environments). +Numeric app IDs are deprecated because they consist of an implementation detail +of the balena backend. We intend to remove support for numeric IDs at some point +in the future. + Examples: $ balena envs --application MyApp + $ balena envs --application myorg/myapp $ balena envs --application MyApp --json $ balena envs --application MyApp --service MyService $ balena envs --application MyApp --service MyService @@ -1003,7 +1141,7 @@ No-op since balena CLI v12.0.0. #### -a, --application APPLICATION -application name +application name, slug (preferred), or numeric ID (deprecated) #### -c, --config @@ -1118,10 +1256,24 @@ therefore the --service option cannot be used when the variable name starts with a reserved prefix. When defining custom application variables, please avoid the reserved prefixes. +Applications may be specified by app name, slug, or numeric ID. App slugs +are the recommended option, as they are unique and unambiguous. Slugs +can be listed with the `balena apps` command. Note that slugs may change +if the application is renamed. +App names are not unique and may result in "Application is ambiguous" errors +at any time (even if it "used to work in the past"), for example if the name +clashes with a newly created public application, or with apps from other balena +accounts that you may have been invited to as a member. For this reason, app +names are especially discouraged in scripts (e.g. CI environments). +Numeric app IDs are deprecated because they consist of an implementation detail +of the balena backend. We intend to remove support for numeric IDs at some point +in the future. + Examples: $ balena env add TERM --application MyApp $ balena env add EDITOR vim --application MyApp + $ balena env add EDITOR vim -a myorg/myapp $ balena env add EDITOR vim --application MyApp,MyApp2 $ balena env add EDITOR vim --application MyApp --service MyService $ balena env add EDITOR vim --application MyApp,MyApp2 --service MyService,MyService2 @@ -1144,7 +1296,7 @@ variable value; if omitted, use value from this process' environment #### -a, --application APPLICATION -application name +application name, slug (preferred), or numeric ID (deprecated) #### -d, --device DEVICE @@ -1228,9 +1380,23 @@ select a service variable (may be used together with the --device option) List all tags and their values for a particular application, device or release. +Applications may be specified by app name, slug, or numeric ID. App slugs +are the recommended option, as they are unique and unambiguous. Slugs +can be listed with the `balena apps` command. Note that slugs may change +if the application is renamed. +App names are not unique and may result in "Application is ambiguous" errors +at any time (even if it "used to work in the past"), for example if the name +clashes with a newly created public application, or with apps from other balena +accounts that you may have been invited to as a member. For this reason, app +names are especially discouraged in scripts (e.g. CI environments). +Numeric app IDs are deprecated because they consist of an implementation detail +of the balena backend. We intend to remove support for numeric IDs at some point +in the future. + Examples: $ balena tags --application MyApp + $ balena tags -a myorg/myapp $ balena tags --device 7cf02a6 $ balena tags --release 1234 $ balena tags --release b376b0e544e9429483b656490e5b9443b4349bd6 @@ -1239,7 +1405,11 @@ Examples: #### -a, --application APPLICATION -application name +application name, slug (preferred), or numeric ID (deprecated) + +#### --app APP + +same as '--application' #### -d, --device DEVICE @@ -1249,17 +1419,27 @@ device UUID release id -#### --app APP - -same as '--application' - ## tag rm <tagKey> Remove a tag from an application, device or release. +Applications may be specified by app name, slug, or numeric ID. App slugs +are the recommended option, as they are unique and unambiguous. Slugs +can be listed with the `balena apps` command. Note that slugs may change +if the application is renamed. +App names are not unique and may result in "Application is ambiguous" errors +at any time (even if it "used to work in the past"), for example if the name +clashes with a newly created public application, or with apps from other balena +accounts that you may have been invited to as a member. For this reason, app +names are especially discouraged in scripts (e.g. CI environments). +Numeric app IDs are deprecated because they consist of an implementation detail +of the balena backend. We intend to remove support for numeric IDs at some point +in the future. + Examples: $ balena tag rm myTagKey --application MyApp + $ balena tag rm myTagKey -a myorg/myapp $ balena tag rm myTagKey --device 7cf02a6 $ balena tag rm myTagKey --release 1234 $ balena tag rm myTagKey --release b376b0e544e9429483b656490e5b9443b4349bd6 @@ -1274,7 +1454,11 @@ the key string of the tag #### -a, --application APPLICATION -application name +application name, slug (preferred), or numeric ID (deprecated) + +#### --app APP + +same as '--application' #### -d, --device DEVICE @@ -1284,10 +1468,6 @@ device UUID release id -#### --app APP - -same as '--application' - ## tag set <tagKey> [value] Set a tag on an application, device or release. @@ -1296,9 +1476,23 @@ You can optionally provide a value to be associated with the created tag, as an extra argument after the tag key. If a value isn't provided, a tag with an empty value is created. +Applications may be specified by app name, slug, or numeric ID. App slugs +are the recommended option, as they are unique and unambiguous. Slugs +can be listed with the `balena apps` command. Note that slugs may change +if the application is renamed. +App names are not unique and may result in "Application is ambiguous" errors +at any time (even if it "used to work in the past"), for example if the name +clashes with a newly created public application, or with apps from other balena +accounts that you may have been invited to as a member. For this reason, app +names are especially discouraged in scripts (e.g. CI environments). +Numeric app IDs are deprecated because they consist of an implementation detail +of the balena backend. We intend to remove support for numeric IDs at some point +in the future. + Examples: $ balena tag set mySimpleTag --application MyApp + $ balena tag set mySimpleTag -a myorg/myapp $ balena tag set myCompositeTag myTagValue --application MyApp $ balena tag set myCompositeTag myTagValue --device 7cf02a6 $ balena tag set myCompositeTag "my tag value with whitespaces" --device 7cf02a6 @@ -1320,7 +1514,11 @@ the optional value associated with the tag #### -a, --application APPLICATION -application name +application name, slug (preferred), or numeric ID (deprecated) + +#### --app APP + +same as '--application' #### -d, --device DEVICE @@ -1330,10 +1528,6 @@ device UUID release id -#### --app APP - -same as '--application' - # Help and Version ## help [command] @@ -1828,6 +2022,19 @@ https://developer.gnome.org/NetworkManager/stable/nm-settings.html 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. +Applications may be specified by app name, slug, or numeric ID. App slugs +are the recommended option, as they are unique and unambiguous. Slugs +can be listed with the `balena apps` command. Note that slugs may change +if the application is renamed. +App names are not unique and may result in "Application is ambiguous" errors +at any time (even if it "used to work in the past"), for example if the name +clashes with a newly created public application, or with apps from other balena +accounts that you may have been invited to as a member. For this reason, app +names are especially discouraged in scripts (e.g. CI environments). +Numeric app IDs are deprecated because they consist of an implementation detail +of the balena backend. We intend to remove support for numeric IDs at some point +in the future. + Note: This command is currently not supported on Windows natively. Windows users are advised to install the Windows Subsystem for Linux (WSL) with Ubuntu, and use the Linux release of the balena CLI: @@ -1838,6 +2045,7 @@ 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 -a myorg/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 @@ -1854,14 +2062,14 @@ path to a balenaOS image file, e.g. "rpi3.img" ask advanced configuration questions (when in interactive mode) +#### -a, --application APPLICATION + +application name, slug (preferred), or numeric ID (deprecated) + #### --app APP same as '--application' -#### -a, --application APPLICATION - -application name - #### --config CONFIG path to a pre-generated config.json file to be injected in the OS image @@ -1954,7 +2162,20 @@ by specifying an option for each question on the command line, if you know the q that will be asked for the relevant device type. 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. +you can pass the --deviceType argument along with --application to specify the target device type. + +Applications may be specified by app name, slug, or numeric ID. App slugs +are the recommended option, as they are unique and unambiguous. Slugs +can be listed with the `balena apps` command. Note that slugs may change +if the application is renamed. +App names are not unique and may result in "Application is ambiguous" errors +at any time (even if it "used to work in the past"), for example if the name +clashes with a newly created public application, or with apps from other balena +accounts that you may have been invited to as a member. For this reason, app +names are especially discouraged in scripts (e.g. CI environments). +Numeric app IDs are deprecated because they consist of an implementation detail +of the balena backend. We intend to remove support for numeric IDs at some point +in the future. Examples: @@ -1963,7 +2184,8 @@ Examples: $ balena config generate --device 7cf02a6 --version 2.12.7 --device-api-key $ balena config generate --device 7cf02a6 --version 2.12.7 --output config.json $ balena config generate --app MyApp --version 2.12.7 - $ balena config generate --app MyApp --version 2.12.7 --device-type fincm3 + $ balena config generate --app myorg/myapp --version 2.12.7 + $ balena config generate --app MyApp --version 2.12.7 --deviceType fincm3 $ balena config generate --app MyApp --version 2.12.7 --output config.json $ balena config generate --app MyApp --version 2.12.7 --network wifi --wifiSsid mySsid --wifiKey abcdefgh --appUpdatePollInterval 1 @@ -1975,7 +2197,7 @@ a balenaOS version #### -a, --application APPLICATION -application name +application name, slug (preferred), or numeric ID (deprecated) #### --app APP @@ -2129,13 +2351,27 @@ After preloading, the balenaOS image file can be flashed to a device's SD card. When the device boots, it will not need to download the application, as it was preloaded. +Applications may be specified by app name, slug, or numeric ID. App slugs +are the recommended option, as they are unique and unambiguous. Slugs +can be listed with the `balena apps` command. Note that slugs may change +if the application is renamed. +App names are not unique and may result in "Application is ambiguous" errors +at any time (even if it "used to work in the past"), for example if the name +clashes with a newly created public application, or with apps from other balena +accounts that you may have been invited to as a member. For this reason, app +names are especially discouraged in scripts (e.g. CI environments). +Numeric app IDs are deprecated because they consist of an implementation detail +of the balena backend. We intend to remove support for numeric IDs at some point +in the future. + Warning: "balena preload" requires Docker to be correctly installed in your shell environment. For more information (including Windows support) check: https://github.com/balena-io/balena-cli/blob/master/INSTALL.md Examples: - $ balena preload balena.img --app 1234 --commit e1f2592fc6ee949e68756d4f4a48e49bff8d72a0 --splash-image image.png + $ balena preload balena.img --app MyApp --commit e1f2592fc6ee949e68756d4f4a48e49bff8d72a0 + $ balena preload balena.img --app myorg/myapp --commit e1f2592fc6ee949e68756d4f4a48e49bff8d72a0 --splash-image image.png $ balena preload balena.img ### Arguments @@ -2148,7 +2384,7 @@ the image file path #### -a, --app APP -name of the application to preload +application name, slug (preferred), or numeric ID (deprecated) #### -c, --commit COMMIT @@ -2946,11 +3182,25 @@ scan the local network for balenaOS devices and prompt you to select one from an interactive picker. This requires root privileges. Likewise, if the application flag is not provided then a picker will be shown. +Applications may be specified by app name, slug, or numeric ID. App slugs +are the recommended option, as they are unique and unambiguous. Slugs +can be listed with the `balena apps` command. Note that slugs may change +if the application is renamed. +App names are not unique and may result in "Application is ambiguous" errors +at any time (even if it "used to work in the past"), for example if the name +clashes with a newly created public application, or with apps from other balena +accounts that you may have been invited to as a member. For this reason, app +names are especially discouraged in scripts (e.g. CI environments). +Numeric app IDs are deprecated because they consist of an implementation detail +of the balena backend. We intend to remove support for numeric IDs at some point +in the future. + Examples: $ balena join $ balena join balena.local $ balena join balena.local --application MyApp + $ balena join balena.local -a myorg/myapp $ balena join 192.168.1.25 $ balena join 192.168.1.25 --application MyApp @@ -2964,7 +3214,7 @@ the IP or hostname of device #### -a, --application APPLICATION -application name +application name, slug (preferred), or numeric ID (deprecated) #### -i, --pollInterval POLLINTERVAL @@ -3020,11 +3270,24 @@ or hours, e.g. '12h', '2d'. Both --device and --application flags accept multiple values, specified as a comma-separated list (with no spaces). +Applications may be specified by app name, slug, or numeric ID. App slugs +are the recommended option, as they are unique and unambiguous. Slugs +can be listed with the `balena apps` command. Note that slugs may change +if the application is renamed. +App names are not unique and may result in "Application is ambiguous" errors +at any time (even if it "used to work in the past"), for example if the name +clashes with a newly created public application, or with apps from other balena +accounts that you may have been invited to as a member. For this reason, app +names are especially discouraged in scripts (e.g. CI environments). +Numeric app IDs are deprecated because they consist of an implementation detail +of the balena backend. We intend to remove support for numeric IDs at some point +in the future. + Examples: balena support enable --device ab346f,cd457a --duration 3d balena support enable --application app3 --duration 12h - balena support disable -a myApp + balena support disable -a myorg/myapp ### Arguments @@ -3040,7 +3303,7 @@ comma-separated list (no spaces) of device UUIDs #### -a, --application APPLICATION -comma-separated list (no spaces) of application names +comma-separated list (no spaces) of application names or org/name slugs #### -t, --duration DURATION diff --git a/lib/commands/app/index.ts b/lib/commands/app/index.ts index 4b05f298..228851ef 100644 --- a/lib/commands/app/index.ts +++ b/lib/commands/app/index.ts @@ -18,7 +18,9 @@ import { flags } from '@oclif/command'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; +import * as ca from '../../utils/common-args'; import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy'; +import { applicationIdInfo } from '../../utils/messages'; import type { Release } from 'balena-sdk'; interface FlagsDef { @@ -26,7 +28,7 @@ interface FlagsDef { } interface ArgsDef { - nameOrSlug: string; + application: string; } export default class AppCmd extends Command { @@ -34,16 +36,12 @@ export default class AppCmd extends Command { Display information about a single application. Display detailed information about a single balena application. + + ${applicationIdInfo.split('\n').join('\n\t\t')} `; public static examples = ['$ balena app MyApp', '$ balena app myorg/myapp']; - public static args = [ - { - name: 'nameOrSlug', - description: 'application name or org/name slug', - required: true, - }, - ]; + public static args = [ca.applicationRequired]; public static usage = 'app '; @@ -61,7 +59,7 @@ export default class AppCmd extends Command { const application = (await getApplication( getBalenaSdk(), - params.nameOrSlug, + params.application, { $expand: { is_for__device_type: { $select: 'slug' }, diff --git a/lib/commands/app/purge.ts b/lib/commands/app/purge.ts index ac4e380f..85360a56 100644 --- a/lib/commands/app/purge.ts +++ b/lib/commands/app/purge.ts @@ -18,35 +18,36 @@ import { flags } from '@oclif/command'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; +import * as ca from '../../utils/common-args'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { tryAsInteger } from '../../utils/validation'; +import { applicationIdInfo } from '../../utils/messages'; interface FlagsDef { help: void; } interface ArgsDef { - name: string; + application: string; } -export default class AppRestartCmd extends Command { +export default class AppPurgeCmd extends Command { public static description = stripIndent` Purge data from an application. Purge data from all devices belonging to an application. This will clear the application's /data directory. -`; - public static examples = ['$ balena app purge MyApp']; - public static args = [ - { - name: 'name', - description: 'application name or numeric ID', - required: true, - }, + ${applicationIdInfo.split('\n').join('\n\t\t')} + `; + + public static examples = [ + '$ balena app purge MyApp', + '$ balena app purge myorg/myapp', ]; - public static usage = 'app purge '; + public static args = [ca.applicationRequired]; + + public static usage = 'app purge '; public static flags: flags.Input = { help: cf.help, @@ -55,21 +56,18 @@ export default class AppRestartCmd extends Command { public static authenticated = true; public async run() { - const { args: params } = this.parse(AppRestartCmd); + const { args: params } = this.parse(AppPurgeCmd); + + const { getApplication } = await import('../../utils/sdk'); const balena = getBalenaSdk(); // balena.models.application.purge only accepts a numeric id - // so we must first fetch the app to get it's id, if we have been given a name - let nameOrId = tryAsInteger(params.name); - - if (typeof nameOrId === 'string') { - const app = await balena.models.application.get(nameOrId); - nameOrId = app.id; - } + // so we must first fetch the app to get it's id, + const application = await getApplication(balena, params.application); try { - await balena.models.application.purge(nameOrId); + await balena.models.application.purge(application.id); } catch (e) { if (e.message.toLowerCase().includes('no online device(s) found')) { // application.purge throws an error if no devices are online diff --git a/lib/commands/app/rename.ts b/lib/commands/app/rename.ts index acca72ed..225c02bf 100644 --- a/lib/commands/app/rename.ts +++ b/lib/commands/app/rename.ts @@ -16,11 +16,11 @@ */ import { flags } from '@oclif/command'; -import type { IArg } from '@oclif/parser/lib/args'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; +import * as ca from '../../utils/common-args'; import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy'; -import { lowercaseIfSlug } from '../../utils/normalization'; +import { applicationIdInfo } from '../../utils/messages'; import type { ApplicationType } from 'balena-sdk'; interface FlagsDef { @@ -28,7 +28,7 @@ interface FlagsDef { } interface ArgsDef { - nameOrSlug: string; + application: string; newName?: string; } @@ -40,6 +40,8 @@ export default class AppRenameCmd extends Command { Note, if the \`newName\` parameter is omitted, it will be prompted for interactively. + + ${applicationIdInfo.split('\n').join('\n\t\t')} `; public static examples = [ @@ -48,20 +50,15 @@ export default class AppRenameCmd extends Command { '$ balena app rename myorg/oldname NewName', ]; - public static args: Array> = [ - { - name: 'nameOrSlug', - description: 'application name or org/name slug', - parse: lowercaseIfSlug, - required: true, - }, + public static args = [ + ca.applicationRequired, { name: 'newName', description: 'the new name for the application', }, ]; - public static usage = 'app rename [newName]'; + public static usage = 'app rename [newName]'; public static flags: flags.Input = { help: cf.help, @@ -77,9 +74,9 @@ export default class AppRenameCmd extends Command { const balena = getBalenaSdk(); - // Disambiguate target application (if nameOrSlug is a number, it could either be an ID or a numerical name) + // Disambiguate target application (if params.params is a number, it could either be an ID or a numerical name) const { getApplication } = await import('../../utils/sdk'); - const application = await getApplication(balena, params.nameOrSlug, { + const application = await getApplication(balena, params.application, { $expand: { application_type: { $select: ['is_legacy'], @@ -98,7 +95,7 @@ export default class AppRenameCmd extends Command { const appType = (application.application_type as ApplicationType[])?.[0]; if (appType.is_legacy) { throw new ExpectedError( - `Application ${params.nameOrSlug} is of 'legacy' type, and cannot be renamed.`, + `Application ${params.application} is of 'legacy' type, and cannot be renamed.`, ); } @@ -119,13 +116,24 @@ export default class AppRenameCmd extends Command { // BalenaRequestError: Request error: "organization" and "app_name" must be unique. if ((e.message || '').toLowerCase().includes('unique')) { throw new ExpectedError( - `Error: application ${params.nameOrSlug} already exists.`, + `Error: application ${params.application} already exists.`, ); } throw e; } + // Get application again, to be sure of results + const renamedApplication = await balena.models.application.get( + application.id, + ); + // Output result - console.log(`Application ${params.nameOrSlug} renamed to ${newName}`); + console.log(`Application renamed`); + console.log('From:'); + console.log(`\tname: ${application.app_name}`); + console.log(`\tslug: ${application.slug}`); + console.log('To:'); + console.log(`\tname: ${renamedApplication.app_name}`); + console.log(`\tslug: ${renamedApplication.slug}`); } } diff --git a/lib/commands/app/restart.ts b/lib/commands/app/restart.ts index 3efb6f18..3b523d50 100644 --- a/lib/commands/app/restart.ts +++ b/lib/commands/app/restart.ts @@ -18,15 +18,16 @@ import { flags } from '@oclif/command'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; +import * as ca from '../../utils/common-args'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { tryAsInteger } from '../../utils/validation'; +import { applicationIdInfo } from '../../utils/messages'; interface FlagsDef { help: void; } interface ArgsDef { - name: string; + application: string; } export default class AppRestartCmd extends Command { @@ -34,18 +35,18 @@ export default class AppRestartCmd extends Command { Restart an application. Restart all devices belonging to an application. -`; - public static examples = ['$ balena app restart MyApp']; - public static args = [ - { - name: 'name', - description: 'application name or numeric ID', - required: true, - }, + ${applicationIdInfo.split('\n').join('\n\t\t')} + `; + + public static examples = [ + '$ balena app restart MyApp', + '$ balena app restart myorg/myapp', ]; - public static usage = 'app restart '; + public static args = [ca.applicationRequired]; + + public static usage = 'app restart '; public static flags: flags.Input = { help: cf.help, @@ -56,6 +57,13 @@ export default class AppRestartCmd extends Command { public async run() { const { args: params } = this.parse(AppRestartCmd); - await getBalenaSdk().models.application.restart(tryAsInteger(params.name)); + const { getApplication } = await import('../../utils/sdk'); + + const balena = getBalenaSdk(); + + // Disambiguate application (if is a number, it could either be an ID or a numerical name) + const application = await getApplication(balena, params.application); + + await balena.models.application.restart(application.id); } } diff --git a/lib/commands/app/rm.ts b/lib/commands/app/rm.ts index 9304fc47..9c10e7b0 100644 --- a/lib/commands/app/rm.ts +++ b/lib/commands/app/rm.ts @@ -18,8 +18,9 @@ import { flags } from '@oclif/command'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; +import * as ca from '../../utils/common-args'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { tryAsInteger } from '../../utils/validation'; +import { applicationIdInfo } from '../../utils/messages'; interface FlagsDef { yes: boolean; @@ -27,7 +28,7 @@ interface FlagsDef { } interface ArgsDef { - name: string; + application: string; } export default class AppRmCmd extends Command { @@ -37,21 +38,19 @@ export default class AppRmCmd extends Command { Permanently remove a balena application. The --yes option may be used to avoid interactive confirmation. -`; + + ${applicationIdInfo.split('\n').join('\n\t\t')} + `; + public static examples = [ '$ balena app rm MyApp', '$ balena app rm MyApp --yes', + '$ balena app rm myorg/myapp', ]; - public static args = [ - { - name: 'name', - description: 'application name or numeric ID', - required: true, - }, - ]; + public static args = [ca.applicationRequired]; - public static usage = 'app rm '; + public static usage = 'app rm '; public static flags: flags.Input = { yes: cf.yes, @@ -65,15 +64,20 @@ export default class AppRmCmd extends Command { AppRmCmd, ); - const patterns = await import('../../utils/patterns'); + const { confirm } = await import('../../utils/patterns'); + const { getApplication } = await import('../../utils/sdk'); + const balena = getBalenaSdk(); // Confirm - await patterns.confirm( + await confirm( options.yes ?? false, - `Are you sure you want to delete application ${params.name}?`, + `Are you sure you want to delete application ${params.application}?`, ); + // Disambiguate application (if is a number, it could either be an ID or a numerical name) + const application = await getApplication(balena, params.application); + // Remove - await getBalenaSdk().models.application.remove(tryAsInteger(params.name)); + await balena.models.application.remove(application.id); } } diff --git a/lib/commands/apps.ts b/lib/commands/apps.ts index c67e60a8..aa9d55ff 100644 --- a/lib/commands/apps.ts +++ b/lib/commands/apps.ts @@ -37,8 +37,9 @@ export default class AppsCmd extends Command { list all your balena applications. For detailed information on a particular application, - use \`balena app instead\`. -`; + use \`balena app \` instead. + `; + public static examples = ['$ balena apps']; public static usage = 'apps'; diff --git a/lib/commands/config/generate.ts b/lib/commands/config/generate.ts index c1c0c897..7a2c81c9 100644 --- a/lib/commands/config/generate.ts +++ b/lib/commands/config/generate.ts @@ -19,6 +19,7 @@ import { flags } from '@oclif/command'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, getCliForm, stripIndent } from '../../utils/lazy'; +import { applicationIdInfo } from '../../utils/messages'; import type { PineDeferred } from 'balena-sdk'; interface FlagsDef { @@ -51,7 +52,9 @@ export default class ConfigGenerateCmd extends Command { that will be asked for the relevant device type. 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. + you can pass the --deviceType argument along with --application to specify the target device type. + + ${applicationIdInfo.split('\n').join('\n\t\t')} `; public static examples = [ @@ -60,7 +63,8 @@ export default class ConfigGenerateCmd extends Command { '$ balena config generate --device 7cf02a6 --version 2.12.7 --device-api-key ', '$ balena config generate --device 7cf02a6 --version 2.12.7 --output config.json', '$ balena config generate --app MyApp --version 2.12.7', - '$ balena config generate --app MyApp --version 2.12.7 --device-type fincm3', + '$ balena config generate --app myorg/myapp --version 2.12.7', + '$ balena config generate --app MyApp --version 2.12.7 --deviceType fincm3', '$ balena config generate --app MyApp --version 2.12.7 --output config.json', '$ balena config generate --app MyApp --version 2.12.7 --network wifi --wifiSsid mySsid --wifiKey abcdefgh --appUpdatePollInterval 1', ]; @@ -72,15 +76,8 @@ export default class ConfigGenerateCmd extends Command { description: 'a balenaOS version', required: true, }), - application: flags.string({ - description: 'application name', - char: 'a', - exclusive: ['app', 'device'], - }), - app: flags.string({ - description: "same as '--application'", - exclusive: ['application', 'device'], - }), + application: { ...cf.application, exclusive: ['app', 'device'] }, + app: { ...cf.app, exclusive: ['application', 'device'] }, device: flags.string({ description: 'device uuid', char: 'd', @@ -154,6 +151,7 @@ export default class ConfigGenerateCmd extends Command { }; resourceDeviceType = device.is_of__device_type[0].slug; } else { + // Disambiguate application (if is a number, it could either be an ID or a numerical name) application = (await getApplication(balena, options.application!, { $expand: { is_for__device_type: { $select: 'slug' }, @@ -227,17 +225,8 @@ export default class ConfigGenerateCmd extends Command { $ balena help config generate `; - protected readonly deviceTypeNotAllowedMessage = stripIndent` - Specifying a different device type is only supported when - generating a config for an application: - - * An application, with --app - * A specific device type, with --device-type - - See the help page for examples: - - $ balena help config generate - `; + protected readonly deviceTypeNotAllowedMessage = + 'The --deviceType option can only be used alongside the --application option'; protected async validateOptions(options: FlagsDef) { const { ExpectedError } = await import('../../errors'); diff --git a/lib/commands/device/init.ts b/lib/commands/device/init.ts index 9898c40f..332c48d9 100644 --- a/lib/commands/device/init.ts +++ b/lib/commands/device/init.ts @@ -19,6 +19,7 @@ import { flags } from '@oclif/command'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; +import { applicationIdInfo } from '../../utils/messages'; import { runCommand } from '../../utils/helpers'; interface FlagsDef { @@ -34,17 +35,21 @@ interface FlagsDef { export default class DeviceInitCmd extends Command { public static description = stripIndent` - Initialise a device with balenaOS. + Initialize a device with balenaOS. - Initialise a device by downloading the OS image of a certain application + Initialize a device by downloading the OS image of a certain application and writing it to an SD Card. Note, if the application option is omitted it will be prompted for interactively. - `; + + ${applicationIdInfo.split('\n').join('\n\t\t')} + `; + public static examples = [ '$ balena device init', '$ balena device init --application MyApp', + '$ balena device init -a myorg/myapp', ]; public static usage = 'device init'; @@ -98,7 +103,7 @@ export default class DeviceInitCmd extends Command { const application = (await getApplication( balena, options['application'] || - (await (await import('../../utils/patterns')).selectApplication()), + (await (await import('../../utils/patterns')).selectApplication()).id, { $expand: { is_for__device_type: { diff --git a/lib/commands/device/move.ts b/lib/commands/device/move.ts index d4189edb..99e191c1 100644 --- a/lib/commands/device/move.ts +++ b/lib/commands/device/move.ts @@ -20,9 +20,8 @@ import type { IArg } from '@oclif/parser/lib/args'; import type { Application, BalenaSDK } from 'balena-sdk'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; -import { expandForAppName } from '../../utils/helpers'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { tryAsInteger } from '../../utils/validation'; +import { applicationIdInfo } from '../../utils/messages'; import { ExpectedError } from '../../errors'; interface ExtendedDevice extends DeviceWithDeviceType { @@ -47,11 +46,15 @@ export default class DeviceMoveCmd extends Command { Note, if the application option is omitted it will be prompted for interactively. - `; + + ${applicationIdInfo.split('\n').join('\n\t\t')} + `; + public static examples = [ '$ balena device move 7cf02a6', '$ balena device move 7cf02a6,dc39e52', '$ balena device move 7cf02a6 --application MyNewApp', + '$ balena device move 7cf02a6 -a myorg/mynewapp', ]; public static args: Array> = [ @@ -80,6 +83,9 @@ export default class DeviceMoveCmd extends Command { const balena = getBalenaSdk(); + const { tryAsInteger } = await import('../../utils/validation'); + const { expandForAppName } = await import('../../utils/helpers'); + options.application = options.application || options.app; delete options.app; @@ -106,16 +112,21 @@ export default class DeviceMoveCmd extends Command { : 'N/a'; } + // Disambiguate application (if is a number, it could either be an ID or a numerical name) + const { getApplication } = await import('../../utils/sdk'); + // Get destination application - const application = - options.application || - (await this.interactivelySelectApplication(balena, devices)); + const application = options.application + ? await getApplication(balena, options.application) + : await this.interactivelySelectApplication(balena, devices); // Move each device for (const uuid of deviceIds) { try { - await balena.models.device.move(uuid, tryAsInteger(application)); - console.info(`${uuid} was moved to ${application}`); + await balena.models.device.move(uuid, application.id); + console.info( + `Device ${uuid} was moved to application ${application.slug}`, + ); } catch (err) { console.info(`${err.message}, uuid: ${uuid}`); process.exitCode = 1; diff --git a/lib/commands/device/register.ts b/lib/commands/device/register.ts index a323b42b..5e380385 100644 --- a/lib/commands/device/register.ts +++ b/lib/commands/device/register.ts @@ -19,7 +19,9 @@ import { flags } from '@oclif/command'; import type { IArg } from '@oclif/parser/lib/args'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; +import * as ca from '../../utils/common-args'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; +import { applicationIdInfo } from '../../utils/messages'; interface FlagsDef { uuid?: string; @@ -35,19 +37,17 @@ export default class DeviceRegisterCmd extends Command { Register a device. Register a device to an application. - `; + + ${applicationIdInfo.split('\n').join('\n\t\t')} + `; + public static examples = [ '$ balena device register MyApp', '$ balena device register MyApp --uuid ', + '$ balena device register myorg/myapp --uuid ', ]; - public static args: Array> = [ - { - name: 'application', - description: 'the name or id of application to register device with', - required: true, - }, - ]; + public static args: Array> = [ca.applicationRequired]; public static usage = 'device register '; diff --git a/lib/commands/devices/index.ts b/lib/commands/devices/index.ts index 466c9317..c4742415 100644 --- a/lib/commands/devices/index.ts +++ b/lib/commands/devices/index.ts @@ -20,7 +20,7 @@ import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { expandForAppName } from '../../utils/helpers'; import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy'; -import { tryAsInteger } from '../../utils/validation'; +import { applicationIdInfo, jsonInfo } from '../../utils/messages'; import type { Application } from 'balena-sdk'; interface ExtendedDevice extends DeviceWithDeviceType { @@ -44,17 +44,16 @@ export default class DevicesCmd extends Command { You can filter the devices by application by using the \`--application\` option. - The --json option is recommended when scripting the output of this command, - because field names are less likely to change in JSON format and because it - better represents data types like arrays, empty strings and null values. - The 'jq' utility may be helpful for querying JSON fields in shell scripts - (https://stedolan.github.io/jq/manual/). + ${applicationIdInfo.split('\n').join('\n\t\t')} + + ${jsonInfo.split('\n').join('\n\t\t')} `; public static examples = [ '$ balena devices', '$ balena devices --application MyApp', '$ balena devices --app MyApp', '$ balena devices -a MyApp', + '$ balena devices -a myorg/myapp', ]; public static usage = 'devices'; @@ -62,11 +61,8 @@ export default class DevicesCmd extends Command { public static flags: flags.Input = { application: cf.application, app: cf.app, + json: cf.json, help: cf.help, - json: flags.boolean({ - char: 'j', - description: 'produce JSON output instead of tabular output', - }), }; public static primary = true; @@ -85,8 +81,10 @@ export default class DevicesCmd extends Command { let devices; if (options.application != null) { + const { getApplication } = await import('../../utils/sdk'); + const application = await getApplication(balena, options.application); devices = (await balena.models.device.getAllByApplication( - tryAsInteger(options.application), + application.id, expandForAppName, )) as ExtendedDevice[]; } else { diff --git a/lib/commands/env/add.ts b/lib/commands/env/add.ts index 484c6f26..7fef3f8f 100644 --- a/lib/commands/env/add.ts +++ b/lib/commands/env/add.ts @@ -18,13 +18,13 @@ import { flags } from '@oclif/command'; import type * as BalenaSdk from 'balena-sdk'; import Command from '../../command'; - import { ExpectedError } from '../../errors'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; +import { applicationIdInfo } from '../../utils/messages'; interface FlagsDef { - application?: string; // application name + application?: string; device?: string; // device UUID help: void; quiet: boolean; @@ -63,10 +63,14 @@ export default class EnvAddCmd extends Command { therefore the --service option cannot be used when the variable name starts with a reserved prefix. When defining custom application variables, please avoid the reserved prefixes. -`; + + ${applicationIdInfo.split('\n').join('\n\t\t')} + `; + public static examples = [ '$ balena env add TERM --application MyApp', '$ balena env add EDITOR vim --application MyApp', + '$ balena env add EDITOR vim -a myorg/myapp', '$ balena env add EDITOR vim --application MyApp,MyApp2', '$ balena env add EDITOR vim --application MyApp --service MyService', '$ balena env add EDITOR vim --application MyApp,MyApp2 --service MyService,MyService2', @@ -93,8 +97,8 @@ export default class EnvAddCmd extends Command { public static usage = 'env add [value]'; public static flags: flags.Input = { - application: { exclusive: ['device'], ...cf.application }, - device: { exclusive: ['application'], ...cf.device }, + application: { ...cf.application, exclusive: ['device'] }, + device: { ...cf.device, exclusive: ['application'] }, help: cf.help, quiet: cf.quiet, service: cf.service, @@ -108,7 +112,7 @@ export default class EnvAddCmd extends Command { if (!options.application && !options.device) { throw new ExpectedError( - 'Either the --application or the --device option must always be used', + 'Either the --application or the --device option must be specified', ); } diff --git a/lib/commands/envs.ts b/lib/commands/envs.ts index 5dc1a606..ce3f068b 100644 --- a/lib/commands/envs.ts +++ b/lib/commands/envs.ts @@ -18,14 +18,14 @@ import { flags } from '@oclif/command'; import type * as SDK from 'balena-sdk'; import * as _ from 'lodash'; import Command from '../command'; - import { ExpectedError } from '../errors'; import * as cf from '../utils/common-flags'; import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy'; +import { applicationIdInfo } from '../utils/messages'; import { isV13 } from '../utils/version'; interface FlagsDef { - application?: string; // application name + application?: string; config: boolean; device?: string; // device UUID json: boolean; @@ -88,9 +88,13 @@ export default class EnvsCmd extends Command { application linked to the device is no longer accessible by the current user (for example, in case the current user has been removed from the application by its owner). -`; + + ${applicationIdInfo.split('\n').join('\n\t\t')} + `; + public static examples = [ '$ balena envs --application MyApp', + '$ balena envs --application myorg/myapp', '$ balena envs --application MyApp --json', '$ balena envs --application MyApp --service MyService', '$ balena envs --application MyApp --service MyService', @@ -124,11 +128,7 @@ export default class EnvsCmd extends Command { }), device: { exclusive: ['application'], ...cf.device }, help: cf.help, - json: flags.boolean({ - default: false, - char: 'j', - description: 'produce JSON output instead of tabular output', - }), + json: cf.json, verbose: cf.verbose, service: { exclusive: ['config'], ...cf.service }, }; @@ -145,7 +145,7 @@ export default class EnvsCmd extends Command { const balena = getBalenaSdk(); - let appName = options.application; + let appNameOrSlug = options.application; let fullUUID: string | undefined; // as oppposed to the short, 7-char UUID if (options.device) { @@ -158,16 +158,16 @@ export default class EnvsCmd extends Command { ); fullUUID = device.uuid; if (app) { - appName = app.app_name; + appNameOrSlug = app.app_name; } } - if (appName && options.service) { - await validateServiceName(balena, options.service, appName); + if (appNameOrSlug && options.service) { + await validateServiceName(balena, options.service, appNameOrSlug); } - variables.push(...(await getAppVars(balena, appName, options))); + variables.push(...(await getAppVars(balena, appNameOrSlug, options))); if (fullUUID) { variables.push( - ...(await getDeviceVars(balena, fullUUID, appName, options)), + ...(await getDeviceVars(balena, fullUUID, appNameOrSlug, options)), ); } if (!options.json && variables.length === 0) { @@ -241,17 +241,17 @@ async function validateServiceName( */ async function getAppVars( sdk: SDK.BalenaSDK, - appName: string | undefined, + appNameOrSlug: string | undefined, options: FlagsDef, ): Promise { const appVars: EnvironmentVariableInfo[] = []; - if (!appName) { + if (!appNameOrSlug) { return appVars; } const vars = await sdk.models.application[ options.config ? 'configVar' : 'envVar' - ].getAllByApplication(appName); - fillInInfoFields(vars, appName); + ].getAllByApplication(appNameOrSlug); + fillInInfoFields(vars, appNameOrSlug); appVars.push(...vars); if (!options.config) { const pineOpts: SDK.PineOptions = { @@ -267,10 +267,10 @@ async function getAppVars( }; } const serviceVars = await sdk.models.service.var.getAllByApplication( - appName, + appNameOrSlug, pineOpts, ); - fillInInfoFields(serviceVars, appName); + fillInInfoFields(serviceVars, appNameOrSlug); appVars.push(...serviceVars); } return appVars; @@ -283,7 +283,7 @@ async function getAppVars( async function getDeviceVars( sdk: SDK.BalenaSDK, fullUUID: string, - appName: string | undefined, + appNameOrSlug: string | undefined, options: FlagsDef, ): Promise { const printedUUID = options.json ? fullUUID : options.device!; @@ -292,7 +292,7 @@ async function getDeviceVars( const deviceConfigVars = await sdk.models.device.configVar.getAllByDevice( fullUUID, ); - fillInInfoFields(deviceConfigVars, appName, printedUUID); + fillInInfoFields(deviceConfigVars, appNameOrSlug, printedUUID); deviceVars.push(...deviceConfigVars); } else { const pineOpts: SDK.PineOptions = { @@ -313,13 +313,13 @@ async function getDeviceVars( fullUUID, pineOpts, ); - fillInInfoFields(deviceServiceVars, appName, printedUUID); + fillInInfoFields(deviceServiceVars, appNameOrSlug, printedUUID); deviceVars.push(...deviceServiceVars); const deviceEnvVars = await sdk.models.device.envVar.getAllByDevice( fullUUID, ); - fillInInfoFields(deviceEnvVars, appName, printedUUID); + fillInInfoFields(deviceEnvVars, appNameOrSlug, printedUUID); deviceVars.push(...deviceEnvVars); } return deviceVars; @@ -335,7 +335,7 @@ function fillInInfoFields( | EnvironmentVariableInfo[] | DeviceServiceEnvironmentVariableInfo[] | ServiceEnvironmentVariableInfo[], - appName?: string, + appNameOrSlug?: string, deviceUUID?: string, ) { for (const envVar of varArray) { @@ -347,7 +347,7 @@ function fillInInfoFields( envVar.serviceName = ((envVar.service_install as SDK.ServiceInstall[])[0] ?.installs__service as SDK.Service[])[0]?.service_name; } - envVar.appName = appName; + envVar.appName = appNameOrSlug; envVar.serviceName = envVar.serviceName || '*'; envVar.deviceUUID = deviceUUID || '*'; } diff --git a/lib/commands/join.ts b/lib/commands/join.ts index 1dd28416..01f395a6 100644 --- a/lib/commands/join.ts +++ b/lib/commands/join.ts @@ -19,6 +19,7 @@ import { flags } from '@oclif/command'; import Command from '../command'; import * as cf from '../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../utils/lazy'; +import { applicationIdInfo } from '../utils/messages'; import { parseAsLocalHostnameOrIp } from '../utils/validation'; interface FlagsDef { @@ -49,12 +50,15 @@ export default class JoinCmd extends Command { scan the local network for balenaOS devices and prompt you to select one from an interactive picker. This requires root privileges. Likewise, if the application flag is not provided then a picker will be shown. + + ${applicationIdInfo.split('\n').join('\n\t\t')} `; public static examples = [ '$ balena join', '$ balena join balena.local', '$ balena join balena.local --application MyApp', + '$ balena join balena.local -a myorg/myapp', '$ balena join 192.168.1.25', '$ balena join 192.168.1.25 --application MyApp', ]; @@ -71,10 +75,7 @@ export default class JoinCmd extends Command { public static usage = 'join [deviceIpOrHostname]'; public static flags: flags.Input = { - application: { - description: 'the name of the application the device should join', - ...cf.application, - }, + application: cf.application, pollInterval: flags.integer({ description: 'the interval in minutes to check for updates', char: 'i', diff --git a/lib/commands/os/configure.ts b/lib/commands/os/configure.ts index 93f23c48..9b19a9c0 100644 --- a/lib/commands/os/configure.ts +++ b/lib/commands/os/configure.ts @@ -18,20 +18,19 @@ import { flags } from '@oclif/command'; import type * as BalenaSdk from 'balena-sdk'; import * as _ from 'lodash'; -import * as path from 'path'; import Command from '../../command'; - import { ExpectedError } from '../../errors'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy'; +import { applicationIdInfo } from '../../utils/messages'; const BOOT_PARTITION = 1; const CONNECTIONS_FOLDER = '/system-connections'; interface FlagsDef { advanced?: boolean; - app?: string; application?: string; + app?: string; config?: string; 'config-app-update-poll-interval'?: number; 'config-network'?: string; @@ -88,15 +87,19 @@ export default class OsConfigureCmd extends Command { ${deviceApiKeyDeprecationMsg.split('\n').join('\n\t\t')} + ${applicationIdInfo.split('\n').join('\n\t\t')} + Note: This command is currently not supported on Windows natively. Windows users are advised to install the Windows Subsystem for Linux (WSL) with Ubuntu, and use the Linux release of the balena CLI: https://docs.microsoft.com/en-us/windows/wsl/about `; + 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 -a myorg/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', @@ -118,11 +121,8 @@ export default class OsConfigureCmd extends Command { description: 'ask advanced configuration questions (when in interactive mode)', }), - app: flags.string({ - description: "same as '--application'", - exclusive: ['application', 'device'], - }), - application: { exclusive: ['app', 'device'], ...cf.application }, + application: { ...cf.application, exclusive: ['app', 'device'] }, + app: { ...cf.app, exclusive: ['application', 'device'] }, config: flags.string({ description: 'path to a pre-generated config.json file to be injected in the OS image', @@ -155,7 +155,6 @@ export default class OsConfigureCmd extends Command { description: 'This option will set the device name when the device provisions', }), - help: cf.help, version: flags.string({ description: 'balenaOS version, for example "2.32.0" or "2.44.0+rev1"', }), @@ -166,6 +165,7 @@ export default class OsConfigureCmd extends Command { description: "paths to local files to place into the 'system-connections' directory", }), + help: cf.help, }; public async run() { @@ -174,7 +174,7 @@ export default class OsConfigureCmd extends Command { ); // Prefer options.application over options.app options.application = options.application || options.app; - options.app = undefined; + delete options.app; await validateOptions(options); @@ -266,6 +266,8 @@ export default class OsConfigureCmd extends Command { ); if (options['system-connection']) { + const path = await import('path'); + const files = await Promise.all( options['system-connection'].map(async (filePath) => { const content = await fs.readFile(filePath, 'utf8'); diff --git a/lib/commands/preload.ts b/lib/commands/preload.ts index b99441b6..c3880563 100644 --- a/lib/commands/preload.ts +++ b/lib/commands/preload.ts @@ -17,12 +17,14 @@ import { flags } from '@oclif/command'; import Command from '../command'; +import * as cf from '../utils/common-flags'; import { getBalenaSdk, getCliForm, getVisuals, stripIndent, } from '../utils/lazy'; +import { applicationIdInfo } from '../utils/messages'; import type { DockerConnectionCliFlags } from '../utils/docker'; import { dockerConnectionCliFlags } from '../utils/docker'; import * as _ from 'lodash'; @@ -62,13 +64,16 @@ export default class PreloadCmd extends Command { When the device boots, it will not need to download the application, as it was preloaded. + ${applicationIdInfo.split('\n').join('\n\t\t')} + Warning: "balena preload" requires Docker to be correctly installed in your shell environment. For more information (including Windows support) check: https://github.com/balena-io/balena-cli/blob/master/INSTALL.md `; public static examples = [ - '$ balena preload balena.img --app 1234 --commit e1f2592fc6ee949e68756d4f4a48e49bff8d72a0 --splash-image image.png', + '$ balena preload balena.img --app MyApp --commit e1f2592fc6ee949e68756d4f4a48e49bff8d72a0', + '$ balena preload balena.img --app myorg/myapp --commit e1f2592fc6ee949e68756d4f4a48e49bff8d72a0 --splash-image image.png', '$ balena preload balena.img', ]; @@ -83,10 +88,8 @@ export default class PreloadCmd extends Command { public static usage = 'preload '; public static flags: flags.Input = { - app: flags.string({ - description: 'name of the application to preload', - char: 'a', - }), + // TODO: Replace with application/a in #v13? + app: cf.application, commit: flags.string({ description: `\ The commit hash for a specific application release to preload, use "current" to specify the current @@ -160,6 +163,7 @@ Can be repeated to add multiple certificates.\ // balena-preload currently does not work with numerical app IDs // Load app here, and use app slug from hereon if (options.app && !options.app.includes('/')) { + // Disambiguate application (if is a number, it could either be an ID or a numerical name) const { getApplication } = await import('../utils/sdk'); const application = await getApplication(balena, options.app); if (!application) { diff --git a/lib/commands/support.ts b/lib/commands/support.ts index d90306be..12a7ba7a 100644 --- a/lib/commands/support.ts +++ b/lib/commands/support.ts @@ -20,6 +20,7 @@ import Command from '../command'; import { ExpectedError } from '../errors'; import * as cf from '../utils/common-flags'; import { getBalenaSdk, getCliUx, stripIndent } from '../utils/lazy'; +import { applicationIdInfo } from '../utils/messages'; interface FlagsDef { application?: string; @@ -45,12 +46,14 @@ export default class SupportCmd extends Command { Both --device and --application flags accept multiple values, specified as a comma-separated list (with no spaces). + + ${applicationIdInfo.split('\n').join('\n\t\t')} `; public static examples = [ 'balena support enable --device ab346f,cd457a --duration 3d', 'balena support enable --application app3 --duration 12h', - 'balena support disable -a myApp', + 'balena support disable -a myorg/myapp', ]; public static args = [ @@ -68,10 +71,11 @@ export default class SupportCmd extends Command { description: 'comma-separated list (no spaces) of device UUIDs', char: 'd', }), - application: flags.string({ - description: 'comma-separated list (no spaces) of application names', - char: 'a', - }), + application: { + ...cf.application, + description: + 'comma-separated list (no spaces) of application names or org/name slugs', + }, duration: flags.string({ description: 'length of time to enable support for, in (h)ours or (d)ays, e.g. 12h, 2d', diff --git a/lib/commands/tag/rm.ts b/lib/commands/tag/rm.ts index acc7348e..636c6231 100644 --- a/lib/commands/tag/rm.ts +++ b/lib/commands/tag/rm.ts @@ -17,11 +17,9 @@ import { flags } from '@oclif/command'; import Command from '../../command'; -import { ExpectedError } from '../../errors'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { disambiguateReleaseParam } from '../../utils/normalization'; -import { tryAsInteger } from '../../utils/validation'; +import { applicationIdInfo } from '../../utils/messages'; interface FlagsDef { application?: string; @@ -40,10 +38,13 @@ export default class TagRmCmd extends Command { Remove a tag from an application, device or release. Remove a tag from an application, device or release. + + ${applicationIdInfo.split('\n').join('\n\t\t')} `; public static examples = [ '$ balena tag rm myTagKey --application MyApp', + '$ balena tag rm myTagKey -a myorg/myapp', '$ balena tag rm myTagKey --device 7cf02a6', '$ balena tag rm myTagKey --release 1234', '$ balena tag rm myTagKey --release b376b0e544e9429483b656490e5b9443b4349bd6', @@ -64,6 +65,10 @@ export default class TagRmCmd extends Command { ...cf.application, exclusive: ['app', 'device', 'release'], }, + app: { + ...cf.app, + exclusive: ['application', 'device', 'release'], + }, device: { ...cf.device, exclusive: ['app', 'application', 'release'], @@ -73,10 +78,6 @@ export default class TagRmCmd extends Command { exclusive: ['app', 'application', 'device'], }, help: cf.help, - app: flags.string({ - description: "same as '--application'", - exclusive: ['application', 'device', 'release'], - }), }; public static authenticated = true; @@ -94,9 +95,12 @@ export default class TagRmCmd extends Command { // Check user has specified one of application/device/release if (!options.application && !options.device && !options.release) { + const { ExpectedError } = await import('../../errors'); throw new ExpectedError(TagRmCmd.missingResourceMessage); } + const { tryAsInteger } = await import('../../utils/validation'); + if (options.application) { return balena.models.application.tags.remove( tryAsInteger(options.application), @@ -110,6 +114,9 @@ export default class TagRmCmd extends Command { ); } if (options.release) { + const { disambiguateReleaseParam } = await import( + '../../utils/normalization' + ); const releaseParam = await disambiguateReleaseParam( balena, options.release, @@ -122,7 +129,7 @@ export default class TagRmCmd extends Command { protected static missingResourceMessage = stripIndent` To remove a resource tag, you must provide exactly one of: - * An application, with --application + * An application, with --application * A device, with --device * A release, with --release diff --git a/lib/commands/tag/set.ts b/lib/commands/tag/set.ts index 9ba8e70f..9b878112 100644 --- a/lib/commands/tag/set.ts +++ b/lib/commands/tag/set.ts @@ -17,11 +17,9 @@ import { flags } from '@oclif/command'; import Command from '../../command'; -import { ExpectedError } from '../../errors'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { disambiguateReleaseParam } from '../../utils/normalization'; -import { tryAsInteger } from '../../utils/validation'; +import { applicationIdInfo } from '../../utils/messages'; interface FlagsDef { application?: string; @@ -45,10 +43,13 @@ export default class TagSetCmd extends Command { You can optionally provide a value to be associated with the created tag, as an extra argument after the tag key. If a value isn't provided, a tag with an empty value is created. + + ${applicationIdInfo.split('\n').join('\n\t\t')} `; public static examples = [ '$ balena tag set mySimpleTag --application MyApp', + '$ balena tag set mySimpleTag -a myorg/myapp', '$ balena tag set myCompositeTag myTagValue --application MyApp', '$ balena tag set myCompositeTag myTagValue --device 7cf02a6', '$ balena tag set myCompositeTag "my tag value with whitespaces" --device 7cf02a6', @@ -77,6 +78,10 @@ export default class TagSetCmd extends Command { ...cf.application, exclusive: ['app', 'device', 'release'], }, + app: { + ...cf.app, + exclusive: ['application', 'device', 'release'], + }, device: { ...cf.device, exclusive: ['app', 'application', 'release'], @@ -86,10 +91,6 @@ export default class TagSetCmd extends Command { exclusive: ['app', 'application', 'device'], }, help: cf.help, - app: flags.string({ - description: "same as '--application'", - exclusive: ['application', 'device', 'release'], - }), }; public static authenticated = true; @@ -107,11 +108,14 @@ export default class TagSetCmd extends Command { // Check user has specified one of application/device/release if (!options.application && !options.device && !options.release) { + const { ExpectedError } = await import('../../errors'); throw new ExpectedError(TagSetCmd.missingResourceMessage); } params.value ??= ''; + const { tryAsInteger } = await import('../../utils/validation'); + if (options.application) { return balena.models.application.tags.set( tryAsInteger(options.application), @@ -127,6 +131,9 @@ export default class TagSetCmd extends Command { ); } if (options.release) { + const { disambiguateReleaseParam } = await import( + '../../utils/normalization' + ); const releaseParam = await disambiguateReleaseParam( balena, options.release, @@ -143,7 +150,7 @@ export default class TagSetCmd extends Command { protected static missingResourceMessage = stripIndent` To set a resource tag, you must provide exactly one of: - * An application, with --application + * An application, with --application * A device, with --device * A release, with --release diff --git a/lib/commands/tags.ts b/lib/commands/tags.ts index c6db0f28..88627463 100644 --- a/lib/commands/tags.ts +++ b/lib/commands/tags.ts @@ -20,8 +20,7 @@ import Command from '../command'; import { ExpectedError } from '../errors'; import * as cf from '../utils/common-flags'; import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy'; -import { disambiguateReleaseParam } from '../utils/normalization'; -import { tryAsInteger } from '../utils/validation'; +import { applicationIdInfo } from '../utils/messages'; interface FlagsDef { application?: string; @@ -37,10 +36,13 @@ export default class TagsCmd extends Command { List all tags and their values for a particular application, device or release. + + ${applicationIdInfo.split('\n').join('\n\t\t')} `; public static examples = [ '$ balena tags --application MyApp', + '$ balena tags -a myorg/myapp', '$ balena tags --device 7cf02a6', '$ balena tags --release 1234', '$ balena tags --release b376b0e544e9429483b656490e5b9443b4349bd6', @@ -53,6 +55,10 @@ export default class TagsCmd extends Command { ...cf.application, exclusive: ['app', 'device', 'release'], }, + app: { + ...cf.app, + exclusive: ['application', 'device', 'release'], + }, device: { ...cf.device, exclusive: ['app', 'application', 'release'], @@ -62,10 +68,6 @@ export default class TagsCmd extends Command { exclusive: ['app', 'application', 'device'], }, help: cf.help, - app: flags.string({ - description: "same as '--application'", - exclusive: ['application', 'device', 'release'], - }), }; public static authenticated = true; @@ -84,6 +86,8 @@ export default class TagsCmd extends Command { throw new ExpectedError(this.missingResourceMessage); } + const { tryAsInteger } = await import('../utils/validation'); + let tags; if (options.application) { @@ -97,6 +101,9 @@ export default class TagsCmd extends Command { ); } if (options.release) { + const { disambiguateReleaseParam } = await import( + '../utils/normalization' + ); const releaseParam = await disambiguateReleaseParam( balena, options.release, @@ -115,7 +122,7 @@ export default class TagsCmd extends Command { protected missingResourceMessage = stripIndent` To list tags for a resource, you must provide exactly one of: - * An application, with --application + * An application, with --application * A device, with --device * A release, with --release diff --git a/lib/utils/common-args.ts b/lib/utils/common-args.ts new file mode 100644 index 00000000..e7309b2f --- /dev/null +++ b/lib/utils/common-args.ts @@ -0,0 +1,24 @@ +/** + * @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. + */ +import { lowercaseIfSlug } from './normalization'; + +export const applicationRequired = { + name: 'application', + description: 'application name, slug (preferred), or numeric ID (deprecated)', + required: true, + parse: lowercaseIfSlug, +}; diff --git a/lib/utils/common-flags.ts b/lib/utils/common-flags.ts index 1ac1ab9c..0dc8309a 100644 --- a/lib/utils/common-flags.ts +++ b/lib/utils/common-flags.ts @@ -19,14 +19,17 @@ import { flags } from '@oclif/command'; import type { IBooleanFlag } from '@oclif/parser/lib/flags'; import { stripIndent } from './lazy'; +import { lowercaseIfSlug } from './normalization'; export const application = flags.string({ char: 'a', - description: 'application name', + description: 'application name, slug (preferred), or numeric ID (deprecated)', + parse: lowercaseIfSlug, }); // TODO: Consider remove second alias 'app' when we can, to simplify. export const app = flags.string({ description: "same as '--application'", + parse: lowercaseIfSlug, }); export const device = flags.string({ @@ -78,3 +81,9 @@ export const drive = flags.string({ Check \`balena util available-drives\` for available options. `, }); + +export const json: IBooleanFlag = flags.boolean({ + char: 'j', + description: 'produce JSON output instead of tabular output', + default: false, +}); diff --git a/lib/utils/messages.ts b/lib/utils/messages.ts index 47d3b4ff..349586b4 100644 --- a/lib/utils/messages.ts +++ b/lib/utils/messages.ts @@ -38,9 +38,9 @@ export const balenaAsciiArt = `\ |_.__/ \\__,_||_| \\____/|_| |_| \\__,_| `; -export const registrySecretsHelp = `\ -REGISTRY SECRETS -The --registry-secrets option specifies a JSON or YAML file containing private +export const registrySecretsHelp = + 'REGISTRY SECRETS \n' + + `The --registry-secrets option specifies a JSON or YAML file containing private Docker registry usernames and passwords to be used when pulling base images. Sample registry-secrets YAML file: \`\`\` @@ -61,9 +61,9 @@ If the --registry-secrets option is not specified, and a secrets.yml or secrets.json file exists in the balena directory (usually $HOME/.balena), this file will be used instead.`; -export const dockerignoreHelp = `\ -DOCKERIGNORE AND GITIGNORE FILES -By default, the balena CLI will use a single ".dockerignore" file (if any) at +export const dockerignoreHelp = + 'DOCKERIGNORE AND GITIGNORE FILES \n' + + `By default, the balena CLI will use a single ".dockerignore" file (if any) at the project root (--source directory) in order to decide which source files to exclude from the "build context" (tar stream) sent to balenaCloud, Docker daemon or balenaEngine. In a microservices (multicontainer) application, the @@ -94,8 +94,8 @@ option if compatibility is required. This option is mutually exclusive with --multi-dockerignore (-m) and will be removed in the CLI's next major version release (v13). -Default .dockerignore patterns -When --gitignore (-g) is NOT used (i.e. when not in v11 compatibility mode), a +Default .dockerignore patterns \n` + + `When --gitignore (-g) is NOT used (i.e. when not in v11 compatibility mode), a few default/hardcoded dockerignore patterns are "merged" (in memory) with the patterns found in the applicable .dockerignore files, in the following order: \`\`\` @@ -113,3 +113,24 @@ adding counter patterns to the applicable .dockerignore file(s), for example \`!mysubmodule/.git\`. For documentation on pattern format, see: - https://docs.docker.com/engine/reference/builder/#dockerignore-file - https://www.npmjs.com/package/@balena/dockerignore`; + +export const applicationIdInfo = `\ +Applications may be specified by app name, slug, or numeric ID. App slugs +are the recommended option, as they are unique and unambiguous. Slugs +can be listed with the \`balena apps\` command. Note that slugs may change +if the application is renamed. +App names are not unique and may result in "Application is ambiguous" errors +at any time (even if it "used to work in the past"), for example if the name +clashes with a newly created public application, or with apps from other balena +accounts that you may have been invited to as a member. For this reason, app +names are especially discouraged in scripts (e.g. CI environments). +Numeric app IDs are deprecated because they consist of an implementation detail +of the balena backend. We intend to remove support for numeric IDs at some point +in the future.`; + +export const jsonInfo = `\ +The --json option is recommended when scripting the output of this command, +because field names are less likely to change in JSON format and because it +better represents data types like arrays, empty strings and null values. +The 'jq' utility may be helpful for querying JSON fields in shell scripts +(https://stedolan.github.io/jq/manual/).`; diff --git a/lib/utils/patterns.ts b/lib/utils/patterns.ts index 491bc7e9..5d298004 100644 --- a/lib/utils/patterns.ts +++ b/lib/utils/patterns.ts @@ -187,7 +187,6 @@ export function selectApplication( } const apps = (await balena.models.application.getAll({ - $select: 'app_name', $expand: { is_for__device_type: { $select: 'slug', @@ -204,8 +203,8 @@ export function selectApplication( message: 'Select an application', type: 'list', choices: _.map(applications, (application) => ({ - name: `${application.app_name} (${application.is_for__device_type[0].slug})`, - value: application.app_name, + name: `${application.app_name} (${application.slug}) [${application.is_for__device_type[0].slug}]`, + value: application, })), }); }); diff --git a/lib/utils/sdk.ts b/lib/utils/sdk.ts index 298790a6..fb492597 100644 --- a/lib/utils/sdk.ts +++ b/lib/utils/sdk.ts @@ -26,6 +26,7 @@ import type { * Wraps the sdk application.get method, * adding disambiguation in cases where the provided * identifier could be interpreted in multiple valid ways. + * // TODO: Remove this once support for numeric App IDs is removed. */ export async function getApplication( sdk: BalenaSDK, diff --git a/tests/commands/device/device-move.spec.ts b/tests/commands/device/device-move.spec.ts index 95524ff9..4c3ac7fa 100644 --- a/tests/commands/device/device-move.spec.ts +++ b/tests/commands/device/device-move.spec.ts @@ -19,32 +19,6 @@ import { expect } from 'chai'; import { BalenaAPIMock } from '../../balena-api-mock'; import { cleanOutput, runCommand } from '../../helpers'; -const HELP_RESPONSE = ` -Move one or more devices to another application. - -USAGE - $ balena device move - -ARGUMENTS - comma-separated list (no blank spaces) of device UUIDs to be moved - -OPTIONS - -a, --application application name - -h, --help show CLI help - --app same as '--application' - -DESCRIPTION - Move one or more devices to another application. - - Note, if the application option is omitted it will be prompted - for interactively. - -EXAMPLES - $ balena device move 7cf02a6 - $ balena device move 7cf02a6,dc39e52 - $ balena device move 7cf02a6 --application MyNewApp -`; - describe('balena device move', function () { let api: BalenaAPIMock; @@ -59,14 +33,6 @@ describe('balena device move', function () { api.done(); }); - it('should print help text with the -h flag', async () => { - const { out, err } = await runCommand('device move -h'); - - expect(cleanOutput(out)).to.deep.equal(cleanOutput([HELP_RESPONSE])); - - expect(err).to.eql([]); - }); - it('should error if uuid not provided', async () => { const { out, err } = await runCommand('device move'); const errLines = cleanOutput(err); diff --git a/tests/commands/device/devices.spec.ts b/tests/commands/device/devices.spec.ts index c767f577..e7656e8d 100644 --- a/tests/commands/device/devices.spec.ts +++ b/tests/commands/device/devices.spec.ts @@ -21,36 +21,6 @@ import * as path from 'path'; import { apiResponsePath, BalenaAPIMock } from '../../balena-api-mock'; import { cleanOutput, runCommand } from '../../helpers'; -const HELP_RESPONSE = ` -List all devices. - -USAGE - $ balena devices - -OPTIONS - -a, --application application name - -h, --help show CLI help - -j, --json produce JSON output instead of tabular output - --app same as '--application' - -DESCRIPTION - list all devices that belong to you. - - You can filter the devices by application by using the \`--application\` option. - - The --json option is recommended when scripting the output of this command, - because field names are less likely to change in JSON format and because it - better represents data types like arrays, empty strings and null values. - The 'jq' utility may be helpful for querying JSON fields in shell scripts - (https://stedolan.github.io/jq/manual/). - -EXAMPLES - $ balena devices - $ balena devices --application MyApp - $ balena devices --app MyApp - $ balena devices -a MyApp -`; - describe('balena devices', function () { let api: BalenaAPIMock; @@ -65,14 +35,6 @@ describe('balena devices', function () { api.done(); }); - it('should print help text with the -h flag', async () => { - const { out, err } = await runCommand('devices -h'); - - expect(cleanOutput(out)).to.deep.equal(cleanOutput([HELP_RESPONSE])); - - expect(err).to.eql([]); - }); - it('should list devices from own and collaborator apps', async () => { api.scope .get(