From 64a44e7a5f6113952eda2a0fc3236331c8acf82e Mon Sep 17 00:00:00 2001 From: Paulo Castro Date: Thu, 15 Jul 2021 14:41:38 +0100 Subject: [PATCH] Rename applications to fleets (stage 1). See: https://git.io/JRuZr - Add fleet(s) commands and -f, --fleet flags as aliases to the app(s) commands and -a, --app, --application flags. - Conditionally rename column/row headers and JSON object properties from 'application' to 'fleet', with some variations. - Print warning messages regarding the renaming, provided that stderr is attached to an interactive terminal. Change-type: minor Resolves: #2302 --- .github/ISSUE_TEMPLATE.md | 10 +- .mocharc.js | 1 - README.md | 2 +- automation/capitanodoc/capitanodoc.ts | 9 +- automation/capitanodoc/index.ts | 2 +- completion/_balena | 6 +- completion/balena-completion.bash | 6 +- doc/automated-init.md | 4 +- doc/cli.markdown | 1228 ++++++++++++++----------- lib/commands/app/create.ts | 75 +- lib/commands/app/index.ts | 78 +- lib/commands/app/purge.ts | 63 +- lib/commands/app/rename.ts | 81 +- lib/commands/app/restart.ts | 61 +- lib/commands/app/rm.ts | 67 +- lib/commands/apps.ts | 62 +- lib/commands/build.ts | 32 +- lib/commands/config/generate.ts | 72 +- lib/commands/deploy.ts | 41 +- lib/commands/device/index.ts | 16 +- lib/commands/device/init.ts | 31 +- lib/commands/device/move.ts | 39 +- lib/commands/device/purge.ts | 6 +- lib/commands/device/register.ts | 20 +- lib/commands/devices/index.ts | 72 +- lib/commands/env/add.ts | 61 +- lib/commands/env/rename.ts | 4 +- lib/commands/env/rm.ts | 4 +- lib/commands/envs.ts | 117 +-- lib/commands/fleet/create.ts | 20 + lib/commands/fleet/index.ts | 20 + lib/commands/fleet/purge.ts | 20 + lib/commands/fleet/rename.ts | 20 + lib/commands/fleet/restart.ts | 20 + lib/commands/fleet/rm.ts | 20 + lib/commands/fleets.ts | 20 + lib/commands/join.ts | 21 +- lib/commands/leave.ts | 4 +- lib/commands/os/configure.ts | 62 +- lib/commands/preload.ts | 71 +- lib/commands/push.ts | 44 +- lib/commands/ssh.ts | 24 +- lib/commands/support.ts | 38 +- lib/commands/tag/rm.ts | 57 +- lib/commands/tag/set.ts | 59 +- lib/commands/tags.ts | 54 +- lib/commands/tunnel.ts | 22 +- lib/events.ts | 6 +- lib/help.ts | 4 +- lib/utils/cloud.ts | 4 +- lib/utils/common-args.ts | 6 +- lib/utils/common-flags.ts | 22 +- lib/utils/compose_ts.ts | 16 +- lib/utils/env-common.ts | 17 +- lib/utils/helpers.ts | 30 +- lib/utils/messages.ts | 71 +- lib/utils/patterns.ts | 14 +- lib/utils/promote.ts | 32 +- lib/utils/validation.ts | 2 +- tests/commands/app/create.spec.ts | 22 +- tests/commands/build.spec.ts | 44 +- tests/commands/deploy.spec.ts | 15 +- tests/commands/device/device.spec.ts | 39 +- tests/commands/device/devices.spec.ts | 12 +- tests/commands/env/envs.spec.ts | 53 +- tests/commands/os/configure.spec.ts | 2 +- tests/commands/push.spec.ts | 72 +- tests/projects.ts | 36 + tests/utils/validation.spec.ts | 8 +- 69 files changed, 2117 insertions(+), 1276 deletions(-) create mode 100644 lib/commands/fleet/create.ts create mode 100644 lib/commands/fleet/index.ts create mode 100644 lib/commands/fleet/purge.ts create mode 100644 lib/commands/fleet/rename.ts create mode 100644 lib/commands/fleet/restart.ts create mode 100644 lib/commands/fleet/rm.ts create mode 100644 lib/commands/fleets.ts diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index bb6fb999..75f6a458 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -32,11 +32,11 @@ Please describe what actually happened instead: Examples: ``` -balena push myApp +balena push myFleet balena push 192.168.0.12 -balena deploy myApp -balena deploy myApp --build -balena build . -a myApp +balena deploy myFleet +balena deploy myFleet --build +balena build . -f myFleet balena build . -A armv7hf -d raspberrypi3 ``` @@ -48,7 +48,7 @@ additional information. The `--logs` option reveals additional information for t ``` balena build . --logs -balena deploy myApp --build --logs +balena deploy myFleet --build --logs ``` # Steps to Reproduce the Problem diff --git a/.mocharc.js b/.mocharc.js index ec00e992..b4209e59 100644 --- a/.mocharc.js +++ b/.mocharc.js @@ -1,5 +1,4 @@ module.exports = { - spec: 'tests/commands/app/create.spec.ts', reporter: 'spec', require: 'ts-node/register/transpile-only', file: './tests/config-tests', diff --git a/README.md b/README.md index b992616b..dff77508 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ guide](https://docs.docker.com/compose/completion/) for system setup instruction ## Logging in Several CLI commands require access to your balenaCloud account, for example in order to push a -new release to your application. Those commands require creating a CLI login session by running: +new release to your fleet. Those commands require creating a CLI login session by running: ```sh $ balena login diff --git a/automation/capitanodoc/capitanodoc.ts b/automation/capitanodoc/capitanodoc.ts index c4cf892f..ed0e2334 100644 --- a/automation/capitanodoc/capitanodoc.ts +++ b/automation/capitanodoc/capitanodoc.ts @@ -34,15 +34,22 @@ const capitanoDoc = { files: ['build/commands/api-key/generate.js'], }, { - title: 'Application', + title: 'Fleet', files: [ 'build/commands/apps.js', + 'build/commands/fleets.js', 'build/commands/app/index.js', + 'build/commands/fleet/index.js', 'build/commands/app/create.js', + 'build/commands/fleet/create.js', 'build/commands/app/purge.js', + 'build/commands/fleet/purge.js', 'build/commands/app/rename.js', + 'build/commands/fleet/rename.js', 'build/commands/app/restart.js', + 'build/commands/fleet/restart.js', 'build/commands/app/rm.js', + 'build/commands/fleet/rm.js', ], }, { diff --git a/automation/capitanodoc/index.ts b/automation/capitanodoc/index.ts index e9816df7..87d792ce 100644 --- a/automation/capitanodoc/index.ts +++ b/automation/capitanodoc/index.ts @@ -58,7 +58,7 @@ class FakeHelpCommand { examples = [ '$ balena help', - '$ balena help apps', + '$ balena help login', '$ balena help os download', ]; diff --git a/completion/_balena b/completion/_balena index eb0fab36..ff9e597b 100644 --- a/completion/_balena +++ b/completion/_balena @@ -8,7 +8,7 @@ _balena() { local context state line curcontext="$curcontext" # Valid top-level completions - main_commands=( apps build deploy envs join keys leave login logout logs note orgs preload push scan settings ssh support tags tunnel version whoami api-key app app config device device devices env internal key key local os tag util ) + main_commands=( apps build deploy envs fleets join keys leave login logout logs note orgs preload push scan settings ssh support tags tunnel version whoami api-key app app config device device devices env fleet fleet internal key key local os tag util ) # Sub-completions api_key_cmds=( generate ) app_cmds=( create purge rename restart rm ) @@ -16,6 +16,7 @@ _balena() { device_cmds=( deactivate identify init local-mode move os-update public-url purge reboot register rename restart rm shutdown ) devices_cmds=( supported ) env_cmds=( add rename rm ) + fleet_cmds=( create purge rename restart rm ) internal_cmds=( osinit ) key_cmds=( add rm ) local_cmds=( configure flash ) @@ -57,6 +58,9 @@ _balena_sec_cmds() { "env") _describe -t env_cmds 'env_cmd' env_cmds "$@" && ret=0 ;; + "fleet") + _describe -t fleet_cmds 'fleet_cmd' fleet_cmds "$@" && ret=0 + ;; "internal") _describe -t internal_cmds 'internal_cmd' internal_cmds "$@" && ret=0 ;; diff --git a/completion/balena-completion.bash b/completion/balena-completion.bash index 1e9c7513..c8228b56 100644 --- a/completion/balena-completion.bash +++ b/completion/balena-completion.bash @@ -7,7 +7,7 @@ _balena_complete() local cur prev # Valid top-level completions - main_commands="apps build deploy envs join keys leave login logout logs note orgs preload push scan settings ssh support tags tunnel version whoami api-key app app config device device devices env internal key key local os tag util" + main_commands="apps build deploy envs fleets join keys leave login logout logs note orgs preload push scan settings ssh support tags tunnel version whoami api-key app app config device device devices env fleet fleet internal key key local os tag util" # Sub-completions api_key_cmds="generate" app_cmds="create purge rename restart rm" @@ -15,6 +15,7 @@ _balena_complete() device_cmds="deactivate identify init local-mode move os-update public-url purge reboot register rename restart rm shutdown" devices_cmds="supported" env_cmds="add rename rm" + fleet_cmds="create purge rename restart rm" internal_cmds="osinit" key_cmds="add rm" local_cmds="configure flash" @@ -51,6 +52,9 @@ _balena_complete() env) COMPREPLY=( $(compgen -W "$env_cmds" -- $cur) ) ;; + fleet) + COMPREPLY=( $(compgen -W "$fleet_cmds" -- $cur) ) + ;; internal) COMPREPLY=( $(compgen -W "$internal_cmds" -- $cur) ) ;; diff --git a/doc/automated-init.md b/doc/automated-init.md index 6bf229e1..0879c369 100644 --- a/doc/automated-init.md +++ b/doc/automated-init.md @@ -7,7 +7,7 @@ It requires collecting some preliminary information _once_. The final command to provision the device looks like this: ```bash -balena device init --app APP_ID --os-version OS_VERSION --drive DRIVE --config CONFIG_FILE --yes +balena device init --fleet FLEET_ID --os-version OS_VERSION --drive DRIVE --config CONFIG_FILE --yes ``` @@ -24,7 +24,7 @@ But before you can run it you need to collect the parameters and build the confi ``` and find the _slug_ for your target device type, like _raspberrypi3_. -1. `APP_ID`. Create an application (`balena app create APP_NAME --type DEVICE_TYPE`) or find an existing one (`balena apps`) and notice its ID. +1. `FLEET_ID`. Create a fleet (`balena fleet create FLEET_NAME --type DEVICE_TYPE`) or find an existing one (`balena fleets`) and notice its ID. 1. `OS_VERSION`. Run ```bash diff --git a/doc/cli.markdown b/doc/cli.markdown index 38cc71b6..39e20651 100644 --- a/doc/cli.markdown +++ b/doc/cli.markdown @@ -50,7 +50,7 @@ guide](https://docs.docker.com/compose/completion/) for system setup instruction ## Logging in Several CLI commands require access to your balenaCloud account, for example in order to push a -new release to your application. Those commands require creating a CLI login session by running: +new release to your fleet. Those commands require creating a CLI login session by running: ```sh $ balena login @@ -160,15 +160,22 @@ Users are encouraged to regularly update the balena CLI to the latest version. - [api-key generate <name>](#api-key-generate-name) -- Application +- Fleet - [apps](#apps) - - [app <nameorslug>](#app-nameorslug) + - [fleets](#fleets) + - [app <fleet>](#app-fleet) + - [fleet <fleet>](#fleet-fleet) - [app create <name>](#app-create-name) - - [app purge <application>](#app-purge-application) - - [app rename <application> [newname]](#app-rename-application-newname) - - [app restart <application>](#app-restart-application) - - [app rm <application>](#app-rm-application) + - [fleet create <name>](#fleet-create-name) + - [app purge <fleet>](#app-purge-fleet) + - [fleet purge <fleet>](#fleet-purge-fleet) + - [app rename <fleet> [newname]](#app-rename-fleet-newname) + - [fleet rename <fleet> [newname]](#fleet-rename-fleet-newname) + - [app restart <fleet>](#app-restart-fleet) + - [fleet restart <fleet>](#fleet-restart-fleet) + - [app rm <fleet>](#app-rm-fleet) + - [fleet rm <fleet>](#fleet-rm-fleet) - Authentication @@ -190,7 +197,7 @@ Users are encouraged to regularly update the balena CLI to the latest version. - [device public-url <uuid>](#device-public-url-uuid) - [device purge <uuid>](#device-purge-uuid) - [device reboot <uuid>](#device-reboot-uuid) - - [device register <application>](#device-register-application) + - [device register <fleet>](#device-register-fleet) - [device rename <uuid> [newname]](#device-rename-uuid-newname) - [device restart <uuid>](#device-restart-uuid) - [device rm <uuid(s)>](#device-rm-uuid-s) @@ -228,8 +235,8 @@ Users are encouraged to regularly update the balena CLI to the latest version. - Network - [scan](#scan) - - [ssh <applicationordevice> [service]](#ssh-applicationordevice-service) - - [tunnel <deviceorapplication>](#tunnel-deviceorapplication) + - [ssh <fleetordevice> [service]](#ssh-fleetordevice-service) + - [tunnel <deviceorfleet>](#tunnel-deviceorfleet) - Notes @@ -257,7 +264,7 @@ Users are encouraged to regularly update the balena CLI to the latest version. - Push - - [push <applicationordevice>](#push-applicationordevice) + - [push <fleetordevice>](#push-fleetordevice) - Settings @@ -271,7 +278,7 @@ Users are encouraged to regularly update the balena CLI to the latest version. - Deploy - [build [source]](#build-source) - - [deploy <appname> [image]](#deploy-appname-image) + - [deploy <fleet> [image]](#deploy-fleet-image) - Platform @@ -308,18 +315,17 @@ the API key name ### Options -# Application +# Fleet ## apps -list all your balena applications. +Renaming notice: The 'apps' command was renamed to 'fleets', and 'apps' +is now an alias. THE ALIAS WILL BE REMOVED in the next major version +of the balena CLI (so that a different 'apps' command can be implemented +in the future). Use 'fleets' instead of 'apps' to avoid this warning. +Find out more at: https://git.io/JRuZr -For detailed information on a particular application, -use `balena app ` instead. - -Examples: - - $ balena apps +For command usage, see 'balena help fleets' ### Options @@ -327,46 +333,106 @@ Examples: No-op since release v12.0.0 -## app <nameOrSlug> +## fleets -Display detailed information about a single balena application. +List all your balena fleets. -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. +For detailed information on a particular fleet, use +`balena fleet ` Examples: - $ balena app MyApp - $ balena app myorg/myapp + $ balena fleets + +### Options + +#### -v, --verbose + +No-op since release v12.0.0 + +## app <fleet> + +Renaming notice: The 'app' command was renamed to 'fleet', and 'app' +is now an alias. THE ALIAS WILL BE REMOVED in the next major version +of the balena CLI (so that a different 'app' command can be implemented +in the future). Use 'fleet' instead of 'app' to avoid this warning. +Find out more at: https://git.io/JRuZr + +For command usage, see 'balena help fleet' ### Arguments -#### APPLICATION +#### FLEET -application name, slug (preferred), or numeric ID (deprecated) +fleet name, slug (preferred), or numeric ID (deprecated) + +### Options + +## fleet <fleet> + +Display detailed information about a single fleet. + +Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are +the recommended option, as they are unique and unambiguous. Slugs can be +listed with the `balena fleets` command. Note that slugs may change if the +fleet is renamed. Fleet names are not unique and may result in "Fleet 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 fleet, or with fleets +from other balena accounts that you may be invited to join under any role. +For this reason, fleet names are especially discouraged in scripts (e.g. CI +environments). Numeric fleet IDs are deprecated because they consist of an +implementation detail of the balena backend. We intend to remove support for +numeric IDs at some point in the future. + +Examples: + + $ balena fleet MyFleet + $ balena fleet myorg/myfleet + +### Arguments + +#### FLEET + +fleet name, slug (preferred), or numeric ID (deprecated) ### Options ## app create <name> -Create a new balena application. +Renaming notice: The 'app' command was renamed to 'fleet', and 'app' +is now an alias. THE ALIAS WILL BE REMOVED in the next major version +of the balena CLI (so that a different 'app' command can be implemented +in the future). Use 'fleet' instead of 'app' to avoid this warning. +Find out more at: https://git.io/JRuZr -You can specify the organization the application should belong to using +For command usage, see 'balena help fleet create' + +### Arguments + +#### NAME + +fleet name + +### Options + +#### -o, --organization ORGANIZATION + +handle of the organization the fleet should belong to + +#### -t, --type TYPE + +fleet device type (Check available types with `balena devices supported`) + +## fleet create <name> + +Create a new balena fleet. + +You can specify the organization the fleet should belong to using the `--organization` option. The organization's handle, not its name, should be provided. Organization handles can be listed with the `balena orgs` command. -The application's default device type is specified with the `--type` option. +The fleet's default device type is specified with the `--type` option. The `balena devices supported` command can be used to list the available device types. @@ -378,155 +444,231 @@ type and organization. Examples: - $ balena app create MyApp - $ balena app create MyApp --organization mmyorg - $ balena app create MyApp -o myorg --type raspberry-pi + $ balena fleet create MyFleet + $ balena fleet create MyFleet --organization mmyorg + $ balena fleet create MyFleet -o myorg --type raspberry-pi ### Arguments #### NAME -application name +fleet name ### Options #### -o, --organization ORGANIZATION -handle of the organization the application should belong to +handle of the organization the fleet should belong to #### -t, --type TYPE -application device type (Check available types with `balena devices supported`) +fleet device type (Check available types with `balena devices supported`) -## app purge <application> +## app purge <fleet> -Purge data from all devices belonging to an application. -This will clear the application's /data directory. +Renaming notice: The 'app' command was renamed to 'fleet', and 'app' +is now an alias. THE ALIAS WILL BE REMOVED in the next major version +of the balena CLI (so that a different 'app' command can be implemented +in the future). Use 'fleet' instead of 'app' to avoid this warning. +Find out more at: https://git.io/JRuZr -Applications may be specified by app name, slug, or numeric ID. App slugs -are the recommended option, as they are unique and unambiguous. Slugs -can be listed with the `balena apps` command. Note that slugs may change -if the application is renamed. -App names are not unique and may result in "Application is ambiguous" errors -at any time (even if it "used to work in the past"), for example if the name -clashes with a newly created public application, or with apps from other balena -accounts that you may have been invited to as a member. For this reason, app -names are especially discouraged in scripts (e.g. CI environments). -Numeric app IDs are deprecated because they consist of an implementation detail -of the balena backend. We intend to remove support for numeric IDs at some point -in the future. - -Examples: - - $ balena app purge MyApp - $ balena app purge myorg/myapp +For command usage, see 'balena help fleet purge' ### Arguments -#### APPLICATION +#### FLEET -application name, slug (preferred), or numeric ID (deprecated) +fleet name, slug (preferred), or numeric ID (deprecated) ### Options -## app rename <application> [newName] +## fleet purge <fleet> -Rename an application. +Purge data from all devices belonging to a fleet. +This will clear the fleet's '/data' directory. + +Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are +the recommended option, as they are unique and unambiguous. Slugs can be +listed with the `balena fleets` command. Note that slugs may change if the +fleet is renamed. Fleet names are not unique and may result in "Fleet 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 fleet, or with fleets +from other balena accounts that you may be invited to join under any role. +For this reason, fleet names are especially discouraged in scripts (e.g. CI +environments). Numeric fleet IDs are deprecated because they consist of an +implementation detail of the balena backend. We intend to remove support for +numeric IDs at some point in the future. + +Examples: + + $ balena fleet purge MyFleet + $ balena fleet purge myorg/myfleet + +### Arguments + +#### FLEET + +fleet name, slug (preferred), or numeric ID (deprecated) + +### Options + +## app rename <fleet> [newName] + +Renaming notice: The 'app' command was renamed to 'fleet', and 'app' +is now an alias. THE ALIAS WILL BE REMOVED in the next major version +of the balena CLI (so that a different 'app' command can be implemented +in the future). Use 'fleet' instead of 'app' to avoid this warning. +Find out more at: https://git.io/JRuZr + +For command usage, see 'balena help fleet rename' + +### Arguments + +#### FLEET + +fleet name, slug (preferred), or numeric ID (deprecated) + +#### NEWNAME + +the new name for the fleet + +### Options + +## fleet rename <fleet> [newName] + +Rename a fleet. Note, if the `newName` parameter is omitted, it will be prompted for interactively. -Applications may be specified by app name, slug, or numeric ID. App slugs -are the recommended option, as they are unique and unambiguous. Slugs -can be listed with the `balena apps` command. Note that slugs may change -if the application is renamed. -App names are not unique and may result in "Application is ambiguous" errors -at any time (even if it "used to work in the past"), for example if the name -clashes with a newly created public application, or with apps from other balena -accounts that you may have been invited to as a member. For this reason, app -names are especially discouraged in scripts (e.g. CI environments). -Numeric app IDs are deprecated because they consist of an implementation detail -of the balena backend. We intend to remove support for numeric IDs at some point -in the future. +Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are +the recommended option, as they are unique and unambiguous. Slugs can be +listed with the `balena fleets` command. Note that slugs may change if the +fleet is renamed. Fleet names are not unique and may result in "Fleet 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 fleet, or with fleets +from other balena accounts that you may be invited to join under any role. +For this reason, fleet names are especially discouraged in scripts (e.g. CI +environments). Numeric fleet IDs are deprecated because they consist of an +implementation detail of the balena backend. We intend to remove support for +numeric IDs at some point in the future. Examples: - $ balena app rename OldName - $ balena app rename OldName NewName - $ balena app rename myorg/oldname NewName + $ balena fleet rename OldName + $ balena fleet rename OldName NewName + $ balena fleet rename myorg/oldname NewName ### Arguments -#### APPLICATION +#### FLEET -application name, slug (preferred), or numeric ID (deprecated) +fleet name, slug (preferred), or numeric ID (deprecated) #### NEWNAME -the new name for the application +the new name for the fleet ### Options -## app restart <application> +## app restart <fleet> -Restart all devices belonging to an application. +Renaming notice: The 'app' command was renamed to 'fleet', and 'app' +is now an alias. THE ALIAS WILL BE REMOVED in the next major version +of the balena CLI (so that a different 'app' command can be implemented +in the future). Use 'fleet' instead of 'app' to avoid this warning. +Find out more at: https://git.io/JRuZr -Applications may be specified by app name, slug, or numeric ID. App slugs -are the recommended option, as they are unique and unambiguous. Slugs -can be listed with the `balena apps` command. Note that slugs may change -if the application is renamed. -App names are not unique and may result in "Application is ambiguous" errors -at any time (even if it "used to work in the past"), for example if the name -clashes with a newly created public application, or with apps from other balena -accounts that you may have been invited to as a member. For this reason, app -names are especially discouraged in scripts (e.g. CI environments). -Numeric app IDs are deprecated because they consist of an implementation detail -of the balena backend. We intend to remove support for numeric IDs at some point -in the future. - -Examples: - - $ balena app restart MyApp - $ balena app restart myorg/myapp +For command usage, see 'balena help fleet restart' ### Arguments -#### APPLICATION +#### FLEET -application name, slug (preferred), or numeric ID (deprecated) +fleet name, slug (preferred), or numeric ID (deprecated) ### Options -## app rm <application> +## fleet restart <fleet> -Permanently remove a balena application. +Restart all devices belonging to a fleet. + +Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are +the recommended option, as they are unique and unambiguous. Slugs can be +listed with the `balena fleets` command. Note that slugs may change if the +fleet is renamed. Fleet names are not unique and may result in "Fleet 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 fleet, or with fleets +from other balena accounts that you may be invited to join under any role. +For this reason, fleet names are especially discouraged in scripts (e.g. CI +environments). Numeric fleet IDs are deprecated because they consist of an +implementation detail of the balena backend. We intend to remove support for +numeric IDs at some point in the future. + +Examples: + + $ balena fleet restart MyFleet + $ balena fleet restart myorg/myfleet + +### Arguments + +#### FLEET + +fleet name, slug (preferred), or numeric ID (deprecated) + +### Options + +## app rm <fleet> + +Renaming notice: The 'app' command was renamed to 'fleet', and 'app' +is now an alias. THE ALIAS WILL BE REMOVED in the next major version +of the balena CLI (so that a different 'app' command can be implemented +in the future). Use 'fleet' instead of 'app' to avoid this warning. +Find out more at: https://git.io/JRuZr + +For command usage, see 'balena help fleet rm' + +### Arguments + +#### FLEET + +fleet name, slug (preferred), or numeric ID (deprecated) + +### Options + +#### -y, --yes + +answer "yes" to all questions (non interactive use) + +## fleet rm <fleet> + +Permanently remove a fleet. 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. +Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are +the recommended option, as they are unique and unambiguous. Slugs can be +listed with the `balena fleets` command. Note that slugs may change if the +fleet is renamed. Fleet names are not unique and may result in "Fleet 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 fleet, or with fleets +from other balena accounts that you may be invited to join under any role. +For this reason, fleet names are especially discouraged in scripts (e.g. CI +environments). Numeric fleet IDs are deprecated because they consist of an +implementation detail of the balena backend. We intend to remove support for +numeric IDs at some point in the future. Examples: - $ balena app rm MyApp - $ balena app rm MyApp --yes - $ balena app rm myorg/myapp + $ balena fleet rm MyFleet + $ balena fleet rm MyFleet --yes + $ balena fleet rm myorg/myfleet ### Arguments -#### APPLICATION +#### FLEET -application name, slug (preferred), or numeric ID (deprecated) +fleet name, slug (preferred), or numeric ID (deprecated) ### Options @@ -613,22 +755,21 @@ Examples: ## devices -list all devices that belong to you. +List all of your devices. -You can filter the devices by application by using the `--application` option. +Devices can be filtered by fleet with the `--fleet` 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. +Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are +the recommended option, as they are unique and unambiguous. Slugs can be +listed with the `balena fleets` command. Note that slugs may change if the +fleet is renamed. Fleet names are not unique and may result in "Fleet 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 fleet, or with fleets +from other balena accounts that you may be invited to join under any role. +For this reason, fleet names are especially discouraged in scripts (e.g. CI +environments). Numeric fleet IDs are deprecated because they consist of an +implementation detail of the balena backend. We intend to remove support for +numeric IDs at some point in the future. The --json option is recommended when scripting the output of this command, because field names are less likely to change in JSON format and because it @@ -639,25 +780,32 @@ The 'jq' utility may be helpful for querying JSON fields in shell scripts Examples: $ balena devices - $ balena devices --application MyApp - $ balena devices --app MyApp - $ balena devices -a MyApp - $ balena devices -a myorg/myapp + $ balena devices --fleet MyFleet + $ balena devices -f myorg/myfleet ### Options #### -a, --application APPLICATION -application name, slug (preferred), or numeric ID (deprecated) +DEPRECATED alias for -f, --fleet #### --app APP -same as '--application' +DEPRECATED alias for -f, --fleet + +#### -f, --fleet FLEET + +fleet name, slug (preferred), or numeric ID #### -j, --json produce JSON output instead of tabular output +#### --v13 + +enable selected balena CLI v13 pre-release features, like the renaming +from "application" to "fleet" in command output + ## devices supported List the supported device types (like 'raspberrypi3' or 'intel-nuc'). @@ -710,6 +858,11 @@ the device uuid ### Options +#### --v13 + +enable selected balena CLI v13 pre-release features, like the renaming +from "application" to "fleet" in command output + ## device deactivate <uuid> Deactivate a device. @@ -752,40 +905,42 @@ the uuid of the device to identify ## device init -Initialize a device by downloading the OS image of a certain application +Initialize a device by downloading the OS image of the specified fleet and writing it to an SD Card. -Note, if the application option is omitted it will be prompted -for interactively. +If the --fleet option is omitted, it will be prompted for interactively. -Applications may be specified by app name, slug, or numeric ID. App slugs -are the recommended option, as they are unique and unambiguous. Slugs -can be listed with the `balena apps` command. Note that slugs may change -if the application is renamed. -App names are not unique and may result in "Application is ambiguous" errors -at any time (even if it "used to work in the past"), for example if the name -clashes with a newly created public application, or with apps from other balena -accounts that you may have been invited to as a member. For this reason, app -names are especially discouraged in scripts (e.g. CI environments). -Numeric app IDs are deprecated because they consist of an implementation detail -of the balena backend. We intend to remove support for numeric IDs at some point -in the future. +Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are +the recommended option, as they are unique and unambiguous. Slugs can be +listed with the `balena fleets` command. Note that slugs may change if the +fleet is renamed. Fleet names are not unique and may result in "Fleet 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 fleet, or with fleets +from other balena accounts that you may be invited to join under any role. +For this reason, fleet names are especially discouraged in scripts (e.g. CI +environments). Numeric fleet IDs are deprecated because they consist of an +implementation detail of the balena backend. We intend to remove support for +numeric IDs at some point in the future. Examples: $ balena device init - $ balena device init --application MyApp - $ balena device init -a myorg/myapp + $ balena device init --fleet MyFleet + $ balena device init -f myorg/myfleet ### Options #### -a, --application APPLICATION -application name, slug (preferred), or numeric ID (deprecated) +DEPRECATED alias for -f, --fleet #### --app APP -same as '--application' +DEPRECATED alias for -f, --fleet + +#### -f, --fleet FLEET + +fleet name, slug (preferred), or numeric ID #### -y, --yes @@ -847,30 +1002,28 @@ output boolean indicating local mode status ## device move <uuid(s)> -Move one or more devices to another application. +Move one or more devices to another fleet. -Note, if the application option is omitted it will be prompted -for interactively. +If --fleet is omitted, the fleet will be prompted for interactively. -Applications may be specified by app name, slug, or numeric ID. App slugs -are the recommended option, as they are unique and unambiguous. Slugs -can be listed with the `balena apps` command. Note that slugs may change -if the application is renamed. -App names are not unique and may result in "Application is ambiguous" errors -at any time (even if it "used to work in the past"), for example if the name -clashes with a newly created public application, or with apps from other balena -accounts that you may have been invited to as a member. For this reason, app -names are especially discouraged in scripts (e.g. CI environments). -Numeric app IDs are deprecated because they consist of an implementation detail -of the balena backend. We intend to remove support for numeric IDs at some point -in the future. +Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are +the recommended option, as they are unique and unambiguous. Slugs can be +listed with the `balena fleets` command. Note that slugs may change if the +fleet is renamed. Fleet names are not unique and may result in "Fleet 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 fleet, or with fleets +from other balena accounts that you may be invited to join under any role. +For this reason, fleet names are especially discouraged in scripts (e.g. CI +environments). Numeric fleet IDs are deprecated because they consist of an +implementation detail of the balena backend. We intend to remove support for +numeric IDs at some point in the future. Examples: $ balena device move 7cf02a6 $ balena device move 7cf02a6,dc39e52 - $ balena device move 7cf02a6 --application MyNewApp - $ balena device move 7cf02a6 -a myorg/mynewapp + $ balena device move 7cf02a6 --fleet MyNewFleet + $ balena device move 7cf02a6 -f myorg/mynewfleet ### Arguments @@ -880,13 +1033,17 @@ comma-separated list (no blank spaces) of device UUIDs to be moved ### Options -#### -a, --application APPLICATION - -application name, slug (preferred), or numeric ID (deprecated) - #### --app APP -same as '--application' +DEPRECATED alias for -f, --fleet + +#### -a, --application APPLICATION + +DEPRECATED alias for -f, --fleet + +#### -f, --fleet FLEET + +fleet name, slug (preferred), or numeric ID ## device os-update <uuid> @@ -960,8 +1117,8 @@ determine if public URL is enabled ## device purge <uuid> -Purge application data from a device. -This will clear the application's /data directory. +Purge data from a device. +This will clear the device's "/data" directory. Multiple devices may be specified with a comma-separated list of values (no spaces). @@ -999,34 +1156,35 @@ the uuid of the device to reboot force action if the update lock is set -## device register <application> +## device register <fleet> -Register a device to an application. +Register a new device with a balena fleet. -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. +If --uuid is not provided, a new UUID will be automatically assigned. + +Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are +the recommended option, as they are unique and unambiguous. Slugs can be +listed with the `balena fleets` command. Note that slugs may change if the +fleet is renamed. Fleet names are not unique and may result in "Fleet 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 fleet, or with fleets +from other balena accounts that you may be invited to join under any role. +For this reason, fleet names are especially discouraged in scripts (e.g. CI +environments). Numeric fleet IDs are deprecated because they consist of an +implementation detail of the balena backend. We intend to remove support for +numeric IDs at some point in the future. Examples: - $ balena device register MyApp - $ balena device register MyApp --uuid - $ balena device register myorg/myapp --uuid + $ balena device register MyFleet + $ balena device register MyFleet --uuid + $ balena device register myorg/myfleet --uuid ### Arguments -#### APPLICATION +#### FLEET -application name, slug (preferred), or numeric ID (deprecated) +fleet name, slug (preferred), or numeric ID (deprecated) ### Options @@ -1136,18 +1294,18 @@ force action if the update lock is set ## envs -List the environment or configuration variables of an application, device or -service, as selected by the respective command-line options. (A service is -an application container in a "microservices" application.) +List the environment or configuration variables of a fleet, device or +service, as selected by the respective command-line options. (A service +corresponds to a Docker image/container in a microservices fleet.) -The results include application-wide (fleet), device-wide (multiple services on -a device) and service-specific variables that apply to the selected application, -device or service. It can be thought of as including "inherited" variables; -for example, a service inherits device-wide variables, and a device inherits -application-wide variables. +The results include fleet-wide (multiple devices), device-specific (multiple +services on a specific device) and service-specific variables that apply to the +selected fleet, device or service. It can be thought of as including inherited +variables; for example, a service inherits device-wide variables, and a device +inherits fleet-wide variables. The printed output may include DEVICE and/or SERVICE columns to distinguish -between application-wide, device-specific and service-specific variables. +between fleet-wide, device-specific and service-specific variables. An asterisk in these columns indicates that the variable applies to "all devices" or "all services". @@ -1161,33 +1319,37 @@ because the JSON format is less likely to change and it better represents data types like lists and empty strings. The 'jq' utility may be helpful in shell scripts (https://stedolan.github.io/jq/manual/). When --json is used, an empty JSON array ([]) is printed instead of an error message when no variables exist -for the given query. When querying variables for a device, note that the -application name may be null in JSON output (or 'N/A' in tabular output) if the -application linked to the device is no longer accessible by the current user -(for example, in case the current user has been removed from the application -by its owner). +for the given query. When querying variables for a device, note that the fleet +name may be null in JSON output (or 'N/A' in tabular output) if the fleet that +the device belonged to is no longer accessible by the current user (for example, +in case the current user was removed from the fleet by the fleet's 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. +Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are +the recommended option, as they are unique and unambiguous. Slugs can be +listed with the `balena fleets` command. Note that slugs may change if the +fleet is renamed. Fleet names are not unique and may result in "Fleet 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 fleet, or with fleets +from other balena accounts that you may be invited to join under any role. +For this reason, fleet names are especially discouraged in scripts (e.g. CI +environments). Numeric fleet 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. + +Renaming notice: The 'app' or 'application' words in table headers +or in JSON object keys/properties will be replaced with 'fleet' in +the next major version of the CLI (v13). The --v13 option may be used +to enable the new names already now, and suppress a warning message. +(The --v13 option will be silently ignored in CLI v13.) +Find out more at: https://git.io/JRuZr Examples: - $ balena envs --application MyApp - $ balena envs --application myorg/myapp - $ balena envs --application MyApp --json - $ balena envs --application MyApp --service MyService - $ balena envs --application MyApp --service MyService - $ balena envs --application MyApp --config + $ balena envs --fleet myorg/myfleet + $ balena envs --fleet MyFleet --json + $ balena envs --fleet MyFleet --service MyService + $ balena envs --fleet MyFleet --service MyService + $ balena envs --fleet MyFleet --config $ balena envs --device 7cf02a6 $ balena envs --device 7cf02a6 --json $ balena envs --device 7cf02a6 --config --json @@ -1201,7 +1363,11 @@ No-op since balena CLI v12.0.0. #### -a, --application APPLICATION -application name, slug (preferred), or numeric ID (deprecated) +DEPRECATED alias for -f, --fleet + +#### -f, --fleet FLEET + +fleet name, slug (preferred), or numeric ID #### -c, --config @@ -1223,31 +1389,36 @@ produce verbose output service name +#### --v13 + +enable selected balena CLI v13 pre-release features, like the renaming +from "application" to "fleet" in command output + ## env rm <id> -Remove a configuration or environment variable from an application, device +Remove a configuration or environment variable from a fleet, device or service, as selected by command-line options. Variables are selected by their database ID (as reported by the 'balena envs' command) and one of six database "resource types": -- application (fleet) environment variable -- application (fleet) configuration variable (--config) -- application (fleet) service variable (--service) +- fleet environment variable +- fleet configuration variable (--config) +- fleet service variable (--service) - device environment variable (--device) - device configuration variable (--device --config) - device service variable (--device --service) -The --device option selects a device-specific variable instead of an application -(fleet) variable. +The --device option selects a device-specific variable instead of a fleet +variable. The --config option selects a configuration variable. Configuration variable names typically start with the 'BALENA_' or 'RESIN_' prefixes and are used to configure balena platform features. The --service option selects a service variable, which is an environment variable -that applies to a specifc service (application container) in a microservices -(multicontainer) application. +that applies to a specifc service (container) in a microservices (multicontainer) +fleet. The --service and --config options cannot be used together, but they can be used alongside the --device option to select a device-specific service or @@ -1280,7 +1451,7 @@ select a configuration variable (may be used together with the --device option) #### -d, --device -select a device-specific variable instead of an application (fleet) variable +select a device-specific variable instead of a fleet variable #### -s, --service @@ -1292,16 +1463,15 @@ do not prompt for confirmation before deleting the variable ## env add <name> [value] -Add an environment or config variable to one or more applications, devices -or services, as selected by the respective command-line options. Either the ---application or the --device option must be provided, and either may be be +Add an environment or config variable to one or more fleets, devices or +services, as selected by the respective command-line options. Either the +--fleet or the --device option must be provided, and either may be be used alongside the --service option to define a service-specific variable. -(A service is an application container in a "microservices" application.) +(A service corresponds to a Docker image/container in a microservices fleet.) When the --service option is used in conjunction with the --device option, -the service variable applies to the selected device only. Otherwise, it -applies to all devices of the selected application (i.e., the application's -fleet). If the --service option is omitted, the variable applies to all -services. +the service variable applies to the selected device only. Otherwise, it +applies to all devices of the selected fleet. If the --service option is +omitted, the variable applies to all services. If VALUE is omitted, the CLI will attempt to use the value of the environment variable of same name in the CLI process' environment. In this case, a warning @@ -1313,30 +1483,28 @@ features and are treated specially by balenaOS and the balena supervisor running on devices. They are also stored differently in the balenaCloud API database. Configuration variables cannot be set for specific services, therefore the --service option cannot be used when the variable name starts -with a reserved prefix. When defining custom application variables, please -avoid the reserved prefixes. +with a reserved prefix. When defining custom fleet variables, please avoid +these 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. +Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are +the recommended option, as they are unique and unambiguous. Slugs can be +listed with the `balena fleets` command. Note that slugs may change if the +fleet is renamed. Fleet names are not unique and may result in "Fleet 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 fleet, or with fleets +from other balena accounts that you may be invited to join under any role. +For this reason, fleet names are especially discouraged in scripts (e.g. CI +environments). Numeric fleet IDs are deprecated because they consist of an +implementation detail of the balena backend. We intend to remove support for +numeric IDs at some point in the future. Examples: - $ balena env add TERM --application MyApp - $ balena env add EDITOR vim --application MyApp - $ balena env add EDITOR vim -a myorg/myapp - $ balena env add EDITOR vim --application MyApp,MyApp2 - $ balena env add EDITOR vim --application MyApp --service MyService - $ balena env add EDITOR vim --application MyApp,MyApp2 --service MyService,MyService2 + $ balena env add TERM --fleet MyFleet + $ balena env add EDITOR vim -f myorg/myfleet + $ balena env add EDITOR vim --fleet MyFleet,MyFleet2 + $ balena env add EDITOR vim --fleet MyFleet --service MyService + $ balena env add EDITOR vim --fleet MyFleet,MyFleet2 --service MyService,MyService2 $ balena env add EDITOR vim --device 7cf02a6 $ balena env add EDITOR vim --device 7cf02a6,d6f1433 $ balena env add EDITOR vim --device 7cf02a6 --service MyService @@ -1356,7 +1524,11 @@ variable value; if omitted, use value from this process' environment #### -a, --application APPLICATION -application name, slug (preferred), or numeric ID (deprecated) +DEPRECATED alias for -f, --fleet + +#### -f, --fleet FLEET + +fleet name, slug (preferred), or numeric ID #### -d, --device DEVICE @@ -1372,29 +1544,29 @@ service name ## env rename <id> <value> -Change the value of a configuration or environment variable for an application, +Change the value of a configuration or environment variable for a fleet, device or service, as selected by command-line options. Variables are selected by their database ID (as reported by the 'balena envs' command) and one of six database "resource types": -- application (fleet) environment variable -- application (fleet) configuration variable (--config) -- application (fleet) service variable (--service) +- fleet environment variable +- fleet configuration variable (--config) +- fleet service variable (--service) - device environment variable (--device) - device configuration variable (--device --config) - device service variable (--device --service) -The --device option selects a device-specific variable instead of an application -(fleet) variable. +The --device option selects a device-specific variable instead of a fleet +variable. The --config option selects a configuration variable. Configuration variable names typically start with the 'BALENA_' or 'RESIN_' prefixes and are used to configure balena platform features. The --service option selects a service variable, which is an environment variable -that applies to a specifc service (application container) in a microservices -(multicontainer) application. +that applies to a specifc service (container) in a microservices (multicontainer) +fleet. The --service and --config options cannot be used together, but they can be used alongside the --device option to select a device-specific service or @@ -1427,7 +1599,7 @@ select a configuration variable (may be used together with the --device option) #### -d, --device -select a device-specific variable instead of an application (fleet) variable +select a device-specific variable instead of a fleet variable #### -s, --service @@ -1437,26 +1609,24 @@ select a service variable (may be used together with the --device option) ## tags -List all tags and their values for a particular application, -device or release. +List all tags and their values for the specified fleet, 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. +Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are +the recommended option, as they are unique and unambiguous. Slugs can be +listed with the `balena fleets` command. Note that slugs may change if the +fleet is renamed. Fleet names are not unique and may result in "Fleet 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 fleet, or with fleets +from other balena accounts that you may be invited to join under any role. +For this reason, fleet names are especially discouraged in scripts (e.g. CI +environments). Numeric fleet IDs are deprecated because they consist of an +implementation detail of the balena backend. We intend to remove support for +numeric IDs at some point in the future. Examples: - $ balena tags --application MyApp - $ balena tags -a myorg/myapp + $ balena tags --fleet MyFleet + $ balena tags -f myorg/myfleet $ balena tags --device 7cf02a6 $ balena tags --release 1234 $ balena tags --release b376b0e544e9429483b656490e5b9443b4349bd6 @@ -1465,11 +1635,15 @@ Examples: #### -a, --application APPLICATION -application name, slug (preferred), or numeric ID (deprecated) +DEPRECATED alias for -f, --fleet #### --app APP -same as '--application' +DEPRECATED alias for -f, --fleet + +#### -f, --fleet FLEET + +fleet name, slug (preferred), or numeric ID #### -d, --device DEVICE @@ -1481,25 +1655,24 @@ release id ## tag rm <tagKey> -Remove a tag from an application, device or release. +Remove a tag from a fleet, 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. +Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are +the recommended option, as they are unique and unambiguous. Slugs can be +listed with the `balena fleets` command. Note that slugs may change if the +fleet is renamed. Fleet names are not unique and may result in "Fleet 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 fleet, or with fleets +from other balena accounts that you may be invited to join under any role. +For this reason, fleet names are especially discouraged in scripts (e.g. CI +environments). Numeric fleet IDs are deprecated because they consist of an +implementation detail of the balena backend. We intend to remove support for +numeric IDs at some point in the future. Examples: - $ balena tag rm myTagKey --application MyApp - $ balena tag rm myTagKey -a myorg/myapp + $ balena tag rm myTagKey --fleet MyFleet + $ balena tag rm myTagKey -f myorg/myfleet $ balena tag rm myTagKey --device 7cf02a6 $ balena tag rm myTagKey --release 1234 $ balena tag rm myTagKey --release b376b0e544e9429483b656490e5b9443b4349bd6 @@ -1514,11 +1687,15 @@ the key string of the tag #### -a, --application APPLICATION -application name, slug (preferred), or numeric ID (deprecated) +DEPRECATED alias for -f, --fleet #### --app APP -same as '--application' +DEPRECATED alias for -f, --fleet + +#### -f, --fleet FLEET + +fleet name, slug (preferred), or numeric ID #### -d, --device DEVICE @@ -1530,30 +1707,29 @@ release id ## tag set <tagKey> [value] -Set a tag on an application, device or release. +Set a tag on a fleet, device or release. You can optionally provide a value to be associated with the created tag, as an extra argument after the tag key. If a value isn't provided, a tag with an empty value is created. -Applications may be specified by app name, slug, or numeric ID. App slugs -are the recommended option, as they are unique and unambiguous. Slugs -can be listed with the `balena apps` command. Note that slugs may change -if the application is renamed. -App names are not unique and may result in "Application is ambiguous" errors -at any time (even if it "used to work in the past"), for example if the name -clashes with a newly created public application, or with apps from other balena -accounts that you may have been invited to as a member. For this reason, app -names are especially discouraged in scripts (e.g. CI environments). -Numeric app IDs are deprecated because they consist of an implementation detail -of the balena backend. We intend to remove support for numeric IDs at some point -in the future. +Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are +the recommended option, as they are unique and unambiguous. Slugs can be +listed with the `balena fleets` command. Note that slugs may change if the +fleet is renamed. Fleet names are not unique and may result in "Fleet 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 fleet, or with fleets +from other balena accounts that you may be invited to join under any role. +For this reason, fleet names are especially discouraged in scripts (e.g. CI +environments). Numeric fleet IDs are deprecated because they consist of an +implementation detail of the balena backend. We intend to remove support for +numeric IDs at some point in the future. Examples: - $ balena tag set mySimpleTag --application MyApp - $ balena tag set mySimpleTag -a myorg/myapp - $ balena tag set myCompositeTag myTagValue --application MyApp + $ balena tag set mySimpleTag --fleet MyFleet + $ balena tag set mySimpleTag -f myorg/myfleet + $ balena tag set myCompositeTag myTagValue --fleet MyFleet $ balena tag set myCompositeTag myTagValue --device 7cf02a6 $ balena tag set myCompositeTag "my tag value with whitespaces" --device 7cf02a6 $ balena tag set myCompositeTag myTagValue --release 1234 @@ -1574,11 +1750,15 @@ the optional value associated with the tag #### -a, --application APPLICATION -application name, slug (preferred), or numeric ID (deprecated) +DEPRECATED alias for -f, --fleet #### --app APP -same as '--application' +DEPRECATED alias for -f, --fleet + +#### -f, --fleet FLEET + +fleet name, slug (preferred), or numeric ID #### -d, --device DEVICE @@ -1597,7 +1777,7 @@ List balena commands, or get detailed help for a specific command. Examples: $ balena help - $ balena help apps + $ balena help login $ balena help os download ### Arguments @@ -1823,14 +2003,14 @@ scan timeout in seconds produce JSON output instead of tabular output -## ssh <applicationOrDevice> [service] +## ssh <fleetOrDevice> [service] Start a shell on a local or remote device. If a service name is not provided, a shell will be opened on the host OS. -If an application is provided, an interactive menu will be presented -for the selection of an online device. A shell will then be opened for the -host OS or service container of the chosen device. +If a fleet is provided, an interactive menu will be presented for the selection +of an online device. A shell will then be opened for the host OS or service +container of the chosen device. For local devices, the IP address and .local domain name are supported. If the device is referenced by IP or `.local` address, the connection @@ -1850,7 +2030,7 @@ support) please check: Examples: - $ balena ssh MyApp + $ balena ssh MyFleet $ balena ssh f49cefd $ balena ssh f49cefd my-service $ balena ssh f49cefd --port @@ -1861,9 +2041,9 @@ Examples: ### Arguments -#### APPLICATIONORDEVICE +#### FLEETORDEVICE -application name/slug/id, device uuid, or address of local device +fleet name/slug/id, device uuid, or address of local device #### SERVICE @@ -1888,7 +2068,7 @@ increase verbosity bypass global proxy configuration for the ssh connection -## tunnel <deviceOrApplication> +## tunnel <deviceOrFleet> Use this command to open local TCP ports that tunnel to listening sockets in a balenaOS device. @@ -1913,7 +2093,7 @@ versions. Examples: # map remote port 22222 to localhost:22222 - $ balena tunnel myApp -p 22222 + $ balena tunnel myFleet -p 22222 # map remote port 22222 to localhost:222 $ balena tunnel 2ead211 -p 22222:222 @@ -1922,16 +2102,16 @@ Examples: $ balena tunnel 1546690 -p 22222:0.0.0.0 # map remote port 22222 to any address on your host machine, port 222 - $ balena tunnel myApp -p 22222:0.0.0.0:222 + $ balena tunnel myFleet -p 22222:0.0.0.0:222 # multiple port tunnels can be specified at any one time - $ balena tunnel myApp -p 8080:3000 -p 8081:9000 + $ balena tunnel myFleet -p 8080:3000 -p 8081:9000 ### Arguments -#### DEVICEORAPPLICATION +#### DEVICEORFLEET -device uuid or application name/slug/id +device UUID or fleet name/slug/ID ### Options @@ -2068,8 +2248,8 @@ path to output JSON file ## os configure <image> -Configure a previously downloaded balenaOS image for a specific device type or -balena application. +Configure a previously downloaded balenaOS image for a specific device type +or fleet. Configuration settings such as WiFi authentication will be taken from the following sources, in precedence order: @@ -2077,8 +2257,8 @@ following sources, in precedence order: 2. A given `config.json` file specified with the `--config` option. 3. User input through interactive prompts (text menus). -The --device-type option may be used to override the application's default -device type, in case of an application with mixed device types. +The --device-type option may be used to override the fleet's default device +type, in case of a fleet with mixed device types. The --system-connection (-c) option can be used to inject NetworkManager connection profiles for additional network interfaces, such as cellular/GSM or additional @@ -2090,18 +2270,17 @@ https://developer.gnome.org/NetworkManager/stable/ref-settings.html The --device-api-key option is deprecated and will be removed in a future release. A suitable key is automatically generated or fetched if this option is omitted. -Applications may be specified by app name, slug, or numeric ID. App slugs -are the recommended option, as they are unique and unambiguous. Slugs -can be listed with the `balena apps` command. Note that slugs may change -if the application is renamed. -App names are not unique and may result in "Application is ambiguous" errors -at any time (even if it "used to work in the past"), for example if the name -clashes with a newly created public application, or with apps from other balena -accounts that you may have been invited to as a member. For this reason, app -names are especially discouraged in scripts (e.g. CI environments). -Numeric app IDs are deprecated because they consist of an implementation detail -of the balena backend. We intend to remove support for numeric IDs at some point -in the future. +Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are +the recommended option, as they are unique and unambiguous. Slugs can be +listed with the `balena fleets` command. Note that slugs may change if the +fleet is renamed. Fleet names are not unique and may result in "Fleet 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 fleet, or with fleets +from other balena accounts that you may be invited to join under any role. +For this reason, fleet names are especially discouraged in scripts (e.g. CI +environments). Numeric fleet IDs are deprecated because they consist of an +implementation detail of the balena backend. We intend to remove support for +numeric IDs at some point in the future. Note: This command is currently not supported on Windows natively. Windows users are advised to install the Windows Subsystem for Linux (WSL) with Ubuntu, and use @@ -2112,11 +2291,10 @@ Examples: $ balena os configure ../path/rpi3.img --device 7cf02a6 $ balena os configure ../path/rpi3.img --device 7cf02a6 --device-api-key - $ balena os configure ../path/rpi3.img --app MyApp - $ balena os configure ../path/rpi3.img -a myorg/myapp - $ balena os configure ../path/rpi3.img --app MyApp --version 2.12.7 - $ balena os configure ../path/rpi3.img --app MyFinApp --device-type raspberrypi3 - $ balena os configure ../path/rpi3.img --app MyFinApp --device-type raspberrypi3 --config myWifiConfig.json + $ balena os configure ../path/rpi3.img --fleet myorg/myfleet + $ balena os configure ../path/rpi3.img --fleet MyFleet --version 2.12.7 + $ balena os configure ../path/rpi3.img -f MyFinFleet --device-type raspberrypi3 + $ balena os configure ../path/rpi3.img -f MyFinFleet --device-type raspberrypi3 --config myWifiConfig.json ### Arguments @@ -2132,11 +2310,15 @@ ask advanced configuration questions (when in interactive mode) #### -a, --application APPLICATION -application name, slug (preferred), or numeric ID (deprecated) +DEPRECATED alias for -f, --fleet #### --app APP -same as '--application' +DEPRECATED alias for -f, --fleet + +#### -f, --fleet FLEET + +fleet name, slug (preferred), or numeric ID #### --config CONFIG @@ -2144,7 +2326,7 @@ path to a pre-generated config.json file to be injected in the OS image #### --config-app-update-poll-interval CONFIG-APP-UPDATE-POLL-INTERVAL -interval (in minutes) for the on-device balena supervisor periodic app update check +supervisor cloud polling interval in minutes (e.g. for variable updates) #### --config-network CONFIG-NETWORK @@ -2168,7 +2350,7 @@ custom device API key (DEPRECATED and only supported with balenaOS 2.0.3+) #### --device-type DEVICE-TYPE -device type slug (e.g. "raspberrypi3") to override the application device type +device type slug (e.g. "raspberrypi3") to override the fleet device type #### --initial-device-name INITIAL-DEVICE-NAME @@ -2221,29 +2403,27 @@ answer "yes" to all questions (non interactive use) ## config generate -Generate a config.json file for a device or application. +Generate a config.json file for a device or fleet. -Calling this command with the exact version number of the targeted image is required. +The target balenaOS version must be specified with the --version option. -This command is interactive by default, but you can do this automatically without interactivity -by specifying an option for each question on the command line, if you know the questions -that will be asked for the relevant device type. +To configure an image for a fleet of mixed device types, use the --fleet option +alongside the --deviceType option to specify the target device type. -In case that you want to configure an image for an application with mixed device types, -you can pass the --deviceType argument along with --application to specify the target device type. +To avoid interactive questions, specify a command line option for each question that +would otherwise be asked. -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. +Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are +the recommended option, as they are unique and unambiguous. Slugs can be +listed with the `balena fleets` command. Note that slugs may change if the +fleet is renamed. Fleet names are not unique and may result in "Fleet 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 fleet, or with fleets +from other balena accounts that you may be invited to join under any role. +For this reason, fleet names are especially discouraged in scripts (e.g. CI +environments). Numeric fleet 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: @@ -2251,11 +2431,11 @@ Examples: $ balena config generate --device 7cf02a6 --version 2.12.7 --generate-device-api-key $ balena config generate --device 7cf02a6 --version 2.12.7 --device-api-key $ balena config generate --device 7cf02a6 --version 2.12.7 --output config.json - $ balena config generate --app MyApp --version 2.12.7 - $ balena config generate --app myorg/myapp --version 2.12.7 - $ balena config generate --app MyApp --version 2.12.7 --deviceType fincm3 - $ balena config generate --app MyApp --version 2.12.7 --output config.json - $ balena config generate --app MyApp --version 2.12.7 --network wifi --wifiSsid mySsid --wifiKey abcdefgh --appUpdatePollInterval 1 + $ balena config generate --fleet MyFleet --version 2.12.7 + $ balena config generate --fleet myorg/myfleet --version 2.12.7 + $ balena config generate --fleet MyFleet --version 2.12.7 --deviceType fincm3 + $ balena config generate --fleet MyFleet --version 2.12.7 --output config.json + $ balena config generate --fleet MyFleet --version 2.12.7 --network wifi --wifiSsid mySsid --wifiKey abcdefgh --appUpdatePollInterval 15 ### Options @@ -2265,15 +2445,23 @@ a balenaOS version #### -a, --application APPLICATION -application name, slug (preferred), or numeric ID (deprecated) +DEPRECATED alias for -f, --fleet #### --app APP -same as '--application' +DEPRECATED alias for -f, --fleet + +#### --appUpdatePollInterval APPUPDATEPOLLINTERVAL + +supervisor cloud polling interval in minutes (e.g. for variable updates) + +#### -f, --fleet FLEET + +fleet name, slug (preferred), or numeric ID #### -d, --device DEVICE -device uuid +device UUID #### -k, --deviceApiKey DEVICEAPIKEY @@ -2281,7 +2469,7 @@ custom device key - note that this is only supported on balenaOS 2.0.3+ #### --deviceType DEVICETYPE -device type slug +device type slug (run 'balena devices supported' for possible values) #### --generate-device-api-key @@ -2303,10 +2491,6 @@ the wifi ssid to use (used only if --network is set to wifi) the wifi key to use (used only if --network is set to wifi) -#### --appUpdatePollInterval APPUPDATEPOLLINTERVAL - -how frequently (in minutes) to poll for application updates - ## config inject <file> Inject a config.json file to the mounted filesystem, @@ -2412,30 +2596,29 @@ path to OS image file (e.g. balena.img) or block device (e.g. /dev/disk2) ## preload <image> -Preload a balena application release (app images/containers), and optionally +Preload a release (service images/containers) from a balena fleet, and optionally a balenaOS splash screen, in a previously downloaded '.img' balenaOS image file in the local disk (a zip file is only accepted for the Intel Edison device type). 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 release, as it was preloaded. This is usually combined with release pinning (https://www.balena.io/docs/learn/deploy/release-strategy/release-policy/) -to avoid the device dowloading a newer release straight away, if one is available. +to avoid the device downloading a newer release straight away, if available. Check also the Preloading and Preregistering section of the balena CLI's advanced masterclass document: https://www.balena.io/docs/learn/more/masterclasses/advanced-cli/#5-preloading-and-preregistering -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. +Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are +the recommended option, as they are unique and unambiguous. Slugs can be +listed with the `balena fleets` command. Note that slugs may change if the +fleet is renamed. Fleet names are not unique and may result in "Fleet 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 fleet, or with fleets +from other balena accounts that you may be invited to join under any role. +For this reason, fleet names are especially discouraged in scripts (e.g. CI +environments). Numeric fleet 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 that the this command requires Docker to be installed, as further detailed in the balena CLI's installation instructions: @@ -2447,8 +2630,8 @@ feature is used to "share" the image with a container that performs the preload. Examples: - $ 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 --fleet MyFleet --commit e1f2592fc6ee949e68756d4f4a48e49bff8d72a0 + $ balena preload balena.img --fleet myorg/myfleet --splash-image image.png $ balena preload balena.img ### Arguments @@ -2461,13 +2644,20 @@ the image file path #### -a, --app APP -application name, slug (preferred), or numeric ID (deprecated) +DEPRECATED alias for -f, --fleet + +#### -f, --fleet FLEET + +fleet name, slug (preferred), or numeric ID #### -c, --commit COMMIT -The commit hash for a specific application release to preload, use "current" to specify the current -release (ignored if no appId is given). The current release is usually also the latest, but can be -manually pinned using https://github.com/balena-io-projects/staged-releases . +The commit hash of the release to preload. Use "current" to specify the current +release (ignored if no appId is given). The current release is usually also the +latest, but can be pinned to a specific release. See: +https://www.balena.io/docs/learn/deploy/release-strategy/release-policy/ +https://www.balena.io/docs/learn/more/masterclasses/fleet-management/#63-pin-using-the-api +https://github.com/balena-io-examples/staged-releases #### -s, --splash-image SPLASH-IMAGE @@ -2475,7 +2665,7 @@ path to a png image to replace the splash screen #### --dont-check-arch -disables check for matching architecture in image and application +disable architecture compatibility check between image and fleet #### -p, --pin-device-to-release @@ -2518,9 +2708,9 @@ Docker host TLS key file # Push -## push <applicationOrDevice> +## push <fleetOrDevice> -Start a build on the remote balenaCloud build servers, or a local mode device. +Build release images on balenaCloud servers or on a local mode device. When building on the balenaCloud servers, the given source directory will be sent to the remote server. This can be used as a drop-in replacement for the @@ -2569,12 +2759,12 @@ DOCKERIGNORE AND GITIGNORE FILES By default, the balena CLI will use a single ".dockerignore" file (if any) at the project root (--source directory) in order to decide which source files to exclude from the "build context" (tar stream) sent to balenaCloud, Docker -daemon or balenaEngine. In a microservices (multicontainer) application, the +daemon or balenaEngine. In a microservices (multicontainer) fleet, the source directory is the directory that contains the "docker-compose.yml" file. The --multi-dockerignore (-m) option may be used with microservices -(multicontainer) applications that define a docker-compose.yml file. When -this option is used, each service subdirectory (defined by the `build` or +(multicontainer) fleets that define a docker-compose.yml file. When this +option is used, each service subdirectory (defined by the `build` or `build.context` service properties in the docker-compose.yml file) is filtered separately according to a .dockerignore file defined in the service subdirectory. If no .dockerignore file exists in a service subdirectory, then @@ -2617,16 +2807,16 @@ adding counter patterns to the applicable .dockerignore file(s), for example - https://docs.docker.com/engine/reference/builder/#dockerignore-file - https://www.npmjs.com/package/@balena/dockerignore -Note: the --service and --env flags must come after the applicationOrDevice +Note: the --service and --env flags must come after the fleetOrDevice parameter, as per examples. Examples: - $ balena push myApp - $ balena push myApp --source - $ balena push myApp -s - $ balena push myApp --release-tag key1 "" key2 "value2 with spaces" - $ balena push myorg/myapp + $ balena push myFleet + $ balena push myFleet --source + $ balena push myFleet -s + $ balena push myFleet --release-tag key1 "" key2 "value2 with spaces" + $ balena push myorg/myfleet $ balena push 10.0.0.1 $ balena push 10.0.0.1 --source @@ -2639,9 +2829,9 @@ Examples: ### Arguments -#### APPLICATIONORDEVICE +#### FLEETORDEVICE -application name or slug, or local device IP address or hostname +fleet name or slug, or local device IP address or ".local" hostname ### Options @@ -2697,7 +2887,7 @@ initial build has completed. When pushing to the cloud, this option will cause the build to start, then return execution back to the shell, with the status and release ID (if applicable). When pushing to a local mode device, this option will cause -the command to not tail application logs when the build has completed. +the command to not tail logs when the build has completed. #### --service SERVICE @@ -2744,7 +2934,7 @@ required until your project can be adapted. #### --release-tag RELEASE-TAG -Set release tags if the push to a cloud application is successful. Multiple +Set release tags if the image build is successful (balenaCloud only). Multiple arguments may be provided, alternating tag keys and values (see examples). Hint: Empty values may be specified with "" (bash, cmd.exe) or '""' (PowerShell). @@ -2820,7 +3010,7 @@ the provided docker daemon in your development machine or balena device. (See also the `balena push` command for the option of building images in the balenaCloud build servers.) -You must provide either an application or a device-type/architecture pair. +You must specify either a fleet, or the device type and architecture. This command will look into the given source directory (or the current working directory if one isn't specified) for a docker-compose.yml file, and if found, @@ -2855,12 +3045,12 @@ DOCKERIGNORE AND GITIGNORE FILES By default, the balena CLI will use a single ".dockerignore" file (if any) at the project root (--source directory) in order to decide which source files to exclude from the "build context" (tar stream) sent to balenaCloud, Docker -daemon or balenaEngine. In a microservices (multicontainer) application, the +daemon or balenaEngine. In a microservices (multicontainer) fleet, the source directory is the directory that contains the "docker-compose.yml" file. The --multi-dockerignore (-m) option may be used with microservices -(multicontainer) applications that define a docker-compose.yml file. When -this option is used, each service subdirectory (defined by the `build` or +(multicontainer) fleets that define a docker-compose.yml file. When this +option is used, each service subdirectory (defined by the `build` or `build.context` service properties in the docker-compose.yml file) is filtered separately according to a .dockerignore file defined in the service subdirectory. If no .dockerignore file exists in a service subdirectory, then @@ -2905,12 +3095,12 @@ adding counter patterns to the applicable .dockerignore file(s), for example Examples: - $ balena build --application myApp - $ balena build ./source/ --application myApp + $ balena build --fleet myFleet + $ balena build ./source/ --fleet myorg/myfleet $ balena build --deviceType raspberrypi3 --arch armv7hf --emulated - $ balena build --docker /var/run/docker.sock --application myApp # Linux, Mac - $ balena build --docker //./pipe/docker_engine --application myApp # Windows - $ balena build --dockerHost my.docker.host --dockerPort 2376 --ca ca.pem --key key.pem --cert cert.pem -a myApp + $ balena build --docker /var/run/docker.sock --fleet myFleet # Linux, Mac + $ balena build --docker //./pipe/docker_engine --fleet myFleet # Windows + $ balena build --dockerHost my.docker.host --dockerPort 2376 --ca ca.pem --key key.pem --cert cert.pem -f myFleet ### Arguments @@ -2930,7 +3120,11 @@ the type of device this build is for #### -a, --application APPLICATION -name of the target balena application this build is for +DEPRECATED alias for -f, --fleet + +#### -f, --fleet FLEET + +fleet name, slug (preferred), or numeric ID #### -e, --emulated @@ -3030,12 +3224,12 @@ Docker host TLS certificate file Docker host TLS key file -## deploy <appName> [image] +## deploy <fleet> [image] -Usage: `deploy ([image] | --build [--source build-dir])` +Usage: `deploy ([image] | --build [--source build-dir])` -Use this command to deploy an image or a complete multicontainer project to an -application, optionally building it first. The source images are searched for +Use this command to deploy an image or a complete multicontainer project to a +fleet, optionally building it first. The source images are searched for (and optionally built) using the docker daemon in your development machine or balena device. (See also the `balena push` command for the option of building the image in the balenaCloud build servers.) @@ -3050,8 +3244,8 @@ If a compose file isn't found, the command will look for a Dockerfile[.template] file (or alternative Dockerfile specified with the `-f` option), and if yet that isn't found, it will try to generate one. -To deploy to an app on which you're a collaborator, use -`balena deploy /`. +To deploy to a fleet where you are a collaborator, use fleet slug including the +organization: `balena deploy /`. REGISTRY SECRETS The --registry-secrets option specifies a JSON or YAML file containing private @@ -3079,12 +3273,12 @@ DOCKERIGNORE AND GITIGNORE FILES By default, the balena CLI will use a single ".dockerignore" file (if any) at the project root (--source directory) in order to decide which source files to exclude from the "build context" (tar stream) sent to balenaCloud, Docker -daemon or balenaEngine. In a microservices (multicontainer) application, the +daemon or balenaEngine. In a microservices (multicontainer) fleet, the source directory is the directory that contains the "docker-compose.yml" file. The --multi-dockerignore (-m) option may be used with microservices -(multicontainer) applications that define a docker-compose.yml file. When -this option is used, each service subdirectory (defined by the `build` or +(multicontainer) fleets that define a docker-compose.yml file. When this +option is used, each service subdirectory (defined by the `build` or `build.context` service properties in the docker-compose.yml file) is filtered separately according to a .dockerignore file defined in the service subdirectory. If no .dockerignore file exists in a service subdirectory, then @@ -3129,16 +3323,16 @@ adding counter patterns to the applicable .dockerignore file(s), for example Examples: - $ balena deploy myApp - $ balena deploy myApp --build --source myBuildDir/ - $ balena deploy myApp myApp/myImage - $ balena deploy myApp myApp/myImage --release-tag key1 "" key2 "value2 with spaces" + $ balena deploy myFleet + $ balena deploy myorg/myfleet --build --source myBuildDir/ + $ balena deploy myorg/myfleet myRepo/myImage + $ balena deploy myFleet myRepo/myImage --release-tag key1 "" key2 "value2 with spaces" ### Arguments -#### APPNAME +#### FLEET -the name of the application to deploy to +fleet name, slug (preferred), or numeric ID (deprecated) #### IMAGE @@ -3266,42 +3460,41 @@ Docker host TLS key file ## join [deviceIpOrHostname] -Move a local device to an application on another balena server, causing +Move a local device to a fleet on another balena server, causing the device to "join" the new server. The device must be running balenaOS. For example, you could provision a device against an openBalena installation where you perform end-to-end tests and then move it to balenaCloud when it's ready for production. -To move a device between applications on the same server, use the +To move a device between fleets on the same server, use the `balena device move` command instead of `balena join`. If you don't specify a device hostname or IP, this command will automatically scan the local network for balenaOS devices and prompt you to select one from an interactive picker. This may require administrator/root privileges. -Likewise, if the application flag is not provided then a picker will be shown. +Likewise, if the fleet option 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. +Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are +the recommended option, as they are unique and unambiguous. Slugs can be +listed with the `balena fleets` command. Note that slugs may change if the +fleet is renamed. Fleet names are not unique and may result in "Fleet 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 fleet, or with fleets +from other balena accounts that you may be invited to join under any role. +For this reason, fleet names are especially discouraged in scripts (e.g. CI +environments). Numeric fleet IDs are deprecated because they consist of an +implementation detail of the balena backend. We intend to remove support for +numeric IDs at some point in the future. Examples: $ balena join $ balena join balena.local - $ balena join balena.local --application MyApp - $ balena join balena.local -a myorg/myapp + $ balena join balena.local --fleet MyFleet + $ balena join balena.local -f myorg/myfleet $ balena join 192.168.1.25 - $ balena join 192.168.1.25 --application MyApp + $ balena join 192.168.1.25 --fleet MyFleet ### Arguments @@ -3313,7 +3506,11 @@ the IP or hostname of device #### -a, --application APPLICATION -application name, slug (preferred), or numeric ID (deprecated) +DEPRECATED alias for -f, --fleet + +#### -f, --fleet FLEET + +fleet name, slug (preferred), or numeric ID #### -i, --pollInterval POLLINTERVAL @@ -3321,7 +3518,7 @@ the interval in minutes to check for updates ## leave [deviceIpOrHostname] -Remove a local device from its balena application, causing the device to +Remove a local device from its balena fleet, causing the device to "leave" the server it is provisioned on. This effectively makes the device "unmanaged". The device must be running balenaOS. @@ -3359,34 +3556,33 @@ Does not list system drives. ## support <action> -Grant or revoke balena support agent access to devices and applications +Grant or revoke balena support agent access to devices or fleets on balenaCloud. (This command does not apply to openBalena.) Access will be automatically revoked once the specified duration has elapsed. Duration defaults to 24h, but can be specified using --duration flag in days or hours, e.g. '12h', '2d'. -Both --device and --application flags accept multiple values, specified as +Both --device and --fleet flags accept multiple values, specified as a comma-separated list (with no spaces). -Applications may be specified by app name, slug, or numeric ID. App slugs -are the recommended option, as they are unique and unambiguous. Slugs -can be listed with the `balena apps` command. Note that slugs may change -if the application is renamed. -App names are not unique and may result in "Application is ambiguous" errors -at any time (even if it "used to work in the past"), for example if the name -clashes with a newly created public application, or with apps from other balena -accounts that you may have been invited to as a member. For this reason, app -names are especially discouraged in scripts (e.g. CI environments). -Numeric app IDs are deprecated because they consist of an implementation detail -of the balena backend. We intend to remove support for numeric IDs at some point -in the future. +Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are +the recommended option, as they are unique and unambiguous. Slugs can be +listed with the `balena fleets` command. Note that slugs may change if the +fleet is renamed. Fleet names are not unique and may result in "Fleet 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 fleet, or with fleets +from other balena accounts that you may be invited to join under any role. +For this reason, fleet names are especially discouraged in scripts (e.g. CI +environments). Numeric fleet IDs are deprecated because they consist of an +implementation detail of the balena backend. We intend to remove support for +numeric IDs at some point in the future. Examples: balena support enable --device ab346f,cd457a --duration 3d - balena support enable --application app3 --duration 12h - balena support disable -a myorg/myapp + balena support enable --fleet myFleet --duration 12h + balena support disable -f myorg/myfleet ### Arguments @@ -3402,7 +3598,11 @@ comma-separated list (no spaces) of device UUIDs #### -a, --application APPLICATION -comma-separated list (no spaces) of application names or org/name slugs +DEPRECATED alias for -f, --fleet + +#### -f, --fleet FLEET + +comma-separated list (no spaces) of fleet names or org/name slugs #### -t, --duration DURATION diff --git a/lib/commands/app/create.ts b/lib/commands/app/create.ts index 6308877e..c19be5a4 100644 --- a/lib/commands/app/create.ts +++ b/lib/commands/app/create.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2016-2020 Balena Ltd. + * Copyright 2016-2021 Balena Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,14 @@ */ import { flags } from '@oclif/command'; +import type { Output as ParserOutput } from '@oclif/parser'; +import type { Application } from 'balena-sdk'; + import Command from '../../command'; import { ExpectedError } from '../../errors'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import type { Application } from 'balena-sdk'; +import { appToFleetCmdMsg, warnify } from '../../utils/messages'; interface FlagsDef { organization?: string; @@ -32,18 +35,18 @@ interface ArgsDef { name: string; } -export default class AppCreateCmd extends Command { +export class FleetCreateCmd extends Command { public static description = stripIndent` - Create an application. + Create a fleet. - Create a new balena application. + Create a new balena fleet. - You can specify the organization the application should belong to using + You can specify the organization the fleet should belong to using the \`--organization\` option. The organization's handle, not its name, should be provided. Organization handles can be listed with the \`balena orgs\` command. - The application's default device type is specified with the \`--type\` option. + The fleet's default device type is specified with the \`--type\` option. The \`balena devices supported\` command can be used to list the available device types. @@ -55,41 +58,39 @@ export default class AppCreateCmd extends Command { `; public static examples = [ - '$ balena app create MyApp', - '$ balena app create MyApp --organization mmyorg', - '$ balena app create MyApp -o myorg --type raspberry-pi', + '$ balena fleet create MyFleet', + '$ balena fleet create MyFleet --organization mmyorg', + '$ balena fleet create MyFleet -o myorg --type raspberry-pi', ]; public static args = [ { name: 'name', - description: 'application name', + description: 'fleet name', required: true, }, ]; - public static usage = 'app create '; + public static usage = 'fleet create '; public static flags: flags.Input = { organization: flags.string({ char: 'o', - description: - 'handle of the organization the application should belong to', + description: 'handle of the organization the fleet should belong to', }), type: flags.string({ char: 't', description: - 'application device type (Check available types with `balena devices supported`)', + 'fleet device type (Check available types with `balena devices supported`)', }), help: cf.help, }; public static authenticated = true; - public async run() { - const { args: params, flags: options } = this.parse( - AppCreateCmd, - ); + public async run(parserOutput?: ParserOutput) { + const { args: params, flags: options } = + parserOutput || this.parse(FleetCreateCmd); // Ascertain device type const deviceType = @@ -112,12 +113,12 @@ export default class AppCreateCmd extends Command { if ((err.message || '').toLowerCase().includes('unique')) { // BalenaRequestError: Request error: "organization" and "app_name" must be unique. throw new ExpectedError( - `Error: application "${params.name}" already exists in organization "${organization}".`, + `Error: fleet "${params.name}" already exists in organization "${organization}".`, ); } else if ((err.message || '').toLowerCase().includes('unauthorized')) { // BalenaRequestError: Request error: Unauthorized throw new ExpectedError( - `Error: You are not authorized to create applications in organization "${organization}".`, + `Error: You are not authorized to create fleets in organization "${organization}".`, ); } @@ -128,8 +129,8 @@ export default class AppCreateCmd extends Command { const { isV13 } = await import('../../utils/version'); console.log( isV13() - ? `Application created: slug "${application.slug}", device type "${deviceType}"` - : `Application created: ${application.slug} (${deviceType}, id ${application.id})`, + ? `Fleet created: slug "${application.slug}", device type "${deviceType}"` + : `Fleet created: ${application.slug} (${deviceType}, id ${application.id})`, ); } @@ -150,3 +151,31 @@ export default class AppCreateCmd extends Command { } } } + +export default class AppCreateCmd extends FleetCreateCmd { + public static description = stripIndent` + DEPRECATED alias for the 'fleet create' command + + ${appToFleetCmdMsg + .split('\n') + .map((l) => `\t\t${l}`) + .join('\n')} + + For command usage, see 'balena help fleet create' + `; + public static examples = []; + public static usage = 'app create '; + public static args = FleetCreateCmd.args; + public static flags = FleetCreateCmd.flags; + public static authenticated = FleetCreateCmd.authenticated; + public static primary = FleetCreateCmd.primary; + + public async run() { + // call this.parse() before deprecation message to parse '-h' + const parserOutput = this.parse(AppCreateCmd); + if (process.stderr.isTTY) { + console.error(warnify(appToFleetCmdMsg)); + } + await super.run(parserOutput); + } +} diff --git a/lib/commands/app/index.ts b/lib/commands/app/index.ts index 228851ef..5d2687f5 100644 --- a/lib/commands/app/index.ts +++ b/lib/commands/app/index.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2016-2020 Balena Ltd. + * Copyright 2016-2021 Balena Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,35 +15,44 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import type { flags } from '@oclif/command'; +import type { Output as ParserOutput } from '@oclif/parser'; +import type { Release } from 'balena-sdk'; + import Command from '../../command'; import * as cf from '../../utils/common-flags'; import * as ca from '../../utils/common-args'; import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy'; -import { applicationIdInfo } from '../../utils/messages'; -import type { Release } from 'balena-sdk'; +import { + applicationIdInfo, + appToFleetCmdMsg, + warnify, +} from '../../utils/messages'; interface FlagsDef { help: void; } interface ArgsDef { - application: string; + fleet: string; } -export default class AppCmd extends Command { +export class FleetCmd extends Command { public static description = stripIndent` - Display information about a single application. + Display information about a single fleet. - Display detailed information about a single balena application. + Display detailed information about a single fleet. ${applicationIdInfo.split('\n').join('\n\t\t')} `; - public static examples = ['$ balena app MyApp', '$ balena app myorg/myapp']; + public static examples = [ + '$ balena fleet MyFleet', + '$ balena fleet myorg/myfleet', + ]; - public static args = [ca.applicationRequired]; + public static args = [ca.fleetRequired]; - public static usage = 'app '; + public static usage = 'fleet '; public static flags: flags.Input = { help: cf.help, @@ -52,21 +61,18 @@ export default class AppCmd extends Command { public static authenticated = true; public static primary = true; - public async run() { - const { args: params } = this.parse(AppCmd); + public async run(parserOutput?: ParserOutput) { + const { args: params } = + parserOutput || this.parse(FleetCmd); const { getApplication } = await import('../../utils/sdk'); - const application = (await getApplication( - getBalenaSdk(), - params.application, - { - $expand: { - is_for__device_type: { $select: 'slug' }, - should_be_running__release: { $select: 'commit' }, - }, + const application = (await getApplication(getBalenaSdk(), params.fleet, { + $expand: { + is_for__device_type: { $select: 'slug' }, + should_be_running__release: { $select: 'commit' }, }, - )) as ApplicationWithDeviceType & { + })) as ApplicationWithDeviceType & { should_be_running__release: [Release?]; // For display purposes: device_type: string; @@ -88,3 +94,31 @@ export default class AppCmd extends Command { ); } } + +export default class AppCmd extends FleetCmd { + public static description = stripIndent` + DEPRECATED alias for the 'fleet' command + + ${appToFleetCmdMsg + .split('\n') + .map((l) => `\t\t${l}`) + .join('\n')} + + For command usage, see 'balena help fleet' + `; + public static examples = []; + public static usage = 'app '; + public static args = FleetCmd.args; + public static flags = FleetCmd.flags; + public static authenticated = FleetCmd.authenticated; + public static primary = FleetCmd.primary; + + public async run() { + // call this.parse() before deprecation message to parse '-h' + const parserOutput = this.parse(AppCmd); + if (process.stderr.isTTY) { + console.error(warnify(appToFleetCmdMsg)); + } + await super.run(parserOutput); + } +} diff --git a/lib/commands/app/purge.ts b/lib/commands/app/purge.ts index 85360a56..18bdb21c 100644 --- a/lib/commands/app/purge.ts +++ b/lib/commands/app/purge.ts @@ -15,39 +15,45 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import type { flags } from '@oclif/command'; +import type { Output as ParserOutput } from '@oclif/parser'; + import Command from '../../command'; import * as cf from '../../utils/common-flags'; import * as ca from '../../utils/common-args'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { applicationIdInfo } from '../../utils/messages'; +import { + applicationIdInfo, + appToFleetCmdMsg, + warnify, +} from '../../utils/messages'; interface FlagsDef { help: void; } interface ArgsDef { - application: string; + fleet: string; } -export default class AppPurgeCmd extends Command { +export class FleetPurgeCmd extends Command { public static description = stripIndent` - Purge data from an application. + Purge data from a fleet. - Purge data from all devices belonging to an application. - This will clear the application's /data directory. + Purge data from all devices belonging to a fleet. + This will clear the fleet's '/data' directory. ${applicationIdInfo.split('\n').join('\n\t\t')} `; public static examples = [ - '$ balena app purge MyApp', - '$ balena app purge myorg/myapp', + '$ balena fleet purge MyFleet', + '$ balena fleet purge myorg/myfleet', ]; - public static args = [ca.applicationRequired]; + public static args = [ca.fleetRequired]; - public static usage = 'app purge '; + public static usage = 'fleet purge '; public static flags: flags.Input = { help: cf.help, @@ -55,8 +61,9 @@ export default class AppPurgeCmd extends Command { public static authenticated = true; - public async run() { - const { args: params } = this.parse(AppPurgeCmd); + public async run(parserOutput?: ParserOutput) { + const { args: params } = + parserOutput || this.parse(FleetPurgeCmd); const { getApplication } = await import('../../utils/sdk'); @@ -64,7 +71,7 @@ export default class AppPurgeCmd extends Command { // balena.models.application.purge only accepts a numeric id // so we must first fetch the app to get it's id, - const application = await getApplication(balena, params.application); + const application = await getApplication(balena, params.fleet); try { await balena.models.application.purge(application.id); @@ -78,3 +85,31 @@ export default class AppPurgeCmd extends Command { } } } + +export default class AppPurgeCmd extends FleetPurgeCmd { + public static description = stripIndent` + DEPRECATED alias for the 'fleet purge' command + + ${appToFleetCmdMsg + .split('\n') + .map((l) => `\t\t${l}`) + .join('\n')} + + For command usage, see 'balena help fleet purge' + `; + public static examples = []; + public static usage = 'app purge '; + public static args = FleetPurgeCmd.args; + public static flags = FleetPurgeCmd.flags; + public static authenticated = FleetPurgeCmd.authenticated; + public static primary = FleetPurgeCmd.primary; + + public async run() { + // call this.parse() before deprecation message to parse '-h' + const parserOutput = this.parse(AppPurgeCmd); + if (process.stderr.isTTY) { + console.error(warnify(appToFleetCmdMsg)); + } + await super.run(parserOutput); + } +} diff --git a/lib/commands/app/rename.ts b/lib/commands/app/rename.ts index 225c02bf..462f1b37 100644 --- a/lib/commands/app/rename.ts +++ b/lib/commands/app/rename.ts @@ -15,28 +15,34 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import type { flags } from '@oclif/command'; +import type { Output as ParserOutput } from '@oclif/parser'; +import type { ApplicationType } from 'balena-sdk'; + import Command from '../../command'; import * as cf from '../../utils/common-flags'; import * as ca from '../../utils/common-args'; import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy'; -import { applicationIdInfo } from '../../utils/messages'; -import type { ApplicationType } from 'balena-sdk'; +import { + applicationIdInfo, + appToFleetCmdMsg, + warnify, +} from '../../utils/messages'; interface FlagsDef { help: void; } interface ArgsDef { - application: string; + fleet: string; newName?: string; } -export default class AppRenameCmd extends Command { +export class FleetRenameCmd extends Command { public static description = stripIndent` - Rename an application. + Rename a fleet. - Rename an application. + Rename a fleet. Note, if the \`newName\` parameter is omitted, it will be prompted for interactively. @@ -45,20 +51,20 @@ export default class AppRenameCmd extends Command { `; public static examples = [ - '$ balena app rename OldName', - '$ balena app rename OldName NewName', - '$ balena app rename myorg/oldname NewName', + '$ balena fleet rename OldName', + '$ balena fleet rename OldName NewName', + '$ balena fleet rename myorg/oldname NewName', ]; public static args = [ - ca.applicationRequired, + ca.fleetRequired, { name: 'newName', - description: 'the new name for the application', + description: 'the new name for the fleet', }, ]; - public static usage = 'app rename [newName]'; + public static usage = 'fleet rename [newName]'; public static flags: flags.Input = { help: cf.help, @@ -66,8 +72,9 @@ export default class AppRenameCmd extends Command { public static authenticated = true; - public async run() { - const { args: params } = this.parse(AppRenameCmd); + public async run(parserOutput?: ParserOutput) { + const { args: params } = + parserOutput || this.parse(FleetRenameCmd); const { validateApplicationName } = await import('../../utils/validation'); const { ExpectedError } = await import('../../errors'); @@ -76,7 +83,7 @@ export default class AppRenameCmd extends Command { // Disambiguate target application (if params.params is a number, it could either be an ID or a numerical name) const { getApplication } = await import('../../utils/sdk'); - const application = await getApplication(balena, params.application, { + const application = await getApplication(balena, params.fleet, { $expand: { application_type: { $select: ['is_legacy'], @@ -86,16 +93,14 @@ export default class AppRenameCmd extends Command { // Check app exists if (!application) { - throw new ExpectedError( - 'Error: application ${params.nameOrSlug} not found.', - ); + throw new ExpectedError(`Error: fleet ${params.fleet} not found.`); } // Check app supports renaming const appType = (application.application_type as ApplicationType[])?.[0]; if (appType.is_legacy) { throw new ExpectedError( - `Application ${params.application} is of 'legacy' type, and cannot be renamed.`, + `Fleet ${params.fleet} is of 'legacy' type, and cannot be renamed.`, ); } @@ -103,7 +108,7 @@ export default class AppRenameCmd extends Command { const newName = params.newName || (await getCliForm().ask({ - message: 'Please enter the new name for this application:', + message: 'Please enter the new name for this fleet:', type: 'input', validate: validateApplicationName, })) || @@ -115,9 +120,7 @@ export default class AppRenameCmd extends Command { } catch (e) { // BalenaRequestError: Request error: "organization" and "app_name" must be unique. if ((e.message || '').toLowerCase().includes('unique')) { - throw new ExpectedError( - `Error: application ${params.application} already exists.`, - ); + throw new ExpectedError(`Error: fleet ${params.fleet} already exists.`); } throw e; } @@ -128,7 +131,7 @@ export default class AppRenameCmd extends Command { ); // Output result - console.log(`Application renamed`); + console.log(`Fleet renamed`); console.log('From:'); console.log(`\tname: ${application.app_name}`); console.log(`\tslug: ${application.slug}`); @@ -137,3 +140,31 @@ export default class AppRenameCmd extends Command { console.log(`\tslug: ${renamedApplication.slug}`); } } + +export default class AppRenameCmd extends FleetRenameCmd { + public static description = stripIndent` + DEPRECATED alias for the 'fleet rename' command + + ${appToFleetCmdMsg + .split('\n') + .map((l) => `\t\t${l}`) + .join('\n')} + + For command usage, see 'balena help fleet rename' + `; + public static examples = []; + public static usage = 'app rename [newName]'; + public static args = FleetRenameCmd.args; + public static flags = FleetRenameCmd.flags; + public static authenticated = FleetRenameCmd.authenticated; + public static primary = FleetRenameCmd.primary; + + public async run() { + // call this.parse() before deprecation message to parse '-h' + const parserOutput = this.parse(AppRenameCmd); + if (process.stderr.isTTY) { + console.error(warnify(appToFleetCmdMsg)); + } + await super.run(parserOutput); + } +} diff --git a/lib/commands/app/restart.ts b/lib/commands/app/restart.ts index 3b523d50..f2dfb1d9 100644 --- a/lib/commands/app/restart.ts +++ b/lib/commands/app/restart.ts @@ -15,38 +15,44 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import type { flags } from '@oclif/command'; +import type { Output as ParserOutput } from '@oclif/parser'; + import Command from '../../command'; import * as cf from '../../utils/common-flags'; import * as ca from '../../utils/common-args'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { applicationIdInfo } from '../../utils/messages'; +import { + applicationIdInfo, + appToFleetCmdMsg, + warnify, +} from '../../utils/messages'; interface FlagsDef { help: void; } interface ArgsDef { - application: string; + fleet: string; } -export default class AppRestartCmd extends Command { +export class FleetRestartCmd extends Command { public static description = stripIndent` - Restart an application. + Restart a fleet. - Restart all devices belonging to an application. + Restart all devices belonging to a fleet. ${applicationIdInfo.split('\n').join('\n\t\t')} `; public static examples = [ - '$ balena app restart MyApp', - '$ balena app restart myorg/myapp', + '$ balena fleet restart MyFleet', + '$ balena fleet restart myorg/myfleet', ]; - public static args = [ca.applicationRequired]; + public static args = [ca.fleetRequired]; - public static usage = 'app restart '; + public static usage = 'fleet restart '; public static flags: flags.Input = { help: cf.help, @@ -54,16 +60,45 @@ export default class AppRestartCmd extends Command { public static authenticated = true; - public async run() { - const { args: params } = this.parse(AppRestartCmd); + public async run(parserOutput?: ParserOutput) { + const { args: params } = + parserOutput || this.parse(FleetRestartCmd); 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); + const application = await getApplication(balena, params.fleet); await balena.models.application.restart(application.id); } } + +export default class AppRestartCmd extends FleetRestartCmd { + public static description = stripIndent` + DEPRECATED alias for the 'fleet restart' command + + ${appToFleetCmdMsg + .split('\n') + .map((l) => `\t\t${l}`) + .join('\n')} + + For command usage, see 'balena help fleet restart' + `; + public static examples = []; + public static usage = 'app restart '; + public static args = FleetRestartCmd.args; + public static flags = FleetRestartCmd.flags; + public static authenticated = FleetRestartCmd.authenticated; + public static primary = FleetRestartCmd.primary; + + public async run() { + // call this.parse() before deprecation message to parse '-h' + const parserOutput = this.parse(AppRestartCmd); + if (process.stderr.isTTY) { + console.error(warnify(appToFleetCmdMsg)); + } + await super.run(parserOutput); + } +} diff --git a/lib/commands/app/rm.ts b/lib/commands/app/rm.ts index 9c10e7b0..333a9f59 100644 --- a/lib/commands/app/rm.ts +++ b/lib/commands/app/rm.ts @@ -15,12 +15,18 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import type { flags } from '@oclif/command'; +import type { Output as ParserOutput } from '@oclif/parser'; + import Command from '../../command'; import * as cf from '../../utils/common-flags'; import * as ca from '../../utils/common-args'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { applicationIdInfo } from '../../utils/messages'; +import { + applicationIdInfo, + appToFleetCmdMsg, + warnify, +} from '../../utils/messages'; interface FlagsDef { yes: boolean; @@ -28,14 +34,14 @@ interface FlagsDef { } interface ArgsDef { - application: string; + fleet: string; } -export default class AppRmCmd extends Command { +export class FleetRmCmd extends Command { public static description = stripIndent` - Remove an application. + Remove a fleet. - Permanently remove a balena application. + Permanently remove a fleet. The --yes option may be used to avoid interactive confirmation. @@ -43,14 +49,14 @@ export default class AppRmCmd extends Command { `; public static examples = [ - '$ balena app rm MyApp', - '$ balena app rm MyApp --yes', - '$ balena app rm myorg/myapp', + '$ balena fleet rm MyFleet', + '$ balena fleet rm MyFleet --yes', + '$ balena fleet rm myorg/myfleet', ]; - public static args = [ca.applicationRequired]; + public static args = [ca.fleetRequired]; - public static usage = 'app rm '; + public static usage = 'fleet rm '; public static flags: flags.Input = { yes: cf.yes, @@ -59,10 +65,9 @@ export default class AppRmCmd extends Command { public static authenticated = true; - public async run() { - const { args: params, flags: options } = this.parse( - AppRmCmd, - ); + public async run(parserOutput?: ParserOutput) { + const { args: params, flags: options } = + parserOutput || this.parse(FleetRmCmd); const { confirm } = await import('../../utils/patterns'); const { getApplication } = await import('../../utils/sdk'); @@ -71,13 +76,41 @@ export default class AppRmCmd extends Command { // Confirm await confirm( options.yes ?? false, - `Are you sure you want to delete application ${params.application}?`, + `Are you sure you want to delete fleet ${params.fleet}?`, ); // Disambiguate application (if is a number, it could either be an ID or a numerical name) - const application = await getApplication(balena, params.application); + const application = await getApplication(balena, params.fleet); // Remove await balena.models.application.remove(application.id); } } + +export default class AppRmCmd extends FleetRmCmd { + public static description = stripIndent` + DEPRECATED alias for the 'fleet rm' command + + ${appToFleetCmdMsg + .split('\n') + .map((l) => `\t\t${l}`) + .join('\n')} + + For command usage, see 'balena help fleet rm' + `; + public static examples = []; + public static usage = 'app rm '; + public static args = FleetRmCmd.args; + public static flags = FleetRmCmd.flags; + public static authenticated = FleetRmCmd.authenticated; + public static primary = FleetRmCmd.primary; + + public async run() { + // call this.parse() before deprecation message to parse '-h' + const parserOutput = this.parse(AppRmCmd); + if (process.stderr.isTTY) { + console.error(warnify(appToFleetCmdMsg)); + } + await super.run(parserOutput); + } +} diff --git a/lib/commands/apps.ts b/lib/commands/apps.ts index aa9d55ff..14b76cca 100644 --- a/lib/commands/apps.ts +++ b/lib/commands/apps.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2016-2020 Balena Ltd. + * Copyright 2016-2021 Balena Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,9 +16,12 @@ */ import { flags } from '@oclif/command'; +import type { Output as ParserOutput } from '@oclif/parser'; + import Command from '../command'; import * as cf from '../utils/common-flags'; import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy'; +import { appToFleetCmdMsg, warnify } from '../utils/messages'; interface ExtendedApplication extends ApplicationWithDeviceType { device_count?: number; @@ -27,22 +30,22 @@ interface ExtendedApplication extends ApplicationWithDeviceType { interface FlagsDef { help: void; - verbose: boolean; + verbose?: boolean; } -export default class AppsCmd extends Command { +export class FleetsCmd extends Command { public static description = stripIndent` - List all applications. + List all fleets. - list all your balena applications. + List all your balena fleets. - For detailed information on a particular application, - use \`balena app \` instead. + For detailed information on a particular fleet, use + \`balena fleet \` `; - public static examples = ['$ balena apps']; + public static examples = ['$ balena fleets']; - public static usage = 'apps'; + public static usage = 'fleets'; public static flags: flags.Input = { help: cf.help, @@ -56,8 +59,10 @@ export default class AppsCmd extends Command { public static authenticated = true; public static primary = true; - public async run() { - this.parse(AppsCmd); + protected useAppWord = false; + + public async run(_parserOutput?: ParserOutput) { + _parserOutput ||= this.parse(FleetsCmd); const balena = getBalenaSdk(); @@ -85,7 +90,7 @@ export default class AppsCmd extends Command { console.log( getVisuals().table.horizontal(applications, [ 'id', - 'app_name', + this.useAppWord ? 'app_name' : 'app_name => NAME', 'slug', 'device_type', 'online_devices', @@ -94,3 +99,36 @@ export default class AppsCmd extends Command { ); } } + +const appsToFleetsRenameMsg = appToFleetCmdMsg + .replace(/'app'/g, "'apps'") + .replace(/'fleet'/g, "'fleets'"); + +export default class AppsCmd extends FleetsCmd { + public static description = stripIndent` + DEPRECATED alias for the 'fleets' command + + ${appsToFleetsRenameMsg + .split('\n') + .map((l) => `\t\t${l}`) + .join('\n')} + + For command usage, see 'balena help fleets' + `; + public static examples = []; + public static usage = 'apps'; + public static args = FleetsCmd.args; + public static flags = FleetsCmd.flags; + public static authenticated = FleetsCmd.authenticated; + public static primary = FleetsCmd.primary; + + public async run() { + // call this.parse() before deprecation message to parse '-h' + const parserOutput = this.parse(AppsCmd); + if (process.stderr.isTTY) { + console.error(warnify(appsToFleetsRenameMsg)); + } + this.useAppWord = true; + await super.run(parserOutput); + } +} diff --git a/lib/commands/build.ts b/lib/commands/build.ts index c46237b5..2a2dd365 100644 --- a/lib/commands/build.ts +++ b/lib/commands/build.ts @@ -18,22 +18,27 @@ import { flags } from '@oclif/command'; import Command from '../command'; import { getBalenaSdk } from '../utils/lazy'; +import * as cf from '../utils/common-flags'; import * as compose from '../utils/compose'; import type { Application, ApplicationType, BalenaSDK } from 'balena-sdk'; import { + appToFleetFlagMsg, buildArgDeprecation, dockerignoreHelp, registrySecretsHelp, + warnify, } from '../utils/messages'; import type { ComposeCliFlags, ComposeOpts } from '../utils/compose-types'; import { buildProject, composeCliFlags } from '../utils/compose_ts'; import type { BuildOpts, DockerCliFlags } from '../utils/docker'; import { dockerCliFlags } from '../utils/docker'; +import { isV13 } from '../utils/version'; interface FlagsDef extends ComposeCliFlags, DockerCliFlags { arch?: string; deviceType?: string; application?: string; + fleet?: string; source?: string; // Not part of command profile - source param copied here. help: void; } @@ -51,7 +56,7 @@ the provided docker daemon in your development machine or balena device. (See also the \`balena push\` command for the option of building images in the balenaCloud build servers.) -You must provide either an application or a device-type/architecture pair. +You must specify either a fleet, or the device type and architecture. This command will look into the given source directory (or the current working directory if one isn't specified) for a docker-compose.yml file, and if found, @@ -65,12 +70,12 @@ ${registrySecretsHelp} ${dockerignoreHelp} `; public static examples = [ - '$ balena build --application myApp', - '$ balena build ./source/ --application myApp', + '$ balena build --fleet myFleet', + '$ balena build ./source/ --fleet myorg/myfleet', '$ balena build --deviceType raspberrypi3 --arch armv7hf --emulated', - '$ balena build --docker /var/run/docker.sock --application myApp # Linux, Mac', - '$ balena build --docker //./pipe/docker_engine --application myApp # Windows', - '$ balena build --dockerHost my.docker.host --dockerPort 2376 --ca ca.pem --key key.pem --cert cert.pem -a myApp', + '$ balena build --docker /var/run/docker.sock --fleet myFleet # Linux, Mac', + '$ balena build --docker //./pipe/docker_engine --fleet myFleet # Windows', + '$ balena build --dockerHost my.docker.host --dockerPort 2376 --ca ca.pem --key key.pem --cert cert.pem -f myFleet', ]; public static args = [ @@ -91,10 +96,8 @@ ${dockerignoreHelp} description: 'the type of device this build is for', char: 'd', }), - application: flags.string({ - description: 'name of the target balena application this build is for', - char: 'a', - }), + ...(isV13() ? {} : { application: cf.application }), + fleet: cf.fleet, ...composeCliFlags, ...dockerCliFlags, // NOTE: Not supporting -h for help, because of clash with -h in DockerCliFlags @@ -109,6 +112,11 @@ ${dockerignoreHelp} BuildCmd, ); + if (options.application && process.stderr.isTTY) { + console.error(warnify(appToFleetFlagMsg)); + } + options.application ||= options.fleet; + await Command.checkLoggedInIf(!!options.application); (await import('events')).defaultMaxListeners = 1000; @@ -160,7 +168,7 @@ ${dockerignoreHelp} ) { const { ExpectedError } = await import('../errors'); throw new ExpectedError( - 'You must specify either an application or an arch/deviceType pair to build for', + 'You must specify either a fleet (-f), or the device type (-d) and architecture (-A)', ); } @@ -240,7 +248,7 @@ ${dockerignoreHelp} !appType.supports_multicontainer ) { logger.logWarn( - 'Target application does not support multiple containers.\n' + + 'Target fleet does not support multiple containers.\n' + 'Continuing with build, but you will not be able to deploy.', ); } diff --git a/lib/commands/config/generate.ts b/lib/commands/config/generate.ts index 7a2c81c9..957240cf 100644 --- a/lib/commands/config/generate.ts +++ b/lib/commands/config/generate.ts @@ -19,13 +19,19 @@ import { flags } from '@oclif/command'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, getCliForm, stripIndent } from '../../utils/lazy'; -import { applicationIdInfo } from '../../utils/messages'; +import { + applicationIdInfo, + appToFleetFlagMsg, + warnify, +} from '../../utils/messages'; +import { isV13 } from '../../utils/version'; import type { PineDeferred } from 'balena-sdk'; interface FlagsDef { version: string; // OS version application?: string; app?: string; // application alias + fleet?: string; device?: string; deviceApiKey?: string; deviceType?: string; @@ -43,16 +49,15 @@ export default class ConfigGenerateCmd extends Command { public static description = stripIndent` Generate a config.json file. - Generate a config.json file for a device or application. + Generate a config.json file for a device or fleet. - Calling this command with the exact version number of the targeted image is required. + The target balenaOS version must be specified with the --version option. - This command is interactive by default, but you can do this automatically without interactivity - by specifying an option for each question on the command line, if you know the questions - that will be asked for the relevant device type. + To configure an image for a fleet of mixed device types, use the --fleet option + alongside the --deviceType option to specify the target device type. - In case that you want to configure an image for an application with mixed device types, - you can pass the --deviceType argument along with --application to specify the target device type. + To avoid interactive questions, specify a command line option for each question that + would otherwise be asked. ${applicationIdInfo.split('\n').join('\n\t\t')} `; @@ -62,11 +67,11 @@ export default class ConfigGenerateCmd extends Command { '$ balena config generate --device 7cf02a6 --version 2.12.7 --generate-device-api-key', '$ balena config generate --device 7cf02a6 --version 2.12.7 --device-api-key ', '$ balena config generate --device 7cf02a6 --version 2.12.7 --output config.json', - '$ balena config generate --app MyApp --version 2.12.7', - '$ balena config generate --app myorg/myapp --version 2.12.7', - '$ balena config generate --app MyApp --version 2.12.7 --deviceType fincm3', - '$ balena config generate --app MyApp --version 2.12.7 --output config.json', - '$ balena config generate --app MyApp --version 2.12.7 --network wifi --wifiSsid mySsid --wifiKey abcdefgh --appUpdatePollInterval 1', + '$ balena config generate --fleet MyFleet --version 2.12.7', + '$ balena config generate --fleet myorg/myfleet --version 2.12.7', + '$ balena config generate --fleet MyFleet --version 2.12.7 --deviceType fincm3', + '$ balena config generate --fleet MyFleet --version 2.12.7 --output config.json', + '$ balena config generate --fleet MyFleet --version 2.12.7 --network wifi --wifiSsid mySsid --wifiKey abcdefgh --appUpdatePollInterval 15', ]; public static usage = 'config generate'; @@ -76,20 +81,28 @@ export default class ConfigGenerateCmd extends Command { description: 'a balenaOS version', required: true, }), - application: { ...cf.application, exclusive: ['app', 'device'] }, - app: { ...cf.app, exclusive: ['application', 'device'] }, - device: flags.string({ - description: 'device uuid', - char: 'd', - exclusive: ['application', 'app'], - }), + ...(isV13() + ? {} + : { + application: { + ...cf.application, + exclusive: ['app', 'fleet', 'device'], + }, + app: { ...cf.app, exclusive: ['application', 'fleet', 'device'] }, + appUpdatePollInterval: flags.string({ + description: 'DEPRECATED alias for --updatePollInterval', + }), + }), + fleet: { ...cf.fleet, exclusive: ['application', 'app', 'device'] }, + device: { ...cf.device, exclusive: ['application', 'app', 'fleet'] }, deviceApiKey: flags.string({ description: 'custom device key - note that this is only supported on balenaOS 2.0.3+', char: 'k', }), deviceType: flags.string({ - description: 'device type slug', + description: + "device type slug (run 'balena devices supported' for possible values)", }), 'generate-device-api-key': flags.boolean({ description: 'generate a fresh device key for the device', @@ -113,7 +126,7 @@ export default class ConfigGenerateCmd extends Command { }), appUpdatePollInterval: flags.string({ description: - 'how frequently (in minutes) to poll for application updates', + 'supervisor cloud polling interval in minutes (e.g. for variable updates)', }), help: cf.help, }; @@ -143,8 +156,8 @@ export default class ConfigGenerateCmd extends Command { if (!rawDevice.belongs_to__application) { const { ExpectedError } = await import('../../errors'); throw new ExpectedError(stripIndent` - Device ${options.device} does not appear to belong to an accessible application. - Try with a different device, or use '--application' instead of '--device'.`); + Device ${options.device} does not appear to belong to an accessible fleet. + Try with a different device, or use '--fleet' instead of '--device'.`); } device = rawDevice as DeviceWithDeviceType & { belongs_to__application: PineDeferred; @@ -177,7 +190,7 @@ export default class ConfigGenerateCmd extends Command { !helpers.areDeviceTypesCompatible(appDeviceManifest, deviceManifest) ) { throw new balena.errors.BalenaInvalidDeviceType( - `Device type ${options.deviceType} is incompatible with application ${options.application}`, + `Device type ${options.deviceType} is incompatible with fleet ${options.application}`, ); } } @@ -218,7 +231,7 @@ export default class ConfigGenerateCmd extends Command { } protected readonly missingDeviceOrAppMessage = stripIndent` - Either a device or an application must be specified. + Either a device or a fleet must be specified. See the help page for examples: @@ -226,13 +239,16 @@ export default class ConfigGenerateCmd extends Command { `; protected readonly deviceTypeNotAllowedMessage = - 'The --deviceType option can only be used alongside the --application option'; + 'The --deviceType option can only be used alongside the --fleet option'; protected async validateOptions(options: FlagsDef) { const { ExpectedError } = await import('../../errors'); + if ((options.application || options.app) && process.stderr.isTTY) { + console.error(warnify(appToFleetFlagMsg)); + } + options.application ||= options.app || options.fleet; // Prefer options.application over options.app - options.application = options.application || options.app; delete options.app; if (options.device == null && options.application == null) { diff --git a/lib/commands/deploy.ts b/lib/commands/deploy.ts index 43fb90d0..1cea8f21 100644 --- a/lib/commands/deploy.ts +++ b/lib/commands/deploy.ts @@ -26,6 +26,7 @@ import { registrySecretsHelp, buildArgDeprecation, } from '../utils/messages'; +import * as ca from '../utils/common-args'; import * as compose from '../utils/compose'; import type { BuiltImage, @@ -62,18 +63,18 @@ interface FlagsDef extends ComposeCliFlags, DockerCliFlags { } interface ArgsDef { - appName: string; + fleet: string; image?: string; } export default class DeployCmd extends Command { public static description = `\ -Deploy a single image or a multicontainer project to a balena application. +Deploy a single image or a multicontainer project to a balena fleet. -Usage: \`deploy ([image] | --build [--source build-dir])\` +Usage: \`deploy ([image] | --build [--source build-dir])\` -Use this command to deploy an image or a complete multicontainer project to an -application, optionally building it first. The source images are searched for +Use this command to deploy an image or a complete multicontainer project to a +fleet, optionally building it first. The source images are searched for (and optionally built) using the docker daemon in your development machine or balena device. (See also the \`balena push\` command for the option of building the image in the balenaCloud build servers.) @@ -88,8 +89,8 @@ If a compose file isn't found, the command will look for a Dockerfile[.template] file (or alternative Dockerfile specified with the \`-f\` option), and if yet that isn't found, it will try to generate one. -To deploy to an app on which you're a collaborator, use -\`balena deploy /\`. +To deploy to a fleet where you are a collaborator, use fleet slug including the +organization: \`balena deploy /\`. ${registrySecretsHelp} @@ -97,25 +98,21 @@ ${dockerignoreHelp} `; public static examples = [ - '$ balena deploy myApp', - '$ balena deploy myApp --build --source myBuildDir/', - '$ balena deploy myApp myApp/myImage', - '$ balena deploy myApp myApp/myImage --release-tag key1 "" key2 "value2 with spaces"', + '$ balena deploy myFleet', + '$ balena deploy myorg/myfleet --build --source myBuildDir/', + '$ balena deploy myorg/myfleet myRepo/myImage', + '$ balena deploy myFleet myRepo/myImage --release-tag key1 "" key2 "value2 with spaces"', ]; public static args = [ - { - name: 'appName', - description: 'the name of the application to deploy to', - required: true, - }, + ca.fleetRequired, { name: 'image', description: 'the image to deploy', }, ]; - public static usage = 'deploy [image]'; + public static usage = 'deploy [image]'; public static flags: flags.Input = { source: flags.string({ @@ -160,7 +157,7 @@ ${dockerignoreHelp} const logger = await Command.getLogger(); logger.logDebug('Parsing input...'); - const { appName, image } = params; + const { fleet, image } = params; // Build args are under consideration for removal - warn user if (options.buildArg) { @@ -200,7 +197,7 @@ ${dockerignoreHelp} } const helpers = await import('../utils/helpers'); - const app = await helpers.getAppWithArch(appName); + const app = await helpers.getAppWithArch(fleet); const dockerUtils = await import('../utils/docker'); const [docker, buildOpts, composeOpts] = await Promise.all([ @@ -211,7 +208,7 @@ ${dockerignoreHelp} const release = await this.deployProject(docker, logger, composeOpts, { app, - appName, // may be prefixed by 'owner/', unlike app.app_name + appName: fleet, // may be prefixed by 'owner/', unlike app.app_name image, shouldPerformBuild: !!options.build, shouldUploadLogs: !options.nologupload, @@ -254,7 +251,7 @@ ${dockerignoreHelp} const project = await loadProject(logger, composeOpts, opts.image); if (project.descriptors.length > 1 && !appType?.supports_multicontainer) { throw new ExpectedError( - 'Target application does not support multiple containers. Aborting!', + 'Target fleet does not support multiple containers. Aborting!', ); } @@ -326,7 +323,7 @@ ${dockerignoreHelp} const { deployLegacy } = require('../utils/deploy-legacy'); const msg = getChalk().yellow( - 'Target application requires legacy deploy method.', + 'Target fleet requires legacy deploy method.', ); logger.logWarn(msg); diff --git a/lib/commands/device/index.ts b/lib/commands/device/index.ts index 2763b449..2389ce7a 100644 --- a/lib/commands/device/index.ts +++ b/lib/commands/device/index.ts @@ -21,7 +21,10 @@ import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { expandForAppName } from '../../utils/helpers'; import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy'; +import { appToFleetOutputMsg, warnify } from '../../utils/messages'; import { tryAsInteger } from '../../utils/validation'; +import { isV13 } from '../../utils/version'; + import type { Application, Release } from 'balena-sdk'; interface ExtendedDevice extends DeviceWithDeviceType { @@ -43,6 +46,7 @@ interface ExtendedDevice extends DeviceWithDeviceType { interface FlagsDef { help: void; + v13: boolean; } interface ArgsDef { @@ -70,13 +74,17 @@ export default class DeviceCmd extends Command { public static flags: flags.Input = { help: cf.help, + v13: cf.v13, }; public static authenticated = true; public static primary = true; public async run() { - const { args: params } = this.parse(DeviceCmd); + const { args: params, flags: options } = this.parse( + DeviceCmd, + ); + const useAppWord = !options.v13 && !isV13(); const balena = getBalenaSdk(); @@ -162,6 +170,10 @@ export default class DeviceCmd extends Command { ); } + if (useAppWord && process.stderr.isTTY) { + console.error(warnify(appToFleetOutputMsg)); + } + console.log( getVisuals().table.vertical(device, [ `$${device.device_name}$`, @@ -172,7 +184,7 @@ export default class DeviceCmd extends Command { 'ip_address', 'public_address', 'mac_address', - 'application_name', + useAppWord ? 'application_name' : 'application_name => FLEET', 'last_seen', 'uuid', 'commit', diff --git a/lib/commands/device/init.ts b/lib/commands/device/init.ts index deafd397..aa9280b0 100644 --- a/lib/commands/device/init.ts +++ b/lib/commands/device/init.ts @@ -19,12 +19,18 @@ import { flags } from '@oclif/command'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { applicationIdInfo } from '../../utils/messages'; +import { + applicationIdInfo, + appToFleetFlagMsg, + warnify, +} from '../../utils/messages'; import { runCommand } from '../../utils/helpers'; +import { isV13 } from '../../utils/version'; interface FlagsDef { application?: string; app?: string; + fleet?: string; yes: boolean; advanced: boolean; 'os-version'?: string; @@ -37,26 +43,30 @@ export default class DeviceInitCmd extends Command { public static description = stripIndent` Initialize a device with balenaOS. - Initialize a device by downloading the OS image of a certain application + Initialize a device by downloading the OS image of the specified fleet and writing it to an SD Card. - Note, if the application option is omitted it will be prompted - for interactively. + If the --fleet option is omitted, it will be prompted for interactively. ${applicationIdInfo.split('\n').join('\n\t\t')} `; public static examples = [ '$ balena device init', - '$ balena device init --application MyApp', - '$ balena device init -a myorg/myapp', + '$ balena device init --fleet MyFleet', + '$ balena device init -f myorg/myfleet', ]; public static usage = 'device init'; public static flags: flags.Input = { - application: cf.application, - app: cf.app, + ...(isV13() + ? {} + : { + application: cf.application, + app: cf.app, + }), + fleet: cf.fleet, yes: cf.yes, advanced: flags.boolean({ char: 'v', @@ -95,8 +105,11 @@ export default class DeviceInitCmd extends Command { const logger = await Command.getLogger(); const balena = getBalenaSdk(); + if ((options.application || options.app) && process.stderr.isTTY) { + console.error(warnify(appToFleetFlagMsg)); + } // Consolidate application options - options.application = options.application || options.app; + options.application ||= options.app || options.fleet; delete options.app; // Get application and diff --git a/lib/commands/device/move.ts b/lib/commands/device/move.ts index ad324bbf..8d2018d6 100644 --- a/lib/commands/device/move.ts +++ b/lib/commands/device/move.ts @@ -15,14 +15,19 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import type { flags } from '@oclif/command'; import type { IArg } from '@oclif/parser/lib/args'; import type { Application, BalenaSDK } from 'balena-sdk'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { applicationIdInfo } from '../../utils/messages'; import { ExpectedError } from '../../errors'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy'; +import { + applicationIdInfo, + appToFleetFlagMsg, + warnify, +} from '../../utils/messages'; +import { isV13 } from '../../utils/version'; interface ExtendedDevice extends DeviceWithDeviceType { application_name?: string; @@ -31,6 +36,7 @@ interface ExtendedDevice extends DeviceWithDeviceType { interface FlagsDef { application?: string; app?: string; + fleet?: string; help: void; } @@ -40,12 +46,11 @@ interface ArgsDef { export default class DeviceMoveCmd extends Command { public static description = stripIndent` - Move one or more devices to another application. + Move one or more devices to another fleet. - Move one or more devices to another application. + Move one or more devices to another fleet. - Note, if the application option is omitted it will be prompted - for interactively. + If --fleet is omitted, the fleet will be prompted for interactively. ${applicationIdInfo.split('\n').join('\n\t\t')} `; @@ -53,8 +58,8 @@ export default class DeviceMoveCmd extends Command { public static examples = [ '$ balena device move 7cf02a6', '$ balena device move 7cf02a6,dc39e52', - '$ balena device move 7cf02a6 --application MyNewApp', - '$ balena device move 7cf02a6 -a myorg/mynewapp', + '$ balena device move 7cf02a6 --fleet MyNewFleet', + '$ balena device move 7cf02a6 -f myorg/mynewfleet', ]; public static args: Array> = [ @@ -69,8 +74,8 @@ export default class DeviceMoveCmd extends Command { public static usage = 'device move '; public static flags: flags.Input = { - application: cf.application, - app: cf.app, + ...(isV13() ? {} : { app: cf.app, application: cf.application }), + fleet: cf.fleet, help: cf.help, }; @@ -81,14 +86,16 @@ export default class DeviceMoveCmd extends Command { DeviceMoveCmd, ); + if ((options.application || options.app) && process.stderr.isTTY) { + console.error(warnify(appToFleetFlagMsg)); + } + options.application ||= options.app || options.fleet; + const balena = getBalenaSdk(); const { tryAsInteger } = await import('../../utils/validation'); const { expandForAppName } = await import('../../utils/helpers'); - options.application = options.application || options.app; - delete options.app; - // Parse ids string into array of correct types const deviceIds: Array = params.uuid .split(',') @@ -126,9 +133,7 @@ export default class DeviceMoveCmd extends Command { for (const uuid of deviceIds) { try { await balena.models.device.move(uuid, application.id); - console.info( - `Device ${uuid} was moved to application ${application.slug}`, - ); + console.info(`Device ${uuid} was moved to fleet ${application.slug}`); } catch (err) { console.info(`${err.message}, uuid: ${uuid}`); process.exitCode = 1; diff --git a/lib/commands/device/purge.ts b/lib/commands/device/purge.ts index a3c979ac..2dbfbc5e 100644 --- a/lib/commands/device/purge.ts +++ b/lib/commands/device/purge.ts @@ -31,10 +31,10 @@ interface ArgsDef { export default class DevicePurgeCmd extends Command { public static description = stripIndent` - Purge application data from a device. + Purge data from a device. - Purge application data from a device. - This will clear the application's /data directory. + Purge data from a device. + This will clear the device's "/data" directory. Multiple devices may be specified with a comma-separated list of values (no spaces). diff --git a/lib/commands/device/register.ts b/lib/commands/device/register.ts index 5e380385..ae4d8745 100644 --- a/lib/commands/device/register.ts +++ b/lib/commands/device/register.ts @@ -29,27 +29,29 @@ interface FlagsDef { } interface ArgsDef { - application: string; + fleet: string; } export default class DeviceRegisterCmd extends Command { public static description = stripIndent` - Register a device. + Register a new device. - Register a device to an application. + Register a new device with a balena fleet. + + If --uuid is not provided, a new UUID will be automatically assigned. ${applicationIdInfo.split('\n').join('\n\t\t')} `; public static examples = [ - '$ balena device register MyApp', - '$ balena device register MyApp --uuid ', - '$ balena device register myorg/myapp --uuid ', + '$ balena device register MyFleet', + '$ balena device register MyFleet --uuid ', + '$ balena device register myorg/myfleet --uuid ', ]; - public static args: Array> = [ca.applicationRequired]; + public static args: Array> = [ca.fleetRequired]; - public static usage = 'device register '; + public static usage = 'device register '; public static flags: flags.Input = { uuid: flags.string({ @@ -70,7 +72,7 @@ export default class DeviceRegisterCmd extends Command { const balena = getBalenaSdk(); - const application = await getApplication(balena, params.application); + const application = await getApplication(balena, params.fleet); const uuid = options.uuid ?? balena.models.device.generateUniqueKey(); console.info(`Registering to ${application.app_name}: ${uuid}`); diff --git a/lib/commands/devices/index.ts b/lib/commands/devices/index.ts index 569f7f72..42796ebd 100644 --- a/lib/commands/devices/index.ts +++ b/lib/commands/devices/index.ts @@ -20,7 +20,15 @@ import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { expandForAppName } from '../../utils/helpers'; import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy'; -import { applicationIdInfo, jsonInfo } from '../../utils/messages'; +import { + applicationIdInfo, + appToFleetFlagMsg, + appToFleetOutputMsg, + jsonInfo, + warnify, +} from '../../utils/messages'; +import { isV13 } from '../../utils/version'; + import type { Application } from 'balena-sdk'; interface ExtendedDevice extends DeviceWithDeviceType { @@ -32,17 +40,19 @@ interface ExtendedDevice extends DeviceWithDeviceType { interface FlagsDef { application?: string; app?: string; + fleet?: string; help: void; json: boolean; + v13: boolean; } export default class DevicesCmd extends Command { public static description = stripIndent` List all devices. - list all devices that belong to you. + List all of your devices. - You can filter the devices by application by using the \`--application\` option. + Devices can be filtered by fleet with the \`--fleet\` option. ${applicationIdInfo.split('\n').join('\n\t\t')} @@ -50,33 +60,51 @@ export default class DevicesCmd extends Command { `; public static examples = [ '$ balena devices', - '$ balena devices --application MyApp', - '$ balena devices --app MyApp', - '$ balena devices -a MyApp', - '$ balena devices -a myorg/myapp', + '$ balena devices --fleet MyFleet', + '$ balena devices -f myorg/myfleet', ]; public static usage = 'devices'; public static flags: flags.Input = { - application: cf.application, - app: cf.app, + ...(isV13() + ? {} + : { + application: { + ...cf.application, + exclusive: ['app', 'fleet', 'v13'], + }, + app: { ...cf.app, exclusive: ['application', 'fleet', 'v13'] }, + }), + fleet: { ...cf.fleet, exclusive: ['app', 'application'] }, json: cf.json, help: cf.help, + v13: cf.v13, }; public static primary = true; public static authenticated = true; + protected useAppWord = false; + protected hasWarned = false; + public async run() { const { flags: options } = this.parse(DevicesCmd); + this.useAppWord = !options.fleet && !options.v13 && !isV13(); const balena = getBalenaSdk(); + if ( + (options.application || options.app) && + !options.json && + process.stderr.isTTY + ) { + this.hasWarned = true; + console.error(warnify(appToFleetFlagMsg)); + } // Consolidate application options - options.application = options.application || options.app; - delete options.app; + options.application ||= options.app || options.fleet; let devices; @@ -106,28 +134,32 @@ export default class DevicesCmd extends Command { return device; }); + const jName = this.useAppWord ? 'application_name' : 'fleet_name'; + const tName = this.useAppWord ? 'APPLICATION NAME' : 'FLEET'; const fields = [ 'id', 'uuid', 'device_name', 'device_type', - 'application_name', + options.json + ? `application_name => ${jName}` + : `application_name => ${tName}`, 'status', 'is_online', 'supervisor_version', 'os_version', 'dashboard_url', ]; - const _ = await import('lodash'); + if (options.json) { - console.log( - JSON.stringify( - devices.map((device) => _.pick(device, fields)), - null, - 4, - ), - ); + const { pickAndRename } = await import('../../utils/helpers'); + const mapped = devices.map((device) => pickAndRename(device, fields)); + console.log(JSON.stringify(mapped, null, 4)); } else { + if (!this.hasWarned && this.useAppWord && process.stderr.isTTY) { + console.error(warnify(appToFleetOutputMsg)); + } + const _ = await import('lodash'); console.log( getVisuals().table.horizontal( devices.map((dev) => _.mapValues(dev, (val) => val ?? 'N/a')), diff --git a/lib/commands/env/add.ts b/lib/commands/env/add.ts index 7fef3f8f..9ea59faf 100644 --- a/lib/commands/env/add.ts +++ b/lib/commands/env/add.ts @@ -21,10 +21,16 @@ import Command from '../../command'; import { ExpectedError } from '../../errors'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { applicationIdInfo } from '../../utils/messages'; +import { + applicationIdInfo, + appToFleetFlagMsg, + warnify, +} from '../../utils/messages'; +import { isV13 } from '../../utils/version'; interface FlagsDef { application?: string; + fleet?: string; device?: string; // device UUID help: void; quiet: boolean; @@ -38,18 +44,17 @@ interface ArgsDef { export default class EnvAddCmd extends Command { public static description = stripIndent` - Add env or config variable to application(s), device(s) or service(s). + Add env or config variable to fleets, devices or services. - Add an environment or config variable to one or more applications, devices - or services, as selected by the respective command-line options. Either the - --application or the --device option must be provided, and either may be be + Add an environment or config variable to one or more fleets, devices or + services, as selected by the respective command-line options. Either the + --fleet or the --device option must be provided, and either may be be used alongside the --service option to define a service-specific variable. - (A service is an application container in a "microservices" application.) + (A service corresponds to a Docker image/container in a microservices fleet.) When the --service option is used in conjunction with the --device option, - the service variable applies to the selected device only. Otherwise, it - applies to all devices of the selected application (i.e., the application's - fleet). If the --service option is omitted, the variable applies to all - services. + the service variable applies to the selected device only. Otherwise, it + applies to all devices of the selected fleet. If the --service option is + omitted, the variable applies to all services. If VALUE is omitted, the CLI will attempt to use the value of the environment variable of same name in the CLI process' environment. In this case, a warning @@ -61,19 +66,18 @@ export default class EnvAddCmd extends Command { running on devices. They are also stored differently in the balenaCloud API database. Configuration variables cannot be set for specific services, therefore the --service option cannot be used when the variable name starts - with a reserved prefix. When defining custom application variables, please - avoid the reserved prefixes. + with a reserved prefix. When defining custom fleet variables, please avoid + these reserved prefixes. ${applicationIdInfo.split('\n').join('\n\t\t')} `; public static examples = [ - '$ balena env add TERM --application MyApp', - '$ balena env add EDITOR vim --application MyApp', - '$ balena env add EDITOR vim -a myorg/myapp', - '$ balena env add EDITOR vim --application MyApp,MyApp2', - '$ balena env add EDITOR vim --application MyApp --service MyService', - '$ balena env add EDITOR vim --application MyApp,MyApp2 --service MyService,MyService2', + '$ balena env add TERM --fleet MyFleet', + '$ balena env add EDITOR vim -f myorg/myfleet', + '$ balena env add EDITOR vim --fleet MyFleet,MyFleet2', + '$ balena env add EDITOR vim --fleet MyFleet --service MyService', + '$ balena env add EDITOR vim --fleet MyFleet,MyFleet2 --service MyService,MyService2', '$ balena env add EDITOR vim --device 7cf02a6', '$ balena env add EDITOR vim --device 7cf02a6,d6f1433', '$ balena env add EDITOR vim --device 7cf02a6 --service MyService', @@ -97,8 +101,11 @@ export default class EnvAddCmd extends Command { public static usage = 'env add [value]'; public static flags: flags.Input = { - application: { ...cf.application, exclusive: ['device'] }, - device: { ...cf.device, exclusive: ['application'] }, + ...(isV13() + ? {} + : { application: { ...cf.application, exclusive: ['fleet', 'device'] } }), + fleet: { ...cf.fleet, exclusive: ['application', 'device'] }, + device: { ...cf.device, exclusive: ['application', 'fleet'] }, help: cf.help, quiet: cf.quiet, service: cf.service, @@ -110,9 +117,13 @@ export default class EnvAddCmd extends Command { ); const cmd = this; + if (options.application && process.stderr.isTTY) { + console.error(warnify(appToFleetFlagMsg)); + } + options.application ||= options.fleet; if (!options.application && !options.device) { throw new ExpectedError( - 'Either the --application or the --device option must be specified', + 'Either the --fleet or the --device option must be specified', ); } @@ -161,7 +172,7 @@ export default class EnvAddCmd extends Command { params.value, ); } catch (err) { - console.error(`${err.message}, app: ${app}`); + console.error(`${err.message}, fleet: ${app}`); process.exitCode = 1; } } @@ -183,7 +194,7 @@ export default class EnvAddCmd extends Command { } /** - * Add service variables for a device or application. + * Add service variables for a device or fleet. */ async function setServiceVars( sdk: BalenaSdk.BalenaSDK, @@ -201,7 +212,7 @@ async function setServiceVars( params.value!, ); } catch (err) { - console.error(`${err.message}, application: ${app}`); + console.error(`${err.message}, fleet: ${app}`); process.exitCode = 1; } } @@ -262,7 +273,7 @@ async function getServiceIdForApp( } if (serviceId === undefined) { throw new ExpectedError( - `Cannot find service ${serviceName} for application ${appName}`, + `Cannot find service ${serviceName} for fleet ${appName}`, ); } return serviceId; diff --git a/lib/commands/env/rename.ts b/lib/commands/env/rename.ts index 72006146..78f528e5 100644 --- a/lib/commands/env/rename.ts +++ b/lib/commands/env/rename.ts @@ -38,9 +38,9 @@ interface ArgsDef { export default class EnvRenameCmd extends Command { public static description = stripIndent` - Change the value of a config or env var for an app, device or service. + Change the value of a config or env var for a fleet, device or service. - Change the value of a configuration or environment variable for an application, + Change the value of a configuration or environment variable for a fleet, device or service, as selected by command-line options. ${ec.rmRenameHelp.split('\n').join('\n\t\t')} diff --git a/lib/commands/env/rm.ts b/lib/commands/env/rm.ts index c3ae935c..51aaf1e7 100644 --- a/lib/commands/env/rm.ts +++ b/lib/commands/env/rm.ts @@ -37,9 +37,9 @@ interface ArgsDef { export default class EnvRmCmd extends Command { public static description = stripIndent` - Remove a config or env var from an application, device or service. + Remove a config or env var from a fleet, device or service. - Remove a configuration or environment variable from an application, device + Remove a configuration or environment variable from a fleet, device or service, as selected by command-line options. ${ec.rmRenameHelp.split('\n').join('\n\t\t')} diff --git a/lib/commands/envs.ts b/lib/commands/envs.ts index cb43c073..19709e7e 100644 --- a/lib/commands/envs.ts +++ b/lib/commands/envs.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2016-2019 Balena Ltd. + * Copyright 2016-2021 Balena Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,17 +21,24 @@ import Command from '../command'; import { ExpectedError } from '../errors'; import * as cf from '../utils/common-flags'; import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy'; -import { applicationIdInfo } from '../utils/messages'; +import { + applicationIdInfo, + appToFleetFlagMsg, + appToFleetOutputMsg, + warnify, +} from '../utils/messages'; import { isV13 } from '../utils/version'; interface FlagsDef { application?: string; + fleet?: string; config: boolean; device?: string; // device UUID json: boolean; help: void; service?: string; // service name verbose: boolean; + v13: boolean; } interface EnvironmentVariableInfo extends SDK.EnvironmentVariableBase { @@ -56,20 +63,20 @@ interface ServiceEnvironmentVariableInfo export default class EnvsCmd extends Command { public static description = stripIndent` - List the environment or config variables of an application, device or service. + List the environment or config variables of a fleet, device or service. - List the environment or configuration variables of an application, device or - service, as selected by the respective command-line options. (A service is - an application container in a "microservices" application.) + List the environment or configuration variables of a fleet, device or + service, as selected by the respective command-line options. (A service + corresponds to a Docker image/container in a microservices fleet.) - The results include application-wide (fleet), device-wide (multiple services on - a device) and service-specific variables that apply to the selected application, - device or service. It can be thought of as including "inherited" variables; - for example, a service inherits device-wide variables, and a device inherits - application-wide variables. + The results include fleet-wide (multiple devices), device-specific (multiple + services on a specific device) and service-specific variables that apply to the + selected fleet, device or service. It can be thought of as including inherited + variables; for example, a service inherits device-wide variables, and a device + inherits fleet-wide variables. The printed output may include DEVICE and/or SERVICE columns to distinguish - between application-wide, device-specific and service-specific variables. + between fleet-wide, device-specific and service-specific variables. An asterisk in these columns indicates that the variable applies to "all devices" or "all services". @@ -83,22 +90,22 @@ export default class EnvsCmd extends Command { types like lists and empty strings. The 'jq' utility may be helpful in shell scripts (https://stedolan.github.io/jq/manual/). When --json is used, an empty JSON array ([]) is printed instead of an error message when no variables exist - for the given query. When querying variables for a device, note that the - application name may be null in JSON output (or 'N/A' in tabular output) if the - application linked to the device is no longer accessible by the current user - (for example, in case the current user has been removed from the application - by its owner). + for the given query. When querying variables for a device, note that the fleet + name may be null in JSON output (or 'N/A' in tabular output) if the fleet that + the device belonged to is no longer accessible by the current user (for example, + in case the current user was removed from the fleet by the fleet's owner). ${applicationIdInfo.split('\n').join('\n\t\t')} + + ${appToFleetOutputMsg.split('\n').join('\n\t\t')} `; public static examples = [ - '$ balena envs --application MyApp', - '$ balena envs --application myorg/myapp', - '$ balena envs --application MyApp --json', - '$ balena envs --application MyApp --service MyService', - '$ balena envs --application MyApp --service MyService', - '$ balena envs --application MyApp --config', + '$ balena envs --fleet myorg/myfleet', + '$ balena envs --fleet MyFleet --json', + '$ balena envs --fleet MyFleet --service MyService', + '$ balena envs --fleet MyFleet --service MyService', + '$ balena envs --fleet MyFleet --config', '$ balena envs --device 7cf02a6', '$ balena envs --device 7cf02a6 --json', '$ balena envs --device 7cf02a6 --config --json', @@ -113,34 +120,47 @@ export default class EnvsCmd extends Command { : { all: flags.boolean({ default: false, - description: stripIndent` - No-op since balena CLI v12.0.0.`, + description: 'No-op since balena CLI v12.0.0.', hidden: true, }), + application: { + exclusive: ['device', 'fleet', 'v13'], + ...cf.application, + }, }), - - application: { exclusive: ['device'], ...cf.application }, + fleet: { exclusive: ['device', 'application'], ...cf.fleet }, config: flags.boolean({ default: false, char: 'c', description: 'show configuration variables only', exclusive: ['service'], }), - device: { exclusive: ['application'], ...cf.device }, + device: { exclusive: ['fleet', 'application'], ...cf.device }, help: cf.help, json: cf.json, verbose: cf.verbose, service: { exclusive: ['config'], ...cf.service }, + v13: cf.v13, }; + protected useAppWord = false; + protected hasWarned = false; + public async run() { const { flags: options } = this.parse(EnvsCmd); + this.useAppWord = !options.fleet && !options.v13 && !isV13(); + const variables: EnvironmentVariableInfo[] = []; await Command.checkLoggedIn(); + if (options.application && !options.json && process.stderr.isTTY) { + this.hasWarned = true; + console.error(warnify(appToFleetFlagMsg)); + } + options.application ||= options.fleet; if (!options.application && !options.device) { - throw new ExpectedError('You must specify an application or device'); + throw new ExpectedError('Missing --fleet or --device option'); } const balena = getBalenaSdk(); @@ -174,7 +194,7 @@ export default class EnvsCmd extends Command { const target = (options.service ? `service "${options.service}" of ` : '') + (options.application - ? `application "${options.application}"` + ? `fleet "${options.application}"` : `device "${options.device}"`); throw new ExpectedError(`No environment variables found for ${target}`); } @@ -194,7 +214,9 @@ export default class EnvsCmd extends Command { return i; }); - fields.push(options.json ? 'appName' : 'appName => APPLICATION'); + const jName = this.useAppWord ? 'appName' : 'fleetName'; + const tName = this.useAppWord ? 'APPLICATION' : 'FLEET'; + fields.push(options.json ? `appName => ${jName}` : `appName => ${tName}`); if (options.device) { fields.push(options.json ? 'deviceUUID' : 'deviceUUID => DEVICE'); } @@ -203,10 +225,13 @@ export default class EnvsCmd extends Command { } if (options.json) { - this.log( - stringifyVarArray(varArray, fields), - ); + const { pickAndRename } = await import('../utils/helpers'); + const mapped = varArray.map((o) => pickAndRename(o, fields)); + this.log(JSON.stringify(mapped, null, 4)); } else { + if (!this.hasWarned && this.useAppWord && process.stderr.isTTY) { + console.error(warnify(appToFleetOutputMsg)); + } this.log( getVisuals().table.horizontal( _.sortBy(varArray, (v: SDK.EnvironmentVariableBase) => v.name), @@ -227,7 +252,7 @@ async function validateServiceName( }); if (services.length === 0) { throw new ExpectedError( - `Service "${serviceName}" not found for application "${appName}"`, + `Service "${serviceName}" not found for fleet "${appName}"`, ); } } @@ -354,25 +379,3 @@ function fillInInfoFields( envVar.deviceUUID = deviceUUID || '*'; } } - -/** - * Transform each object (item) of varArray to preserve only the - * fields (keys) listed in the fields argument. - */ -function stringifyVarArray>( - varArray: T[], - fields: string[], -): string { - const transformed = varArray.map((o: Dictionary) => - _.transform( - o, - (result, value, key) => { - if (fields.includes(key)) { - result[key] = value; - } - }, - {} as Dictionary, - ), - ); - return JSON.stringify(transformed, null, 4); -} diff --git a/lib/commands/fleet/create.ts b/lib/commands/fleet/create.ts new file mode 100644 index 00000000..ac652815 --- /dev/null +++ b/lib/commands/fleet/create.ts @@ -0,0 +1,20 @@ +/** + * @license + * Copyright 2021 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 { FleetCreateCmd } from '../app/create'; + +export default FleetCreateCmd; diff --git a/lib/commands/fleet/index.ts b/lib/commands/fleet/index.ts new file mode 100644 index 00000000..7dc1cbe3 --- /dev/null +++ b/lib/commands/fleet/index.ts @@ -0,0 +1,20 @@ +/** + * @license + * Copyright 2021 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 { FleetCmd } from '../app'; + +export default FleetCmd; diff --git a/lib/commands/fleet/purge.ts b/lib/commands/fleet/purge.ts new file mode 100644 index 00000000..5cf78f24 --- /dev/null +++ b/lib/commands/fleet/purge.ts @@ -0,0 +1,20 @@ +/** + * @license + * Copyright 2021 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 { FleetPurgeCmd } from '../app/purge'; + +export default FleetPurgeCmd; diff --git a/lib/commands/fleet/rename.ts b/lib/commands/fleet/rename.ts new file mode 100644 index 00000000..d5d7d2dc --- /dev/null +++ b/lib/commands/fleet/rename.ts @@ -0,0 +1,20 @@ +/** + * @license + * Copyright 2021 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 { FleetRenameCmd } from '../app/rename'; + +export default FleetRenameCmd; diff --git a/lib/commands/fleet/restart.ts b/lib/commands/fleet/restart.ts new file mode 100644 index 00000000..47be4166 --- /dev/null +++ b/lib/commands/fleet/restart.ts @@ -0,0 +1,20 @@ +/** + * @license + * Copyright 2021 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 { FleetRestartCmd } from '../app/restart'; + +export default FleetRestartCmd; diff --git a/lib/commands/fleet/rm.ts b/lib/commands/fleet/rm.ts new file mode 100644 index 00000000..d4e2480c --- /dev/null +++ b/lib/commands/fleet/rm.ts @@ -0,0 +1,20 @@ +/** + * @license + * Copyright 2021 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 { FleetRmCmd } from '../app/rm'; + +export default FleetRmCmd; diff --git a/lib/commands/fleets.ts b/lib/commands/fleets.ts new file mode 100644 index 00000000..2da8afaa --- /dev/null +++ b/lib/commands/fleets.ts @@ -0,0 +1,20 @@ +/** + * @license + * Copyright 2016-2021 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 { FleetsCmd } from './apps'; + +export default FleetsCmd; diff --git a/lib/commands/join.ts b/lib/commands/join.ts index 8d84b692..0af34f0a 100644 --- a/lib/commands/join.ts +++ b/lib/commands/join.ts @@ -21,9 +21,11 @@ import * as cf from '../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../utils/lazy'; import { applicationIdInfo } from '../utils/messages'; import { parseAsLocalHostnameOrIp } from '../utils/validation'; +import { isV13 } from '../utils/version'; interface FlagsDef { application?: string; + fleet?: string; pollInterval?: number; help?: void; } @@ -34,22 +36,22 @@ interface ArgsDef { export default class JoinCmd extends Command { public static description = stripIndent` - Move a local device to an application on another balena server. + Move a local device to a fleet on another balena server. - Move a local device to an application on another balena server, causing + Move a local device to a fleet on another balena server, causing the device to "join" the new server. The device must be running balenaOS. For example, you could provision a device against an openBalena installation where you perform end-to-end tests and then move it to balenaCloud when it's ready for production. - To move a device between applications on the same server, use the + To move a device between fleets on the same server, use the \`balena device move\` command instead of \`balena join\`. If you don't specify a device hostname or IP, this command will automatically scan the local network for balenaOS devices and prompt you to select one from an interactive picker. This may require administrator/root privileges. - Likewise, if the application flag is not provided then a picker will be shown. + Likewise, if the fleet option is not provided then a picker will be shown. ${applicationIdInfo.split('\n').join('\n\t\t')} `; @@ -57,10 +59,10 @@ export default class JoinCmd extends Command { public static examples = [ '$ balena join', '$ balena join balena.local', - '$ balena join balena.local --application MyApp', - '$ balena join balena.local -a myorg/myapp', + '$ balena join balena.local --fleet MyFleet', + '$ balena join balena.local -f myorg/myfleet', '$ balena join 192.168.1.25', - '$ balena join 192.168.1.25 --application MyApp', + '$ balena join 192.168.1.25 --fleet MyFleet', ]; public static args = [ @@ -75,7 +77,8 @@ export default class JoinCmd extends Command { public static usage = 'join [deviceIpOrHostname]'; public static flags: flags.Input = { - application: cf.application, + ...(isV13() ? {} : { application: cf.application }), + fleet: cf.fleet, pollInterval: flags.integer({ description: 'the interval in minutes to check for updates', char: 'i', @@ -98,7 +101,7 @@ export default class JoinCmd extends Command { logger, sdk, params.deviceIpOrHostname, - options.application, + options.application || options.fleet, options.pollInterval, ); } diff --git a/lib/commands/leave.ts b/lib/commands/leave.ts index 119ea676..b2bf3e06 100644 --- a/lib/commands/leave.ts +++ b/lib/commands/leave.ts @@ -31,9 +31,9 @@ interface ArgsDef { export default class LeaveCmd extends Command { public static description = stripIndent` - Remove a local device from its balena application. + Remove a local device from its balena fleet. - Remove a local device from its balena application, causing the device to + Remove a local device from its balena fleet, causing the device to "leave" the server it is provisioned on. This effectively makes the device "unmanaged". The device must be running balenaOS. diff --git a/lib/commands/os/configure.ts b/lib/commands/os/configure.ts index 8c30aba2..7d7aa8b5 100644 --- a/lib/commands/os/configure.ts +++ b/lib/commands/os/configure.ts @@ -23,7 +23,12 @@ import Command from '../../command'; import { ExpectedError } from '../../errors'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy'; -import { applicationIdInfo } from '../../utils/messages'; +import { + applicationIdInfo, + appToFleetFlagMsg, + warnify, +} from '../../utils/messages'; +import { isV13 } from '../../utils/version'; const CONNECTIONS_FOLDER = '/system-connections'; @@ -31,6 +36,7 @@ interface FlagsDef { advanced?: boolean; application?: string; app?: string; + fleet?: string; config?: string; 'config-app-update-poll-interval'?: number; 'config-network'?: string; @@ -66,8 +72,8 @@ export default class OsConfigureCmd extends Command { public static description = stripIndent` Configure a previously downloaded balenaOS image. - Configure a previously downloaded balenaOS image for a specific device type or - balena application. + Configure a previously downloaded balenaOS image for a specific device type + or fleet. Configuration settings such as WiFi authentication will be taken from the following sources, in precedence order: @@ -75,8 +81,8 @@ export default class OsConfigureCmd extends Command { 2. A given \`config.json\` file specified with the \`--config\` option. 3. User input through interactive prompts (text menus). - The --device-type option may be used to override the application's default - device type, in case of an application with mixed device types. + The --device-type option may be used to override the fleet's default device + type, in case of a fleet with mixed device types. The --system-connection (-c) option can be used to inject NetworkManager connection profiles for additional network interfaces, such as cellular/GSM or additional @@ -98,11 +104,10 @@ export default class OsConfigureCmd extends Command { public static examples = [ '$ balena os configure ../path/rpi3.img --device 7cf02a6', '$ balena os configure ../path/rpi3.img --device 7cf02a6 --device-api-key ', - '$ balena os configure ../path/rpi3.img --app MyApp', - '$ balena os configure ../path/rpi3.img -a myorg/myapp', - '$ balena os configure ../path/rpi3.img --app MyApp --version 2.12.7', - '$ balena os configure ../path/rpi3.img --app MyFinApp --device-type raspberrypi3', - '$ balena os configure ../path/rpi3.img --app MyFinApp --device-type raspberrypi3 --config myWifiConfig.json', + '$ balena os configure ../path/rpi3.img --fleet myorg/myfleet', + '$ balena os configure ../path/rpi3.img --fleet MyFleet --version 2.12.7', + '$ balena os configure ../path/rpi3.img -f MyFinFleet --device-type raspberrypi3', + '$ balena os configure ../path/rpi3.img -f MyFinFleet --device-type raspberrypi3 --config myWifiConfig.json', ]; public static args = [ @@ -121,15 +126,29 @@ export default class OsConfigureCmd extends Command { description: 'ask advanced configuration questions (when in interactive mode)', }), - application: { ...cf.application, exclusive: ['app', 'device'] }, - app: { ...cf.app, exclusive: ['application', 'device'] }, + ...(isV13() + ? {} + : { + application: { + ...cf.application, + exclusive: ['app', 'fleet', 'device'], + }, + app: { + ...cf.app, + exclusive: ['application', 'fleet', 'device'], + }, + }), + fleet: { + ...cf.fleet, + exclusive: ['app', 'application', 'device'], + }, config: flags.string({ description: 'path to a pre-generated config.json file to be injected in the OS image', }), 'config-app-update-poll-interval': flags.integer({ description: - 'interval (in minutes) for the on-device balena supervisor periodic app update check', + 'supervisor cloud polling interval in minutes (e.g. for variable updates)', }), 'config-network': flags.string({ description: 'device network type (non-interactive configuration)', @@ -141,7 +160,7 @@ export default class OsConfigureCmd extends Command { 'config-wifi-ssid': flags.string({ description: 'WiFi SSID (network name) (non-interactive configuration)', }), - device: { exclusive: ['app', 'application'], ...cf.device }, + device: { exclusive: ['app', 'application', 'fleet'], ...cf.device }, 'device-api-key': flags.string({ char: 'k', description: @@ -149,7 +168,7 @@ export default class OsConfigureCmd extends Command { }), 'device-type': flags.string({ description: - 'device type slug (e.g. "raspberrypi3") to override the application device type', + 'device type slug (e.g. "raspberrypi3") to override the fleet device type', }), 'initial-device-name': flags.string({ description: @@ -172,9 +191,10 @@ export default class OsConfigureCmd extends Command { const { args: params, flags: options } = this.parse( OsConfigureCmd, ); - // Prefer options.application over options.app - options.application = options.application || options.app; - delete options.app; + if ((options.application || options.app) && process.stderr.isTTY) { + console.error(warnify(appToFleetFlagMsg)); + } + options.application ||= options.app || options.fleet; await validateOptions(options); @@ -302,12 +322,12 @@ async function validateOptions(options: FlagsDef) { // flag definitions above, so oclif will enforce that they are not both used together. if (!options.device && !options.application) { throw new ExpectedError( - "Either the '--device' or the '--application' option must be provided", + "Either the '--device' or the '--fleet' option must be provided", ); } if (!options.application && options['device-type']) { throw new ExpectedError( - "The '--device-type' option can only be used in conjunction with the '--application' option", + "The '--device-type' option can only be used in conjunction with the '--fleet' option", ); } if (options['device-api-key']) { @@ -366,7 +386,7 @@ async function checkDeviceTypeCompatibility( const helpers = await import('../../utils/helpers'); if (!helpers.areDeviceTypesCompatible(appDeviceType, optionDeviceType)) { throw new ExpectedError( - `Device type ${options['device-type']} is incompatible with application ${options.application}`, + `Device type ${options['device-type']} is incompatible with fleet ${options.application}`, ); } } diff --git a/lib/commands/preload.ts b/lib/commands/preload.ts index 6e1e3e08..cf1ec164 100644 --- a/lib/commands/preload.ts +++ b/lib/commands/preload.ts @@ -15,8 +15,8 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; import Command from '../command'; +import { ExpectedError } from '../errors'; import * as cf from '../utils/common-flags'; import { getBalenaSdk, @@ -24,9 +24,17 @@ import { getVisuals, stripIndent, } from '../utils/lazy'; -import { applicationIdInfo } from '../utils/messages'; +import { + applicationIdInfo, + appToFleetFlagMsg, + warnify, +} from '../utils/messages'; import type { DockerConnectionCliFlags } from '../utils/docker'; import { dockerConnectionCliFlags } from '../utils/docker'; +import { parseAsInteger } from '../utils/validation'; +import { isV13 } from '../utils/version'; + +import { flags } from '@oclif/command'; import * as _ from 'lodash'; import type { Application, @@ -36,11 +44,10 @@ import type { Release, } from 'balena-sdk'; import type { Preloader } from 'balena-preload'; -import { parseAsInteger } from '../utils/validation'; -import { ExpectedError } from '../errors'; interface FlagsDef extends DockerConnectionCliFlags { app?: string; + fleet?: string; commit?: string; 'splash-image'?: string; 'dont-check-arch': boolean; @@ -56,16 +63,16 @@ interface ArgsDef { export default class PreloadCmd extends Command { public static description = stripIndent` - Preload an app on a disk image (or Edison zip archive). + Preload a release on a disk image (or Edison zip archive). - Preload a balena application release (app images/containers), and optionally + Preload a release (service images/containers) from a balena fleet, and optionally a balenaOS splash screen, in a previously downloaded '.img' balenaOS image file in the local disk (a zip file is only accepted for the Intel Edison device type). 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 release, as it was preloaded. This is usually combined with release pinning (https://www.balena.io/docs/learn/deploy/release-strategy/release-policy/) - to avoid the device dowloading a newer release straight away, if one is available. + to avoid the device downloading a newer release straight away, if available. Check also the Preloading and Preregistering section of the balena CLI's advanced masterclass document: https://www.balena.io/docs/learn/more/masterclasses/advanced-cli/#5-preloading-and-preregistering @@ -82,8 +89,8 @@ export default class PreloadCmd extends Command { `; public static examples = [ - '$ 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 --fleet MyFleet --commit e1f2592fc6ee949e68756d4f4a48e49bff8d72a0', + '$ balena preload balena.img --fleet myorg/myfleet --splash-image image.png', '$ balena preload balena.img', ]; @@ -98,13 +105,16 @@ export default class PreloadCmd extends Command { public static usage = 'preload '; public static flags: flags.Input = { - // TODO: Replace with application/a in #v13? - app: cf.application, + ...(isV13() ? {} : { app: cf.application }), + fleet: cf.fleet, commit: flags.string({ description: `\ -The commit hash for a specific application release to preload, use "current" to specify the current -release (ignored if no appId is given). The current release is usually also the latest, but can be -manually pinned using https://github.com/balena-io-projects/staged-releases .\ +The commit hash of the release to preload. Use "current" to specify the current +release (ignored if no appId is given). The current release is usually also the +latest, but can be pinned to a specific release. See: +https://www.balena.io/docs/learn/deploy/release-strategy/release-policy/ +https://www.balena.io/docs/learn/more/masterclasses/fleet-management/#63-pin-using-the-api +https://github.com/balena-io-examples/staged-releases\ `, char: 'c', }), @@ -115,7 +125,7 @@ manually pinned using https://github.com/balena-io-projects/staged-releases .\ 'dont-check-arch': flags.boolean({ default: false, description: - 'disables check for matching architecture in image and application', + 'disable architecture compatibility check between image and fleet', }), 'pin-device-to-release': flags.boolean({ default: false, @@ -159,6 +169,11 @@ Can be repeated to add multiple certificates.\ PreloadCmd, ); + if (options.app && process.stderr.isTTY) { + console.error(warnify(appToFleetFlagMsg)); + } + options.app ||= options.fleet; + const balena = getBalenaSdk(); const balenaPreload = await import('balena-preload'); const visuals = getVisuals(); @@ -190,7 +205,7 @@ Can be repeated to add multiple certificates.\ const { getApplication } = await import('../utils/sdk'); const application = await getApplication(balena, options.app); if (!application) { - throw new ExpectedError(`Application not found: ${options.app}`); + throw new ExpectedError(`Fleet not found: ${options.app}`); } options.app = application.slug; } @@ -239,7 +254,7 @@ Can be repeated to add multiple certificates.\ if (dontCheckArch && !appId) { throw new ExpectedError( - 'You need to specify an application if you disable the architecture check.', + 'You need to specify a fleet if you disable the architecture check.', ); } @@ -417,11 +432,11 @@ Can be repeated to add multiple certificates.\ applicationInfoSpinner.stop(); if (applications.length === 0) { throw new ExpectedError( - `You have no apps with successful releases for a '${deviceTypeSlug}' device type.`, + `No fleets found with successful releases for device type '${deviceTypeSlug}'`, ); } return getCliForm().ask({ - message: 'Select an application', + message: 'Select a fleet', type: 'list', choices: applications.map((app) => ({ name: app.app_name, @@ -432,7 +447,7 @@ Can be repeated to add multiple certificates.\ selectApplicationCommit(releases: Release[]) { if (releases.length === 0) { - throw new ExpectedError('This application has no successful releases.'); + throw new ExpectedError('This fleet has no successful releases.'); } const DEFAULT_CHOICE = { name: 'current', value: 'current' }; const choices = [DEFAULT_CHOICE].concat( @@ -465,23 +480,23 @@ Can be repeated to add multiple certificates.\ } const message = `\ -This application is set to track the latest release, and non-pinned devices +This fleet is set to track the latest release, and non-pinned devices are automatically updated when a new release is available. This may lead to unexpected behavior: The preloaded device will download and install the latest release once it is online. -This prompt gives you the opportunity to disable automatic updates for this -application now. Note that this would result in the application being pinned -to the current latest release, rather than some other release that may have +This prompt gives you the opportunity to disable automatic updates for +this fleet now. Note that this would result in the fleet being pinned to +the current latest release, rather than some other release that may have been selected for preloading. The pinned released may be further managed through the web dashboard or programatically through the balena API / SDK. -Documentation about release policies and app/device pinning can be found at: +Documentation about release policies and pinning can be found at: https://www.balena.io/docs/learn/deploy/release-strategy/release-policy/ Alternatively, the --pin-device-to-release flag may be used to pin only the preloaded device to the selected release. -Would you like to disable automatic updates for this application now?\ +Would you like to disable automatic updates for this fleet now?\ `; const update = await getCliForm().ask({ message, @@ -531,7 +546,7 @@ Would you like to disable automatic updates for this application now?\ if (this.isCurrentCommit(options.commit)) { if (!appCommit) { throw new Error( - `Unexpected empty commit hash for app ID "${application.id}"`, + `Unexpected empty commit hash for fleet ID "${application.id}"`, ); } // handle `--commit current` (and its `--commit latest` synonym) diff --git a/lib/commands/push.ts b/lib/commands/push.ts index d6e023ca..ffcf3207 100644 --- a/lib/commands/push.ts +++ b/lib/commands/push.ts @@ -36,7 +36,7 @@ enum BuildTarget { } interface ArgsDef { - applicationOrDevice: string; + fleetOrDevice: string; } interface FlagsDef { @@ -63,9 +63,9 @@ interface FlagsDef { export default class PushCmd extends Command { public static description = stripIndent` - Start a build on the remote balenaCloud build servers, or a local mode device. + Build release images on balenaCloud servers or on a local mode device. - Start a build on the remote balenaCloud build servers, or a local mode device. + Build release images on balenaCloud servers or on a local mode device. When building on the balenaCloud servers, the given source directory will be sent to the remote server. This can be used as a drop-in replacement for the @@ -92,16 +92,16 @@ export default class PushCmd extends Command { ${dockerignoreHelp.split('\n').join('\n\t\t')} - Note: the --service and --env flags must come after the applicationOrDevice + Note: the --service and --env flags must come after the fleetOrDevice parameter, as per examples. `; public static examples = [ - '$ balena push myApp', - '$ balena push myApp --source ', - '$ balena push myApp -s ', - '$ balena push myApp --release-tag key1 "" key2 "value2 with spaces"', - '$ balena push myorg/myapp', + '$ balena push myFleet', + '$ balena push myFleet --source ', + '$ balena push myFleet -s ', + '$ balena push myFleet --release-tag key1 "" key2 "value2 with spaces"', + '$ balena push myorg/myfleet', '', '$ balena push 10.0.0.1', '$ balena push 10.0.0.1 --source ', @@ -115,15 +115,15 @@ export default class PushCmd extends Command { public static args = [ { - name: 'applicationOrDevice', + name: 'fleetOrDevice', description: - 'application name or slug, or local device IP address or hostname', + 'fleet name or slug, or local device IP address or ".local" hostname', required: true, parse: lowercaseIfSlug, }, ]; - public static usage = 'push '; + public static usage = 'push '; public static flags: flags.Input = { source: flags.string({ @@ -188,7 +188,7 @@ export default class PushCmd extends Command { When pushing to the cloud, this option will cause the build to start, then return execution back to the shell, with the status and release ID (if applicable). When pushing to a local mode device, this option will cause - the command to not tail application logs when the build has completed.`, + the command to not tail logs when the build has completed.`, char: 'd', default: false, }), @@ -260,7 +260,7 @@ export default class PushCmd extends Command { }), 'release-tag': flags.string({ description: stripIndent` - Set release tags if the push to a cloud application is successful. Multiple + Set release tags if the image build is successful (balenaCloud only). Multiple arguments may be provided, alternating tag keys and values (see examples). Hint: Empty values may be specified with "" (bash, cmd.exe) or '""' (PowerShell). `, @@ -292,14 +292,12 @@ export default class PushCmd extends Command { }, ); - switch (await this.getBuildTarget(params.applicationOrDevice)) { + switch (await this.getBuildTarget(params.fleetOrDevice)) { case BuildTarget.Cloud: - logger.logDebug( - `Pushing to cloud for application: ${params.applicationOrDevice}`, - ); + logger.logDebug(`Pushing to cloud for fleet: ${params.fleetOrDevice}`); await this.pushToCloud( - params.applicationOrDevice, + params.fleetOrDevice, options, sdk, dockerfilePath, @@ -308,11 +306,9 @@ export default class PushCmd extends Command { break; case BuildTarget.Device: - logger.logDebug( - `Pushing to local device: ${params.applicationOrDevice}`, - ); + logger.logDebug(`Pushing to local device: ${params.fleetOrDevice}`); await this.pushToDevice( - params.applicationOrDevice, + params.fleetOrDevice, options, dockerfilePath, registrySecrets, @@ -402,7 +398,7 @@ export default class PushCmd extends Command { this.checkInvalidOptions( remoteOnlyOptions, options, - 'is only valid when pushing to an application', + 'is only valid when pushing to a fleet', ); const deviceDeploy = await import('../utils/device/deploy'); diff --git a/lib/commands/ssh.ts b/lib/commands/ssh.ts index 23dcd11a..b61e20bb 100644 --- a/lib/commands/ssh.ts +++ b/lib/commands/ssh.ts @@ -31,20 +31,20 @@ interface FlagsDef { } interface ArgsDef { - applicationOrDevice: string; + fleetOrDevice: string; service?: string; } export default class SshCmd extends Command { public static description = stripIndent` - SSH into the host or application container of a device. + Open a SSH prompt on a device's host OS or service container. Start a shell on a local or remote device. If a service name is not provided, a shell will be opened on the host OS. - If an application is provided, an interactive menu will be presented - for the selection of an online device. A shell will then be opened for the - host OS or service container of the chosen device. + If a fleet is provided, an interactive menu will be presented for the selection + of an online device. A shell will then be opened for the host OS or service + container of the chosen device. For local devices, the IP address and .local domain name are supported. If the device is referenced by IP or \`.local\` address, the connection @@ -64,7 +64,7 @@ export default class SshCmd extends Command { `; public static examples = [ - '$ balena ssh MyApp', + '$ balena ssh MyFleet', '$ balena ssh f49cefd', '$ balena ssh f49cefd my-service', '$ balena ssh f49cefd --port ', @@ -76,9 +76,9 @@ export default class SshCmd extends Command { public static args = [ { - name: 'applicationOrDevice', + name: 'fleetOrDevice', description: - 'application name/slug/id, device uuid, or address of local device', + 'fleet name/slug/id, device uuid, or address of local device', required: true, }, { @@ -88,7 +88,7 @@ export default class SshCmd extends Command { }, ]; - public static usage = 'ssh [service]'; + public static usage = 'ssh [service]'; public static flags: flags.Input = { port: flags.integer({ @@ -124,10 +124,10 @@ export default class SshCmd extends Command { ); // Local connection - if (validateLocalHostnameOrIp(params.applicationOrDevice)) { + if (validateLocalHostnameOrIp(params.fleetOrDevice)) { const { performLocalDeviceSSH } = await import('../utils/device/ssh'); return await performLocalDeviceSSH({ - address: params.applicationOrDevice, + address: params.fleetOrDevice, port: options.port, forceTTY: options.tty, verbose: options.verbose, @@ -147,7 +147,7 @@ export default class SshCmd extends Command { await Command.checkLoggedIn(); const deviceUuid = await getOnlineTargetDeviceUuid( sdk, - params.applicationOrDevice, + params.fleetOrDevice, ); const device = await sdk.models.device.get(deviceUuid, { diff --git a/lib/commands/support.ts b/lib/commands/support.ts index 12a7ba7a..2a776376 100644 --- a/lib/commands/support.ts +++ b/lib/commands/support.ts @@ -20,10 +20,16 @@ import Command from '../command'; import { ExpectedError } from '../errors'; import * as cf from '../utils/common-flags'; import { getBalenaSdk, getCliUx, stripIndent } from '../utils/lazy'; -import { applicationIdInfo } from '../utils/messages'; +import { + applicationIdInfo, + appToFleetFlagMsg, + warnify, +} from '../utils/messages'; +import { isV13 } from '../utils/version'; interface FlagsDef { application?: string; + fleet?: string; device?: string; duration?: string; help: void; @@ -35,16 +41,16 @@ interface ArgsDef { export default class SupportCmd extends Command { public static description = stripIndent` - Grant or revoke support access for devices and applications. + Grant or revoke support access for devices or fleets. - Grant or revoke balena support agent access to devices and applications + Grant or revoke balena support agent access to devices or fleets on balenaCloud. (This command does not apply to openBalena.) Access will be automatically revoked once the specified duration has elapsed. Duration defaults to 24h, but can be specified using --duration flag in days or hours, e.g. '12h', '2d'. - Both --device and --application flags accept multiple values, specified as + Both --device and --fleet flags accept multiple values, specified as a comma-separated list (with no spaces). ${applicationIdInfo.split('\n').join('\n\t\t')} @@ -52,8 +58,8 @@ export default class SupportCmd extends Command { public static examples = [ 'balena support enable --device ab346f,cd457a --duration 3d', - 'balena support enable --application app3 --duration 12h', - 'balena support disable -a myorg/myapp', + 'balena support enable --fleet myFleet --duration 12h', + 'balena support disable -f myorg/myfleet', ]; public static args = [ @@ -71,10 +77,11 @@ export default class SupportCmd extends Command { description: 'comma-separated list (no spaces) of device UUIDs', char: 'd', }), - application: { - ...cf.application, + ...(isV13() ? {} : { application: cf.application }), + fleet: { + ...cf.fleet, description: - 'comma-separated list (no spaces) of application names or org/name slugs', + 'comma-separated list (no spaces) of fleet names or org/name slugs', }, duration: flags.string({ description: @@ -91,6 +98,11 @@ export default class SupportCmd extends Command { SupportCmd, ); + if (options.application && process.stderr.isTTY) { + console.error(warnify(appToFleetFlagMsg)); + } + options.application ||= options.fleet; + const balena = getBalenaSdk(); const ux = getCliUx(); @@ -98,9 +110,7 @@ export default class SupportCmd extends Command { // Validation if (!options.device && !options.application) { - throw new ExpectedError( - 'At least one device or application must be specified', - ); + throw new ExpectedError('At least one device or fleet must be specified'); } if (options.duration != null && !enabling) { @@ -135,10 +145,10 @@ export default class SupportCmd extends Command { // Process applications for (const appName of appNames) { if (enabling) { - ux.action.start(`${enablingMessage} application ${appName}`); + ux.action.start(`${enablingMessage} fleet ${appName}`); await balena.models.application.grantSupportAccess(appName, expiryTs); } else if (params.action === 'disable') { - ux.action.start(`${disablingMessage} application ${appName}`); + ux.action.start(`${disablingMessage} fleet ${appName}`); await balena.models.application.revokeSupportAccess(appName); } ux.action.stop(); diff --git a/lib/commands/tag/rm.ts b/lib/commands/tag/rm.ts index e46940b3..b897b8b8 100644 --- a/lib/commands/tag/rm.ts +++ b/lib/commands/tag/rm.ts @@ -19,14 +19,20 @@ import { flags } from '@oclif/command'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { applicationIdInfo } from '../../utils/messages'; +import { + applicationIdInfo, + appToFleetFlagMsg, + warnify, +} from '../../utils/messages'; +import { isV13 } from '../../utils/version'; interface FlagsDef { + app?: string; application?: string; + fleet?: string; device?: string; release?: string; help: void; - app?: string; } interface ArgsDef { @@ -35,16 +41,16 @@ interface ArgsDef { export default class TagRmCmd extends Command { public static description = stripIndent` - Remove a tag from an application, device or release. + Remove a tag from a fleet, device or release. - Remove a tag from an application, device or release. + Remove a tag from a fleet, device or release. ${applicationIdInfo.split('\n').join('\n\t\t')} `; public static examples = [ - '$ balena tag rm myTagKey --application MyApp', - '$ balena tag rm myTagKey -a myorg/myapp', + '$ balena tag rm myTagKey --fleet MyFleet', + '$ balena tag rm myTagKey -f myorg/myfleet', '$ balena tag rm myTagKey --device 7cf02a6', '$ balena tag rm myTagKey --release 1234', '$ balena tag rm myTagKey --release b376b0e544e9429483b656490e5b9443b4349bd6', @@ -61,21 +67,29 @@ export default class TagRmCmd extends Command { public static usage = 'tag rm '; public static flags: flags.Input = { - application: { - ...cf.application, - exclusive: ['app', 'device', 'release'], - }, - app: { - ...cf.app, - exclusive: ['application', 'device', 'release'], + ...(isV13() + ? {} + : { + application: { + ...cf.application, + exclusive: ['app', 'fleet', 'device', 'release'], + }, + app: { + ...cf.app, + exclusive: ['application', 'fleet', 'device', 'release'], + }, + }), + fleet: { + ...cf.fleet, + exclusive: ['app', 'application', 'device', 'release'], }, device: { ...cf.device, - exclusive: ['app', 'application', 'release'], + exclusive: ['app', 'application', 'fleet', 'release'], }, release: { ...cf.release, - exclusive: ['app', 'application', 'device'], + exclusive: ['app', 'application', 'fleet', 'device'], }, help: cf.help, }; @@ -87,9 +101,10 @@ export default class TagRmCmd extends Command { TagRmCmd, ); - // Prefer options.application over options.app - options.application = options.application || options.app; - delete options.app; + if ((options.application || options.app) && process.stderr.isTTY) { + console.error(warnify(appToFleetFlagMsg)); + } + options.application ||= options.app || options.fleet; const balena = getBalenaSdk(); @@ -130,9 +145,9 @@ export default class TagRmCmd extends Command { protected static missingResourceMessage = stripIndent` To remove a resource tag, you must provide exactly one of: - * An application, with --application - * A device, with --device - * A release, with --release + * A fleet, with --fleet + * A device, with --device + * A release, with --release See the help page for examples: diff --git a/lib/commands/tag/set.ts b/lib/commands/tag/set.ts index 8237effb..9e6d4afd 100644 --- a/lib/commands/tag/set.ts +++ b/lib/commands/tag/set.ts @@ -19,14 +19,20 @@ import { flags } from '@oclif/command'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { applicationIdInfo } from '../../utils/messages'; +import { + applicationIdInfo, + appToFleetFlagMsg, + warnify, +} from '../../utils/messages'; +import { isV13 } from '../../utils/version'; interface FlagsDef { + app?: string; application?: string; + fleet?: string; device?: string; release?: string; help: void; - app?: string; } interface ArgsDef { @@ -36,9 +42,9 @@ interface ArgsDef { export default class TagSetCmd extends Command { public static description = stripIndent` - Set a tag on an application, device or release. + Set a tag on a fleet, device or release. - Set a tag on an application, device or release. + Set a tag on a fleet, device or release. 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 @@ -48,9 +54,9 @@ export default class TagSetCmd extends Command { `; public static examples = [ - '$ balena tag set mySimpleTag --application MyApp', - '$ balena tag set mySimpleTag -a myorg/myapp', - '$ balena tag set myCompositeTag myTagValue --application MyApp', + '$ balena tag set mySimpleTag --fleet MyFleet', + '$ balena tag set mySimpleTag -f myorg/myfleet', + '$ balena tag set myCompositeTag myTagValue --fleet MyFleet', '$ balena tag set myCompositeTag myTagValue --device 7cf02a6', '$ balena tag set myCompositeTag "my tag value with whitespaces" --device 7cf02a6', '$ balena tag set myCompositeTag myTagValue --release 1234', @@ -74,21 +80,29 @@ export default class TagSetCmd extends Command { public static usage = 'tag set [value]'; public static flags: flags.Input = { - application: { - ...cf.application, - exclusive: ['app', 'device', 'release'], - }, - app: { - ...cf.app, - exclusive: ['application', 'device', 'release'], + ...(isV13() + ? {} + : { + application: { + ...cf.application, + exclusive: ['app', 'fleet', 'device', 'release'], + }, + app: { + ...cf.app, + exclusive: ['application', 'fleet', 'device', 'release'], + }, + }), + fleet: { + ...cf.fleet, + exclusive: ['app', 'application', 'device', 'release'], }, device: { ...cf.device, - exclusive: ['app', 'application', 'release'], + exclusive: ['app', 'application', 'fleet', 'release'], }, release: { ...cf.release, - exclusive: ['app', 'application', 'device'], + exclusive: ['app', 'application', 'fleet', 'device'], }, help: cf.help, }; @@ -100,9 +114,10 @@ export default class TagSetCmd extends Command { TagSetCmd, ); - // Prefer options.application over options.app - options.application = options.application || options.app; - delete options.app; + if ((options.application || options.app) && process.stderr.isTTY) { + console.error(warnify(appToFleetFlagMsg)); + } + options.application ||= options.app || options.fleet; const balena = getBalenaSdk(); @@ -151,9 +166,9 @@ export default class TagSetCmd extends Command { protected static missingResourceMessage = stripIndent` To set a resource tag, you must provide exactly one of: - * An application, with --application - * A device, with --device - * A release, with --release + * A fleet, with --fleet + * A device, with --device + * A release, with --release See the help page for examples: diff --git a/lib/commands/tags.ts b/lib/commands/tags.ts index b3a383fc..ea9365b5 100644 --- a/lib/commands/tags.ts +++ b/lib/commands/tags.ts @@ -20,29 +20,34 @@ import Command from '../command'; import { ExpectedError } from '../errors'; import * as cf from '../utils/common-flags'; import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy'; -import { applicationIdInfo } from '../utils/messages'; +import { + applicationIdInfo, + appToFleetFlagMsg, + warnify, +} from '../utils/messages'; +import { isV13 } from '../utils/version'; interface FlagsDef { + app?: string; application?: string; + fleet?: string; device?: string; release?: string; help: void; - app?: string; } export default class TagsCmd extends Command { public static description = stripIndent` - List all tags for an application, device or release. + List all tags for a fleet, device or release. - List all tags and their values for a particular application, - device or release. + List all tags and their values for the specified fleet, device or release. ${applicationIdInfo.split('\n').join('\n\t\t')} `; public static examples = [ - '$ balena tags --application MyApp', - '$ balena tags -a myorg/myapp', + '$ balena tags --fleet MyFleet', + '$ balena tags -f myorg/myfleet', '$ balena tags --device 7cf02a6', '$ balena tags --release 1234', '$ balena tags --release b376b0e544e9429483b656490e5b9443b4349bd6', @@ -51,21 +56,29 @@ export default class TagsCmd extends Command { public static usage = 'tags'; public static flags: flags.Input = { - application: { - ...cf.application, - exclusive: ['app', 'device', 'release'], - }, - app: { - ...cf.app, - exclusive: ['application', 'device', 'release'], + ...(isV13() + ? {} + : { + application: { + ...cf.application, + exclusive: ['app', 'fleet', 'device', 'release'], + }, + app: { + ...cf.app, + exclusive: ['application', 'fleet', 'device', 'release'], + }, + }), + fleet: { + ...cf.fleet, + exclusive: ['app', 'application', 'device', 'release'], }, device: { ...cf.device, - exclusive: ['app', 'application', 'release'], + exclusive: ['app', 'application', 'fleet', 'release'], }, release: { ...cf.release, - exclusive: ['app', 'application', 'device'], + exclusive: ['app', 'application', 'fleet', 'device'], }, help: cf.help, }; @@ -75,9 +88,10 @@ export default class TagsCmd extends Command { public async run() { const { flags: options } = this.parse(TagsCmd); - // Prefer options.application over options.app - options.application = options.application || options.app; - delete options.app; + if ((options.application || options.app) && process.stderr.isTTY) { + console.error(warnify(appToFleetFlagMsg)); + } + options.application ||= options.app || options.fleet; const balena = getBalenaSdk(); @@ -123,7 +137,7 @@ export default class TagsCmd extends Command { protected missingResourceMessage = stripIndent` To list tags for a resource, you must provide exactly one of: - * An application, with --application + * A fleet, with --fleet * A device, with --device * A release, with --release diff --git a/lib/commands/tunnel.ts b/lib/commands/tunnel.ts index a7f209ec..4bfb0a67 100644 --- a/lib/commands/tunnel.ts +++ b/lib/commands/tunnel.ts @@ -24,6 +24,8 @@ import { } from '../errors'; import * as cf from '../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../utils/lazy'; +import { lowercaseIfSlug } from '../utils/normalization'; + import type { Server, Socket } from 'net'; interface FlagsDef { @@ -32,7 +34,7 @@ interface FlagsDef { } interface ArgsDef { - deviceOrApplication: string; + deviceOrFleet: string; } export default class TunnelCmd extends Command { @@ -62,7 +64,7 @@ export default class TunnelCmd extends Command { public static examples = [ '# map remote port 22222 to localhost:22222', - '$ balena tunnel myApp -p 22222', + '$ balena tunnel myFleet -p 22222', '', '# map remote port 22222 to localhost:222', '$ balena tunnel 2ead211 -p 22222:222', @@ -71,21 +73,22 @@ export default class TunnelCmd extends Command { '$ balena tunnel 1546690 -p 22222:0.0.0.0', '', '# map remote port 22222 to any address on your host machine, port 222', - '$ balena tunnel myApp -p 22222:0.0.0.0:222', + '$ balena tunnel myFleet -p 22222:0.0.0.0:222', '', '# multiple port tunnels can be specified at any one time', - '$ balena tunnel myApp -p 8080:3000 -p 8081:9000', + '$ balena tunnel myFleet -p 8080:3000 -p 8081:9000', ]; public static args = [ { - name: 'deviceOrApplication', - description: 'device uuid or application name/slug/id', + name: 'deviceOrFleet', + description: 'device UUID or fleet name/slug/ID', required: true, + parse: lowercaseIfSlug, }, ]; - public static usage = 'tunnel '; + public static usage = 'tunnel '; public static flags: flags.Input = { port: flags.string({ @@ -132,10 +135,7 @@ export default class TunnelCmd extends Command { // Ascertain device uuid const { getOnlineTargetDeviceUuid } = await import('../utils/patterns'); - const uuid = await getOnlineTargetDeviceUuid( - sdk, - params.deviceOrApplication, - ); + const uuid = await getOnlineTargetDeviceUuid(sdk, params.deviceOrFleet); const device = await sdk.models.device.get(uuid); logger.logInfo(`Opening a tunnel to ${device.uuid}...`); diff --git a/lib/events.ts b/lib/events.ts index 31e2f655..308e7398 100644 --- a/lib/events.ts +++ b/lib/events.ts @@ -37,9 +37,9 @@ interface CachedUsername { * Mixpanel.com analytics tracking (information on balena CLI usage). * * @param commandSignature A string like, for example: - * "push " - * That's literally so: "applicationOrDevice" is NOT replaced with the actual - * application ID or device ID. The purpose is to find out the most / least + * "push " + * That's literally so: "fleetOrDevice" is NOT replaced with the actual + * fleet ID or device ID. The purpose is to find out the most / least * used command verbs, so we can focus our development effort where it is most * beneficial to end users. * diff --git a/lib/help.ts b/lib/help.ts index 099b6463..3f6139e0 100644 --- a/lib/help.ts +++ b/lib/help.ts @@ -182,8 +182,8 @@ export default class BalenaHelp extends Help { 'push', 'logs', 'ssh', - 'apps', - 'app', + 'fleets', + 'fleet', 'devices', 'device', 'tunnel', diff --git a/lib/utils/cloud.ts b/lib/utils/cloud.ts index 30205825..8d6a5c4d 100644 --- a/lib/utils/cloud.ts +++ b/lib/utils/cloud.ts @@ -64,8 +64,8 @@ export const getDeviceAndAppFromUUID = _.memoize( ); if (app == null) { throw new ExpectedError(stripIndent` - Unable to access the application that device ${deviceUUID} belongs to. - Hint: check whether the application owner might have withdrawn access to it. + Unable to access the fleet that device ${deviceUUID} belongs to. + Hint: check whether the fleet owner withdrew access to it. `); } return [device, app]; diff --git a/lib/utils/common-args.ts b/lib/utils/common-args.ts index e7309b2f..002ade91 100644 --- a/lib/utils/common-args.ts +++ b/lib/utils/common-args.ts @@ -16,9 +16,9 @@ */ import { lowercaseIfSlug } from './normalization'; -export const applicationRequired = { - name: 'application', - description: 'application name, slug (preferred), or numeric ID (deprecated)', +export const fleetRequired = { + name: 'fleet', + description: 'fleet name, slug (preferred), or numeric ID (deprecated)', required: true, parse: lowercaseIfSlug, }; diff --git a/lib/utils/common-flags.ts b/lib/utils/common-flags.ts index eeb7d08e..81f053fc 100644 --- a/lib/utils/common-flags.ts +++ b/lib/utils/common-flags.ts @@ -20,15 +20,33 @@ import { flags } from '@oclif/command'; import type { IBooleanFlag } from '@oclif/parser/lib/flags'; import { stripIndent } from './lazy'; import { lowercaseIfSlug } from './normalization'; +import { isV13 } from './version'; + +export const v13: IBooleanFlag = flags.boolean({ + description: stripIndent`\ + enable selected balena CLI v13 pre-release features, like the renaming + from "application" to "fleet" in command output`, + default: false, +}); export const application = flags.string({ char: 'a', - description: 'application name, slug (preferred), or numeric ID (deprecated)', + description: 'DEPRECATED alias for -f, --fleet', parse: lowercaseIfSlug, }); // TODO: Consider remove second alias 'app' when we can, to simplify. export const app = flags.string({ - description: "same as '--application'", + description: 'DEPRECATED alias for -f, --fleet', + parse: lowercaseIfSlug, +}); +export const fleet = flags.string({ + char: 'f', + description: isV13() + ? 'fleet name, slug (preferred), or numeric ID (deprecated)' + : // avoid the '(deprecated)' remark in v12 while cf.application and + // cf.app are also described as deprecated, to avoid the impression + // that cf.fleet is deprecated as well. + 'fleet name, slug (preferred), or numeric ID', parse: lowercaseIfSlug, }); diff --git a/lib/utils/compose_ts.ts b/lib/utils/compose_ts.ts index eba51143..1f5e137b 100644 --- a/lib/utils/compose_ts.ts +++ b/lib/utils/compose_ts.ts @@ -824,16 +824,16 @@ function printDockerignoreWarn( ); if (multiDockerignore) { msg.push(stripIndent` - When --multi-dockerignore (-m) is used, only .dockerignore files at the root of - each service's build context (in a microservices/multicontainer application), - plus a .dockerignore file at the overall project root, are used. + When --multi-dockerignore (-m) is used, only .dockerignore files at the + root of each service's build context (in a microservices/multicontainer + fleet), plus a .dockerignore file at the overall project root, are used. See "balena help ${Logger.command}" for more details.`); } else { msg.push(stripIndent` - By default, only one .dockerignore file at the source folder (project root) - is used. Microservices (multicontainer) applications may use a separate - .dockerignore file for each service with the --multi-dockerignore (-m) option. - See "balena help ${Logger.command}" for more details.`); + By default, only one .dockerignore file at the source folder (project + root) is used. Microservices (multicontainer) fleets may use a separate + .dockerignore file for each service with the --multi-dockerignore (-m) + option. See "balena help ${Logger.command}" for more details.`); } } // No unused .dockerignore files. Print info-level advice in some cases. @@ -853,7 +853,7 @@ function printDockerignoreWarn( msg.push( stripIndent` The --multi-dockerignore (-m) option was specified, but it has no effect for - single-container (non-microservices) apps. Only one .dockerignore file at the + single-container (non-microservices) fleets. Only one .dockerignore file at the project source (root) directory, if any, is used. See "balena help ${Logger.command}".`, ); } diff --git a/lib/utils/env-common.ts b/lib/utils/env-common.ts index 0fbea364..142e1fa0 100644 --- a/lib/utils/env-common.ts +++ b/lib/utils/env-common.ts @@ -32,8 +32,7 @@ export const booleanConfig: IBooleanFlag = flags.boolean({ export const booleanDevice: IBooleanFlag = flags.boolean({ char: 'd', - description: - 'select a device-specific variable instead of an application (fleet) variable', + description: 'select a device-specific variable instead of a fleet variable', default: false, }); @@ -49,23 +48,23 @@ export const rmRenameHelp = stripIndent` Variables are selected by their database ID (as reported by the 'balena envs' command) and one of six database "resource types": - - application (fleet) environment variable - - application (fleet) configuration variable (--config) - - application (fleet) service variable (--service) + - fleet environment variable + - fleet configuration variable (--config) + - fleet service variable (--service) - device environment variable (--device) - device configuration variable (--device --config) - device service variable (--device --service) - The --device option selects a device-specific variable instead of an application - (fleet) variable. + The --device option selects a device-specific variable instead of a fleet + variable. The --config option selects a configuration variable. Configuration variable names typically start with the 'BALENA_' or 'RESIN_' prefixes and are used to configure balena platform features. The --service option selects a service variable, which is an environment variable - that applies to a specifc service (application container) in a microservices - (multicontainer) application. + that applies to a specifc service (container) in a microservices (multicontainer) + fleet. The --service and --config options cannot be used together, but they can be used alongside the --device option to select a device-specific service or diff --git a/lib/utils/helpers.ts b/lib/utils/helpers.ts index 9ca11d96..1e890b6c 100644 --- a/lib/utils/helpers.ts +++ b/lib/utils/helpers.ts @@ -160,7 +160,7 @@ export function getAppWithArch( }); if (!config) { - throw new Error('Could not read application information!'); + throw new Error(`balena API request failed for fleet ${applicationName}`); } return { ...app, arch: config.arch }; @@ -530,3 +530,31 @@ export async function awaitInterruptibleTask< process.removeListener('SIGINT', sigintHandler); } } + +/** + * Pick object fields like lodash _.pick(), but also interpret "renaming + * specifications" for object keys such as "appName => fleetName" as used + * by the 'resin-cli-visuals' package. + * + * Sample input: ({ 'a': 1, 'b': 2, 'c': 3 }, ['b => x', 'c', 'd']) + * Sample output: { 'x': 2, 'c': 3 } + */ +export function pickAndRename>( + obj: T, + fields: string[], +): Dictionary { + const rename: Dictionary = {}; + // map 'a => b' to 'a' and setup rename['a'] = 'b' + fields = fields.map((f) => { + let renameFrom = f; + let renameTo = f; + const match = f.match(/(?\S+)\s+=>\s+(?\S+)/); + if (match && match.groups) { + renameFrom = match.groups.from; + renameTo = match.groups.to; + } + rename[renameFrom] = renameTo; + return renameFrom; + }); + return _.mapKeys(_.pick(obj, fields), (_val, key) => rename[key]); +} diff --git a/lib/utils/messages.ts b/lib/utils/messages.ts index 4ba124ce..3e5454b5 100644 --- a/lib/utils/messages.ts +++ b/lib/utils/messages.ts @@ -30,6 +30,24 @@ export const help = reachingOut; // is parsed, so its evaluation cannot happen at module loading time. export const getHelp = () => (process.env.DEBUG ? '' : debugHint) + help; +/** + * Take a multiline string like: + * Line One + * Line Two + * and return a string like: + * --------------- + * [Warn] Line One + * [Warn] Line Two + * --------------- + * where the length of the dash rows matches the length of the longest line. + */ +export function warnify(msg: string) { + const lines = msg.split('\n').map((l) => `[Warn] ${l}`); + const maxLength = Math.max(...lines.map((l) => l.length)); + const hr = '-'.repeat(maxLength); + return [hr, ...lines, hr].join('\n'); +} + export const balenaAsciiArt = `\ _ _ | |__ __ _ | | ____ _ __ __ _ @@ -66,12 +84,12 @@ export const dockerignoreHelp = `By default, the balena CLI will use a single ".dockerignore" file (if any) at the project root (--source directory) in order to decide which source files to exclude from the "build context" (tar stream) sent to balenaCloud, Docker -daemon or balenaEngine. In a microservices (multicontainer) application, the +daemon or balenaEngine. In a microservices (multicontainer) fleet, the source directory is the directory that contains the "docker-compose.yml" file. The --multi-dockerignore (-m) option may be used with microservices -(multicontainer) applications that define a docker-compose.yml file. When -this option is used, each service subdirectory (defined by the \`build\` or +(multicontainer) fleets that define a docker-compose.yml file. When this +option is used, each service subdirectory (defined by the \`build\` or \`build.context\` service properties in the docker-compose.yml file) is filtered separately according to a .dockerignore file defined in the service subdirectory. If no .dockerignore file exists in a service subdirectory, then @@ -115,18 +133,17 @@ adding counter patterns to the applicable .dockerignore file(s), for example - 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.`; +Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are +the recommended option, as they are unique and unambiguous. Slugs can be +listed with the \`balena fleets\` command. Note that slugs may change if the +fleet is renamed. Fleet names are not unique and may result in "Fleet 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 fleet, or with fleets +from other balena accounts that you may be invited to join under any role. +For this reason, fleet names are especially discouraged in scripts (e.g. CI +environments). Numeric fleet 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, @@ -143,3 +160,27 @@ https://www.balena.io/docs/learn/deploy/deployment/#build-time-secrets-and-varia If you have a particular use for buildArg, which is not satisfied by build-time secrets, please contact us via support or the forums: https://forums.balena.io/ \n`; + +// Note: if you edit this message, check that the regex replace +// logic in lib/commands/apps.ts still works. +export const appToFleetCmdMsg = `\ +Renaming notice: The 'app' command was renamed to 'fleet', and 'app' +is now an alias. THE ALIAS WILL BE REMOVED in the next major version +of the balena CLI (so that a different 'app' command can be implemented +in the future). Use 'fleet' instead of 'app' to avoid this warning. +Find out more at: https://git.io/JRuZr`; + +export const appToFleetFlagMsg = `\ +Renaming notice: The '-a', '--app' or '--application' options are now +aliases for the '-f' or '--fleet' options. THE ALIASES WILL BE REMOVED +in the next major version of the balena CLI (so that a different '--app' +option can be implemented in the future). Use '-f' or '--fleet' instead. +Find out more at: https://git.io/JRuZr`; + +export const appToFleetOutputMsg = `\ +Renaming notice: The 'app' or 'application' words in table headers +or in JSON object keys/properties will be replaced with 'fleet' in +the next major version of the CLI (v13). The --v13 option may be used +to enable the new names already now, and suppress a warning message. +(The --v13 option will be silently ignored in CLI v13.) +Find out more at: https://git.io/JRuZr`; diff --git a/lib/utils/patterns.ts b/lib/utils/patterns.ts index 47860261..263fe895 100644 --- a/lib/utils/patterns.ts +++ b/lib/utils/patterns.ts @@ -184,7 +184,7 @@ export function selectApplication( .hasAny() .then(async (hasAnyApplications) => { if (!hasAnyApplications) { - throw new ExpectedError("You don't have any applications"); + throw new ExpectedError('No fleets found'); } const apps = (await balena.models.application.getAll({ @@ -198,7 +198,7 @@ export function selectApplication( }) .then((applications) => { if (errorOnEmptySelection && applications.length === 0) { - throw new ExpectedError('No suitable applications found for selection'); + throw new ExpectedError('No suitable fleets found for selection'); } return getCliForm().ask({ message: 'Select an application', @@ -378,15 +378,13 @@ export async function getOnlineTargetDeviceUuid( // Not a device UUID, try app let app: Application; try { - logger.logDebug( - `Trying to fetch application by name/slug/ID: ${applicationOrDevice}`, - ); + logger.logDebug(`Fetching fleet ${applicationOrDevice}`); app = await getApplication(sdk, applicationOrDevice); } catch (err) { const { BalenaApplicationNotFound } = await import('balena-errors'); if (instanceOf(err, BalenaApplicationNotFound)) { throw new ExpectedError( - `Application or Device not found: ${applicationOrDevice}`, + `Fleet or Device not found: ${applicationOrDevice}`, ); } else { throw err; @@ -402,13 +400,13 @@ export async function getOnlineTargetDeviceUuid( // Throw if no devices online if (_.isEmpty(devices)) { throw new ExpectedError( - `Application ${app.slug} found, but has no devices online.`, + `Fleet ${app.slug} found, but has no devices online.`, ); } // Ask user to select from online devices for application return getCliForm().ask({ - message: `Select a device on application ${app.slug}`, + message: `Select a device on fleet ${app.slug}`, type: 'list', default: devices[0].uuid, choices: _.map(devices, (device) => ({ diff --git a/lib/utils/promote.ts b/lib/utils/promote.ts index 1f8ff662..1b681381 100644 --- a/lib/utils/promote.ts +++ b/lib/utils/promote.ts @@ -39,10 +39,10 @@ export async function join( const deviceType = await getDeviceType(deviceHostnameOrIp); logger.logDebug(`Device type: ${deviceType}`); - logger.logDebug('Determining application...'); + logger.logDebug('Determining fleet...'); const app = await getOrSelectApplication(sdk, deviceType, appName); logger.logDebug( - `Using application: ${app.app_name} (${app.is_for__device_type[0].slug})`, + `Using fleet: ${app.app_name} (${app.is_for__device_type[0].slug})`, ); if (app.is_for__device_type[0].slug !== deviceType) { logger.logDebug(`Forcing device type to: ${deviceType}`); @@ -53,7 +53,7 @@ export async function join( const deviceOsVersion = await getOsVersion(deviceHostnameOrIp); logger.logDebug(`Device OS version: ${deviceOsVersion}`); - logger.logDebug('Generating application config...'); + logger.logDebug('Generating fleet config...'); const config = await generateApplicationConfig(sdk, app, { version: deviceOsVersion, appUpdatePollInterval, @@ -175,10 +175,10 @@ async function selectAppFromList( const _ = await import('lodash'); const { selectFromList } = await import('../utils/patterns'); - // Present a list to the user which shows the fully qualified application - // name (user/appname) and allows them to select. + // Present a list to the user which shows the fully qualified fleet + // name (user/fleetname) and allows them to select. return selectFromList( - 'Select application', + 'Select fleet', _.map(applications, (app) => { return { name: app.slug, ...app }; }), @@ -220,11 +220,11 @@ async function getOrSelectApplication( }, }; - // Check for an app of the form `user/application` and update the API query. + // Check for a fleet slug of the form `user/fleet` and update the API query. let name: string; const match = appName.split('/'); if (match.length > 1) { - // These will match at most one app + // These will match at most one fleet options.$filter = { slug: appName.toLowerCase() }; name = match[1]; } else { @@ -241,7 +241,7 @@ async function getOrSelectApplication( if (applications.length === 0) { const shouldCreateApp = await getCliForm().ask({ message: - `No application found with name "${appName}".\n` + + `No fleet found with name "${appName}".\n` + 'Would you like to create it now?', type: 'confirm', default: true, @@ -252,14 +252,14 @@ async function getOrSelectApplication( process.exit(1); } - // We've found at least one app with the given name. - // Filter out apps for non-matching device types and see what we're left with. + // We've found at least one fleet with the given name. + // Filter out fleets for non-matching device types and see what we're left with. const validApplications = applications.filter((app) => _.includes(compatibleDeviceTypes, app.is_for__device_type[0].slug), ); if (validApplications.length === 0) { - throw new ExpectedError('No application found with a matching device type'); + throw new ExpectedError('No fleet found with a matching device type'); } if (validApplications.length === 1) { @@ -277,7 +277,7 @@ async function createOrSelectAppOrExit( compatibleDeviceTypes: string[], deviceType: string, ): Promise { - // No application specified, show a list to select one. + // No fleet specified, show a list to select one. const applications = (await sdk.models.application.getAll({ $expand: { is_for__device_type: { $select: 'slug' } }, $filter: { @@ -293,7 +293,7 @@ async function createOrSelectAppOrExit( if (applications.length === 0) { const shouldCreateApp = await getCliForm().ask({ message: - 'You have no applications this device can join.\n' + + 'You have no fleets this device can join.\n' + 'Would you like to create one now?', type: 'confirm', default: true, @@ -323,7 +323,7 @@ async function createApplication( while (true) { try { const appName = await getCliForm().ask({ - message: 'Enter a name for your new application:', + message: 'Enter a name for your new fleet:', type: 'input', default: name, validate: validation.validateApplicationName, @@ -341,7 +341,7 @@ async function createApplication( // TODO: This is the only example in the codebase where `printErrorMessage()` // is called directly. Consider refactoring. printErrorMessage( - 'You already have an application with that name; please choose another.', + 'You already have a fleet with that name; please choose another.', ); continue; } catch (err) { diff --git a/lib/utils/validation.ts b/lib/utils/validation.ts index 937cbf0c..54ecbfa8 100644 --- a/lib/utils/validation.ts +++ b/lib/utils/validation.ts @@ -43,7 +43,7 @@ export function validatePassword(input: string) { export function validateApplicationName(input: string) { if (input.length < 4) { - return 'The application name should be at least 4 characters'; + return 'The fleet name should be at least 4 characters long'; } return APPNAME_REGEX.test(input); diff --git a/tests/commands/app/create.spec.ts b/tests/commands/app/create.spec.ts index 7ed25dc3..d4080827 100644 --- a/tests/commands/app/create.spec.ts +++ b/tests/commands/app/create.spec.ts @@ -19,27 +19,7 @@ import { expect } from 'chai'; import { BalenaAPIMock } from '../../balena-api-mock'; import { cleanOutput, runCommand } from '../../helpers'; -const HELP_MESSAGE = ` -Usage: app create - -Use this command to create a new balena application. - -You can specify the application device type with the \`--type\` option. -Otherwise, an interactive dropdown will be shown for you to select from. - -You can see a list of supported device types with - -\t$ balena devices supported - -Examples: - -\t$ balena app create MyApp -\t$ balena app create MyApp --type raspberry-pi - -Options: - - --type, -t application device type (Check available types with \`balena devices supported\`) -`; +const HELP_MESSAGE = ''; describe('balena app create', function () { let api: BalenaAPIMock; diff --git a/tests/commands/build.spec.ts b/tests/commands/build.spec.ts index 999ed413..a22f306d 100644 --- a/tests/commands/build.spec.ts +++ b/tests/commands/build.spec.ts @@ -29,6 +29,8 @@ import { cleanOutput, runCommand } from '../helpers'; import { ExpectedTarStreamFiles, ExpectedTarStreamFilesByService, + getDockerignoreWarn1, + getDockerignoreWarn2, } from '../projects'; const repoPath = path.normalize(path.join(__dirname, '..', '..')); @@ -230,16 +232,10 @@ describe('balena build', function () { `[Info] Creating default composition with source: "${projectPath}"`, '[Info] Building for rpi/raspberry-pi', '[Info] Emulation is enabled', - ...[ - `[Warn] ${hr}`, - '[Warn] The following .dockerignore file(s) will not be used:', - `[Warn] * ${path.join(projectPath, 'src', '.dockerignore')}`, - '[Warn] By default, only one .dockerignore file at the source folder (project root)', - '[Warn] is used. Microservices (multicontainer) applications may use a separate', - '[Warn] .dockerignore file for each service with the --multi-dockerignore (-m) option.', - '[Warn] See "balena help build" for more details.', - `[Warn] ${hr}`, - ], + ...getDockerignoreWarn1( + [path.join(projectPath, 'src', '.dockerignore')], + 'build', + ), '[Build] main Step 1/4 : FROM busybox', '[Success] Build succeeded!', ]; @@ -315,16 +311,10 @@ describe('balena build', function () { ...commonResponseLines[responseFilename], `[Info] No "docker-compose.yml" file found at "${projectPath}"`, `[Info] Creating default composition with source: "${projectPath}"`, - ...[ - `[Warn] ${hr}`, - '[Warn] The following .dockerignore file(s) will not be used:', - `[Warn] * ${path.join(projectPath, 'src', '.dockerignore')}`, - '[Warn] When --multi-dockerignore (-m) is used, only .dockerignore files at the root of', - "[Warn] each service's build context (in a microservices/multicontainer application),", - '[Warn] plus a .dockerignore file at the overall project root, are used.', - '[Warn] See "balena help build" for more details.', - `[Warn] ${hr}`, - ], + ...getDockerignoreWarn2( + [path.join(projectPath, 'src', '.dockerignore')], + 'build', + ), '[Build] main Step 1/4 : FROM busybox', ]; if (isWindows) { @@ -410,16 +400,10 @@ describe('balena build', function () { '[Build] service1 Step 1/4 : FROM busybox', '[Build] service2 Step 1/4 : FROM busybox', ], - ...[ - `[Warn] ${hr}`, - '[Warn] The following .dockerignore file(s) will not be used:', - `[Warn] * ${path.join(projectPath, 'service2', '.dockerignore')}`, - '[Warn] By default, only one .dockerignore file at the source folder (project root)', - '[Warn] is used. Microservices (multicontainer) applications may use a separate', - '[Warn] .dockerignore file for each service with the --multi-dockerignore (-m) option.', - '[Warn] See "balena help build" for more details.', - `[Warn] ${hr}`, - ], + ...getDockerignoreWarn1( + [path.join(projectPath, 'service2', '.dockerignore')], + 'build', + ), ]; if (isWindows) { expectedResponseLines.push( diff --git a/tests/commands/deploy.spec.ts b/tests/commands/deploy.spec.ts index 7a50b717..45206d5c 100644 --- a/tests/commands/deploy.spec.ts +++ b/tests/commands/deploy.spec.ts @@ -28,6 +28,7 @@ import { cleanOutput, runCommand, switchSentry } from '../helpers'; import { ExpectedTarStreamFiles, ExpectedTarStreamFilesByService, + getDockerignoreWarn1, } from '../projects'; const repoPath = path.normalize(path.join(__dirname, '..', '..')); @@ -124,16 +125,10 @@ describe('balena deploy', function () { ...commonResponseLines[responseFilename], `[Info] No "docker-compose.yml" file found at "${projectPath}"`, `[Info] Creating default composition with source: "${projectPath}"`, - ...[ - `[Warn] ${hr}`, - '[Warn] The following .dockerignore file(s) will not be used:', - `[Warn] * ${path.join(projectPath, 'src', '.dockerignore')}`, - '[Warn] By default, only one .dockerignore file at the source folder (project root)', - '[Warn] is used. Microservices (multicontainer) applications may use a separate', - '[Warn] .dockerignore file for each service with the --multi-dockerignore (-m) option.', - '[Warn] See "balena help deploy" for more details.', - `[Warn] ${hr}`, - ], + ...getDockerignoreWarn1( + [path.join(projectPath, 'src', '.dockerignore')], + 'deploy', + ), ]; if (isWindows) { const fname = path.join(projectPath, 'src', 'windows-crlf.sh'); diff --git a/tests/commands/device/device.spec.ts b/tests/commands/device/device.spec.ts index 5599467c..ac071299 100644 --- a/tests/commands/device/device.spec.ts +++ b/tests/commands/device/device.spec.ts @@ -21,24 +21,8 @@ import * as path from 'path'; import { apiResponsePath, BalenaAPIMock } from '../../balena-api-mock'; import { cleanOutput, runCommand } from '../../helpers'; -const HELP_RESPONSE = ` -Show info about a single device. - -USAGE - $ balena device - -ARGUMENTS - the device uuid - -OPTIONS - -h, --help show CLI help - -DESCRIPTION - Show information about a single device. - -EXAMPLE - $ balena device 7cf02a6 -`; +import { appToFleetOutputMsg, warnify } from '../../../build/utils/messages'; +import { isV13 } from '../../../build/utils/version'; describe('balena device', function () { let api: BalenaAPIMock; @@ -54,13 +38,12 @@ describe('balena device', function () { api.done(); }); - it('should print help text with the -h flag', async () => { - const { out, err } = await runCommand('device -h'); - - expect(cleanOutput(out)).to.deep.equal(cleanOutput([HELP_RESPONSE])); - - expect(err).to.eql([]); - }); + const expectedWarn = + !isV13() && + process.stderr.isTTY && + process.env.BALENA_CLI_TEST_TYPE !== 'standalone' + ? warnify(appToFleetOutputMsg) + '\n' + : ''; it('should error if uuid not provided', async () => { const { out, err } = await runCommand('device'); @@ -88,7 +71,7 @@ describe('balena device', function () { expect(lines[0]).to.equal('== SPARKLING WOOD'); expect(lines[6].split(':')[1].trim()).to.equal('test app'); - expect(err).to.eql([]); + expect(err.join('')).to.eql(expectedWarn); }); it.skip('correctly handles devices with missing fields', async () => { @@ -112,7 +95,7 @@ describe('balena device', function () { expect(lines[0]).to.equal('== SPARKLING WOOD'); expect(lines[6].split(':')[1].trim()).to.equal('test app'); - expect(err).to.eql([]); + expect(err.join('')).to.eql(expectedWarn); }); it('correctly handles devices with missing application', async () => { @@ -138,6 +121,6 @@ describe('balena device', function () { expect(lines[0]).to.equal('== SPARKLING WOOD'); expect(lines[6].split(':')[1].trim()).to.equal('N/a'); - expect(err).to.eql([]); + expect(err.join('')).to.eql(expectedWarn); }); }); diff --git a/tests/commands/device/devices.spec.ts b/tests/commands/device/devices.spec.ts index e7656e8d..a0016319 100644 --- a/tests/commands/device/devices.spec.ts +++ b/tests/commands/device/devices.spec.ts @@ -21,6 +21,9 @@ import * as path from 'path'; import { apiResponsePath, BalenaAPIMock } from '../../balena-api-mock'; import { cleanOutput, runCommand } from '../../helpers'; +import { appToFleetOutputMsg, warnify } from '../../../build/utils/messages'; +import { isV13 } from '../../../build/utils/version'; + describe('balena devices', function () { let api: BalenaAPIMock; @@ -35,6 +38,13 @@ describe('balena devices', function () { api.done(); }); + const expectedWarn = + !isV13() && + process.stderr.isTTY && + process.env.BALENA_CLI_TEST_TYPE !== 'standalone' + ? warnify(appToFleetOutputMsg) + '\n' + : ''; + it('should list devices from own and collaborator apps', async () => { api.scope .get( @@ -60,6 +70,6 @@ describe('balena devices', function () { // e.g. When user has a device associated with app that user is no longer a collaborator of. expect(lines.some((l) => l.includes('N/a'))).to.be.true; - expect(err).to.eql([]); + expect(err.join('')).to.eql(expectedWarn); }); }); diff --git a/tests/commands/env/envs.spec.ts b/tests/commands/env/envs.spec.ts index d548ca1b..35fcc79a 100644 --- a/tests/commands/env/envs.spec.ts +++ b/tests/commands/env/envs.spec.ts @@ -21,6 +21,13 @@ import { stripIndent } from '../../../lib/utils/lazy'; import { BalenaAPIMock } from '../../balena-api-mock'; import { runCommand } from '../../helpers'; +import { + appToFleetFlagMsg, + appToFleetOutputMsg, + warnify, +} from '../../../build/utils/messages'; +import { isV13 } from '../../../build/utils/version'; + describe('balena envs', function () { const appName = 'test'; let fullUUID: string; @@ -41,7 +48,21 @@ describe('balena envs', function () { api.done(); }); - it('should successfully list env vars for a test app', async () => { + const appToFleetFlagWarn = + !isV13() && + process.stderr.isTTY && + process.env.BALENA_CLI_TEST_TYPE !== 'standalone' + ? warnify(appToFleetFlagMsg) + '\n' + : ''; + + const appToFleetOutputWarn = + !isV13() && + process.stderr.isTTY && + process.env.BALENA_CLI_TEST_TYPE !== 'standalone' + ? warnify(appToFleetOutputMsg) + '\n' + : ''; + + it('should successfully list env vars for a test fleet', async () => { api.expectGetApplication(); api.expectGetAppEnvVars(); api.expectGetAppServiceVars(); @@ -57,18 +78,18 @@ describe('balena envs', function () { 120102 var2 22 test * ` + '\n', ); - expect(err.join('')).to.equal(''); + expect(err.join('')).to.equal(appToFleetFlagWarn); }); - it('should successfully list config vars for a test app', async () => { + it('should successfully list config vars for a test fleet', async () => { api.expectGetApplication(); api.expectGetAppConfigVars(); - const { out, err } = await runCommand(`envs -a ${appName} --config`); + const { out, err } = await runCommand(`envs -f ${appName} --config`); expect(out.join('')).to.equal( stripIndent` - ID NAME VALUE APPLICATION + ID NAME VALUE FLEET 120300 RESIN_SUPERVISOR_NATIVE_LOGGER false test ` + '\n', ); @@ -76,7 +97,7 @@ describe('balena envs', function () { expect(err.join('')).to.equal(''); }); - it('should successfully list config vars for a test app (JSON output)', async () => { + it('should successfully list config vars for a test fleet (JSON output)', async () => { api.expectGetApplication(); api.expectGetAppConfigVars(); @@ -93,7 +114,7 @@ describe('balena envs', function () { expect(err.join('')).to.equal(''); }); - it('should successfully list service variables for a test app (-s flag)', async () => { + it('should successfully list service variables for a test fleet (-s flag)', async () => { const serviceName = 'service2'; api.expectGetService({ serviceName }); api.expectGetApplication(); @@ -112,10 +133,10 @@ describe('balena envs', function () { 120102 var2 22 test * ` + '\n', ); - expect(err.join('')).to.equal(''); + expect(err.join('')).to.equal(appToFleetFlagWarn); }); - it('should successfully list env and service vars for a test app (-s flags)', async () => { + it('should successfully list env and service vars for a test fleet (-s flags)', async () => { const serviceName = 'service1'; api.expectGetService({ serviceName }); api.expectGetApplication(); @@ -134,7 +155,7 @@ describe('balena envs', function () { 120102 var2 22 test * ` + '\n', ); - expect(err.join('')).to.equal(''); + expect(err.join('')).to.equal(appToFleetFlagWarn); }); it('should successfully list env variables for a test device', async () => { @@ -162,7 +183,7 @@ describe('balena envs', function () { ` + '\n', ); - expect(err.join('')).to.equal(''); + expect(err.join('')).to.equal(appToFleetOutputWarn); }); it('should successfully list env variables for a test device (JSON output)', async () => { @@ -207,7 +228,7 @@ describe('balena envs', function () { ` + '\n', ); - expect(err.join('')).to.equal(''); + expect(err.join('')).to.equal(appToFleetOutputWarn); }); it('should successfully list service variables for a test device (-s flag)', async () => { @@ -235,10 +256,10 @@ describe('balena envs', function () { ` + '\n', ); - expect(err.join('')).to.equal(''); + expect(err.join('')).to.equal(appToFleetOutputWarn); }); - it('should successfully list env and service variables for a test device (unknown app)', async () => { + it('should successfully list env and service variables for a test device (unknown fleet)', async () => { api.expectGetDevice({ fullUUID, inaccessibleApp: true }); api.expectGetDeviceEnvVars(); api.expectGetDeviceServiceVars(); @@ -256,7 +277,7 @@ describe('balena envs', function () { 120204 var4 44 N/A ${uuid} * ` + '\n', ); - expect(err.join('')).to.equal(''); + expect(err.join('')).to.equal(appToFleetOutputWarn); }); it('should successfully list env and service vars for a test device (-s flags)', async () => { @@ -283,7 +304,7 @@ describe('balena envs', function () { 120204 var4 44 test ${uuid} * ` + '\n', ); - expect(err.join('')).to.equal(''); + expect(err.join('')).to.equal(appToFleetOutputWarn); }); it('should successfully list env and service vars for a test device (-js flags)', async () => { diff --git a/tests/commands/os/configure.spec.ts b/tests/commands/os/configure.spec.ts index 48f73fb9..9e482b84 100644 --- a/tests/commands/os/configure.spec.ts +++ b/tests/commands/os/configure.spec.ts @@ -55,7 +55,7 @@ if (process.platform !== 'win32') { `os configure ${tmpPath}`, '--device-type raspberrypi3', '--version 2.47.0+rev1', - '--application testApp', + '--fleet testApp', '--config-app-update-poll-interval 10', '--config-network ethernet', '--initial-device-name testDeviceName', diff --git a/tests/commands/push.spec.ts b/tests/commands/push.spec.ts index b092c464..b25691cb 100644 --- a/tests/commands/push.spec.ts +++ b/tests/commands/push.spec.ts @@ -26,6 +26,8 @@ import { cleanOutput, runCommand } from '../helpers'; import { addRegSecretsEntries, ExpectedTarStreamFiles, + getDockerignoreWarn1, + getDockerignoreWarn2, setupDockerignoreTestData, } from '../projects'; @@ -124,16 +126,10 @@ describe('balena push', function () { ); const expectedResponseLines = [ ...commonResponseLines[responseFilename], - ...[ - `[Warn] ${hr}`, - '[Warn] The following .dockerignore file(s) will not be used:', - `[Warn] * ${path.join(projectPath, 'src', '.dockerignore')}`, - '[Warn] By default, only one .dockerignore file at the source folder (project root)', - '[Warn] is used. Microservices (multicontainer) applications may use a separate', - '[Warn] .dockerignore file for each service with the --multi-dockerignore (-m) option.', - '[Warn] See "balena help push" for more details.', - `[Warn] ${hr}`, - ], + ...getDockerignoreWarn1( + [path.join(projectPath, 'src', '.dockerignore')], + 'push', + ), ]; if (isWindows) { const fname = path.join(projectPath, 'src', 'windows-crlf.sh'); @@ -171,16 +167,10 @@ describe('balena push', function () { ); const expectedResponseLines = [ ...commonResponseLines[responseFilename], - ...[ - `[Warn] ${hr}`, - '[Warn] The following .dockerignore file(s) will not be used:', - `[Warn] * ${path.join(projectPath, 'src', '.dockerignore')}`, - '[Warn] By default, only one .dockerignore file at the source folder (project root)', - '[Warn] is used. Microservices (multicontainer) applications may use a separate', - '[Warn] .dockerignore file for each service with the --multi-dockerignore (-m) option.', - '[Warn] See "balena help push" for more details.', - `[Warn] ${hr}`, - ], + ...getDockerignoreWarn1( + [path.join(projectPath, 'src', '.dockerignore')], + 'push', + ), ]; const expectedQueryParams = commonQueryParams.map((i) => i[0] === 'dockerfilePath' ? ['dockerfilePath', 'Dockerfile-alt'] : i, @@ -218,16 +208,10 @@ describe('balena push', function () { ); const expectedResponseLines = [ ...commonResponseLines[responseFilename], - ...[ - `[Warn] ${hr}`, - '[Warn] The following .dockerignore file(s) will not be used:', - `[Warn] * ${path.join(projectPath, 'src', '.dockerignore')}`, - '[Warn] By default, only one .dockerignore file at the source folder (project root)', - '[Warn] is used. Microservices (multicontainer) applications may use a separate', - '[Warn] .dockerignore file for each service with the --multi-dockerignore (-m) option.', - '[Warn] See "balena help push" for more details.', - `[Warn] ${hr}`, - ], + ...getDockerignoreWarn1( + [path.join(projectPath, 'src', '.dockerignore')], + 'push', + ), ]; if (isWindows) { const fname = path.join(projectPath, 'src', 'windows-crlf.sh'); @@ -434,16 +418,10 @@ describe('balena push', function () { 'utf8', ); const expectedResponseLines: string[] = [ - ...[ - `[Warn] ${hr}`, - '[Warn] The following .dockerignore file(s) will not be used:', - `[Warn] * ${path.join(projectPath, 'lib', '.dockerignore')}`, - '[Warn] When --multi-dockerignore (-m) is used, only .dockerignore files at the root of', - "[Warn] each service's build context (in a microservices/multicontainer application),", - '[Warn] plus a .dockerignore file at the overall project root, are used.', - '[Warn] See "balena help push" for more details.', - `[Warn] ${hr}`, - ], + ...getDockerignoreWarn2( + [path.join(projectPath, 'lib', '.dockerignore')], + 'push', + ), ...commonResponseLines[responseFilename], ]; @@ -484,16 +462,10 @@ describe('balena push', function () { ); const expectedResponseLines: string[] = [ ...commonResponseLines[responseFilename], - ...[ - `[Warn] ${hr}`, - '[Warn] The following .dockerignore file(s) will not be used:', - `[Warn] * ${path.join(projectPath, 'service2', '.dockerignore')}`, - '[Warn] By default, only one .dockerignore file at the source folder (project root)', - '[Warn] is used. Microservices (multicontainer) applications may use a separate', - '[Warn] .dockerignore file for each service with the --multi-dockerignore (-m) option.', - '[Warn] See "balena help push" for more details.', - `[Warn] ${hr}`, - ], + ...getDockerignoreWarn1( + [path.join(projectPath, 'service2', '.dockerignore')], + 'push', + ), ]; if (isWindows) { expectedResponseLines.push( diff --git a/tests/projects.ts b/tests/projects.ts index 6b137c51..80639b36 100644 --- a/tests/projects.ts +++ b/tests/projects.ts @@ -83,3 +83,39 @@ export async function addRegSecretsEntries( }; return regSecretsPath; } + +export function getDockerignoreWarn1(paths: string[], cmd: string) { + const lines = [ + '[Warn] ----------------------------------------------------------------------', + '[Warn] The following .dockerignore file(s) will not be used:', + ]; + lines.push(...paths.map((p) => `[Warn] * ${p}`)); + lines.push( + ...[ + '[Warn] By default, only one .dockerignore file at the source folder (project', + '[Warn] root) is used. Microservices (multicontainer) fleets may use a separate', + '[Warn] .dockerignore file for each service with the --multi-dockerignore (-m)', + `[Warn] option. See "balena help ${cmd}" for more details.`, + '[Warn] ----------------------------------------------------------------------', + ], + ); + return lines; +} + +export function getDockerignoreWarn2(paths: string[], cmd: string) { + const lines = [ + '[Warn] ----------------------------------------------------------------------', + '[Warn] The following .dockerignore file(s) will not be used:', + ]; + lines.push(...paths.map((p) => `[Warn] * ${p}`)); + lines.push( + ...[ + '[Warn] When --multi-dockerignore (-m) is used, only .dockerignore files at the', + "[Warn] root of each service's build context (in a microservices/multicontainer", + '[Warn] fleet), plus a .dockerignore file at the overall project root, are used.', + `[Warn] See "balena help ${cmd}" for more details.`, + '[Warn] ----------------------------------------------------------------------', + ], + ); + return lines; +} diff --git a/tests/utils/validation.spec.ts b/tests/utils/validation.spec.ts index fc6d05ab..c8d6066b 100644 --- a/tests/utils/validation.spec.ts +++ b/tests/utils/validation.spec.ts @@ -56,20 +56,20 @@ describe('validatePassword() function', () => { }); describe('validateApplicationName() function', () => { - it('should reject applications names shorter than 4 characters, with a message', () => { - const errorMessage = 'The application name should be at least 4 characters'; + it('should reject fleet names shorter than 4 characters, with a message', () => { + const errorMessage = 'The fleet name should be at least 4 characters long'; expect(v.validateApplicationName('abc')).to.equal(errorMessage); expect(v.validateApplicationName('')).to.equal(errorMessage); }); - it('should return false for application names with characters other than `a-z,A-Z,0-9,_-`', () => { + it('should return false for fleet names with characters other than `a-z,A-Z,0-9,_-`', () => { expect(v.validateApplicationName('abcd.')).to.equal(false); expect(v.validateApplicationName('abcd$')).to.equal(false); expect(v.validateApplicationName('ab cd')).to.equal(false); expect(v.validateApplicationName('(abcd)')).to.equal(false); }); - it('should return true for valid application names', () => { + it('should return true for valid fleet names', () => { expect(v.validateApplicationName('Test-Application1')).to.equal(true); expect(v.validateApplicationName('test_application2')).to.equal(true); });