Update various commands to support organizations

Change-type: minor
Connects-to: #2119
Signed-off-by: Scott Lowe <scott@balena.io>
This commit is contained in:
Scott Lowe 2020-12-10 13:30:17 +01:00
parent 6fc3b0df58
commit c898747468
28 changed files with 645 additions and 344 deletions

View File

@ -164,10 +164,10 @@ Users are encouraged to regularly update the balena CLI to the latest version.
- [apps](#apps) - [apps](#apps)
- [app &#60;nameorslug&#62;](#app-nameorslug) - [app &#60;nameorslug&#62;](#app-nameorslug)
- [app create &#60;name&#62;](#app-create-name) - [app create &#60;name&#62;](#app-create-name)
- [app purge &#60;name&#62;](#app-purge-name) - [app purge &#60;application&#62;](#app-purge-application)
- [app rename &#60;nameorslug&#62; [newname]](#app-rename-nameorslug-newname) - [app rename &#60;application&#62; [newname]](#app-rename-application-newname)
- [app restart &#60;name&#62;](#app-restart-name) - [app restart &#60;application&#62;](#app-restart-application)
- [app rm &#60;name&#62;](#app-rm-name) - [app rm &#60;application&#62;](#app-rm-application)
- Authentication - Authentication
@ -312,7 +312,7 @@ the API key name
list all your balena applications. list all your balena applications.
For detailed information on a particular application, For detailed information on a particular application,
use `balena app <name> instead`. use `balena app <application>` instead.
Examples: Examples:
@ -328,6 +328,19 @@ No-op since release v12.0.0
Display detailed information about a single balena application. 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: Examples:
$ balena app MyApp $ balena app MyApp
@ -335,9 +348,9 @@ Examples:
### Arguments ### Arguments
#### NAMEORSLUG #### APPLICATION
application name or org/name slug application name, slug (preferred), or numeric ID (deprecated)
### Options ### Options
@ -382,30 +395,57 @@ handle of the organization the application should belong to
application device type (Check available types with `balena devices supported`) application device type (Check available types with `balena devices supported`)
## app purge &#60;name&#62; ## app purge &#60;application&#62;
Purge data from all devices belonging to an application. Purge data from all devices belonging to an application.
This will clear the application's /data directory. 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: Examples:
$ balena app purge MyApp $ balena app purge MyApp
$ balena app purge myorg/myapp
### Arguments ### Arguments
#### NAME #### APPLICATION
application name or numeric ID application name, slug (preferred), or numeric ID (deprecated)
### Options ### Options
## app rename &#60;nameOrSlug&#62; [newName] ## app rename &#60;application&#62; [newName]
Rename an application. Rename an application.
Note, if the `newName` parameter is omitted, it will be Note, if the `newName` parameter is omitted, it will be
prompted for interactively. 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: Examples:
$ balena app rename OldName $ balena app rename OldName
@ -414,9 +454,9 @@ Examples:
### Arguments ### Arguments
#### NAMEORSLUG #### APPLICATION
application name or org/name slug application name, slug (preferred), or numeric ID (deprecated)
#### NEWNAME #### NEWNAME
@ -424,38 +464,66 @@ the new name for the application
### Options ### Options
## app restart &#60;name&#62; ## app restart &#60;application&#62;
Restart all devices belonging to an 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: Examples:
$ balena app restart MyApp $ balena app restart MyApp
$ balena app restart myorg/myapp
### Arguments ### Arguments
#### NAME #### APPLICATION
application name or numeric ID application name, slug (preferred), or numeric ID (deprecated)
### Options ### Options
## app rm &#60;name&#62; ## app rm &#60;application&#62;
Permanently remove a balena application. Permanently remove a balena application.
The --yes option may be used to avoid interactive confirmation. 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: Examples:
$ balena app rm MyApp $ balena app rm MyApp
$ balena app rm MyApp --yes $ balena app rm MyApp --yes
$ balena app rm myorg/myapp
### Arguments ### Arguments
#### NAME #### APPLICATION
application name or numeric ID application name, slug (preferred), or numeric ID (deprecated)
### Options ### Options
@ -546,6 +614,19 @@ list all devices that belong to you.
You can filter the devices by application by using the `--application` option. 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, 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 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. better represents data types like arrays, empty strings and null values.
@ -558,12 +639,13 @@ Examples:
$ balena devices --application MyApp $ balena devices --application MyApp
$ balena devices --app MyApp $ balena devices --app MyApp
$ balena devices -a MyApp $ balena devices -a MyApp
$ balena devices -a myorg/myapp
### Options ### Options
#### -a, --application APPLICATION #### -a, --application APPLICATION
application name application name, slug (preferred), or numeric ID (deprecated)
#### --app APP #### --app APP
@ -642,22 +724,36 @@ the uuid of the device to identify
## device init ## 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. and writing it to an SD Card.
Note, if the application option is omitted it will be prompted Note, if the application option is omitted it will be prompted
for interactively. 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: Examples:
$ balena device init $ balena device init
$ balena device init --application MyApp $ balena device init --application MyApp
$ balena device init -a myorg/myapp
### Options ### Options
#### -a, --application APPLICATION #### -a, --application APPLICATION
application name application name, slug (preferred), or numeric ID (deprecated)
#### --app APP #### --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 Note, if the application option is omitted it will be prompted
for interactively. 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: Examples:
$ balena device move 7cf02a6 $ balena device move 7cf02a6
$ balena device move 7cf02a6,dc39e52 $ balena device move 7cf02a6,dc39e52
$ balena device move 7cf02a6 --application MyNewApp $ balena device move 7cf02a6 --application MyNewApp
$ balena device move 7cf02a6 -a myorg/mynewapp
### Arguments ### Arguments
@ -712,7 +822,7 @@ comma-separated list (no blank spaces) of device UUIDs to be moved
#### -a, --application APPLICATION #### -a, --application APPLICATION
application name application name, slug (preferred), or numeric ID (deprecated)
#### --app APP #### --app APP
@ -833,16 +943,30 @@ force action if the update lock is set
Register a device to an application. 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: Examples:
$ balena device register MyApp $ balena device register MyApp
$ balena device register MyApp --uuid <uuid> $ balena device register MyApp --uuid <uuid>
$ balena device register myorg/myapp --uuid <uuid>
### Arguments ### Arguments
#### APPLICATION #### APPLICATION
the name or id of application to register device with application name, slug (preferred), or numeric ID (deprecated)
### Options ### 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 (for example, in case the current user has been removed from the application
by its owner). 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: Examples:
$ balena envs --application MyApp $ balena envs --application MyApp
$ balena envs --application myorg/myapp
$ balena envs --application MyApp --json $ balena envs --application MyApp --json
$ balena envs --application MyApp --service MyService $ balena envs --application MyApp --service MyService
$ 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 #### -a, --application APPLICATION
application name application name, slug (preferred), or numeric ID (deprecated)
#### -c, --config #### -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 with a reserved prefix. When defining custom application variables, please
avoid the reserved prefixes. 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: Examples:
$ balena env add TERM --application MyApp $ balena env add TERM --application MyApp
$ balena env add EDITOR vim --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,MyApp2
$ balena env add EDITOR vim --application MyApp --service MyService $ balena env add EDITOR vim --application MyApp --service MyService
$ balena env add EDITOR vim --application MyApp,MyApp2 --service MyService,MyService2 $ 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 #### -a, --application APPLICATION
application name application name, slug (preferred), or numeric ID (deprecated)
#### -d, --device DEVICE #### -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, List all tags and their values for a particular application,
device or release. 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: Examples:
$ balena tags --application MyApp $ balena tags --application MyApp
$ balena tags -a myorg/myapp
$ balena tags --device 7cf02a6 $ balena tags --device 7cf02a6
$ balena tags --release 1234 $ balena tags --release 1234
$ balena tags --release b376b0e544e9429483b656490e5b9443b4349bd6 $ balena tags --release b376b0e544e9429483b656490e5b9443b4349bd6
@ -1239,7 +1405,11 @@ Examples:
#### -a, --application APPLICATION #### -a, --application APPLICATION
application name application name, slug (preferred), or numeric ID (deprecated)
#### --app APP
same as '--application'
#### -d, --device DEVICE #### -d, --device DEVICE
@ -1249,17 +1419,27 @@ device UUID
release id release id
#### --app APP
same as '--application'
## tag rm &#60;tagKey&#62; ## tag rm &#60;tagKey&#62;
Remove a tag from an application, device or release. 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: Examples:
$ balena tag rm myTagKey --application MyApp $ balena tag rm myTagKey --application MyApp
$ balena tag rm myTagKey -a myorg/myapp
$ balena tag rm myTagKey --device 7cf02a6 $ balena tag rm myTagKey --device 7cf02a6
$ balena tag rm myTagKey --release 1234 $ balena tag rm myTagKey --release 1234
$ balena tag rm myTagKey --release b376b0e544e9429483b656490e5b9443b4349bd6 $ balena tag rm myTagKey --release b376b0e544e9429483b656490e5b9443b4349bd6
@ -1274,7 +1454,11 @@ the key string of the tag
#### -a, --application APPLICATION #### -a, --application APPLICATION
application name application name, slug (preferred), or numeric ID (deprecated)
#### --app APP
same as '--application'
#### -d, --device DEVICE #### -d, --device DEVICE
@ -1284,10 +1468,6 @@ device UUID
release id release id
#### --app APP
same as '--application'
## tag set &#60;tagKey&#62; [value] ## tag set &#60;tagKey&#62; [value]
Set a tag on an application, device or release. 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 tag, as an extra argument after the tag key. If a value isn't
provided, a tag with an empty value is created. 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: Examples:
$ balena tag set mySimpleTag --application MyApp $ 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 --application MyApp
$ balena tag set myCompositeTag myTagValue --device 7cf02a6 $ balena tag set myCompositeTag myTagValue --device 7cf02a6
$ balena tag set myCompositeTag "my tag value with whitespaces" --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 #### -a, --application APPLICATION
application name application name, slug (preferred), or numeric ID (deprecated)
#### --app APP
same as '--application'
#### -d, --device DEVICE #### -d, --device DEVICE
@ -1330,10 +1528,6 @@ device UUID
release id release id
#### --app APP
same as '--application'
# Help and Version # Help and Version
## help [command] ## 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. 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. 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 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 are advised to install the Windows Subsystem for Linux (WSL) with Ubuntu, and use
the Linux release of the balena CLI: 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
$ balena os configure ../path/rpi3.img --device 7cf02a6 --device-api-key <existingDeviceKey> $ balena os configure ../path/rpi3.img --device 7cf02a6 --device-api-key <existingDeviceKey>
$ balena os configure ../path/rpi3.img --app MyApp $ 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 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
$ balena os configure ../path/rpi3.img --app MyFinApp --device-type raspberrypi3 --config myWifiConfig.json $ 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) ask advanced configuration questions (when in interactive mode)
#### -a, --application APPLICATION
application name, slug (preferred), or numeric ID (deprecated)
#### --app APP #### --app APP
same as '--application' same as '--application'
#### -a, --application APPLICATION
application name
#### --config CONFIG #### --config CONFIG
path to a pre-generated config.json file to be injected in the OS image 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. 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, 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: Examples:
@ -1963,7 +2184,8 @@ Examples:
$ balena config generate --device 7cf02a6 --version 2.12.7 --device-api-key <existingDeviceKey> $ balena config generate --device 7cf02a6 --version 2.12.7 --device-api-key <existingDeviceKey>
$ balena config generate --device 7cf02a6 --version 2.12.7 --output config.json $ 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
$ 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 --output config.json
$ balena config generate --app MyApp --version 2.12.7 --network wifi --wifiSsid mySsid --wifiKey abcdefgh --appUpdatePollInterval 1 $ 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 #### -a, --application APPLICATION
application name application name, slug (preferred), or numeric ID (deprecated)
#### --app APP #### --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 When the device boots, it will not need to download the application, as it was
preloaded. 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 Warning: "balena preload" requires Docker to be correctly installed in
your shell environment. For more information (including Windows support) your shell environment. For more information (including Windows support)
check: https://github.com/balena-io/balena-cli/blob/master/INSTALL.md check: https://github.com/balena-io/balena-cli/blob/master/INSTALL.md
Examples: 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 $ balena preload balena.img
### Arguments ### Arguments
@ -2148,7 +2384,7 @@ the image file path
#### -a, --app APP #### -a, --app APP
name of the application to preload application name, slug (preferred), or numeric ID (deprecated)
#### -c, --commit COMMIT #### -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 from an interactive picker. This requires root privileges. Likewise, if
the application flag is not provided then a picker will be shown. 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: Examples:
$ balena join $ balena join
$ balena join balena.local $ balena join balena.local
$ balena join balena.local --application MyApp $ 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
$ balena join 192.168.1.25 --application MyApp $ balena join 192.168.1.25 --application MyApp
@ -2964,7 +3214,7 @@ the IP or hostname of device
#### -a, --application APPLICATION #### -a, --application APPLICATION
application name application name, slug (preferred), or numeric ID (deprecated)
#### -i, --pollInterval POLLINTERVAL #### -i, --pollInterval POLLINTERVAL
@ -3020,11 +3270,24 @@ or hours, e.g. '12h', '2d'.
Both --device and --application flags accept multiple values, specified as Both --device and --application flags accept multiple values, specified as
a comma-separated list (with no spaces). 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: Examples:
balena support enable --device ab346f,cd457a --duration 3d balena support enable --device ab346f,cd457a --duration 3d
balena support enable --application app3 --duration 12h balena support enable --application app3 --duration 12h
balena support disable -a myApp balena support disable -a myorg/myapp
### Arguments ### Arguments
@ -3040,7 +3303,7 @@ comma-separated list (no spaces) of device UUIDs
#### -a, --application APPLICATION #### -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 #### -t, --duration DURATION

View File

@ -18,7 +18,9 @@
import { flags } from '@oclif/command'; import { flags } from '@oclif/command';
import Command from '../../command'; import Command from '../../command';
import * as cf from '../../utils/common-flags'; import * as cf from '../../utils/common-flags';
import * as ca from '../../utils/common-args';
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy'; import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
import { applicationIdInfo } from '../../utils/messages';
import type { Release } from 'balena-sdk'; import type { Release } from 'balena-sdk';
interface FlagsDef { interface FlagsDef {
@ -26,7 +28,7 @@ interface FlagsDef {
} }
interface ArgsDef { interface ArgsDef {
nameOrSlug: string; application: string;
} }
export default class AppCmd extends Command { export default class AppCmd extends Command {
@ -34,16 +36,12 @@ export default class AppCmd extends Command {
Display information about a single application. Display information about a single application.
Display detailed information about a single balena 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 examples = ['$ balena app MyApp', '$ balena app myorg/myapp'];
public static args = [ public static args = [ca.applicationRequired];
{
name: 'nameOrSlug',
description: 'application name or org/name slug',
required: true,
},
];
public static usage = 'app <nameOrSlug>'; public static usage = 'app <nameOrSlug>';
@ -61,7 +59,7 @@ export default class AppCmd extends Command {
const application = (await getApplication( const application = (await getApplication(
getBalenaSdk(), getBalenaSdk(),
params.nameOrSlug, params.application,
{ {
$expand: { $expand: {
is_for__device_type: { $select: 'slug' }, is_for__device_type: { $select: 'slug' },

View File

@ -18,35 +18,36 @@
import { flags } from '@oclif/command'; import { flags } from '@oclif/command';
import Command from '../../command'; import Command from '../../command';
import * as cf from '../../utils/common-flags'; import * as cf from '../../utils/common-flags';
import * as ca from '../../utils/common-args';
import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { tryAsInteger } from '../../utils/validation'; import { applicationIdInfo } from '../../utils/messages';
interface FlagsDef { interface FlagsDef {
help: void; help: void;
} }
interface ArgsDef { interface ArgsDef {
name: string; application: string;
} }
export default class AppRestartCmd extends Command { export default class AppPurgeCmd extends Command {
public static description = stripIndent` public static description = stripIndent`
Purge data from an application. Purge data from an application.
Purge data from all devices belonging to an application. Purge data from all devices belonging to an application.
This will clear the application's /data directory. This will clear the application's /data directory.
`;
public static examples = ['$ balena app purge MyApp'];
public static args = [ ${applicationIdInfo.split('\n').join('\n\t\t')}
{ `;
name: 'name',
description: 'application name or numeric ID', public static examples = [
required: true, '$ balena app purge MyApp',
}, '$ balena app purge myorg/myapp',
]; ];
public static usage = 'app purge <name>'; public static args = [ca.applicationRequired];
public static usage = 'app purge <application>';
public static flags: flags.Input<FlagsDef> = { public static flags: flags.Input<FlagsDef> = {
help: cf.help, help: cf.help,
@ -55,21 +56,18 @@ export default class AppRestartCmd extends Command {
public static authenticated = true; public static authenticated = true;
public async run() { public async run() {
const { args: params } = this.parse<FlagsDef, ArgsDef>(AppRestartCmd); const { args: params } = this.parse<FlagsDef, ArgsDef>(AppPurgeCmd);
const { getApplication } = await import('../../utils/sdk');
const balena = getBalenaSdk(); const balena = getBalenaSdk();
// balena.models.application.purge only accepts a numeric id // 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 // so we must first fetch the app to get it's id,
let nameOrId = tryAsInteger(params.name); const application = await getApplication(balena, params.application);
if (typeof nameOrId === 'string') {
const app = await balena.models.application.get(nameOrId);
nameOrId = app.id;
}
try { try {
await balena.models.application.purge(nameOrId); await balena.models.application.purge(application.id);
} catch (e) { } catch (e) {
if (e.message.toLowerCase().includes('no online device(s) found')) { if (e.message.toLowerCase().includes('no online device(s) found')) {
// application.purge throws an error if no devices are online // application.purge throws an error if no devices are online

View File

@ -16,11 +16,11 @@
*/ */
import { flags } from '@oclif/command'; import { flags } from '@oclif/command';
import type { IArg } from '@oclif/parser/lib/args';
import Command from '../../command'; import Command from '../../command';
import * as cf from '../../utils/common-flags'; import * as cf from '../../utils/common-flags';
import * as ca from '../../utils/common-args';
import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy'; import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy';
import { lowercaseIfSlug } from '../../utils/normalization'; import { applicationIdInfo } from '../../utils/messages';
import type { ApplicationType } from 'balena-sdk'; import type { ApplicationType } from 'balena-sdk';
interface FlagsDef { interface FlagsDef {
@ -28,7 +28,7 @@ interface FlagsDef {
} }
interface ArgsDef { interface ArgsDef {
nameOrSlug: string; application: string;
newName?: string; newName?: string;
} }
@ -40,6 +40,8 @@ export default class AppRenameCmd extends Command {
Note, if the \`newName\` parameter is omitted, it will be Note, if the \`newName\` parameter is omitted, it will be
prompted for interactively. prompted for interactively.
${applicationIdInfo.split('\n').join('\n\t\t')}
`; `;
public static examples = [ public static examples = [
@ -48,20 +50,15 @@ export default class AppRenameCmd extends Command {
'$ balena app rename myorg/oldname NewName', '$ balena app rename myorg/oldname NewName',
]; ];
public static args: Array<IArg<any>> = [ public static args = [
{ ca.applicationRequired,
name: 'nameOrSlug',
description: 'application name or org/name slug',
parse: lowercaseIfSlug,
required: true,
},
{ {
name: 'newName', name: 'newName',
description: 'the new name for the application', description: 'the new name for the application',
}, },
]; ];
public static usage = 'app rename <nameOrSlug> [newName]'; public static usage = 'app rename <application> [newName]';
public static flags: flags.Input<FlagsDef> = { public static flags: flags.Input<FlagsDef> = {
help: cf.help, help: cf.help,
@ -77,9 +74,9 @@ export default class AppRenameCmd extends Command {
const balena = getBalenaSdk(); 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 { getApplication } = await import('../../utils/sdk');
const application = await getApplication(balena, params.nameOrSlug, { const application = await getApplication(balena, params.application, {
$expand: { $expand: {
application_type: { application_type: {
$select: ['is_legacy'], $select: ['is_legacy'],
@ -98,7 +95,7 @@ export default class AppRenameCmd extends Command {
const appType = (application.application_type as ApplicationType[])?.[0]; const appType = (application.application_type as ApplicationType[])?.[0];
if (appType.is_legacy) { if (appType.is_legacy) {
throw new ExpectedError( 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. // BalenaRequestError: Request error: "organization" and "app_name" must be unique.
if ((e.message || '').toLowerCase().includes('unique')) { if ((e.message || '').toLowerCase().includes('unique')) {
throw new ExpectedError( throw new ExpectedError(
`Error: application ${params.nameOrSlug} already exists.`, `Error: application ${params.application} already exists.`,
); );
} }
throw e; throw e;
} }
// Get application again, to be sure of results
const renamedApplication = await balena.models.application.get(
application.id,
);
// Output result // 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}`);
} }
} }

View File

@ -18,15 +18,16 @@
import { flags } from '@oclif/command'; import { flags } from '@oclif/command';
import Command from '../../command'; import Command from '../../command';
import * as cf from '../../utils/common-flags'; import * as cf from '../../utils/common-flags';
import * as ca from '../../utils/common-args';
import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { tryAsInteger } from '../../utils/validation'; import { applicationIdInfo } from '../../utils/messages';
interface FlagsDef { interface FlagsDef {
help: void; help: void;
} }
interface ArgsDef { interface ArgsDef {
name: string; application: string;
} }
export default class AppRestartCmd extends Command { export default class AppRestartCmd extends Command {
@ -34,18 +35,18 @@ export default class AppRestartCmd extends Command {
Restart an application. Restart an application.
Restart all devices belonging to an application. Restart all devices belonging to an application.
`;
public static examples = ['$ balena app restart MyApp'];
public static args = [ ${applicationIdInfo.split('\n').join('\n\t\t')}
{ `;
name: 'name',
description: 'application name or numeric ID', public static examples = [
required: true, '$ balena app restart MyApp',
}, '$ balena app restart myorg/myapp',
]; ];
public static usage = 'app restart <name>'; public static args = [ca.applicationRequired];
public static usage = 'app restart <application>';
public static flags: flags.Input<FlagsDef> = { public static flags: flags.Input<FlagsDef> = {
help: cf.help, help: cf.help,
@ -56,6 +57,13 @@ export default class AppRestartCmd extends Command {
public async run() { public async run() {
const { args: params } = this.parse<FlagsDef, ArgsDef>(AppRestartCmd); const { args: params } = this.parse<FlagsDef, ArgsDef>(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);
} }
} }

View File

@ -18,8 +18,9 @@
import { flags } from '@oclif/command'; import { flags } from '@oclif/command';
import Command from '../../command'; import Command from '../../command';
import * as cf from '../../utils/common-flags'; import * as cf from '../../utils/common-flags';
import * as ca from '../../utils/common-args';
import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { tryAsInteger } from '../../utils/validation'; import { applicationIdInfo } from '../../utils/messages';
interface FlagsDef { interface FlagsDef {
yes: boolean; yes: boolean;
@ -27,7 +28,7 @@ interface FlagsDef {
} }
interface ArgsDef { interface ArgsDef {
name: string; application: string;
} }
export default class AppRmCmd extends Command { export default class AppRmCmd extends Command {
@ -37,21 +38,19 @@ export default class AppRmCmd extends Command {
Permanently remove a balena application. Permanently remove a balena application.
The --yes option may be used to avoid interactive confirmation. The --yes option may be used to avoid interactive confirmation.
`;
${applicationIdInfo.split('\n').join('\n\t\t')}
`;
public static examples = [ public static examples = [
'$ balena app rm MyApp', '$ balena app rm MyApp',
'$ balena app rm MyApp --yes', '$ balena app rm MyApp --yes',
'$ balena app rm myorg/myapp',
]; ];
public static args = [ public static args = [ca.applicationRequired];
{
name: 'name',
description: 'application name or numeric ID',
required: true,
},
];
public static usage = 'app rm <name>'; public static usage = 'app rm <application>';
public static flags: flags.Input<FlagsDef> = { public static flags: flags.Input<FlagsDef> = {
yes: cf.yes, yes: cf.yes,
@ -65,15 +64,20 @@ export default class AppRmCmd extends Command {
AppRmCmd, AppRmCmd,
); );
const patterns = await import('../../utils/patterns'); const { confirm } = await import('../../utils/patterns');
const { getApplication } = await import('../../utils/sdk');
const balena = getBalenaSdk();
// Confirm // Confirm
await patterns.confirm( await confirm(
options.yes ?? false, 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 // Remove
await getBalenaSdk().models.application.remove(tryAsInteger(params.name)); await balena.models.application.remove(application.id);
} }
} }

View File

@ -37,8 +37,9 @@ export default class AppsCmd extends Command {
list all your balena applications. list all your balena applications.
For detailed information on a particular application, For detailed information on a particular application,
use \`balena app <name> instead\`. use \`balena app <application>\` instead.
`; `;
public static examples = ['$ balena apps']; public static examples = ['$ balena apps'];
public static usage = 'apps'; public static usage = 'apps';

View File

@ -19,6 +19,7 @@ import { flags } from '@oclif/command';
import Command from '../../command'; import Command from '../../command';
import * as cf from '../../utils/common-flags'; import * as cf from '../../utils/common-flags';
import { getBalenaSdk, getCliForm, stripIndent } from '../../utils/lazy'; import { getBalenaSdk, getCliForm, stripIndent } from '../../utils/lazy';
import { applicationIdInfo } from '../../utils/messages';
import type { PineDeferred } from 'balena-sdk'; import type { PineDeferred } from 'balena-sdk';
interface FlagsDef { interface FlagsDef {
@ -51,7 +52,9 @@ export default class ConfigGenerateCmd extends Command {
that will be asked for the relevant device type. 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, 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 = [ 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 <existingDeviceKey>', '$ balena config generate --device 7cf02a6 --version 2.12.7 --device-api-key <existingDeviceKey>',
'$ balena config generate --device 7cf02a6 --version 2.12.7 --output config.json', '$ 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',
'$ 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 --output config.json',
'$ balena config generate --app MyApp --version 2.12.7 --network wifi --wifiSsid mySsid --wifiKey abcdefgh --appUpdatePollInterval 1', '$ 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', description: 'a balenaOS version',
required: true, required: true,
}), }),
application: flags.string({ application: { ...cf.application, exclusive: ['app', 'device'] },
description: 'application name', app: { ...cf.app, exclusive: ['application', 'device'] },
char: 'a',
exclusive: ['app', 'device'],
}),
app: flags.string({
description: "same as '--application'",
exclusive: ['application', 'device'],
}),
device: flags.string({ device: flags.string({
description: 'device uuid', description: 'device uuid',
char: 'd', char: 'd',
@ -154,6 +151,7 @@ export default class ConfigGenerateCmd extends Command {
}; };
resourceDeviceType = device.is_of__device_type[0].slug; resourceDeviceType = device.is_of__device_type[0].slug;
} else { } else {
// Disambiguate application (if is a number, it could either be an ID or a numerical name)
application = (await getApplication(balena, options.application!, { application = (await getApplication(balena, options.application!, {
$expand: { $expand: {
is_for__device_type: { $select: 'slug' }, is_for__device_type: { $select: 'slug' },
@ -227,17 +225,8 @@ export default class ConfigGenerateCmd extends Command {
$ balena help config generate $ balena help config generate
`; `;
protected readonly deviceTypeNotAllowedMessage = stripIndent` protected readonly deviceTypeNotAllowedMessage =
Specifying a different device type is only supported when 'The --deviceType option can only be used alongside the --application option';
generating a config for an application:
* An application, with --app <appname>
* A specific device type, with --device-type <deviceTypeSlug>
See the help page for examples:
$ balena help config generate
`;
protected async validateOptions(options: FlagsDef) { protected async validateOptions(options: FlagsDef) {
const { ExpectedError } = await import('../../errors'); const { ExpectedError } = await import('../../errors');

View File

@ -19,6 +19,7 @@ import { flags } from '@oclif/command';
import Command from '../../command'; import Command from '../../command';
import * as cf from '../../utils/common-flags'; import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { applicationIdInfo } from '../../utils/messages';
import { runCommand } from '../../utils/helpers'; import { runCommand } from '../../utils/helpers';
interface FlagsDef { interface FlagsDef {
@ -34,17 +35,21 @@ interface FlagsDef {
export default class DeviceInitCmd extends Command { export default class DeviceInitCmd extends Command {
public static description = stripIndent` 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. and writing it to an SD Card.
Note, if the application option is omitted it will be prompted Note, if the application option is omitted it will be prompted
for interactively. for interactively.
${applicationIdInfo.split('\n').join('\n\t\t')}
`; `;
public static examples = [ public static examples = [
'$ balena device init', '$ balena device init',
'$ balena device init --application MyApp', '$ balena device init --application MyApp',
'$ balena device init -a myorg/myapp',
]; ];
public static usage = 'device init'; public static usage = 'device init';
@ -98,7 +103,7 @@ export default class DeviceInitCmd extends Command {
const application = (await getApplication( const application = (await getApplication(
balena, balena,
options['application'] || options['application'] ||
(await (await import('../../utils/patterns')).selectApplication()), (await (await import('../../utils/patterns')).selectApplication()).id,
{ {
$expand: { $expand: {
is_for__device_type: { is_for__device_type: {

View File

@ -20,9 +20,8 @@ import type { IArg } from '@oclif/parser/lib/args';
import type { Application, BalenaSDK } from 'balena-sdk'; import type { Application, BalenaSDK } from 'balena-sdk';
import Command from '../../command'; import Command from '../../command';
import * as cf from '../../utils/common-flags'; import * as cf from '../../utils/common-flags';
import { expandForAppName } from '../../utils/helpers';
import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { tryAsInteger } from '../../utils/validation'; import { applicationIdInfo } from '../../utils/messages';
import { ExpectedError } from '../../errors'; import { ExpectedError } from '../../errors';
interface ExtendedDevice extends DeviceWithDeviceType { 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 Note, if the application option is omitted it will be prompted
for interactively. for interactively.
${applicationIdInfo.split('\n').join('\n\t\t')}
`; `;
public static examples = [ public static examples = [
'$ balena device move 7cf02a6', '$ balena device move 7cf02a6',
'$ balena device move 7cf02a6,dc39e52', '$ balena device move 7cf02a6,dc39e52',
'$ balena device move 7cf02a6 --application MyNewApp', '$ balena device move 7cf02a6 --application MyNewApp',
'$ balena device move 7cf02a6 -a myorg/mynewapp',
]; ];
public static args: Array<IArg<any>> = [ public static args: Array<IArg<any>> = [
@ -80,6 +83,9 @@ export default class DeviceMoveCmd extends Command {
const balena = getBalenaSdk(); const balena = getBalenaSdk();
const { tryAsInteger } = await import('../../utils/validation');
const { expandForAppName } = await import('../../utils/helpers');
options.application = options.application || options.app; options.application = options.application || options.app;
delete options.app; delete options.app;
@ -106,16 +112,21 @@ export default class DeviceMoveCmd extends Command {
: 'N/a'; : '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 // Get destination application
const application = const application = options.application
options.application || ? await getApplication(balena, options.application)
(await this.interactivelySelectApplication(balena, devices)); : await this.interactivelySelectApplication(balena, devices);
// Move each device // Move each device
for (const uuid of deviceIds) { for (const uuid of deviceIds) {
try { try {
await balena.models.device.move(uuid, tryAsInteger(application)); await balena.models.device.move(uuid, application.id);
console.info(`${uuid} was moved to ${application}`); console.info(
`Device ${uuid} was moved to application ${application.slug}`,
);
} catch (err) { } catch (err) {
console.info(`${err.message}, uuid: ${uuid}`); console.info(`${err.message}, uuid: ${uuid}`);
process.exitCode = 1; process.exitCode = 1;

View File

@ -19,7 +19,9 @@ import { flags } from '@oclif/command';
import type { IArg } from '@oclif/parser/lib/args'; import type { IArg } from '@oclif/parser/lib/args';
import Command from '../../command'; import Command from '../../command';
import * as cf from '../../utils/common-flags'; import * as cf from '../../utils/common-flags';
import * as ca from '../../utils/common-args';
import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { applicationIdInfo } from '../../utils/messages';
interface FlagsDef { interface FlagsDef {
uuid?: string; uuid?: string;
@ -35,19 +37,17 @@ export default class DeviceRegisterCmd extends Command {
Register a device. Register a device.
Register a device to an application. Register a device to an application.
${applicationIdInfo.split('\n').join('\n\t\t')}
`; `;
public static examples = [ public static examples = [
'$ balena device register MyApp', '$ balena device register MyApp',
'$ balena device register MyApp --uuid <uuid>', '$ balena device register MyApp --uuid <uuid>',
'$ balena device register myorg/myapp --uuid <uuid>',
]; ];
public static args: Array<IArg<any>> = [ public static args: Array<IArg<any>> = [ca.applicationRequired];
{
name: 'application',
description: 'the name or id of application to register device with',
required: true,
},
];
public static usage = 'device register <application>'; public static usage = 'device register <application>';

View File

@ -20,7 +20,7 @@ import Command from '../../command';
import * as cf from '../../utils/common-flags'; import * as cf from '../../utils/common-flags';
import { expandForAppName } from '../../utils/helpers'; import { expandForAppName } from '../../utils/helpers';
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy'; import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
import { tryAsInteger } from '../../utils/validation'; import { applicationIdInfo, jsonInfo } from '../../utils/messages';
import type { Application } from 'balena-sdk'; import type { Application } from 'balena-sdk';
interface ExtendedDevice extends DeviceWithDeviceType { 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. You can filter the devices by application by using the \`--application\` option.
The --json option is recommended when scripting the output of this command, ${applicationIdInfo.split('\n').join('\n\t\t')}
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. ${jsonInfo.split('\n').join('\n\t\t')}
The 'jq' utility may be helpful for querying JSON fields in shell scripts
(https://stedolan.github.io/jq/manual/).
`; `;
public static examples = [ public static examples = [
'$ balena devices', '$ balena devices',
'$ balena devices --application MyApp', '$ balena devices --application MyApp',
'$ balena devices --app MyApp', '$ balena devices --app MyApp',
'$ balena devices -a MyApp', '$ balena devices -a MyApp',
'$ balena devices -a myorg/myapp',
]; ];
public static usage = 'devices'; public static usage = 'devices';
@ -62,11 +61,8 @@ export default class DevicesCmd extends Command {
public static flags: flags.Input<FlagsDef> = { public static flags: flags.Input<FlagsDef> = {
application: cf.application, application: cf.application,
app: cf.app, app: cf.app,
json: cf.json,
help: cf.help, help: cf.help,
json: flags.boolean({
char: 'j',
description: 'produce JSON output instead of tabular output',
}),
}; };
public static primary = true; public static primary = true;
@ -85,8 +81,10 @@ export default class DevicesCmd extends Command {
let devices; let devices;
if (options.application != null) { if (options.application != null) {
const { getApplication } = await import('../../utils/sdk');
const application = await getApplication(balena, options.application);
devices = (await balena.models.device.getAllByApplication( devices = (await balena.models.device.getAllByApplication(
tryAsInteger(options.application), application.id,
expandForAppName, expandForAppName,
)) as ExtendedDevice[]; )) as ExtendedDevice[];
} else { } else {

View File

@ -18,13 +18,13 @@
import { flags } from '@oclif/command'; import { flags } from '@oclif/command';
import type * as BalenaSdk from 'balena-sdk'; import type * as BalenaSdk from 'balena-sdk';
import Command from '../../command'; import Command from '../../command';
import { ExpectedError } from '../../errors'; import { ExpectedError } from '../../errors';
import * as cf from '../../utils/common-flags'; import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { applicationIdInfo } from '../../utils/messages';
interface FlagsDef { interface FlagsDef {
application?: string; // application name application?: string;
device?: string; // device UUID device?: string; // device UUID
help: void; help: void;
quiet: boolean; quiet: boolean;
@ -63,10 +63,14 @@ export default class EnvAddCmd extends Command {
therefore the --service option cannot be used when the variable name starts therefore the --service option cannot be used when the variable name starts
with a reserved prefix. When defining custom application variables, please with a reserved prefix. When defining custom application variables, please
avoid the reserved prefixes. avoid the reserved prefixes.
`;
${applicationIdInfo.split('\n').join('\n\t\t')}
`;
public static examples = [ public static examples = [
'$ balena env add TERM --application MyApp', '$ balena env add TERM --application MyApp',
'$ balena env add EDITOR vim --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,MyApp2',
'$ balena env add EDITOR vim --application MyApp --service MyService', '$ balena env add EDITOR vim --application MyApp --service MyService',
'$ balena env add EDITOR vim --application MyApp,MyApp2 --service MyService,MyService2', '$ 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 <name> [value]'; public static usage = 'env add <name> [value]';
public static flags: flags.Input<FlagsDef> = { public static flags: flags.Input<FlagsDef> = {
application: { exclusive: ['device'], ...cf.application }, application: { ...cf.application, exclusive: ['device'] },
device: { exclusive: ['application'], ...cf.device }, device: { ...cf.device, exclusive: ['application'] },
help: cf.help, help: cf.help,
quiet: cf.quiet, quiet: cf.quiet,
service: cf.service, service: cf.service,
@ -108,7 +112,7 @@ export default class EnvAddCmd extends Command {
if (!options.application && !options.device) { if (!options.application && !options.device) {
throw new ExpectedError( throw new ExpectedError(
'Either the --application or the --device option must always be used', 'Either the --application or the --device option must be specified',
); );
} }

View File

@ -18,14 +18,14 @@ import { flags } from '@oclif/command';
import type * as SDK from 'balena-sdk'; import type * as SDK from 'balena-sdk';
import * as _ from 'lodash'; import * as _ from 'lodash';
import Command from '../command'; import Command from '../command';
import { ExpectedError } from '../errors'; import { ExpectedError } from '../errors';
import * as cf from '../utils/common-flags'; import * as cf from '../utils/common-flags';
import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy'; import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy';
import { applicationIdInfo } from '../utils/messages';
import { isV13 } from '../utils/version'; import { isV13 } from '../utils/version';
interface FlagsDef { interface FlagsDef {
application?: string; // application name application?: string;
config: boolean; config: boolean;
device?: string; // device UUID device?: string; // device UUID
json: boolean; 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 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 (for example, in case the current user has been removed from the application
by its owner). by its owner).
`;
${applicationIdInfo.split('\n').join('\n\t\t')}
`;
public static examples = [ public static examples = [
'$ balena envs --application MyApp', '$ balena envs --application MyApp',
'$ balena envs --application myorg/myapp',
'$ balena envs --application MyApp --json', '$ balena envs --application MyApp --json',
'$ balena envs --application MyApp --service MyService', '$ balena envs --application MyApp --service MyService',
'$ 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 }, device: { exclusive: ['application'], ...cf.device },
help: cf.help, help: cf.help,
json: flags.boolean({ json: cf.json,
default: false,
char: 'j',
description: 'produce JSON output instead of tabular output',
}),
verbose: cf.verbose, verbose: cf.verbose,
service: { exclusive: ['config'], ...cf.service }, service: { exclusive: ['config'], ...cf.service },
}; };
@ -145,7 +145,7 @@ export default class EnvsCmd extends Command {
const balena = getBalenaSdk(); const balena = getBalenaSdk();
let appName = options.application; let appNameOrSlug = options.application;
let fullUUID: string | undefined; // as oppposed to the short, 7-char UUID let fullUUID: string | undefined; // as oppposed to the short, 7-char UUID
if (options.device) { if (options.device) {
@ -158,16 +158,16 @@ export default class EnvsCmd extends Command {
); );
fullUUID = device.uuid; fullUUID = device.uuid;
if (app) { if (app) {
appName = app.app_name; appNameOrSlug = app.app_name;
} }
} }
if (appName && options.service) { if (appNameOrSlug && options.service) {
await validateServiceName(balena, options.service, appName); await validateServiceName(balena, options.service, appNameOrSlug);
} }
variables.push(...(await getAppVars(balena, appName, options))); variables.push(...(await getAppVars(balena, appNameOrSlug, options)));
if (fullUUID) { if (fullUUID) {
variables.push( variables.push(
...(await getDeviceVars(balena, fullUUID, appName, options)), ...(await getDeviceVars(balena, fullUUID, appNameOrSlug, options)),
); );
} }
if (!options.json && variables.length === 0) { if (!options.json && variables.length === 0) {
@ -241,17 +241,17 @@ async function validateServiceName(
*/ */
async function getAppVars( async function getAppVars(
sdk: SDK.BalenaSDK, sdk: SDK.BalenaSDK,
appName: string | undefined, appNameOrSlug: string | undefined,
options: FlagsDef, options: FlagsDef,
): Promise<EnvironmentVariableInfo[]> { ): Promise<EnvironmentVariableInfo[]> {
const appVars: EnvironmentVariableInfo[] = []; const appVars: EnvironmentVariableInfo[] = [];
if (!appName) { if (!appNameOrSlug) {
return appVars; return appVars;
} }
const vars = await sdk.models.application[ const vars = await sdk.models.application[
options.config ? 'configVar' : 'envVar' options.config ? 'configVar' : 'envVar'
].getAllByApplication(appName); ].getAllByApplication(appNameOrSlug);
fillInInfoFields(vars, appName); fillInInfoFields(vars, appNameOrSlug);
appVars.push(...vars); appVars.push(...vars);
if (!options.config) { if (!options.config) {
const pineOpts: SDK.PineOptions<SDK.ServiceEnvironmentVariable> = { const pineOpts: SDK.PineOptions<SDK.ServiceEnvironmentVariable> = {
@ -267,10 +267,10 @@ async function getAppVars(
}; };
} }
const serviceVars = await sdk.models.service.var.getAllByApplication( const serviceVars = await sdk.models.service.var.getAllByApplication(
appName, appNameOrSlug,
pineOpts, pineOpts,
); );
fillInInfoFields(serviceVars, appName); fillInInfoFields(serviceVars, appNameOrSlug);
appVars.push(...serviceVars); appVars.push(...serviceVars);
} }
return appVars; return appVars;
@ -283,7 +283,7 @@ async function getAppVars(
async function getDeviceVars( async function getDeviceVars(
sdk: SDK.BalenaSDK, sdk: SDK.BalenaSDK,
fullUUID: string, fullUUID: string,
appName: string | undefined, appNameOrSlug: string | undefined,
options: FlagsDef, options: FlagsDef,
): Promise<EnvironmentVariableInfo[]> { ): Promise<EnvironmentVariableInfo[]> {
const printedUUID = options.json ? fullUUID : options.device!; const printedUUID = options.json ? fullUUID : options.device!;
@ -292,7 +292,7 @@ async function getDeviceVars(
const deviceConfigVars = await sdk.models.device.configVar.getAllByDevice( const deviceConfigVars = await sdk.models.device.configVar.getAllByDevice(
fullUUID, fullUUID,
); );
fillInInfoFields(deviceConfigVars, appName, printedUUID); fillInInfoFields(deviceConfigVars, appNameOrSlug, printedUUID);
deviceVars.push(...deviceConfigVars); deviceVars.push(...deviceConfigVars);
} else { } else {
const pineOpts: SDK.PineOptions<SDK.DeviceServiceEnvironmentVariable> = { const pineOpts: SDK.PineOptions<SDK.DeviceServiceEnvironmentVariable> = {
@ -313,13 +313,13 @@ async function getDeviceVars(
fullUUID, fullUUID,
pineOpts, pineOpts,
); );
fillInInfoFields(deviceServiceVars, appName, printedUUID); fillInInfoFields(deviceServiceVars, appNameOrSlug, printedUUID);
deviceVars.push(...deviceServiceVars); deviceVars.push(...deviceServiceVars);
const deviceEnvVars = await sdk.models.device.envVar.getAllByDevice( const deviceEnvVars = await sdk.models.device.envVar.getAllByDevice(
fullUUID, fullUUID,
); );
fillInInfoFields(deviceEnvVars, appName, printedUUID); fillInInfoFields(deviceEnvVars, appNameOrSlug, printedUUID);
deviceVars.push(...deviceEnvVars); deviceVars.push(...deviceEnvVars);
} }
return deviceVars; return deviceVars;
@ -335,7 +335,7 @@ function fillInInfoFields(
| EnvironmentVariableInfo[] | EnvironmentVariableInfo[]
| DeviceServiceEnvironmentVariableInfo[] | DeviceServiceEnvironmentVariableInfo[]
| ServiceEnvironmentVariableInfo[], | ServiceEnvironmentVariableInfo[],
appName?: string, appNameOrSlug?: string,
deviceUUID?: string, deviceUUID?: string,
) { ) {
for (const envVar of varArray) { for (const envVar of varArray) {
@ -347,7 +347,7 @@ function fillInInfoFields(
envVar.serviceName = ((envVar.service_install as SDK.ServiceInstall[])[0] envVar.serviceName = ((envVar.service_install as SDK.ServiceInstall[])[0]
?.installs__service as SDK.Service[])[0]?.service_name; ?.installs__service as SDK.Service[])[0]?.service_name;
} }
envVar.appName = appName; envVar.appName = appNameOrSlug;
envVar.serviceName = envVar.serviceName || '*'; envVar.serviceName = envVar.serviceName || '*';
envVar.deviceUUID = deviceUUID || '*'; envVar.deviceUUID = deviceUUID || '*';
} }

View File

@ -19,6 +19,7 @@ import { flags } from '@oclif/command';
import Command from '../command'; import Command from '../command';
import * as cf from '../utils/common-flags'; import * as cf from '../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../utils/lazy'; import { getBalenaSdk, stripIndent } from '../utils/lazy';
import { applicationIdInfo } from '../utils/messages';
import { parseAsLocalHostnameOrIp } from '../utils/validation'; import { parseAsLocalHostnameOrIp } from '../utils/validation';
interface FlagsDef { 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 scan the local network for balenaOS devices and prompt you to select one
from an interactive picker. This requires root privileges. Likewise, if from an interactive picker. This requires root privileges. Likewise, if
the application flag is not provided then a picker will be shown. the application flag is not provided then a picker will be shown.
${applicationIdInfo.split('\n').join('\n\t\t')}
`; `;
public static examples = [ public static examples = [
'$ balena join', '$ balena join',
'$ balena join balena.local', '$ balena join balena.local',
'$ balena join balena.local --application MyApp', '$ 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',
'$ balena join 192.168.1.25 --application MyApp', '$ 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 usage = 'join [deviceIpOrHostname]';
public static flags: flags.Input<FlagsDef> = { public static flags: flags.Input<FlagsDef> = {
application: { application: cf.application,
description: 'the name of the application the device should join',
...cf.application,
},
pollInterval: flags.integer({ pollInterval: flags.integer({
description: 'the interval in minutes to check for updates', description: 'the interval in minutes to check for updates',
char: 'i', char: 'i',

View File

@ -18,20 +18,19 @@
import { flags } from '@oclif/command'; import { flags } from '@oclif/command';
import type * as BalenaSdk from 'balena-sdk'; import type * as BalenaSdk from 'balena-sdk';
import * as _ from 'lodash'; import * as _ from 'lodash';
import * as path from 'path';
import Command from '../../command'; import Command from '../../command';
import { ExpectedError } from '../../errors'; import { ExpectedError } from '../../errors';
import * as cf from '../../utils/common-flags'; import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy'; import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy';
import { applicationIdInfo } from '../../utils/messages';
const BOOT_PARTITION = 1; const BOOT_PARTITION = 1;
const CONNECTIONS_FOLDER = '/system-connections'; const CONNECTIONS_FOLDER = '/system-connections';
interface FlagsDef { interface FlagsDef {
advanced?: boolean; advanced?: boolean;
app?: string;
application?: string; application?: string;
app?: string;
config?: string; config?: string;
'config-app-update-poll-interval'?: number; 'config-app-update-poll-interval'?: number;
'config-network'?: string; 'config-network'?: string;
@ -88,15 +87,19 @@ export default class OsConfigureCmd extends Command {
${deviceApiKeyDeprecationMsg.split('\n').join('\n\t\t')} ${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 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 are advised to install the Windows Subsystem for Linux (WSL) with Ubuntu, and use
the Linux release of the balena CLI: the Linux release of the balena CLI:
https://docs.microsoft.com/en-us/windows/wsl/about https://docs.microsoft.com/en-us/windows/wsl/about
`; `;
public static examples = [ public static examples = [
'$ balena os configure ../path/rpi3.img --device 7cf02a6', '$ balena os configure ../path/rpi3.img --device 7cf02a6',
'$ balena os configure ../path/rpi3.img --device 7cf02a6 --device-api-key <existingDeviceKey>', '$ balena os configure ../path/rpi3.img --device 7cf02a6 --device-api-key <existingDeviceKey>',
'$ balena os configure ../path/rpi3.img --app MyApp', '$ 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 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',
'$ balena os configure ../path/rpi3.img --app MyFinApp --device-type raspberrypi3 --config myWifiConfig.json', '$ 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: description:
'ask advanced configuration questions (when in interactive mode)', 'ask advanced configuration questions (when in interactive mode)',
}), }),
app: flags.string({ application: { ...cf.application, exclusive: ['app', 'device'] },
description: "same as '--application'", app: { ...cf.app, exclusive: ['application', 'device'] },
exclusive: ['application', 'device'],
}),
application: { exclusive: ['app', 'device'], ...cf.application },
config: flags.string({ config: flags.string({
description: description:
'path to a pre-generated config.json file to be injected in the OS image', '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: description:
'This option will set the device name when the device provisions', 'This option will set the device name when the device provisions',
}), }),
help: cf.help,
version: flags.string({ version: flags.string({
description: 'balenaOS version, for example "2.32.0" or "2.44.0+rev1"', description: 'balenaOS version, for example "2.32.0" or "2.44.0+rev1"',
}), }),
@ -166,6 +165,7 @@ export default class OsConfigureCmd extends Command {
description: description:
"paths to local files to place into the 'system-connections' directory", "paths to local files to place into the 'system-connections' directory",
}), }),
help: cf.help,
}; };
public async run() { public async run() {
@ -174,7 +174,7 @@ export default class OsConfigureCmd extends Command {
); );
// Prefer options.application over options.app // Prefer options.application over options.app
options.application = options.application || options.app; options.application = options.application || options.app;
options.app = undefined; delete options.app;
await validateOptions(options); await validateOptions(options);
@ -266,6 +266,8 @@ export default class OsConfigureCmd extends Command {
); );
if (options['system-connection']) { if (options['system-connection']) {
const path = await import('path');
const files = await Promise.all( const files = await Promise.all(
options['system-connection'].map(async (filePath) => { options['system-connection'].map(async (filePath) => {
const content = await fs.readFile(filePath, 'utf8'); const content = await fs.readFile(filePath, 'utf8');

View File

@ -17,12 +17,14 @@
import { flags } from '@oclif/command'; import { flags } from '@oclif/command';
import Command from '../command'; import Command from '../command';
import * as cf from '../utils/common-flags';
import { import {
getBalenaSdk, getBalenaSdk,
getCliForm, getCliForm,
getVisuals, getVisuals,
stripIndent, stripIndent,
} from '../utils/lazy'; } from '../utils/lazy';
import { applicationIdInfo } from '../utils/messages';
import type { DockerConnectionCliFlags } from '../utils/docker'; import type { DockerConnectionCliFlags } from '../utils/docker';
import { dockerConnectionCliFlags } from '../utils/docker'; import { dockerConnectionCliFlags } from '../utils/docker';
import * as _ from 'lodash'; 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 When the device boots, it will not need to download the application, as it was
preloaded. preloaded.
${applicationIdInfo.split('\n').join('\n\t\t')}
Warning: "balena preload" requires Docker to be correctly installed in Warning: "balena preload" requires Docker to be correctly installed in
your shell environment. For more information (including Windows support) your shell environment. For more information (including Windows support)
check: https://github.com/balena-io/balena-cli/blob/master/INSTALL.md check: https://github.com/balena-io/balena-cli/blob/master/INSTALL.md
`; `;
public static examples = [ 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', '$ balena preload balena.img',
]; ];
@ -83,10 +88,8 @@ export default class PreloadCmd extends Command {
public static usage = 'preload <image>'; public static usage = 'preload <image>';
public static flags: flags.Input<FlagsDef> = { public static flags: flags.Input<FlagsDef> = {
app: flags.string({ // TODO: Replace with application/a in #v13?
description: 'name of the application to preload', app: cf.application,
char: 'a',
}),
commit: flags.string({ commit: flags.string({
description: `\ description: `\
The commit hash for a specific application release to preload, use "current" to specify the current 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 // balena-preload currently does not work with numerical app IDs
// Load app here, and use app slug from hereon // Load app here, and use app slug from hereon
if (options.app && !options.app.includes('/')) { 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 { getApplication } = await import('../utils/sdk');
const application = await getApplication(balena, options.app); const application = await getApplication(balena, options.app);
if (!application) { if (!application) {

View File

@ -20,6 +20,7 @@ import Command from '../command';
import { ExpectedError } from '../errors'; import { ExpectedError } from '../errors';
import * as cf from '../utils/common-flags'; import * as cf from '../utils/common-flags';
import { getBalenaSdk, getCliUx, stripIndent } from '../utils/lazy'; import { getBalenaSdk, getCliUx, stripIndent } from '../utils/lazy';
import { applicationIdInfo } from '../utils/messages';
interface FlagsDef { interface FlagsDef {
application?: string; application?: string;
@ -45,12 +46,14 @@ export default class SupportCmd extends Command {
Both --device and --application flags accept multiple values, specified as Both --device and --application flags accept multiple values, specified as
a comma-separated list (with no spaces). a comma-separated list (with no spaces).
${applicationIdInfo.split('\n').join('\n\t\t')}
`; `;
public static examples = [ public static examples = [
'balena support enable --device ab346f,cd457a --duration 3d', 'balena support enable --device ab346f,cd457a --duration 3d',
'balena support enable --application app3 --duration 12h', 'balena support enable --application app3 --duration 12h',
'balena support disable -a myApp', 'balena support disable -a myorg/myapp',
]; ];
public static args = [ public static args = [
@ -68,10 +71,11 @@ export default class SupportCmd extends Command {
description: 'comma-separated list (no spaces) of device UUIDs', description: 'comma-separated list (no spaces) of device UUIDs',
char: 'd', char: 'd',
}), }),
application: flags.string({ application: {
description: 'comma-separated list (no spaces) of application names', ...cf.application,
char: 'a', description:
}), 'comma-separated list (no spaces) of application names or org/name slugs',
},
duration: flags.string({ duration: flags.string({
description: description:
'length of time to enable support for, in (h)ours or (d)ays, e.g. 12h, 2d', 'length of time to enable support for, in (h)ours or (d)ays, e.g. 12h, 2d',

View File

@ -17,11 +17,9 @@
import { flags } from '@oclif/command'; import { flags } from '@oclif/command';
import Command from '../../command'; import Command from '../../command';
import { ExpectedError } from '../../errors';
import * as cf from '../../utils/common-flags'; import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { disambiguateReleaseParam } from '../../utils/normalization'; import { applicationIdInfo } from '../../utils/messages';
import { tryAsInteger } from '../../utils/validation';
interface FlagsDef { interface FlagsDef {
application?: string; 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.
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 = [ public static examples = [
'$ balena tag rm myTagKey --application MyApp', '$ balena tag rm myTagKey --application MyApp',
'$ balena tag rm myTagKey -a myorg/myapp',
'$ balena tag rm myTagKey --device 7cf02a6', '$ balena tag rm myTagKey --device 7cf02a6',
'$ balena tag rm myTagKey --release 1234', '$ balena tag rm myTagKey --release 1234',
'$ balena tag rm myTagKey --release b376b0e544e9429483b656490e5b9443b4349bd6', '$ balena tag rm myTagKey --release b376b0e544e9429483b656490e5b9443b4349bd6',
@ -64,6 +65,10 @@ export default class TagRmCmd extends Command {
...cf.application, ...cf.application,
exclusive: ['app', 'device', 'release'], exclusive: ['app', 'device', 'release'],
}, },
app: {
...cf.app,
exclusive: ['application', 'device', 'release'],
},
device: { device: {
...cf.device, ...cf.device,
exclusive: ['app', 'application', 'release'], exclusive: ['app', 'application', 'release'],
@ -73,10 +78,6 @@ export default class TagRmCmd extends Command {
exclusive: ['app', 'application', 'device'], exclusive: ['app', 'application', 'device'],
}, },
help: cf.help, help: cf.help,
app: flags.string({
description: "same as '--application'",
exclusive: ['application', 'device', 'release'],
}),
}; };
public static authenticated = true; public static authenticated = true;
@ -94,9 +95,12 @@ export default class TagRmCmd extends Command {
// Check user has specified one of application/device/release // Check user has specified one of application/device/release
if (!options.application && !options.device && !options.release) { if (!options.application && !options.device && !options.release) {
const { ExpectedError } = await import('../../errors');
throw new ExpectedError(TagRmCmd.missingResourceMessage); throw new ExpectedError(TagRmCmd.missingResourceMessage);
} }
const { tryAsInteger } = await import('../../utils/validation');
if (options.application) { if (options.application) {
return balena.models.application.tags.remove( return balena.models.application.tags.remove(
tryAsInteger(options.application), tryAsInteger(options.application),
@ -110,6 +114,9 @@ export default class TagRmCmd extends Command {
); );
} }
if (options.release) { if (options.release) {
const { disambiguateReleaseParam } = await import(
'../../utils/normalization'
);
const releaseParam = await disambiguateReleaseParam( const releaseParam = await disambiguateReleaseParam(
balena, balena,
options.release, options.release,
@ -122,7 +129,7 @@ export default class TagRmCmd extends Command {
protected static missingResourceMessage = stripIndent` protected static missingResourceMessage = stripIndent`
To remove a resource tag, you must provide exactly one of: To remove a resource tag, you must provide exactly one of:
* An application, with --application <appname> * An application, with --application <appNameOrSlug>
* A device, with --device <uuid> * A device, with --device <uuid>
* A release, with --release <id or commit> * A release, with --release <id or commit>

View File

@ -17,11 +17,9 @@
import { flags } from '@oclif/command'; import { flags } from '@oclif/command';
import Command from '../../command'; import Command from '../../command';
import { ExpectedError } from '../../errors';
import * as cf from '../../utils/common-flags'; import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { disambiguateReleaseParam } from '../../utils/normalization'; import { applicationIdInfo } from '../../utils/messages';
import { tryAsInteger } from '../../utils/validation';
interface FlagsDef { interface FlagsDef {
application?: string; application?: string;
@ -45,10 +43,13 @@ export default class TagSetCmd extends Command {
You can optionally provide a value to be associated with the created 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 tag, as an extra argument after the tag key. If a value isn't
provided, a tag with an empty value is created. provided, a tag with an empty value is created.
${applicationIdInfo.split('\n').join('\n\t\t')}
`; `;
public static examples = [ public static examples = [
'$ balena tag set mySimpleTag --application MyApp', '$ 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 --application MyApp',
'$ balena tag set myCompositeTag myTagValue --device 7cf02a6', '$ balena tag set myCompositeTag myTagValue --device 7cf02a6',
'$ balena tag set myCompositeTag "my tag value with whitespaces" --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, ...cf.application,
exclusive: ['app', 'device', 'release'], exclusive: ['app', 'device', 'release'],
}, },
app: {
...cf.app,
exclusive: ['application', 'device', 'release'],
},
device: { device: {
...cf.device, ...cf.device,
exclusive: ['app', 'application', 'release'], exclusive: ['app', 'application', 'release'],
@ -86,10 +91,6 @@ export default class TagSetCmd extends Command {
exclusive: ['app', 'application', 'device'], exclusive: ['app', 'application', 'device'],
}, },
help: cf.help, help: cf.help,
app: flags.string({
description: "same as '--application'",
exclusive: ['application', 'device', 'release'],
}),
}; };
public static authenticated = true; public static authenticated = true;
@ -107,11 +108,14 @@ export default class TagSetCmd extends Command {
// Check user has specified one of application/device/release // Check user has specified one of application/device/release
if (!options.application && !options.device && !options.release) { if (!options.application && !options.device && !options.release) {
const { ExpectedError } = await import('../../errors');
throw new ExpectedError(TagSetCmd.missingResourceMessage); throw new ExpectedError(TagSetCmd.missingResourceMessage);
} }
params.value ??= ''; params.value ??= '';
const { tryAsInteger } = await import('../../utils/validation');
if (options.application) { if (options.application) {
return balena.models.application.tags.set( return balena.models.application.tags.set(
tryAsInteger(options.application), tryAsInteger(options.application),
@ -127,6 +131,9 @@ export default class TagSetCmd extends Command {
); );
} }
if (options.release) { if (options.release) {
const { disambiguateReleaseParam } = await import(
'../../utils/normalization'
);
const releaseParam = await disambiguateReleaseParam( const releaseParam = await disambiguateReleaseParam(
balena, balena,
options.release, options.release,
@ -143,7 +150,7 @@ export default class TagSetCmd extends Command {
protected static missingResourceMessage = stripIndent` protected static missingResourceMessage = stripIndent`
To set a resource tag, you must provide exactly one of: To set a resource tag, you must provide exactly one of:
* An application, with --application <appname> * An application, with --application <appNameOrSlug>
* A device, with --device <uuid> * A device, with --device <uuid>
* A release, with --release <id or commit> * A release, with --release <id or commit>

View File

@ -20,8 +20,7 @@ import Command from '../command';
import { ExpectedError } from '../errors'; import { ExpectedError } from '../errors';
import * as cf from '../utils/common-flags'; import * as cf from '../utils/common-flags';
import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy'; import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy';
import { disambiguateReleaseParam } from '../utils/normalization'; import { applicationIdInfo } from '../utils/messages';
import { tryAsInteger } from '../utils/validation';
interface FlagsDef { interface FlagsDef {
application?: string; application?: string;
@ -37,10 +36,13 @@ export default class TagsCmd extends Command {
List all tags and their values for a particular application, List all tags and their values for a particular application,
device or release. device or release.
${applicationIdInfo.split('\n').join('\n\t\t')}
`; `;
public static examples = [ public static examples = [
'$ balena tags --application MyApp', '$ balena tags --application MyApp',
'$ balena tags -a myorg/myapp',
'$ balena tags --device 7cf02a6', '$ balena tags --device 7cf02a6',
'$ balena tags --release 1234', '$ balena tags --release 1234',
'$ balena tags --release b376b0e544e9429483b656490e5b9443b4349bd6', '$ balena tags --release b376b0e544e9429483b656490e5b9443b4349bd6',
@ -53,6 +55,10 @@ export default class TagsCmd extends Command {
...cf.application, ...cf.application,
exclusive: ['app', 'device', 'release'], exclusive: ['app', 'device', 'release'],
}, },
app: {
...cf.app,
exclusive: ['application', 'device', 'release'],
},
device: { device: {
...cf.device, ...cf.device,
exclusive: ['app', 'application', 'release'], exclusive: ['app', 'application', 'release'],
@ -62,10 +68,6 @@ export default class TagsCmd extends Command {
exclusive: ['app', 'application', 'device'], exclusive: ['app', 'application', 'device'],
}, },
help: cf.help, help: cf.help,
app: flags.string({
description: "same as '--application'",
exclusive: ['application', 'device', 'release'],
}),
}; };
public static authenticated = true; public static authenticated = true;
@ -84,6 +86,8 @@ export default class TagsCmd extends Command {
throw new ExpectedError(this.missingResourceMessage); throw new ExpectedError(this.missingResourceMessage);
} }
const { tryAsInteger } = await import('../utils/validation');
let tags; let tags;
if (options.application) { if (options.application) {
@ -97,6 +101,9 @@ export default class TagsCmd extends Command {
); );
} }
if (options.release) { if (options.release) {
const { disambiguateReleaseParam } = await import(
'../utils/normalization'
);
const releaseParam = await disambiguateReleaseParam( const releaseParam = await disambiguateReleaseParam(
balena, balena,
options.release, options.release,
@ -115,7 +122,7 @@ export default class TagsCmd extends Command {
protected missingResourceMessage = stripIndent` protected missingResourceMessage = stripIndent`
To list tags for a resource, you must provide exactly one of: To list tags for a resource, you must provide exactly one of:
* An application, with --application <appname> * An application, with --application <appNameOrSlug>
* A device, with --device <uuid> * A device, with --device <uuid>
* A release, with --release <id or commit> * A release, with --release <id or commit>

24
lib/utils/common-args.ts Normal file
View File

@ -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,
};

View File

@ -19,14 +19,17 @@ import { flags } from '@oclif/command';
import type { IBooleanFlag } from '@oclif/parser/lib/flags'; import type { IBooleanFlag } from '@oclif/parser/lib/flags';
import { stripIndent } from './lazy'; import { stripIndent } from './lazy';
import { lowercaseIfSlug } from './normalization';
export const application = flags.string({ export const application = flags.string({
char: 'a', 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. // TODO: Consider remove second alias 'app' when we can, to simplify.
export const app = flags.string({ export const app = flags.string({
description: "same as '--application'", description: "same as '--application'",
parse: lowercaseIfSlug,
}); });
export const device = flags.string({ export const device = flags.string({
@ -78,3 +81,9 @@ export const drive = flags.string({
Check \`balena util available-drives\` for available options. Check \`balena util available-drives\` for available options.
`, `,
}); });
export const json: IBooleanFlag<boolean> = flags.boolean({
char: 'j',
description: 'produce JSON output instead of tabular output',
default: false,
});

View File

@ -38,9 +38,9 @@ export const balenaAsciiArt = `\
|_.__/ \\__,_||_| \\____/|_| |_| \\__,_| |_.__/ \\__,_||_| \\____/|_| |_| \\__,_|
`; `;
export const registrySecretsHelp = `\ export const registrySecretsHelp =
REGISTRY SECRETS 'REGISTRY SECRETS \n' +
The --registry-secrets option specifies a JSON or YAML file containing private `The --registry-secrets option specifies a JSON or YAML file containing private
Docker registry usernames and passwords to be used when pulling base images. Docker registry usernames and passwords to be used when pulling base images.
Sample registry-secrets YAML file: 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), secrets.json file exists in the balena directory (usually $HOME/.balena),
this file will be used instead.`; this file will be used instead.`;
export const dockerignoreHelp = `\ export const dockerignoreHelp =
DOCKERIGNORE AND GITIGNORE FILES 'DOCKERIGNORE AND GITIGNORE FILES \n' +
By default, the balena CLI will use a single ".dockerignore" file (if any) at `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 the project root (--source directory) in order to decide which source files to
exclude from the "build context" (tar stream) sent to balenaCloud, Docker exclude from the "build context" (tar stream) sent to balenaCloud, Docker
daemon or balenaEngine. In a microservices (multicontainer) application, the 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 --multi-dockerignore (-m) and will be removed in the CLI's next major version
release (v13). release (v13).
Default .dockerignore patterns Default .dockerignore patterns \n` +
When --gitignore (-g) is NOT used (i.e. when not in v11 compatibility mode), a `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 few default/hardcoded dockerignore patterns are "merged" (in memory) with the
patterns found in the applicable .dockerignore files, in the following order: 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: \`!mysubmodule/.git\`. For documentation on pattern format, see:
- https://docs.docker.com/engine/reference/builder/#dockerignore-file - https://docs.docker.com/engine/reference/builder/#dockerignore-file
- https://www.npmjs.com/package/@balena/dockerignore`; - 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/).`;

View File

@ -187,7 +187,6 @@ export function selectApplication(
} }
const apps = (await balena.models.application.getAll({ const apps = (await balena.models.application.getAll({
$select: 'app_name',
$expand: { $expand: {
is_for__device_type: { is_for__device_type: {
$select: 'slug', $select: 'slug',
@ -204,8 +203,8 @@ export function selectApplication(
message: 'Select an application', message: 'Select an application',
type: 'list', type: 'list',
choices: _.map(applications, (application) => ({ choices: _.map(applications, (application) => ({
name: `${application.app_name} (${application.is_for__device_type[0].slug})`, name: `${application.app_name} (${application.slug}) [${application.is_for__device_type[0].slug}]`,
value: application.app_name, value: application,
})), })),
}); });
}); });

View File

@ -26,6 +26,7 @@ import type {
* Wraps the sdk application.get method, * Wraps the sdk application.get method,
* adding disambiguation in cases where the provided * adding disambiguation in cases where the provided
* identifier could be interpreted in multiple valid ways. * identifier could be interpreted in multiple valid ways.
* // TODO: Remove this once support for numeric App IDs is removed.
*/ */
export async function getApplication( export async function getApplication(
sdk: BalenaSDK, sdk: BalenaSDK,

View File

@ -19,32 +19,6 @@ import { expect } from 'chai';
import { BalenaAPIMock } from '../../balena-api-mock'; import { BalenaAPIMock } from '../../balena-api-mock';
import { cleanOutput, runCommand } from '../../helpers'; import { cleanOutput, runCommand } from '../../helpers';
const HELP_RESPONSE = `
Move one or more devices to another application.
USAGE
$ balena device move <uuid(s)>
ARGUMENTS
<uuid> comma-separated list (no blank spaces) of device UUIDs to be moved
OPTIONS
-a, --application <application> application name
-h, --help show CLI help
--app <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 () { describe('balena device move', function () {
let api: BalenaAPIMock; let api: BalenaAPIMock;
@ -59,14 +33,6 @@ describe('balena device move', function () {
api.done(); 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 () => { it('should error if uuid not provided', async () => {
const { out, err } = await runCommand('device move'); const { out, err } = await runCommand('device move');
const errLines = cleanOutput(err); const errLines = cleanOutput(err);

View File

@ -21,36 +21,6 @@ import * as path from 'path';
import { apiResponsePath, BalenaAPIMock } from '../../balena-api-mock'; import { apiResponsePath, BalenaAPIMock } from '../../balena-api-mock';
import { cleanOutput, runCommand } from '../../helpers'; import { cleanOutput, runCommand } from '../../helpers';
const HELP_RESPONSE = `
List all devices.
USAGE
$ balena devices
OPTIONS
-a, --application <application> application name
-h, --help show CLI help
-j, --json produce JSON output instead of tabular output
--app <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 () { describe('balena devices', function () {
let api: BalenaAPIMock; let api: BalenaAPIMock;
@ -65,14 +35,6 @@ describe('balena devices', function () {
api.done(); 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 () => { it('should list devices from own and collaborator apps', async () => {
api.scope api.scope
.get( .get(