mirror of
https://github.com/balena-io/balena-cli.git
synced 2024-12-20 22:23:07 +00:00
commit
66608b32e9
@ -267,4 +267,3 @@ gotchas to bear in mind:
|
|||||||
replace: `spec: 'tests/**/*.spec.ts',`
|
replace: `spec: 'tests/**/*.spec.ts',`
|
||||||
|
|
||||||
with: `spec: ['tests/auth/*.spec.ts', 'tests/**/deploy.spec.ts'],`
|
with: `spec: ['tests/auth/*.spec.ts', 'tests/**/deploy.spec.ts'],`
|
||||||
|
|
||||||
|
@ -36,19 +36,12 @@ const capitanoDoc = {
|
|||||||
{
|
{
|
||||||
title: 'Fleet',
|
title: 'Fleet',
|
||||||
files: [
|
files: [
|
||||||
'build/commands/apps.js',
|
|
||||||
'build/commands/fleets.js',
|
'build/commands/fleets.js',
|
||||||
'build/commands/app/index.js',
|
|
||||||
'build/commands/fleet/index.js',
|
'build/commands/fleet/index.js',
|
||||||
'build/commands/app/create.js',
|
|
||||||
'build/commands/fleet/create.js',
|
'build/commands/fleet/create.js',
|
||||||
'build/commands/app/purge.js',
|
|
||||||
'build/commands/fleet/purge.js',
|
'build/commands/fleet/purge.js',
|
||||||
'build/commands/app/rename.js',
|
|
||||||
'build/commands/fleet/rename.js',
|
'build/commands/fleet/rename.js',
|
||||||
'build/commands/app/restart.js',
|
|
||||||
'build/commands/fleet/restart.js',
|
'build/commands/fleet/restart.js',
|
||||||
'build/commands/app/rm.js',
|
|
||||||
'build/commands/fleet/rm.js',
|
'build/commands/fleet/rm.js',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -8,10 +8,9 @@ _balena() {
|
|||||||
local context state line curcontext="$curcontext"
|
local context state line curcontext="$curcontext"
|
||||||
|
|
||||||
# Valid top-level completions
|
# Valid top-level completions
|
||||||
main_commands=( apps build deploy envs fleets join keys leave login logout logs note orgs preload push releases scan settings ssh support tags tunnel version whoami api-key app app config device device devices env fleet fleet internal key key local os release release tag util )
|
main_commands=( build deploy envs fleets join keys leave login logout logs note orgs preload push releases scan settings ssh support tags tunnel version whoami api-key config device device devices env fleet fleet internal key key local os release release tag util )
|
||||||
# Sub-completions
|
# Sub-completions
|
||||||
api_key_cmds=( generate )
|
api_key_cmds=( generate )
|
||||||
app_cmds=( create purge rename restart rm )
|
|
||||||
config_cmds=( generate inject read reconfigure write )
|
config_cmds=( generate inject read reconfigure write )
|
||||||
device_cmds=( deactivate identify init local-mode move os-update public-url purge reboot register rename restart rm shutdown )
|
device_cmds=( deactivate identify init local-mode move os-update public-url purge reboot register rename restart rm shutdown )
|
||||||
devices_cmds=( supported )
|
devices_cmds=( supported )
|
||||||
@ -44,9 +43,6 @@ _balena_sec_cmds() {
|
|||||||
"api-key")
|
"api-key")
|
||||||
_describe -t api_key_cmds 'api-key_cmd' api_key_cmds "$@" && ret=0
|
_describe -t api_key_cmds 'api-key_cmd' api_key_cmds "$@" && ret=0
|
||||||
;;
|
;;
|
||||||
"app")
|
|
||||||
_describe -t app_cmds 'app_cmd' app_cmds "$@" && ret=0
|
|
||||||
;;
|
|
||||||
"config")
|
"config")
|
||||||
_describe -t config_cmds 'config_cmd' config_cmds "$@" && ret=0
|
_describe -t config_cmds 'config_cmd' config_cmds "$@" && ret=0
|
||||||
;;
|
;;
|
||||||
|
@ -7,10 +7,9 @@ _balena_complete()
|
|||||||
local cur prev
|
local cur prev
|
||||||
|
|
||||||
# Valid top-level completions
|
# Valid top-level completions
|
||||||
main_commands="apps build deploy envs fleets join keys leave login logout logs note orgs preload push releases scan settings ssh support tags tunnel version whoami api-key app app config device device devices env fleet fleet internal key key local os release release tag util"
|
main_commands="build deploy envs fleets join keys leave login logout logs note orgs preload push releases scan settings ssh support tags tunnel version whoami api-key config device device devices env fleet fleet internal key key local os release release tag util"
|
||||||
# Sub-completions
|
# Sub-completions
|
||||||
api_key_cmds="generate"
|
api_key_cmds="generate"
|
||||||
app_cmds="create purge rename restart rm"
|
|
||||||
config_cmds="generate inject read reconfigure write"
|
config_cmds="generate inject read reconfigure write"
|
||||||
device_cmds="deactivate identify init local-mode move os-update public-url purge reboot register rename restart rm shutdown"
|
device_cmds="deactivate identify init local-mode move os-update public-url purge reboot register rename restart rm shutdown"
|
||||||
devices_cmds="supported"
|
devices_cmds="supported"
|
||||||
@ -38,9 +37,6 @@ _balena_complete()
|
|||||||
api-key)
|
api-key)
|
||||||
COMPREPLY=( $(compgen -W "$api_key_cmds" -- $cur) )
|
COMPREPLY=( $(compgen -W "$api_key_cmds" -- $cur) )
|
||||||
;;
|
;;
|
||||||
app)
|
|
||||||
COMPREPLY=( $(compgen -W "$app_cmds" -- $cur) )
|
|
||||||
;;
|
|
||||||
config)
|
config)
|
||||||
COMPREPLY=( $(compgen -W "$config_cmds" -- $cur) )
|
COMPREPLY=( $(compgen -W "$config_cmds" -- $cur) )
|
||||||
;;
|
;;
|
||||||
|
@ -166,19 +166,12 @@ are encouraged to regularly update the balena CLI to the latest version.
|
|||||||
|
|
||||||
- Fleet
|
- Fleet
|
||||||
|
|
||||||
- [apps](#apps)
|
|
||||||
- [fleets](#fleets)
|
- [fleets](#fleets)
|
||||||
- [app <fleet>](#app-fleet)
|
|
||||||
- [fleet <fleet>](#fleet-fleet)
|
- [fleet <fleet>](#fleet-fleet)
|
||||||
- [app create <name>](#app-create-name)
|
|
||||||
- [fleet create <name>](#fleet-create-name)
|
- [fleet create <name>](#fleet-create-name)
|
||||||
- [app purge <fleet>](#app-purge-fleet)
|
|
||||||
- [fleet purge <fleet>](#fleet-purge-fleet)
|
- [fleet purge <fleet>](#fleet-purge-fleet)
|
||||||
- [app rename <fleet> [newname]](#app-rename-fleet-newname)
|
|
||||||
- [fleet rename <fleet> [newname]](#fleet-rename-fleet-newname)
|
- [fleet rename <fleet> [newname]](#fleet-rename-fleet-newname)
|
||||||
- [app restart <fleet>](#app-restart-fleet)
|
|
||||||
- [fleet restart <fleet>](#fleet-restart-fleet)
|
- [fleet restart <fleet>](#fleet-restart-fleet)
|
||||||
- [app rm <fleet>](#app-rm-fleet)
|
|
||||||
- [fleet rm <fleet>](#fleet-rm-fleet)
|
- [fleet rm <fleet>](#fleet-rm-fleet)
|
||||||
|
|
||||||
- Authentication
|
- Authentication
|
||||||
@ -327,22 +320,6 @@ the API key name
|
|||||||
|
|
||||||
# Fleet
|
# Fleet
|
||||||
|
|
||||||
## apps
|
|
||||||
|
|
||||||
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 command usage, see 'balena help fleets'
|
|
||||||
|
|
||||||
### Options
|
|
||||||
|
|
||||||
#### -v, --verbose
|
|
||||||
|
|
||||||
No-op since release v12.0.0
|
|
||||||
|
|
||||||
## fleets
|
## fleets
|
||||||
|
|
||||||
List all your balena fleets.
|
List all your balena fleets.
|
||||||
@ -356,28 +333,6 @@ Examples:
|
|||||||
|
|
||||||
### Options
|
### 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
|
|
||||||
|
|
||||||
#### FLEET
|
|
||||||
|
|
||||||
fleet name, slug (preferred), or numeric ID (deprecated)
|
|
||||||
|
|
||||||
### Options
|
|
||||||
|
|
||||||
## fleet <fleet>
|
## fleet <fleet>
|
||||||
|
|
||||||
Display detailed information about a single fleet.
|
Display detailed information about a single fleet.
|
||||||
@ -407,32 +362,6 @@ fleet name, slug (preferred), or numeric ID (deprecated)
|
|||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
## app create <name>
|
|
||||||
|
|
||||||
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 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>
|
## fleet create <name>
|
||||||
|
|
||||||
Create a new balena fleet.
|
Create a new balena fleet.
|
||||||
@ -474,24 +403,6 @@ handle of the organization the fleet should belong to
|
|||||||
|
|
||||||
fleet device type (Check available types with `balena devices supported`)
|
fleet device type (Check available types with `balena devices supported`)
|
||||||
|
|
||||||
## app purge <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 purge'
|
|
||||||
|
|
||||||
### Arguments
|
|
||||||
|
|
||||||
#### FLEET
|
|
||||||
|
|
||||||
fleet name, slug (preferred), or numeric ID (deprecated)
|
|
||||||
|
|
||||||
### Options
|
|
||||||
|
|
||||||
## fleet purge <fleet>
|
## fleet purge <fleet>
|
||||||
|
|
||||||
Purge data from all devices belonging to a fleet.
|
Purge data from all devices belonging to a fleet.
|
||||||
@ -522,28 +433,6 @@ fleet name, slug (preferred), or numeric ID (deprecated)
|
|||||||
|
|
||||||
### Options
|
### 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]
|
## fleet rename <fleet> [newName]
|
||||||
|
|
||||||
Rename a fleet.
|
Rename a fleet.
|
||||||
@ -581,24 +470,6 @@ the new name for the fleet
|
|||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
## app restart <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 restart'
|
|
||||||
|
|
||||||
### Arguments
|
|
||||||
|
|
||||||
#### FLEET
|
|
||||||
|
|
||||||
fleet name, slug (preferred), or numeric ID (deprecated)
|
|
||||||
|
|
||||||
### Options
|
|
||||||
|
|
||||||
## fleet restart <fleet>
|
## fleet restart <fleet>
|
||||||
|
|
||||||
Restart all devices belonging to a fleet.
|
Restart all devices belonging to a fleet.
|
||||||
@ -628,28 +499,6 @@ fleet name, slug (preferred), or numeric ID (deprecated)
|
|||||||
|
|
||||||
### Options
|
### 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>
|
## fleet rm <fleet>
|
||||||
|
|
||||||
Permanently remove a fleet.
|
Permanently remove a fleet.
|
||||||
@ -795,37 +644,18 @@ Examples:
|
|||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
#### -a, --application APPLICATION
|
|
||||||
|
|
||||||
DEPRECATED alias for -f, --fleet
|
|
||||||
|
|
||||||
#### --app APP
|
|
||||||
|
|
||||||
DEPRECATED alias for -f, --fleet
|
|
||||||
|
|
||||||
#### -f, --fleet FLEET
|
#### -f, --fleet FLEET
|
||||||
|
|
||||||
fleet name, slug (preferred), or numeric ID
|
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||||
|
|
||||||
#### -j, --json
|
#### -j, --json
|
||||||
|
|
||||||
produce JSON output instead of tabular output
|
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
|
## devices supported
|
||||||
|
|
||||||
List the supported device types (like 'raspberrypi3' or 'intel-nuc').
|
List the supported device types (like 'raspberrypi3' or 'intel-nuc').
|
||||||
|
|
||||||
The --verbose option may add extra columns/fields to the output. Currently
|
|
||||||
this includes the "STATE" column which is DEPRECATED and whose values are one
|
|
||||||
of 'new', 'released' or 'discontinued'. However, 'discontinued' device types
|
|
||||||
are only listed if the '--discontinued' option is also used, and this option
|
|
||||||
is also DEPRECATED.
|
|
||||||
|
|
||||||
The --json option is recommended when scripting the output of this command,
|
The --json option is recommended when scripting the output of this command,
|
||||||
because the JSON format is less likely to change and it better represents data
|
because the JSON format is less likely to change and it better represents data
|
||||||
types like lists and empty strings (for example, the ALIASES column contains a
|
types like lists and empty strings (for example, the ALIASES column contains a
|
||||||
@ -835,23 +665,14 @@ list of zero or more values). The 'jq' utility may be helpful in shell scripts
|
|||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
$ balena devices supported
|
$ balena devices supported
|
||||||
$ balena devices supported --verbose
|
$ balena devices supported --json
|
||||||
$ balena devices supported -vj
|
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
#### --discontinued
|
|
||||||
|
|
||||||
include "discontinued" device types (DEPRECATED)
|
|
||||||
|
|
||||||
#### -j, --json
|
#### -j, --json
|
||||||
|
|
||||||
produce JSON output instead of tabular output
|
produce JSON output instead of tabular output
|
||||||
|
|
||||||
#### -v, --verbose
|
|
||||||
|
|
||||||
add extra columns in the tabular output (DEPRECATED)
|
|
||||||
|
|
||||||
## device <uuid>
|
## device <uuid>
|
||||||
|
|
||||||
Show information about a single device.
|
Show information about a single device.
|
||||||
@ -868,11 +689,6 @@ the device uuid
|
|||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
#### --v13
|
|
||||||
|
|
||||||
enable selected balena CLI v13 pre-release features, like the renaming
|
|
||||||
from "application" to "fleet" in command output
|
|
||||||
|
|
||||||
## device deactivate <uuid>
|
## device deactivate <uuid>
|
||||||
|
|
||||||
Deactivate a device.
|
Deactivate a device.
|
||||||
@ -961,17 +777,9 @@ Examples:
|
|||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
#### -a, --application APPLICATION
|
|
||||||
|
|
||||||
DEPRECATED alias for -f, --fleet
|
|
||||||
|
|
||||||
#### --app APP
|
|
||||||
|
|
||||||
DEPRECATED alias for -f, --fleet
|
|
||||||
|
|
||||||
#### -f, --fleet FLEET
|
#### -f, --fleet FLEET
|
||||||
|
|
||||||
fleet name, slug (preferred), or numeric ID
|
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||||
|
|
||||||
#### -y, --yes
|
#### -y, --yes
|
||||||
|
|
||||||
@ -1068,17 +876,9 @@ comma-separated list (no blank spaces) of device UUIDs to be moved
|
|||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
#### --app APP
|
|
||||||
|
|
||||||
DEPRECATED alias for -f, --fleet
|
|
||||||
|
|
||||||
#### -a, --application APPLICATION
|
|
||||||
|
|
||||||
DEPRECATED alias for -f, --fleet
|
|
||||||
|
|
||||||
#### -f, --fleet FLEET
|
#### -f, --fleet FLEET
|
||||||
|
|
||||||
fleet name, slug (preferred), or numeric ID
|
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||||
|
|
||||||
## device os-update <uuid>
|
## device os-update <uuid>
|
||||||
|
|
||||||
@ -1348,7 +1148,7 @@ Examples:
|
|||||||
|
|
||||||
#### FLEET
|
#### FLEET
|
||||||
|
|
||||||
fleet name or slug
|
fleet name or slug (preferred)
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
@ -1445,13 +1245,6 @@ environments). Numeric fleet IDs are deprecated because they consist of an
|
|||||||
implementation detail of the balena backend. We intend to remove support for
|
implementation detail of the balena backend. We intend to remove support for
|
||||||
numeric IDs at some point in the future.
|
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:
|
Examples:
|
||||||
|
|
||||||
$ balena envs --fleet myorg/myfleet
|
$ balena envs --fleet myorg/myfleet
|
||||||
@ -1466,17 +1259,9 @@ Examples:
|
|||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
#### --all
|
|
||||||
|
|
||||||
No-op since balena CLI v12.0.0.
|
|
||||||
|
|
||||||
#### -a, --application APPLICATION
|
|
||||||
|
|
||||||
DEPRECATED alias for -f, --fleet
|
|
||||||
|
|
||||||
#### -f, --fleet FLEET
|
#### -f, --fleet FLEET
|
||||||
|
|
||||||
fleet name, slug (preferred), or numeric ID
|
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||||
|
|
||||||
#### -c, --config
|
#### -c, --config
|
||||||
|
|
||||||
@ -1490,19 +1275,10 @@ device UUID
|
|||||||
|
|
||||||
produce JSON output instead of tabular output
|
produce JSON output instead of tabular output
|
||||||
|
|
||||||
#### -v, --verbose
|
|
||||||
|
|
||||||
produce verbose output
|
|
||||||
|
|
||||||
#### -s, --service SERVICE
|
#### -s, --service SERVICE
|
||||||
|
|
||||||
service name
|
service name
|
||||||
|
|
||||||
#### --v13
|
|
||||||
|
|
||||||
enable selected balena CLI v13 pre-release features, like the renaming
|
|
||||||
from "application" to "fleet" in command output
|
|
||||||
|
|
||||||
## env rm <id>
|
## env rm <id>
|
||||||
|
|
||||||
Remove a configuration or environment variable from a fleet, device
|
Remove a configuration or environment variable from a fleet, device
|
||||||
@ -1631,13 +1407,9 @@ variable value; if omitted, use value from this process' environment
|
|||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
#### -a, --application APPLICATION
|
|
||||||
|
|
||||||
DEPRECATED alias for -f, --fleet
|
|
||||||
|
|
||||||
#### -f, --fleet FLEET
|
#### -f, --fleet FLEET
|
||||||
|
|
||||||
fleet name, slug (preferred), or numeric ID
|
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||||
|
|
||||||
#### -d, --device DEVICE
|
#### -d, --device DEVICE
|
||||||
|
|
||||||
@ -1742,17 +1514,9 @@ Examples:
|
|||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
#### -a, --application APPLICATION
|
|
||||||
|
|
||||||
DEPRECATED alias for -f, --fleet
|
|
||||||
|
|
||||||
#### --app APP
|
|
||||||
|
|
||||||
DEPRECATED alias for -f, --fleet
|
|
||||||
|
|
||||||
#### -f, --fleet FLEET
|
#### -f, --fleet FLEET
|
||||||
|
|
||||||
fleet name, slug (preferred), or numeric ID
|
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||||
|
|
||||||
#### -d, --device DEVICE
|
#### -d, --device DEVICE
|
||||||
|
|
||||||
@ -1794,17 +1558,9 @@ the key string of the tag
|
|||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
#### -a, --application APPLICATION
|
|
||||||
|
|
||||||
DEPRECATED alias for -f, --fleet
|
|
||||||
|
|
||||||
#### --app APP
|
|
||||||
|
|
||||||
DEPRECATED alias for -f, --fleet
|
|
||||||
|
|
||||||
#### -f, --fleet FLEET
|
#### -f, --fleet FLEET
|
||||||
|
|
||||||
fleet name, slug (preferred), or numeric ID
|
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||||
|
|
||||||
#### -d, --device DEVICE
|
#### -d, --device DEVICE
|
||||||
|
|
||||||
@ -1857,17 +1613,9 @@ the optional value associated with the tag
|
|||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
#### -a, --application APPLICATION
|
|
||||||
|
|
||||||
DEPRECATED alias for -f, --fleet
|
|
||||||
|
|
||||||
#### --app APP
|
|
||||||
|
|
||||||
DEPRECATED alias for -f, --fleet
|
|
||||||
|
|
||||||
#### -f, --fleet FLEET
|
#### -f, --fleet FLEET
|
||||||
|
|
||||||
fleet name, slug (preferred), or numeric ID
|
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||||
|
|
||||||
#### -d, --device DEVICE
|
#### -d, --device DEVICE
|
||||||
|
|
||||||
@ -2388,9 +2136,6 @@ are multiple files to inject. See connection profile examples and reference at:
|
|||||||
https://www.balena.io/docs/reference/OS/network/2.x/
|
https://www.balena.io/docs/reference/OS/network/2.x/
|
||||||
https://developer.gnome.org/NetworkManager/stable/ref-settings.html
|
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.
|
|
||||||
|
|
||||||
Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are
|
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
|
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
|
listed with the `balena fleets` command. Note that slugs may change if the
|
||||||
@ -2411,7 +2156,6 @@ https://docs.microsoft.com/en-us/windows/wsl/about
|
|||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
$ balena os configure ../path/rpi3.img --device 7cf02a6
|
$ balena os configure ../path/rpi3.img --device 7cf02a6
|
||||||
$ balena os configure ../path/rpi3.img --device 7cf02a6 --device-api-key <existingDeviceKey>
|
|
||||||
$ balena os configure ../path/rpi3.img --fleet myorg/myfleet
|
$ 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 --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
|
||||||
@ -2429,17 +2173,9 @@ path to a balenaOS image file, e.g. "rpi3.img"
|
|||||||
|
|
||||||
ask advanced configuration questions (when in interactive mode)
|
ask advanced configuration questions (when in interactive mode)
|
||||||
|
|
||||||
#### -a, --application APPLICATION
|
|
||||||
|
|
||||||
DEPRECATED alias for -f, --fleet
|
|
||||||
|
|
||||||
#### --app APP
|
|
||||||
|
|
||||||
DEPRECATED alias for -f, --fleet
|
|
||||||
|
|
||||||
#### -f, --fleet FLEET
|
#### -f, --fleet FLEET
|
||||||
|
|
||||||
fleet name, slug (preferred), or numeric ID
|
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||||
|
|
||||||
#### --config CONFIG
|
#### --config CONFIG
|
||||||
|
|
||||||
@ -2465,10 +2201,6 @@ WiFi SSID (network name) (non-interactive configuration)
|
|||||||
|
|
||||||
device UUID
|
device UUID
|
||||||
|
|
||||||
#### -k, --device-api-key DEVICE-API-KEY
|
|
||||||
|
|
||||||
custom device API key (DEPRECATED and only supported with balenaOS 2.0.3+)
|
|
||||||
|
|
||||||
#### --device-type DEVICE-TYPE
|
#### --device-type DEVICE-TYPE
|
||||||
|
|
||||||
device type slug (e.g. "raspberrypi3") to override the fleet device type
|
device type slug (e.g. "raspberrypi3") to override the fleet device type
|
||||||
@ -2568,21 +2300,9 @@ Examples:
|
|||||||
|
|
||||||
a balenaOS version
|
a balenaOS version
|
||||||
|
|
||||||
#### -a, --application APPLICATION
|
|
||||||
|
|
||||||
DEPRECATED alias for -f, --fleet
|
|
||||||
|
|
||||||
#### --app APP
|
|
||||||
|
|
||||||
DEPRECATED alias for -f, --fleet
|
|
||||||
|
|
||||||
#### --appUpdatePollInterval APPUPDATEPOLLINTERVAL
|
|
||||||
|
|
||||||
supervisor cloud polling interval in minutes (e.g. for variable updates)
|
|
||||||
|
|
||||||
#### -f, --fleet FLEET
|
#### -f, --fleet FLEET
|
||||||
|
|
||||||
fleet name, slug (preferred), or numeric ID
|
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||||
|
|
||||||
#### -d, --device DEVICE
|
#### -d, --device DEVICE
|
||||||
|
|
||||||
@ -2616,6 +2336,10 @@ 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)
|
the wifi key to use (used only if --network is set to wifi)
|
||||||
|
|
||||||
|
#### --appUpdatePollInterval APPUPDATEPOLLINTERVAL
|
||||||
|
|
||||||
|
supervisor cloud polling interval in minutes (e.g. for device variables)
|
||||||
|
|
||||||
#### --provisioning-key-name PROVISIONING-KEY-NAME
|
#### --provisioning-key-name PROVISIONING-KEY-NAME
|
||||||
|
|
||||||
custom key name assigned to generated provisioning api key
|
custom key name assigned to generated provisioning api key
|
||||||
@ -2796,13 +2520,9 @@ the image file path
|
|||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
#### -a, --app APP
|
|
||||||
|
|
||||||
DEPRECATED alias for -f, --fleet
|
|
||||||
|
|
||||||
#### -f, --fleet FLEET
|
#### -f, --fleet FLEET
|
||||||
|
|
||||||
fleet name, slug (preferred), or numeric ID
|
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||||
|
|
||||||
#### -c, --commit COMMIT
|
#### -c, --commit COMMIT
|
||||||
|
|
||||||
@ -2935,15 +2655,12 @@ compatibility with the standard docker-compose tool, while still allowing a
|
|||||||
root .dockerignore file (at the overall project root) to filter files and
|
root .dockerignore file (at the overall project root) to filter files and
|
||||||
folders that are outside service subdirectories.
|
folders that are outside service subdirectories.
|
||||||
|
|
||||||
balena CLI releases older than v12.0.0 also took .gitignore files into account.
|
balena CLI v11 also took .gitignore files into account. This behavior was
|
||||||
This behavior is deprecated, but may still be enabled with the --gitignore (-g)
|
deprecated in CLI v12 and removed in CLI v13. Please use .dockerignore files
|
||||||
option if compatibility is required. This option is mutually exclusive with
|
instead.
|
||||||
--multi-dockerignore (-m) and will be removed in the CLI's next major version
|
|
||||||
release (v13).
|
|
||||||
|
|
||||||
Default .dockerignore patterns
|
Default .dockerignore patterns
|
||||||
When --gitignore (-g) is NOT used (i.e. when not in v11 compatibility mode), a
|
A few default/hardcoded dockerignore patterns are "merged" (in memory) with the
|
||||||
few default/hardcoded dockerignore patterns are "merged" (in memory) with the
|
|
||||||
patterns found in the applicable .dockerignore files, in the following order:
|
patterns found in the applicable .dockerignore files, in the following order:
|
||||||
```
|
```
|
||||||
**/.git
|
**/.git
|
||||||
@ -2956,7 +2673,7 @@ patterns found in the applicable .dockerignore files, in the following order:
|
|||||||
```
|
```
|
||||||
These patterns always apply, whether or not .dockerignore files exist in the
|
These patterns always apply, whether or not .dockerignore files exist in the
|
||||||
project. If necessary, the effect of the `**/.git` pattern may be modified by
|
project. If necessary, the effect of the `**/.git` pattern may be modified by
|
||||||
adding counter patterns to the applicable .dockerignore file(s), for example
|
adding exception patterns to the applicable .dockerignore file(s), for example
|
||||||
`!mysubmodule/.git`. For documentation on pattern format, see:
|
`!mysubmodule/.git`. For documentation on pattern format, see:
|
||||||
- https://docs.docker.com/engine/reference/builder/#dockerignore-file
|
- https://docs.docker.com/engine/reference/builder/#dockerignore-file
|
||||||
- https://www.npmjs.com/package/@balena/dockerignore
|
- https://www.npmjs.com/package/@balena/dockerignore
|
||||||
@ -3064,10 +2781,6 @@ separated by a colon, e.g:
|
|||||||
Note that if the service name cannot be found in the composition, the entire
|
Note that if the service name cannot be found in the composition, the entire
|
||||||
left hand side of the = character will be treated as the variable name.
|
left hand side of the = character will be treated as the variable name.
|
||||||
|
|
||||||
#### -l, --convert-eol
|
|
||||||
|
|
||||||
No-op and deprecated since balena CLI v12.0.0
|
|
||||||
|
|
||||||
#### --noconvert-eol
|
#### --noconvert-eol
|
||||||
|
|
||||||
Don't convert line endings from CRLF (Windows format) to LF (Unix format).
|
Don't convert line endings from CRLF (Windows format) to LF (Unix format).
|
||||||
@ -3076,16 +2789,6 @@ Don't convert line endings from CRLF (Windows format) to LF (Unix format).
|
|||||||
|
|
||||||
Have each service use its own .dockerignore file. See "balena help push".
|
Have each service use its own .dockerignore file. See "balena help push".
|
||||||
|
|
||||||
#### -g, --gitignore
|
|
||||||
|
|
||||||
Consider .gitignore files in addition to the .dockerignore file. This reverts
|
|
||||||
to the CLI v11 behavior/implementation (deprecated) if compatibility is
|
|
||||||
required until your project can be adapted.
|
|
||||||
|
|
||||||
#### -G, --nogitignore
|
|
||||||
|
|
||||||
No-op (default behavior) since balena CLI v12.0.0. See "balena help push".
|
|
||||||
|
|
||||||
#### --release-tag RELEASE-TAG
|
#### --release-tag RELEASE-TAG
|
||||||
|
|
||||||
Set release tags if the image build is successful (balenaCloud only). Multiple
|
Set release tags if the image build is successful (balenaCloud only). Multiple
|
||||||
@ -3228,15 +2931,12 @@ compatibility with the standard docker-compose tool, while still allowing a
|
|||||||
root .dockerignore file (at the overall project root) to filter files and
|
root .dockerignore file (at the overall project root) to filter files and
|
||||||
folders that are outside service subdirectories.
|
folders that are outside service subdirectories.
|
||||||
|
|
||||||
balena CLI releases older than v12.0.0 also took .gitignore files into account.
|
balena CLI v11 also took .gitignore files into account. This behavior was
|
||||||
This behavior is deprecated, but may still be enabled with the --gitignore (-g)
|
deprecated in CLI v12 and removed in CLI v13. Please use .dockerignore files
|
||||||
option if compatibility is required. This option is mutually exclusive with
|
instead.
|
||||||
--multi-dockerignore (-m) and will be removed in the CLI's next major version
|
|
||||||
release (v13).
|
|
||||||
|
|
||||||
Default .dockerignore patterns
|
Default .dockerignore patterns
|
||||||
When --gitignore (-g) is NOT used (i.e. when not in v11 compatibility mode), a
|
A few default/hardcoded dockerignore patterns are "merged" (in memory) with the
|
||||||
few default/hardcoded dockerignore patterns are "merged" (in memory) with the
|
|
||||||
patterns found in the applicable .dockerignore files, in the following order:
|
patterns found in the applicable .dockerignore files, in the following order:
|
||||||
```
|
```
|
||||||
**/.git
|
**/.git
|
||||||
@ -3249,7 +2949,7 @@ patterns found in the applicable .dockerignore files, in the following order:
|
|||||||
```
|
```
|
||||||
These patterns always apply, whether or not .dockerignore files exist in the
|
These patterns always apply, whether or not .dockerignore files exist in the
|
||||||
project. If necessary, the effect of the `**/.git` pattern may be modified by
|
project. If necessary, the effect of the `**/.git` pattern may be modified by
|
||||||
adding counter patterns to the applicable .dockerignore file(s), for example
|
adding exception patterns to the applicable .dockerignore file(s), for example
|
||||||
`!mysubmodule/.git`. For documentation on pattern format, see:
|
`!mysubmodule/.git`. For documentation on pattern format, see:
|
||||||
- https://docs.docker.com/engine/reference/builder/#dockerignore-file
|
- https://docs.docker.com/engine/reference/builder/#dockerignore-file
|
||||||
- https://www.npmjs.com/package/@balena/dockerignore
|
- https://www.npmjs.com/package/@balena/dockerignore
|
||||||
@ -3279,13 +2979,9 @@ the architecture to build for
|
|||||||
|
|
||||||
the type of device this build is for
|
the type of device this build is for
|
||||||
|
|
||||||
#### -a, --application APPLICATION
|
|
||||||
|
|
||||||
DEPRECATED alias for -f, --fleet
|
|
||||||
|
|
||||||
#### -f, --fleet FLEET
|
#### -f, --fleet FLEET
|
||||||
|
|
||||||
fleet name, slug (preferred), or numeric ID
|
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||||
|
|
||||||
#### -e, --emulated
|
#### -e, --emulated
|
||||||
|
|
||||||
@ -3303,16 +2999,6 @@ No-op and deprecated since balena CLI v12.0.0. Build logs are now shown by defau
|
|||||||
|
|
||||||
Hide the image build log output (produce less verbose output)
|
Hide the image build log output (produce less verbose output)
|
||||||
|
|
||||||
#### -g, --gitignore
|
|
||||||
|
|
||||||
Consider .gitignore files in addition to the .dockerignore file. This reverts
|
|
||||||
to the CLI v11 behavior/implementation (deprecated) if compatibility is required
|
|
||||||
until your project can be adapted.
|
|
||||||
|
|
||||||
#### -G, --nogitignore
|
|
||||||
|
|
||||||
No-op (default behavior) since balena CLI v12.0.0. See "balena help build".
|
|
||||||
|
|
||||||
#### -m, --multi-dockerignore
|
#### -m, --multi-dockerignore
|
||||||
|
|
||||||
Have each service use its own .dockerignore file. See "balena help build".
|
Have each service use its own .dockerignore file. See "balena help build".
|
||||||
@ -3325,10 +3011,6 @@ Disable project validation check of 'docker-compose.yml' file in parent folder
|
|||||||
|
|
||||||
Path to a YAML or JSON file with passwords for a private Docker registry
|
Path to a YAML or JSON file with passwords for a private Docker registry
|
||||||
|
|
||||||
#### -l, --convert-eol
|
|
||||||
|
|
||||||
No-op and deprecated since balena CLI v12.0.0
|
|
||||||
|
|
||||||
#### --noconvert-eol
|
#### --noconvert-eol
|
||||||
|
|
||||||
Don't convert line endings from CRLF (Windows format) to LF (Unix format).
|
Don't convert line endings from CRLF (Windows format) to LF (Unix format).
|
||||||
@ -3458,15 +3140,12 @@ compatibility with the standard docker-compose tool, while still allowing a
|
|||||||
root .dockerignore file (at the overall project root) to filter files and
|
root .dockerignore file (at the overall project root) to filter files and
|
||||||
folders that are outside service subdirectories.
|
folders that are outside service subdirectories.
|
||||||
|
|
||||||
balena CLI releases older than v12.0.0 also took .gitignore files into account.
|
balena CLI v11 also took .gitignore files into account. This behavior was
|
||||||
This behavior is deprecated, but may still be enabled with the --gitignore (-g)
|
deprecated in CLI v12 and removed in CLI v13. Please use .dockerignore files
|
||||||
option if compatibility is required. This option is mutually exclusive with
|
instead.
|
||||||
--multi-dockerignore (-m) and will be removed in the CLI's next major version
|
|
||||||
release (v13).
|
|
||||||
|
|
||||||
Default .dockerignore patterns
|
Default .dockerignore patterns
|
||||||
When --gitignore (-g) is NOT used (i.e. when not in v11 compatibility mode), a
|
A few default/hardcoded dockerignore patterns are "merged" (in memory) with the
|
||||||
few default/hardcoded dockerignore patterns are "merged" (in memory) with the
|
|
||||||
patterns found in the applicable .dockerignore files, in the following order:
|
patterns found in the applicable .dockerignore files, in the following order:
|
||||||
```
|
```
|
||||||
**/.git
|
**/.git
|
||||||
@ -3479,7 +3158,7 @@ patterns found in the applicable .dockerignore files, in the following order:
|
|||||||
```
|
```
|
||||||
These patterns always apply, whether or not .dockerignore files exist in the
|
These patterns always apply, whether or not .dockerignore files exist in the
|
||||||
project. If necessary, the effect of the `**/.git` pattern may be modified by
|
project. If necessary, the effect of the `**/.git` pattern may be modified by
|
||||||
adding counter patterns to the applicable .dockerignore file(s), for example
|
adding exception patterns to the applicable .dockerignore file(s), for example
|
||||||
`!mysubmodule/.git`. For documentation on pattern format, see:
|
`!mysubmodule/.git`. For documentation on pattern format, see:
|
||||||
- https://docs.docker.com/engine/reference/builder/#dockerignore-file
|
- https://docs.docker.com/engine/reference/builder/#dockerignore-file
|
||||||
- https://www.npmjs.com/package/@balena/dockerignore
|
- https://www.npmjs.com/package/@balena/dockerignore
|
||||||
@ -3544,16 +3223,6 @@ No-op and deprecated since balena CLI v12.0.0. Build logs are now shown by defau
|
|||||||
|
|
||||||
Hide the image build log output (produce less verbose output)
|
Hide the image build log output (produce less verbose output)
|
||||||
|
|
||||||
#### -g, --gitignore
|
|
||||||
|
|
||||||
Consider .gitignore files in addition to the .dockerignore file. This reverts
|
|
||||||
to the CLI v11 behavior/implementation (deprecated) if compatibility is required
|
|
||||||
until your project can be adapted.
|
|
||||||
|
|
||||||
#### -G, --nogitignore
|
|
||||||
|
|
||||||
No-op (default behavior) since balena CLI v12.0.0. See "balena help build".
|
|
||||||
|
|
||||||
#### -m, --multi-dockerignore
|
#### -m, --multi-dockerignore
|
||||||
|
|
||||||
Have each service use its own .dockerignore file. See "balena help build".
|
Have each service use its own .dockerignore file. See "balena help build".
|
||||||
@ -3566,10 +3235,6 @@ Disable project validation check of 'docker-compose.yml' file in parent folder
|
|||||||
|
|
||||||
Path to a YAML or JSON file with passwords for a private Docker registry
|
Path to a YAML or JSON file with passwords for a private Docker registry
|
||||||
|
|
||||||
#### -l, --convert-eol
|
|
||||||
|
|
||||||
No-op and deprecated since balena CLI v12.0.0
|
|
||||||
|
|
||||||
#### --noconvert-eol
|
#### --noconvert-eol
|
||||||
|
|
||||||
Don't convert line endings from CRLF (Windows format) to LF (Unix format).
|
Don't convert line endings from CRLF (Windows format) to LF (Unix format).
|
||||||
@ -3676,13 +3341,9 @@ the IP or hostname of device
|
|||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
#### -a, --application APPLICATION
|
|
||||||
|
|
||||||
DEPRECATED alias for -f, --fleet
|
|
||||||
|
|
||||||
#### -f, --fleet FLEET
|
#### -f, --fleet FLEET
|
||||||
|
|
||||||
fleet name, slug (preferred), or numeric ID
|
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||||
|
|
||||||
#### -i, --pollInterval POLLINTERVAL
|
#### -i, --pollInterval POLLINTERVAL
|
||||||
|
|
||||||
@ -3768,13 +3429,9 @@ enable|disable support access
|
|||||||
|
|
||||||
comma-separated list (no spaces) of device UUIDs
|
comma-separated list (no spaces) of device UUIDs
|
||||||
|
|
||||||
#### -a, --application APPLICATION
|
|
||||||
|
|
||||||
DEPRECATED alias for -f, --fleet
|
|
||||||
|
|
||||||
#### -f, --fleet FLEET
|
#### -f, --fleet FLEET
|
||||||
|
|
||||||
comma-separated list (no spaces) of fleet names or org/name slugs
|
comma-separated list (no spaces) of fleet names or slugs (preferred)
|
||||||
|
|
||||||
#### -t, --duration DURATION
|
#### -t, --duration DURATION
|
||||||
|
|
||||||
|
@ -1,181 +0,0 @@
|
|||||||
/**
|
|
||||||
* @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 { 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 { appToFleetCmdMsg, warnify } from '../../utils/messages';
|
|
||||||
|
|
||||||
interface FlagsDef {
|
|
||||||
organization?: string;
|
|
||||||
type?: string; // application device type
|
|
||||||
help: void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ArgsDef {
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FleetCreateCmd extends Command {
|
|
||||||
public static description = stripIndent`
|
|
||||||
Create a fleet.
|
|
||||||
|
|
||||||
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 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.
|
|
||||||
|
|
||||||
Interactive dropdowns will be shown for selection if no device type or
|
|
||||||
organization is specified and there are multiple options to choose from.
|
|
||||||
If there is a single option to choose from, it will be chosen automatically.
|
|
||||||
This interactive behavior can be disabled by explicitly specifying a device
|
|
||||||
type and organization.
|
|
||||||
`;
|
|
||||||
|
|
||||||
public static examples = [
|
|
||||||
'$ 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: 'fleet name',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
public static usage = 'fleet create <name>';
|
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
|
||||||
organization: flags.string({
|
|
||||||
char: 'o',
|
|
||||||
description: 'handle of the organization the fleet should belong to',
|
|
||||||
}),
|
|
||||||
type: flags.string({
|
|
||||||
char: 't',
|
|
||||||
description:
|
|
||||||
'fleet device type (Check available types with `balena devices supported`)',
|
|
||||||
}),
|
|
||||||
help: cf.help,
|
|
||||||
};
|
|
||||||
|
|
||||||
public static authenticated = true;
|
|
||||||
|
|
||||||
public async run(parserOutput?: ParserOutput<FlagsDef, ArgsDef>) {
|
|
||||||
const { args: params, flags: options } =
|
|
||||||
parserOutput || this.parse<FlagsDef, ArgsDef>(FleetCreateCmd);
|
|
||||||
|
|
||||||
// Ascertain device type
|
|
||||||
const deviceType =
|
|
||||||
options.type ||
|
|
||||||
(await (await import('../../utils/patterns')).selectDeviceType());
|
|
||||||
|
|
||||||
// Ascertain organization
|
|
||||||
const organization =
|
|
||||||
options.organization?.toLowerCase() || (await this.getOrganization());
|
|
||||||
|
|
||||||
// Create application
|
|
||||||
let application: Application;
|
|
||||||
try {
|
|
||||||
application = await getBalenaSdk().models.application.create({
|
|
||||||
name: params.name,
|
|
||||||
deviceType,
|
|
||||||
organization,
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
if ((err.message || '').toLowerCase().includes('unique')) {
|
|
||||||
// BalenaRequestError: Request error: "organization" and "app_name" must be unique.
|
|
||||||
throw new ExpectedError(
|
|
||||||
`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 fleets in organization "${organization}".`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output
|
|
||||||
const { isV13 } = await import('../../utils/version');
|
|
||||||
console.log(
|
|
||||||
isV13()
|
|
||||||
? `Fleet created: slug "${application.slug}", device type "${deviceType}"`
|
|
||||||
: `Fleet created: ${application.slug} (${deviceType}, id ${application.id})`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getOrganization() {
|
|
||||||
const { getOwnOrganizations } = await import('../../utils/sdk');
|
|
||||||
const organizations = await getOwnOrganizations(getBalenaSdk());
|
|
||||||
|
|
||||||
if (organizations.length === 0) {
|
|
||||||
// User is not a member of any organizations (should not happen).
|
|
||||||
throw new Error('This account is not a member of any organizations');
|
|
||||||
} else if (organizations.length === 1) {
|
|
||||||
// User is a member of only one organization - use this.
|
|
||||||
return organizations[0].handle;
|
|
||||||
} else {
|
|
||||||
// User is a member of multiple organizations -
|
|
||||||
const { selectOrganization } = await import('../../utils/patterns');
|
|
||||||
return selectOrganization(organizations);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 <name>';
|
|
||||||
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<FlagsDef, ArgsDef>(AppCreateCmd);
|
|
||||||
if (process.stderr.isTTY) {
|
|
||||||
console.error(warnify(appToFleetCmdMsg));
|
|
||||||
}
|
|
||||||
await super.run(parserOutput);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,135 +0,0 @@
|
|||||||
/**
|
|
||||||
* @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 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,
|
|
||||||
appToFleetCmdMsg,
|
|
||||||
warnify,
|
|
||||||
} from '../../utils/messages';
|
|
||||||
import { isV13 } from '../../utils/version';
|
|
||||||
import type { DataOutputOptions } from '../../framework';
|
|
||||||
|
|
||||||
interface FlagsDef extends DataOutputOptions {
|
|
||||||
help: void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ArgsDef {
|
|
||||||
fleet: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FleetCmd extends Command {
|
|
||||||
public static description = stripIndent`
|
|
||||||
Display information about a single fleet.
|
|
||||||
|
|
||||||
Display detailed information about a single fleet.
|
|
||||||
|
|
||||||
${applicationIdInfo.split('\n').join('\n\t\t')}
|
|
||||||
`;
|
|
||||||
public static examples = [
|
|
||||||
'$ balena fleet MyFleet',
|
|
||||||
'$ balena fleet myorg/myfleet',
|
|
||||||
];
|
|
||||||
|
|
||||||
public static args = [ca.fleetRequired];
|
|
||||||
|
|
||||||
public static usage = 'fleet <fleet>';
|
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
|
||||||
help: cf.help,
|
|
||||||
...(isV13() ? cf.dataOutputFlags : {}),
|
|
||||||
};
|
|
||||||
|
|
||||||
public static authenticated = true;
|
|
||||||
public static primary = true;
|
|
||||||
|
|
||||||
public async run(parserOutput?: ParserOutput<FlagsDef, ArgsDef>) {
|
|
||||||
const { args: params, flags: options } =
|
|
||||||
parserOutput || this.parse<FlagsDef, ArgsDef>(FleetCmd);
|
|
||||||
|
|
||||||
const { getApplication } = await import('../../utils/sdk');
|
|
||||||
|
|
||||||
const application = (await getApplication(getBalenaSdk(), params.fleet, {
|
|
||||||
$expand: {
|
|
||||||
is_for__device_type: { $select: 'slug' },
|
|
||||||
should_be_running__release: { $select: 'commit' },
|
|
||||||
},
|
|
||||||
})) as ApplicationWithDeviceType & {
|
|
||||||
should_be_running__release: [Release?];
|
|
||||||
// For display purposes:
|
|
||||||
device_type: string;
|
|
||||||
commit?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
application.device_type = application.is_for__device_type[0].slug;
|
|
||||||
application.commit = application.should_be_running__release[0]?.commit;
|
|
||||||
|
|
||||||
if (isV13()) {
|
|
||||||
await this.outputData(
|
|
||||||
application,
|
|
||||||
['app_name', 'id', 'device_type', 'slug', 'commit'],
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Emulate table.vertical title output, but avoid uppercasing and inserting spaces
|
|
||||||
console.log(`== ${application.app_name}`);
|
|
||||||
console.log(
|
|
||||||
getVisuals().table.vertical(application, [
|
|
||||||
'id',
|
|
||||||
'device_type',
|
|
||||||
'slug',
|
|
||||||
'commit',
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 <fleet>';
|
|
||||||
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<FlagsDef, ArgsDef>(AppCmd);
|
|
||||||
if (process.stderr.isTTY) {
|
|
||||||
console.error(warnify(appToFleetCmdMsg));
|
|
||||||
}
|
|
||||||
await super.run(parserOutput);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,115 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright 2016-2020 Balena Ltd.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import 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,
|
|
||||||
appToFleetCmdMsg,
|
|
||||||
warnify,
|
|
||||||
} from '../../utils/messages';
|
|
||||||
|
|
||||||
interface FlagsDef {
|
|
||||||
help: void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ArgsDef {
|
|
||||||
fleet: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FleetPurgeCmd extends Command {
|
|
||||||
public static description = stripIndent`
|
|
||||||
Purge data from a fleet.
|
|
||||||
|
|
||||||
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 fleet purge MyFleet',
|
|
||||||
'$ balena fleet purge myorg/myfleet',
|
|
||||||
];
|
|
||||||
|
|
||||||
public static args = [ca.fleetRequired];
|
|
||||||
|
|
||||||
public static usage = 'fleet purge <fleet>';
|
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
|
||||||
help: cf.help,
|
|
||||||
};
|
|
||||||
|
|
||||||
public static authenticated = true;
|
|
||||||
|
|
||||||
public async run(parserOutput?: ParserOutput<FlagsDef, ArgsDef>) {
|
|
||||||
const { args: params } =
|
|
||||||
parserOutput || this.parse<FlagsDef, ArgsDef>(FleetPurgeCmd);
|
|
||||||
|
|
||||||
const { getApplication } = await import('../../utils/sdk');
|
|
||||||
|
|
||||||
const balena = getBalenaSdk();
|
|
||||||
|
|
||||||
// balena.models.application.purge only accepts a numeric id
|
|
||||||
// so we must first fetch the app to get it's id,
|
|
||||||
const application = await getApplication(balena, params.fleet);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await balena.models.application.purge(application.id);
|
|
||||||
} catch (e) {
|
|
||||||
if (e.message.toLowerCase().includes('no online device(s) found')) {
|
|
||||||
// application.purge throws an error if no devices are online
|
|
||||||
// ignore in this case.
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 <fleet>';
|
|
||||||
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<FlagsDef, ArgsDef>(AppPurgeCmd);
|
|
||||||
if (process.stderr.isTTY) {
|
|
||||||
console.error(warnify(appToFleetCmdMsg));
|
|
||||||
}
|
|
||||||
await super.run(parserOutput);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,170 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright 2016-2020 Balena Ltd.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import 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,
|
|
||||||
appToFleetCmdMsg,
|
|
||||||
warnify,
|
|
||||||
} from '../../utils/messages';
|
|
||||||
|
|
||||||
interface FlagsDef {
|
|
||||||
help: void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ArgsDef {
|
|
||||||
fleet: string;
|
|
||||||
newName?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FleetRenameCmd extends Command {
|
|
||||||
public static description = stripIndent`
|
|
||||||
Rename a fleet.
|
|
||||||
|
|
||||||
Rename a fleet.
|
|
||||||
|
|
||||||
Note, if the \`newName\` parameter is omitted, it will be
|
|
||||||
prompted for interactively.
|
|
||||||
|
|
||||||
${applicationIdInfo.split('\n').join('\n\t\t')}
|
|
||||||
`;
|
|
||||||
|
|
||||||
public static examples = [
|
|
||||||
'$ balena fleet rename OldName',
|
|
||||||
'$ balena fleet rename OldName NewName',
|
|
||||||
'$ balena fleet rename myorg/oldname NewName',
|
|
||||||
];
|
|
||||||
|
|
||||||
public static args = [
|
|
||||||
ca.fleetRequired,
|
|
||||||
{
|
|
||||||
name: 'newName',
|
|
||||||
description: 'the new name for the fleet',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
public static usage = 'fleet rename <fleet> [newName]';
|
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
|
||||||
help: cf.help,
|
|
||||||
};
|
|
||||||
|
|
||||||
public static authenticated = true;
|
|
||||||
|
|
||||||
public async run(parserOutput?: ParserOutput<FlagsDef, ArgsDef>) {
|
|
||||||
const { args: params } =
|
|
||||||
parserOutput || this.parse<FlagsDef, ArgsDef>(FleetRenameCmd);
|
|
||||||
|
|
||||||
const { validateApplicationName } = await import('../../utils/validation');
|
|
||||||
const { ExpectedError } = await import('../../errors');
|
|
||||||
|
|
||||||
const balena = getBalenaSdk();
|
|
||||||
|
|
||||||
// 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.fleet, {
|
|
||||||
$expand: {
|
|
||||||
application_type: {
|
|
||||||
$select: ['is_legacy'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check app exists
|
|
||||||
if (!application) {
|
|
||||||
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(
|
|
||||||
`Fleet ${params.fleet} is of 'legacy' type, and cannot be renamed.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ascertain new name
|
|
||||||
const newName =
|
|
||||||
params.newName ||
|
|
||||||
(await getCliForm().ask({
|
|
||||||
message: 'Please enter the new name for this fleet:',
|
|
||||||
type: 'input',
|
|
||||||
validate: validateApplicationName,
|
|
||||||
})) ||
|
|
||||||
'';
|
|
||||||
|
|
||||||
// Rename
|
|
||||||
try {
|
|
||||||
await balena.models.application.rename(application.id, newName);
|
|
||||||
} catch (e) {
|
|
||||||
// BalenaRequestError: Request error: "organization" and "app_name" must be unique.
|
|
||||||
if ((e.message || '').toLowerCase().includes('unique')) {
|
|
||||||
throw new ExpectedError(`Error: fleet ${newName} already exists.`);
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get application again, to be sure of results
|
|
||||||
const renamedApplication = await balena.models.application.get(
|
|
||||||
application.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Output result
|
|
||||||
console.log(`Fleet renamed`);
|
|
||||||
console.log('From:');
|
|
||||||
console.log(`\tname: ${application.app_name}`);
|
|
||||||
console.log(`\tslug: ${application.slug}`);
|
|
||||||
console.log('To:');
|
|
||||||
console.log(`\tname: ${renamedApplication.app_name}`);
|
|
||||||
console.log(`\tslug: ${renamedApplication.slug}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 <fleet> [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<FlagsDef, ArgsDef>(AppRenameCmd);
|
|
||||||
if (process.stderr.isTTY) {
|
|
||||||
console.error(warnify(appToFleetCmdMsg));
|
|
||||||
}
|
|
||||||
await super.run(parserOutput);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,104 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright 2016-2020 Balena Ltd.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import 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,
|
|
||||||
appToFleetCmdMsg,
|
|
||||||
warnify,
|
|
||||||
} from '../../utils/messages';
|
|
||||||
|
|
||||||
interface FlagsDef {
|
|
||||||
help: void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ArgsDef {
|
|
||||||
fleet: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FleetRestartCmd extends Command {
|
|
||||||
public static description = stripIndent`
|
|
||||||
Restart a fleet.
|
|
||||||
|
|
||||||
Restart all devices belonging to a fleet.
|
|
||||||
|
|
||||||
${applicationIdInfo.split('\n').join('\n\t\t')}
|
|
||||||
`;
|
|
||||||
|
|
||||||
public static examples = [
|
|
||||||
'$ balena fleet restart MyFleet',
|
|
||||||
'$ balena fleet restart myorg/myfleet',
|
|
||||||
];
|
|
||||||
|
|
||||||
public static args = [ca.fleetRequired];
|
|
||||||
|
|
||||||
public static usage = 'fleet restart <fleet>';
|
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
|
||||||
help: cf.help,
|
|
||||||
};
|
|
||||||
|
|
||||||
public static authenticated = true;
|
|
||||||
|
|
||||||
public async run(parserOutput?: ParserOutput<FlagsDef, ArgsDef>) {
|
|
||||||
const { args: params } =
|
|
||||||
parserOutput || this.parse<FlagsDef, ArgsDef>(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.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 <fleet>';
|
|
||||||
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<FlagsDef, ArgsDef>(AppRestartCmd);
|
|
||||||
if (process.stderr.isTTY) {
|
|
||||||
console.error(warnify(appToFleetCmdMsg));
|
|
||||||
}
|
|
||||||
await super.run(parserOutput);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,116 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright 2016-2020 Balena Ltd.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import 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,
|
|
||||||
appToFleetCmdMsg,
|
|
||||||
warnify,
|
|
||||||
} from '../../utils/messages';
|
|
||||||
|
|
||||||
interface FlagsDef {
|
|
||||||
yes: boolean;
|
|
||||||
help: void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ArgsDef {
|
|
||||||
fleet: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FleetRmCmd extends Command {
|
|
||||||
public static description = stripIndent`
|
|
||||||
Remove a fleet.
|
|
||||||
|
|
||||||
Permanently remove a fleet.
|
|
||||||
|
|
||||||
The --yes option may be used to avoid interactive confirmation.
|
|
||||||
|
|
||||||
${applicationIdInfo.split('\n').join('\n\t\t')}
|
|
||||||
`;
|
|
||||||
|
|
||||||
public static examples = [
|
|
||||||
'$ balena fleet rm MyFleet',
|
|
||||||
'$ balena fleet rm MyFleet --yes',
|
|
||||||
'$ balena fleet rm myorg/myfleet',
|
|
||||||
];
|
|
||||||
|
|
||||||
public static args = [ca.fleetRequired];
|
|
||||||
|
|
||||||
public static usage = 'fleet rm <fleet>';
|
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
|
||||||
yes: cf.yes,
|
|
||||||
help: cf.help,
|
|
||||||
};
|
|
||||||
|
|
||||||
public static authenticated = true;
|
|
||||||
|
|
||||||
public async run(parserOutput?: ParserOutput<FlagsDef, ArgsDef>) {
|
|
||||||
const { args: params, flags: options } =
|
|
||||||
parserOutput || this.parse<FlagsDef, ArgsDef>(FleetRmCmd);
|
|
||||||
|
|
||||||
const { confirm } = await import('../../utils/patterns');
|
|
||||||
const { getApplication } = await import('../../utils/sdk');
|
|
||||||
const balena = getBalenaSdk();
|
|
||||||
|
|
||||||
// Confirm
|
|
||||||
await confirm(
|
|
||||||
options.yes ?? false,
|
|
||||||
`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.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 <fleet>';
|
|
||||||
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<FlagsDef, ArgsDef>(AppRmCmd);
|
|
||||||
if (process.stderr.isTTY) {
|
|
||||||
console.error(warnify(appToFleetCmdMsg));
|
|
||||||
}
|
|
||||||
await super.run(parserOutput);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,152 +0,0 @@
|
|||||||
/**
|
|
||||||
* @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 { 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';
|
|
||||||
import { isV13 } from '../utils/version';
|
|
||||||
import type { DataSetOutputOptions } from '../framework';
|
|
||||||
|
|
||||||
interface ExtendedApplication extends ApplicationWithDeviceType {
|
|
||||||
device_count: number;
|
|
||||||
online_devices: number;
|
|
||||||
device_type?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FlagsDef extends DataSetOutputOptions {
|
|
||||||
help: void;
|
|
||||||
verbose?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FleetsCmd extends Command {
|
|
||||||
public static description = stripIndent`
|
|
||||||
List all fleets.
|
|
||||||
|
|
||||||
List all your balena fleets.
|
|
||||||
|
|
||||||
For detailed information on a particular fleet, use
|
|
||||||
\`balena fleet <fleet>\`
|
|
||||||
`;
|
|
||||||
|
|
||||||
public static examples = ['$ balena fleets'];
|
|
||||||
|
|
||||||
public static usage = 'fleets';
|
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
|
||||||
...(isV13()
|
|
||||||
? {}
|
|
||||||
: {
|
|
||||||
verbose: flags.boolean({
|
|
||||||
default: false,
|
|
||||||
char: 'v',
|
|
||||||
description: 'No-op since release v12.0.0',
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
...(isV13() ? cf.dataSetOutputFlags : {}),
|
|
||||||
help: cf.help,
|
|
||||||
};
|
|
||||||
|
|
||||||
public static authenticated = true;
|
|
||||||
public static primary = true;
|
|
||||||
|
|
||||||
protected useAppWord = false;
|
|
||||||
|
|
||||||
public async run(_parserOutput?: ParserOutput<FlagsDef, {}>) {
|
|
||||||
_parserOutput ||= this.parse<FlagsDef, {}>(FleetsCmd);
|
|
||||||
|
|
||||||
const balena = getBalenaSdk();
|
|
||||||
|
|
||||||
// Get applications
|
|
||||||
const applications = (await balena.models.application.getAll({
|
|
||||||
$select: ['id', 'app_name', 'slug'],
|
|
||||||
$expand: {
|
|
||||||
is_for__device_type: { $select: 'slug' },
|
|
||||||
owns__device: { $select: 'is_online' },
|
|
||||||
},
|
|
||||||
})) as ExtendedApplication[];
|
|
||||||
|
|
||||||
// Add extended properties
|
|
||||||
applications.forEach((application) => {
|
|
||||||
application.device_count = application.owns__device?.length ?? 0;
|
|
||||||
application.online_devices =
|
|
||||||
application.owns__device?.filter((d) => d.is_online).length || 0;
|
|
||||||
application.device_type = application.is_for__device_type[0].slug;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isV13()) {
|
|
||||||
await this.outputData(
|
|
||||||
applications,
|
|
||||||
[
|
|
||||||
'id',
|
|
||||||
'app_name',
|
|
||||||
'slug',
|
|
||||||
'device_type',
|
|
||||||
'device_count',
|
|
||||||
'online_devices',
|
|
||||||
],
|
|
||||||
_parserOutput.flags,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
console.log(
|
|
||||||
getVisuals().table.horizontal(applications, [
|
|
||||||
'id',
|
|
||||||
this.useAppWord ? 'app_name' : 'app_name => NAME',
|
|
||||||
'slug',
|
|
||||||
'device_type',
|
|
||||||
'online_devices',
|
|
||||||
'device_count',
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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<FlagsDef, {}>(AppsCmd);
|
|
||||||
if (process.stderr.isTTY) {
|
|
||||||
console.error(warnify(appsToFleetsRenameMsg));
|
|
||||||
}
|
|
||||||
this.useAppWord = true;
|
|
||||||
await super.run(parserOutput);
|
|
||||||
}
|
|
||||||
}
|
|
@ -22,22 +22,18 @@ import * as cf from '../utils/common-flags';
|
|||||||
import * as compose from '../utils/compose';
|
import * as compose from '../utils/compose';
|
||||||
import type { Application, ApplicationType, BalenaSDK } from 'balena-sdk';
|
import type { Application, ApplicationType, BalenaSDK } from 'balena-sdk';
|
||||||
import {
|
import {
|
||||||
appToFleetFlagMsg,
|
|
||||||
buildArgDeprecation,
|
buildArgDeprecation,
|
||||||
dockerignoreHelp,
|
dockerignoreHelp,
|
||||||
registrySecretsHelp,
|
registrySecretsHelp,
|
||||||
warnify,
|
|
||||||
} from '../utils/messages';
|
} from '../utils/messages';
|
||||||
import type { ComposeCliFlags, ComposeOpts } from '../utils/compose-types';
|
import type { ComposeCliFlags, ComposeOpts } from '../utils/compose-types';
|
||||||
import { buildProject, composeCliFlags } from '../utils/compose_ts';
|
import { buildProject, composeCliFlags } from '../utils/compose_ts';
|
||||||
import type { BuildOpts, DockerCliFlags } from '../utils/docker';
|
import type { BuildOpts, DockerCliFlags } from '../utils/docker';
|
||||||
import { dockerCliFlags } from '../utils/docker';
|
import { dockerCliFlags } from '../utils/docker';
|
||||||
import { isV13 } from '../utils/version';
|
|
||||||
|
|
||||||
interface FlagsDef extends ComposeCliFlags, DockerCliFlags {
|
interface FlagsDef extends ComposeCliFlags, DockerCliFlags {
|
||||||
arch?: string;
|
arch?: string;
|
||||||
deviceType?: string;
|
deviceType?: string;
|
||||||
application?: string;
|
|
||||||
fleet?: string;
|
fleet?: string;
|
||||||
source?: string; // Not part of command profile - source param copied here.
|
source?: string; // Not part of command profile - source param copied here.
|
||||||
help: void;
|
help: void;
|
||||||
@ -96,7 +92,6 @@ ${dockerignoreHelp}
|
|||||||
description: 'the type of device this build is for',
|
description: 'the type of device this build is for',
|
||||||
char: 'd',
|
char: 'd',
|
||||||
}),
|
}),
|
||||||
...(isV13() ? {} : { application: cf.application }),
|
|
||||||
fleet: cf.fleet,
|
fleet: cf.fleet,
|
||||||
...composeCliFlags,
|
...composeCliFlags,
|
||||||
...dockerCliFlags,
|
...dockerCliFlags,
|
||||||
@ -112,12 +107,7 @@ ${dockerignoreHelp}
|
|||||||
BuildCmd,
|
BuildCmd,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (options.application && process.stderr.isTTY) {
|
await Command.checkLoggedInIf(!!options.fleet);
|
||||||
console.error(warnify(appToFleetFlagMsg));
|
|
||||||
}
|
|
||||||
options.application ||= options.fleet;
|
|
||||||
|
|
||||||
await Command.checkLoggedInIf(!!options.application);
|
|
||||||
|
|
||||||
(await import('events')).defaultMaxListeners = 1000;
|
(await import('events')).defaultMaxListeners = 1000;
|
||||||
|
|
||||||
@ -161,10 +151,8 @@ ${dockerignoreHelp}
|
|||||||
protected async validateOptions(opts: FlagsDef, sdk: BalenaSDK) {
|
protected async validateOptions(opts: FlagsDef, sdk: BalenaSDK) {
|
||||||
// Validate option combinations
|
// Validate option combinations
|
||||||
if (
|
if (
|
||||||
(opts.application == null &&
|
(opts.fleet == null && (opts.arch == null || opts.deviceType == null)) ||
|
||||||
(opts.arch == null || opts.deviceType == null)) ||
|
(opts.fleet != null && (opts.arch != null || opts.deviceType != null))
|
||||||
(opts.application != null &&
|
|
||||||
(opts.arch != null || opts.deviceType != null))
|
|
||||||
) {
|
) {
|
||||||
const { ExpectedError } = await import('../errors');
|
const { ExpectedError } = await import('../errors');
|
||||||
throw new ExpectedError(
|
throw new ExpectedError(
|
||||||
@ -189,9 +177,9 @@ ${dockerignoreHelp}
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async getAppAndResolveArch(opts: FlagsDef) {
|
protected async getAppAndResolveArch(opts: FlagsDef) {
|
||||||
if (opts.application) {
|
if (opts.fleet) {
|
||||||
const { getAppWithArch } = await import('../utils/helpers');
|
const { getAppWithArch } = await import('../utils/helpers');
|
||||||
const app = await getAppWithArch(opts.application);
|
const app = await getAppWithArch(opts.fleet);
|
||||||
opts.arch = app.arch;
|
opts.arch = app.arch;
|
||||||
opts.deviceType = app.is_for__device_type[0].slug;
|
opts.deviceType = app.is_for__device_type[0].slug;
|
||||||
return app;
|
return app;
|
||||||
@ -271,7 +259,6 @@ ${dockerignoreHelp}
|
|||||||
inlineLogs: composeOpts.inlineLogs,
|
inlineLogs: composeOpts.inlineLogs,
|
||||||
convertEol: composeOpts.convertEol,
|
convertEol: composeOpts.convertEol,
|
||||||
dockerfilePath: composeOpts.dockerfilePath,
|
dockerfilePath: composeOpts.dockerfilePath,
|
||||||
nogitignore: composeOpts.nogitignore, // v13: delete this line
|
|
||||||
multiDockerignore: composeOpts.multiDockerignore,
|
multiDockerignore: composeOpts.multiDockerignore,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -19,18 +19,11 @@ import { flags } from '@oclif/command';
|
|||||||
import Command from '../../command';
|
import Command from '../../command';
|
||||||
import * as cf from '../../utils/common-flags';
|
import * as cf from '../../utils/common-flags';
|
||||||
import { getBalenaSdk, getCliForm, stripIndent } from '../../utils/lazy';
|
import { getBalenaSdk, getCliForm, stripIndent } from '../../utils/lazy';
|
||||||
import {
|
import { applicationIdInfo } from '../../utils/messages';
|
||||||
applicationIdInfo,
|
|
||||||
appToFleetFlagMsg,
|
|
||||||
warnify,
|
|
||||||
} from '../../utils/messages';
|
|
||||||
import { isV13 } from '../../utils/version';
|
|
||||||
import type { PineDeferred } from 'balena-sdk';
|
import type { PineDeferred } from 'balena-sdk';
|
||||||
|
|
||||||
interface FlagsDef {
|
interface FlagsDef {
|
||||||
version: string; // OS version
|
version: string; // OS version
|
||||||
application?: string;
|
|
||||||
app?: string; // application alias
|
|
||||||
fleet?: string;
|
fleet?: string;
|
||||||
device?: string;
|
device?: string;
|
||||||
deviceApiKey?: string;
|
deviceApiKey?: string;
|
||||||
@ -82,22 +75,10 @@ export default class ConfigGenerateCmd extends Command {
|
|||||||
description: 'a balenaOS version',
|
description: 'a balenaOS version',
|
||||||
required: true,
|
required: true,
|
||||||
}),
|
}),
|
||||||
...(isV13()
|
fleet: { ...cf.fleet, exclusive: ['device'] },
|
||||||
? {}
|
|
||||||
: {
|
|
||||||
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: {
|
device: {
|
||||||
...cf.device,
|
...cf.device,
|
||||||
exclusive: ['application', 'app', 'fleet', 'provisioning-key-name'],
|
exclusive: ['fleet', 'provisioning-key-name'],
|
||||||
},
|
},
|
||||||
deviceApiKey: flags.string({
|
deviceApiKey: flags.string({
|
||||||
description:
|
description:
|
||||||
@ -130,7 +111,7 @@ export default class ConfigGenerateCmd extends Command {
|
|||||||
}),
|
}),
|
||||||
appUpdatePollInterval: flags.string({
|
appUpdatePollInterval: flags.string({
|
||||||
description:
|
description:
|
||||||
'supervisor cloud polling interval in minutes (e.g. for variable updates)',
|
'supervisor cloud polling interval in minutes (e.g. for device variables)',
|
||||||
}),
|
}),
|
||||||
'provisioning-key-name': flags.string({
|
'provisioning-key-name': flags.string({
|
||||||
description: 'custom key name assigned to generated provisioning api key',
|
description: 'custom key name assigned to generated provisioning api key',
|
||||||
@ -173,7 +154,7 @@ export default class ConfigGenerateCmd extends Command {
|
|||||||
resourceDeviceType = device.is_of__device_type[0].slug;
|
resourceDeviceType = device.is_of__device_type[0].slug;
|
||||||
} else {
|
} else {
|
||||||
// Disambiguate application (if is a number, it could either be an ID or a numerical name)
|
// Disambiguate application (if is a number, it could either be an ID or a numerical name)
|
||||||
application = (await getApplication(balena, options.application!, {
|
application = (await getApplication(balena, options.fleet!, {
|
||||||
$expand: {
|
$expand: {
|
||||||
is_for__device_type: { $select: 'slug' },
|
is_for__device_type: { $select: 'slug' },
|
||||||
},
|
},
|
||||||
@ -188,7 +169,7 @@ export default class ConfigGenerateCmd extends Command {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Check compatibility if application and deviceType provided
|
// Check compatibility if application and deviceType provided
|
||||||
if (options.application && options.deviceType) {
|
if (options.fleet && options.deviceType) {
|
||||||
const appDeviceManifest = await balena.models.device.getManifestBySlug(
|
const appDeviceManifest = await balena.models.device.getManifestBySlug(
|
||||||
resourceDeviceType,
|
resourceDeviceType,
|
||||||
);
|
);
|
||||||
@ -198,7 +179,7 @@ export default class ConfigGenerateCmd extends Command {
|
|||||||
!helpers.areDeviceTypesCompatible(appDeviceManifest, deviceManifest)
|
!helpers.areDeviceTypesCompatible(appDeviceManifest, deviceManifest)
|
||||||
) {
|
) {
|
||||||
throw new balena.errors.BalenaInvalidDeviceType(
|
throw new balena.errors.BalenaInvalidDeviceType(
|
||||||
`Device type ${options.deviceType} is incompatible with fleet ${options.application}`,
|
`Device type ${options.deviceType} is incompatible with fleet ${options.fleet}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -207,7 +188,7 @@ export default class ConfigGenerateCmd extends Command {
|
|||||||
// Pass params as an override: if there is any param with exactly the same name as a
|
// Pass params as an override: if there is any param with exactly the same name as a
|
||||||
// required option, that value is used (and the corresponding question is not asked)
|
// required option, that value is used (and the corresponding question is not asked)
|
||||||
const answers = await getCliForm().run(deviceManifest.options, {
|
const answers = await getCliForm().run(deviceManifest.options, {
|
||||||
override: options,
|
override: { ...options, app: options.fleet, application: options.fleet },
|
||||||
});
|
});
|
||||||
answers.version = options.version;
|
answers.version = options.version;
|
||||||
answers.provisioningKeyName = options['provisioning-key-name'];
|
answers.provisioningKeyName = options['provisioning-key-name'];
|
||||||
@ -253,18 +234,11 @@ export default class ConfigGenerateCmd extends Command {
|
|||||||
protected async validateOptions(options: FlagsDef) {
|
protected async validateOptions(options: FlagsDef) {
|
||||||
const { ExpectedError } = await import('../../errors');
|
const { ExpectedError } = await import('../../errors');
|
||||||
|
|
||||||
if ((options.application || options.app) && process.stderr.isTTY) {
|
if (options.device == null && options.fleet == null) {
|
||||||
console.error(warnify(appToFleetFlagMsg));
|
|
||||||
}
|
|
||||||
options.application ||= options.app || options.fleet;
|
|
||||||
// Prefer options.application over options.app
|
|
||||||
delete options.app;
|
|
||||||
|
|
||||||
if (options.device == null && options.application == null) {
|
|
||||||
throw new ExpectedError(this.missingDeviceOrAppMessage);
|
throw new ExpectedError(this.missingDeviceOrAppMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!options.application && options.deviceType) {
|
if (!options.fleet && options.deviceType) {
|
||||||
throw new ExpectedError(this.deviceTypeNotAllowedMessage);
|
throw new ExpectedError(this.deviceTypeNotAllowedMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -319,7 +319,6 @@ ${dockerignoreHelp}
|
|||||||
inlineLogs: composeOpts.inlineLogs,
|
inlineLogs: composeOpts.inlineLogs,
|
||||||
convertEol: composeOpts.convertEol,
|
convertEol: composeOpts.convertEol,
|
||||||
dockerfilePath: composeOpts.dockerfilePath,
|
dockerfilePath: composeOpts.dockerfilePath,
|
||||||
nogitignore: composeOpts.nogitignore, // v13: delete this line
|
|
||||||
multiDockerignore: composeOpts.multiDockerignore,
|
multiDockerignore: composeOpts.multiDockerignore,
|
||||||
});
|
});
|
||||||
builtImagesByService = _.keyBy(builtImages, 'serviceName');
|
builtImagesByService = _.keyBy(builtImages, 'serviceName');
|
||||||
|
@ -21,15 +21,13 @@ import Command from '../../command';
|
|||||||
import * as cf from '../../utils/common-flags';
|
import * as cf from '../../utils/common-flags';
|
||||||
import { expandForAppName } from '../../utils/helpers';
|
import { expandForAppName } from '../../utils/helpers';
|
||||||
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
||||||
import { appToFleetOutputMsg, warnify } from '../../utils/messages';
|
|
||||||
import { tryAsInteger } from '../../utils/validation';
|
import { tryAsInteger } from '../../utils/validation';
|
||||||
import { isV13 } from '../../utils/version';
|
|
||||||
|
|
||||||
import type { Application, Release } from 'balena-sdk';
|
import type { Application, Release } from 'balena-sdk';
|
||||||
|
|
||||||
interface ExtendedDevice extends DeviceWithDeviceType {
|
interface ExtendedDevice extends DeviceWithDeviceType {
|
||||||
dashboard_url?: string;
|
dashboard_url?: string;
|
||||||
application_name?: string;
|
fleet: string; // 'org/name' slug
|
||||||
device_type?: string;
|
device_type?: string;
|
||||||
commit?: string;
|
commit?: string;
|
||||||
last_seen?: string;
|
last_seen?: string;
|
||||||
@ -46,7 +44,6 @@ interface ExtendedDevice extends DeviceWithDeviceType {
|
|||||||
|
|
||||||
interface FlagsDef {
|
interface FlagsDef {
|
||||||
help: void;
|
help: void;
|
||||||
v13: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ArgsDef {
|
interface ArgsDef {
|
||||||
@ -74,17 +71,13 @@ export default class DeviceCmd extends Command {
|
|||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
help: cf.help,
|
help: cf.help,
|
||||||
v13: cf.v13,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public static authenticated = true;
|
public static authenticated = true;
|
||||||
public static primary = true;
|
public static primary = true;
|
||||||
|
|
||||||
public async run() {
|
public async run() {
|
||||||
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
|
const { args: params } = this.parse<FlagsDef, ArgsDef>(DeviceCmd);
|
||||||
DeviceCmd,
|
|
||||||
);
|
|
||||||
const useAppWord = !options.v13 && !isV13();
|
|
||||||
|
|
||||||
const balena = getBalenaSdk();
|
const balena = getBalenaSdk();
|
||||||
|
|
||||||
@ -121,8 +114,8 @@ export default class DeviceCmd extends Command {
|
|||||||
|
|
||||||
const belongsToApplication =
|
const belongsToApplication =
|
||||||
device.belongs_to__application as Application[];
|
device.belongs_to__application as Application[];
|
||||||
device.application_name = belongsToApplication?.[0]
|
device.fleet = belongsToApplication?.[0]
|
||||||
? belongsToApplication[0].app_name
|
? belongsToApplication[0].slug
|
||||||
: 'N/a';
|
: 'N/a';
|
||||||
|
|
||||||
device.device_type = device.is_of__device_type[0].slug;
|
device.device_type = device.is_of__device_type[0].slug;
|
||||||
@ -170,10 +163,6 @@ export default class DeviceCmd extends Command {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useAppWord && process.stderr.isTTY) {
|
|
||||||
console.error(warnify(appToFleetOutputMsg));
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
getVisuals().table.vertical(device, [
|
getVisuals().table.vertical(device, [
|
||||||
`$${device.device_name}$`,
|
`$${device.device_name}$`,
|
||||||
@ -184,7 +173,7 @@ export default class DeviceCmd extends Command {
|
|||||||
'ip_address',
|
'ip_address',
|
||||||
'public_address',
|
'public_address',
|
||||||
'mac_address',
|
'mac_address',
|
||||||
useAppWord ? 'application_name' : 'application_name => FLEET',
|
'fleet',
|
||||||
'last_seen',
|
'last_seen',
|
||||||
'uuid',
|
'uuid',
|
||||||
'commit',
|
'commit',
|
||||||
|
@ -19,17 +19,10 @@ import { flags } from '@oclif/command';
|
|||||||
import Command from '../../command';
|
import Command from '../../command';
|
||||||
import * as cf from '../../utils/common-flags';
|
import * as cf from '../../utils/common-flags';
|
||||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||||
import {
|
import { applicationIdInfo } from '../../utils/messages';
|
||||||
applicationIdInfo,
|
|
||||||
appToFleetFlagMsg,
|
|
||||||
warnify,
|
|
||||||
} from '../../utils/messages';
|
|
||||||
import { runCommand } from '../../utils/helpers';
|
import { runCommand } from '../../utils/helpers';
|
||||||
import { isV13 } from '../../utils/version';
|
|
||||||
|
|
||||||
interface FlagsDef {
|
interface FlagsDef {
|
||||||
application?: string;
|
|
||||||
app?: string;
|
|
||||||
fleet?: string;
|
fleet?: string;
|
||||||
yes: boolean;
|
yes: boolean;
|
||||||
advanced: boolean;
|
advanced: boolean;
|
||||||
@ -82,12 +75,6 @@ export default class DeviceInitCmd extends Command {
|
|||||||
public static usage = 'device init';
|
public static usage = 'device init';
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
...(isV13()
|
|
||||||
? {}
|
|
||||||
: {
|
|
||||||
application: cf.application,
|
|
||||||
app: cf.app,
|
|
||||||
}),
|
|
||||||
fleet: cf.fleet,
|
fleet: cf.fleet,
|
||||||
yes: cf.yes,
|
yes: cf.yes,
|
||||||
advanced: flags.boolean({
|
advanced: flags.boolean({
|
||||||
@ -130,17 +117,10 @@ export default class DeviceInitCmd extends Command {
|
|||||||
const logger = await Command.getLogger();
|
const logger = await Command.getLogger();
|
||||||
const balena = getBalenaSdk();
|
const balena = getBalenaSdk();
|
||||||
|
|
||||||
if ((options.application || options.app) && process.stderr.isTTY) {
|
|
||||||
console.error(warnify(appToFleetFlagMsg));
|
|
||||||
}
|
|
||||||
// Consolidate application options
|
|
||||||
options.application ||= options.app || options.fleet;
|
|
||||||
delete options.app;
|
|
||||||
|
|
||||||
// Get application and
|
// Get application and
|
||||||
const application = (await getApplication(
|
const application = (await getApplication(
|
||||||
balena,
|
balena,
|
||||||
options['application'] ||
|
options.fleet ||
|
||||||
(
|
(
|
||||||
await (await import('../../utils/patterns')).selectApplication()
|
await (await import('../../utils/patterns')).selectApplication()
|
||||||
).id,
|
).id,
|
||||||
@ -155,7 +135,7 @@ export default class DeviceInitCmd extends Command {
|
|||||||
|
|
||||||
// Register new device
|
// Register new device
|
||||||
const deviceUuid = balena.models.device.generateUniqueKey();
|
const deviceUuid = balena.models.device.generateUniqueKey();
|
||||||
console.info(`Registering to ${application.app_name}: ${deviceUuid}`);
|
console.info(`Registering to ${application.slug}: ${deviceUuid}`);
|
||||||
await balena.models.device.register(application.id, deviceUuid);
|
await balena.models.device.register(application.id, deviceUuid);
|
||||||
const device = await balena.models.device.get(deviceUuid);
|
const device = await balena.models.device.get(deviceUuid);
|
||||||
|
|
||||||
|
@ -27,12 +27,7 @@ import Command from '../../command';
|
|||||||
import * as cf from '../../utils/common-flags';
|
import * as cf from '../../utils/common-flags';
|
||||||
import { ExpectedError } from '../../errors';
|
import { ExpectedError } from '../../errors';
|
||||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||||
import {
|
import { applicationIdInfo } from '../../utils/messages';
|
||||||
applicationIdInfo,
|
|
||||||
appToFleetFlagMsg,
|
|
||||||
warnify,
|
|
||||||
} from '../../utils/messages';
|
|
||||||
import { isV13 } from '../../utils/version';
|
|
||||||
|
|
||||||
type ExtendedDevice = PineTypedResult<
|
type ExtendedDevice = PineTypedResult<
|
||||||
Device,
|
Device,
|
||||||
@ -42,8 +37,6 @@ type ExtendedDevice = PineTypedResult<
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface FlagsDef {
|
interface FlagsDef {
|
||||||
application?: string;
|
|
||||||
app?: string;
|
|
||||||
fleet?: string;
|
fleet?: string;
|
||||||
help: void;
|
help: void;
|
||||||
}
|
}
|
||||||
@ -82,7 +75,6 @@ export default class DeviceMoveCmd extends Command {
|
|||||||
public static usage = 'device move <uuid(s)>';
|
public static usage = 'device move <uuid(s)>';
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
...(isV13() ? {} : { app: cf.app, application: cf.application }),
|
|
||||||
fleet: cf.fleet,
|
fleet: cf.fleet,
|
||||||
help: cf.help,
|
help: cf.help,
|
||||||
};
|
};
|
||||||
@ -94,11 +86,6 @@ export default class DeviceMoveCmd extends Command {
|
|||||||
DeviceMoveCmd,
|
DeviceMoveCmd,
|
||||||
);
|
);
|
||||||
|
|
||||||
if ((options.application || options.app) && process.stderr.isTTY) {
|
|
||||||
console.error(warnify(appToFleetFlagMsg));
|
|
||||||
}
|
|
||||||
options.application ||= options.app || options.fleet;
|
|
||||||
|
|
||||||
const balena = getBalenaSdk();
|
const balena = getBalenaSdk();
|
||||||
|
|
||||||
const { tryAsInteger } = await import('../../utils/validation');
|
const { tryAsInteger } = await import('../../utils/validation');
|
||||||
@ -132,8 +119,8 @@ export default class DeviceMoveCmd extends Command {
|
|||||||
const { getApplication } = await import('../../utils/sdk');
|
const { getApplication } = await import('../../utils/sdk');
|
||||||
|
|
||||||
// Get destination application
|
// Get destination application
|
||||||
const application = options.application
|
const application = options.fleet
|
||||||
? await getApplication(balena, options.application)
|
? await getApplication(balena, options.fleet)
|
||||||
: await this.interactivelySelectApplication(balena, devices);
|
: await this.interactivelySelectApplication(balena, devices);
|
||||||
|
|
||||||
// Move each device
|
// Move each device
|
||||||
|
@ -75,7 +75,7 @@ export default class DeviceRegisterCmd extends Command {
|
|||||||
const application = await getApplication(balena, params.fleet);
|
const application = await getApplication(balena, params.fleet);
|
||||||
const uuid = options.uuid ?? balena.models.device.generateUniqueKey();
|
const uuid = options.uuid ?? balena.models.device.generateUniqueKey();
|
||||||
|
|
||||||
console.info(`Registering to ${application.app_name}: ${uuid}`);
|
console.info(`Registering to ${application.slug}: ${uuid}`);
|
||||||
|
|
||||||
const result = await balena.models.device.register(application.id, uuid);
|
const result = await balena.models.device.register(application.id, uuid);
|
||||||
|
|
||||||
|
@ -20,30 +20,20 @@ import Command from '../../command';
|
|||||||
import * as cf from '../../utils/common-flags';
|
import * as cf from '../../utils/common-flags';
|
||||||
import { expandForAppName } from '../../utils/helpers';
|
import { expandForAppName } from '../../utils/helpers';
|
||||||
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
||||||
import {
|
import { applicationIdInfo, jsonInfo } from '../../utils/messages';
|
||||||
applicationIdInfo,
|
|
||||||
appToFleetFlagMsg,
|
|
||||||
appToFleetOutputMsg,
|
|
||||||
jsonInfo,
|
|
||||||
warnify,
|
|
||||||
} from '../../utils/messages';
|
|
||||||
import { isV13 } from '../../utils/version';
|
|
||||||
|
|
||||||
import type { Application } from 'balena-sdk';
|
import type { Application } from 'balena-sdk';
|
||||||
|
|
||||||
interface ExtendedDevice extends DeviceWithDeviceType {
|
interface ExtendedDevice extends DeviceWithDeviceType {
|
||||||
dashboard_url?: string;
|
dashboard_url?: string;
|
||||||
application_name?: string | null;
|
fleet?: string | null; // 'org/name' slug
|
||||||
device_type?: string | null;
|
device_type?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FlagsDef {
|
interface FlagsDef {
|
||||||
application?: string;
|
|
||||||
app?: string;
|
|
||||||
fleet?: string;
|
fleet?: string;
|
||||||
help: void;
|
help: void;
|
||||||
json: boolean;
|
json: boolean;
|
||||||
v13: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class DevicesCmd extends Command {
|
export default class DevicesCmd extends Command {
|
||||||
@ -67,50 +57,25 @@ export default class DevicesCmd extends Command {
|
|||||||
public static usage = 'devices';
|
public static usage = 'devices';
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
...(isV13()
|
fleet: cf.fleet,
|
||||||
? {}
|
|
||||||
: {
|
|
||||||
application: {
|
|
||||||
...cf.application,
|
|
||||||
exclusive: ['app', 'fleet', 'v13'],
|
|
||||||
},
|
|
||||||
app: { ...cf.app, exclusive: ['application', 'fleet', 'v13'] },
|
|
||||||
}),
|
|
||||||
fleet: { ...cf.fleet, exclusive: ['app', 'application'] },
|
|
||||||
json: cf.json,
|
json: cf.json,
|
||||||
help: cf.help,
|
help: cf.help,
|
||||||
v13: cf.v13,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public static primary = true;
|
public static primary = true;
|
||||||
|
|
||||||
public static authenticated = true;
|
public static authenticated = true;
|
||||||
|
|
||||||
protected useAppWord = false;
|
|
||||||
protected hasWarned = false;
|
|
||||||
|
|
||||||
public async run() {
|
public async run() {
|
||||||
const { flags: options } = this.parse<FlagsDef, {}>(DevicesCmd);
|
const { flags: options } = this.parse<FlagsDef, {}>(DevicesCmd);
|
||||||
this.useAppWord = !options.fleet && !options.v13 && !isV13();
|
|
||||||
|
|
||||||
const balena = getBalenaSdk();
|
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.app || options.fleet;
|
|
||||||
|
|
||||||
let devices;
|
let devices;
|
||||||
|
|
||||||
if (options.application != null) {
|
if (options.fleet != null) {
|
||||||
const { getApplication } = await import('../../utils/sdk');
|
const { getApplication } = await import('../../utils/sdk');
|
||||||
const application = await getApplication(balena, options.application);
|
const application = await getApplication(balena, options.fleet);
|
||||||
devices = (await balena.models.device.getAllByApplication(
|
devices = (await balena.models.device.getAllByApplication(
|
||||||
application.id,
|
application.id,
|
||||||
expandForAppName,
|
expandForAppName,
|
||||||
@ -126,7 +91,7 @@ export default class DevicesCmd extends Command {
|
|||||||
|
|
||||||
const belongsToApplication =
|
const belongsToApplication =
|
||||||
device.belongs_to__application as Application[];
|
device.belongs_to__application as Application[];
|
||||||
device.application_name = belongsToApplication?.[0]?.app_name || null;
|
device.fleet = belongsToApplication?.[0]?.slug || null;
|
||||||
|
|
||||||
device.uuid = options.json ? device.uuid : device.uuid.slice(0, 7);
|
device.uuid = options.json ? device.uuid : device.uuid.slice(0, 7);
|
||||||
|
|
||||||
@ -134,16 +99,12 @@ export default class DevicesCmd extends Command {
|
|||||||
return device;
|
return device;
|
||||||
});
|
});
|
||||||
|
|
||||||
const jName = this.useAppWord ? 'application_name' : 'fleet_name';
|
|
||||||
const tName = this.useAppWord ? 'APPLICATION NAME' : 'FLEET';
|
|
||||||
const fields = [
|
const fields = [
|
||||||
'id',
|
'id',
|
||||||
'uuid',
|
'uuid',
|
||||||
'device_name',
|
'device_name',
|
||||||
'device_type',
|
'device_type',
|
||||||
options.json
|
'fleet',
|
||||||
? `application_name => ${jName}`
|
|
||||||
: `application_name => ${tName}`,
|
|
||||||
'status',
|
'status',
|
||||||
'is_online',
|
'is_online',
|
||||||
'supervisor_version',
|
'supervisor_version',
|
||||||
@ -156,9 +117,6 @@ export default class DevicesCmd extends Command {
|
|||||||
const mapped = devices.map((device) => pickAndRename(device, fields));
|
const mapped = devices.map((device) => pickAndRename(device, fields));
|
||||||
console.log(JSON.stringify(mapped, null, 4));
|
console.log(JSON.stringify(mapped, null, 4));
|
||||||
} else {
|
} else {
|
||||||
if (!this.hasWarned && this.useAppWord && process.stderr.isTTY) {
|
|
||||||
console.error(warnify(appToFleetOutputMsg));
|
|
||||||
}
|
|
||||||
const _ = await import('lodash');
|
const _ = await import('lodash');
|
||||||
console.log(
|
console.log(
|
||||||
getVisuals().table.horizontal(
|
getVisuals().table.horizontal(
|
||||||
|
@ -21,33 +21,18 @@ import Command from '../../command';
|
|||||||
import * as cf from '../../utils/common-flags';
|
import * as cf from '../../utils/common-flags';
|
||||||
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
||||||
import { CommandHelp } from '../../utils/oclif-utils';
|
import { CommandHelp } from '../../utils/oclif-utils';
|
||||||
import { isV13 } from '../../utils/version';
|
|
||||||
|
|
||||||
interface FlagsDef {
|
interface FlagsDef {
|
||||||
discontinued: boolean;
|
|
||||||
help: void;
|
help: void;
|
||||||
json?: boolean;
|
json?: boolean;
|
||||||
verbose?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const deprecatedInfo = isV13()
|
|
||||||
? ''
|
|
||||||
: `
|
|
||||||
The --verbose option may add extra columns/fields to the output. Currently
|
|
||||||
this includes the "STATE" column which is DEPRECATED and whose values are one
|
|
||||||
of 'new', 'released' or 'discontinued'. However, 'discontinued' device types
|
|
||||||
are only listed if the '--discontinued' option is also used, and this option
|
|
||||||
is also DEPRECATED.
|
|
||||||
`
|
|
||||||
.split('\n')
|
|
||||||
.join(`\n\t\t`);
|
|
||||||
|
|
||||||
export default class DevicesSupportedCmd extends Command {
|
export default class DevicesSupportedCmd extends Command {
|
||||||
public static description = stripIndent`
|
public static description = stripIndent`
|
||||||
List the supported device types (like 'raspberrypi3' or 'intel-nuc').
|
List the supported device types (like 'raspberrypi3' or 'intel-nuc').
|
||||||
|
|
||||||
List the supported device types (like 'raspberrypi3' or 'intel-nuc').
|
List the supported device types (like 'raspberrypi3' or 'intel-nuc').
|
||||||
${deprecatedInfo}
|
|
||||||
The --json option is recommended when scripting the output of this command,
|
The --json option is recommended when scripting the output of this command,
|
||||||
because the JSON format is less likely to change and it better represents data
|
because the JSON format is less likely to change and it better represents data
|
||||||
types like lists and empty strings (for example, the ALIASES column contains a
|
types like lists and empty strings (for example, the ALIASES column contains a
|
||||||
@ -56,8 +41,7 @@ export default class DevicesSupportedCmd extends Command {
|
|||||||
`;
|
`;
|
||||||
public static examples = [
|
public static examples = [
|
||||||
'$ balena devices supported',
|
'$ balena devices supported',
|
||||||
'$ balena devices supported --verbose',
|
'$ balena devices supported --json',
|
||||||
'$ balena devices supported -vj',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
public static usage = (
|
public static usage = (
|
||||||
@ -66,22 +50,11 @@ export default class DevicesSupportedCmd extends Command {
|
|||||||
).trim();
|
).trim();
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
discontinued: flags.boolean({
|
|
||||||
description: isV13()
|
|
||||||
? 'No effect (DEPRECATED)'
|
|
||||||
: 'include "discontinued" device types (DEPRECATED)',
|
|
||||||
}),
|
|
||||||
help: cf.help,
|
help: cf.help,
|
||||||
json: flags.boolean({
|
json: flags.boolean({
|
||||||
char: 'j',
|
char: 'j',
|
||||||
description: 'produce JSON output instead of tabular output',
|
description: 'produce JSON output instead of tabular output',
|
||||||
}),
|
}),
|
||||||
verbose: flags.boolean({
|
|
||||||
char: 'v',
|
|
||||||
description: isV13()
|
|
||||||
? 'No effect (DEPRECATED)'
|
|
||||||
: 'add extra columns in the tabular output (DEPRECATED)',
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public async run() {
|
public async run() {
|
||||||
@ -95,53 +68,28 @@ export default class DevicesSupportedCmd extends Command {
|
|||||||
]);
|
]);
|
||||||
const dtsBySlug = _.keyBy(dts, (dt) => dt.slug);
|
const dtsBySlug = _.keyBy(dts, (dt) => dt.slug);
|
||||||
const configDTsBySlug = _.keyBy(configDTs, (dt) => dt.slug);
|
const configDTsBySlug = _.keyBy(configDTs, (dt) => dt.slug);
|
||||||
const discontinuedDTs = isV13()
|
|
||||||
? []
|
|
||||||
: configDTs.filter((dt) => dt.state === 'DISCONTINUED');
|
|
||||||
const discontinuedDTsBySlug = _.keyBy(discontinuedDTs, (dt) => dt.slug);
|
|
||||||
// set of slugs from models.deviceType.getAllSupported() plus slugs of
|
|
||||||
// discontinued device types as per models.config.getDeviceTypes()
|
|
||||||
const slugsOfInterest = new Set([
|
|
||||||
...Object.keys(dtsBySlug),
|
|
||||||
...Object.keys(discontinuedDTsBySlug),
|
|
||||||
]);
|
|
||||||
interface DT {
|
interface DT {
|
||||||
slug: string;
|
slug: string;
|
||||||
aliases: string[];
|
aliases: string[];
|
||||||
arch: string;
|
arch: string;
|
||||||
state?: string; // to be removed in CLI v13
|
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
let deviceTypes: DT[] = [];
|
let deviceTypes: DT[] = [];
|
||||||
for (const slug of slugsOfInterest) {
|
for (const slug of Object.keys(dtsBySlug)) {
|
||||||
const configDT: Partial<typeof configDTs[0]> =
|
const configDT: Partial<typeof configDTs[0]> =
|
||||||
configDTsBySlug[slug] || {};
|
configDTsBySlug[slug] || {};
|
||||||
if (configDT.state === 'DISCONTINUED' && !options.discontinued) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const dt: Partial<typeof dts[0]> = dtsBySlug[slug] || {};
|
|
||||||
const aliases = (configDT.aliases || []).filter(
|
const aliases = (configDT.aliases || []).filter(
|
||||||
(alias) => alias !== slug,
|
(alias) => alias !== slug,
|
||||||
);
|
);
|
||||||
|
const dt: Partial<typeof dts[0]> = dtsBySlug[slug] || {};
|
||||||
deviceTypes.push({
|
deviceTypes.push({
|
||||||
slug,
|
slug,
|
||||||
aliases: options.json ? aliases : [aliases.join(', ')],
|
aliases: options.json ? aliases : [aliases.join(', ')],
|
||||||
arch:
|
arch: (dt.is_of__cpu_architecture as any)?.[0]?.slug || 'n/a',
|
||||||
(dt.is_of__cpu_architecture as any)?.[0]?.slug ||
|
name: dt.name || 'N/A',
|
||||||
configDT.arch ||
|
|
||||||
'n/a',
|
|
||||||
// 'BETA' renamed to 'NEW'
|
|
||||||
// https://www.flowdock.com/app/rulemotion/i-cli/threads/1svvyaf8FAZeSdG4dPJc4kHOvJU
|
|
||||||
state: isV13()
|
|
||||||
? undefined
|
|
||||||
: (configDT.state || 'NEW').replace('BETA', 'NEW'),
|
|
||||||
name: dt.name || configDT.name || 'N/A',
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const fields =
|
const fields = ['slug', 'aliases', 'arch', 'name'];
|
||||||
options.verbose && !isV13()
|
|
||||||
? ['slug', 'aliases', 'arch', 'state', 'name']
|
|
||||||
: ['slug', 'aliases', 'arch', 'name'];
|
|
||||||
deviceTypes = _.sortBy(deviceTypes, fields);
|
deviceTypes = _.sortBy(deviceTypes, fields);
|
||||||
if (options.json) {
|
if (options.json) {
|
||||||
console.log(JSON.stringify(deviceTypes, null, 4));
|
console.log(JSON.stringify(deviceTypes, null, 4));
|
||||||
|
32
lib/commands/env/add.ts
vendored
32
lib/commands/env/add.ts
vendored
@ -21,15 +21,9 @@ import Command from '../../command';
|
|||||||
import { ExpectedError } from '../../errors';
|
import { ExpectedError } from '../../errors';
|
||||||
import * as cf from '../../utils/common-flags';
|
import * as cf from '../../utils/common-flags';
|
||||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||||
import {
|
import { applicationIdInfo } from '../../utils/messages';
|
||||||
applicationIdInfo,
|
|
||||||
appToFleetFlagMsg,
|
|
||||||
warnify,
|
|
||||||
} from '../../utils/messages';
|
|
||||||
import { isV13 } from '../../utils/version';
|
|
||||||
|
|
||||||
interface FlagsDef {
|
interface FlagsDef {
|
||||||
application?: string;
|
|
||||||
fleet?: string;
|
fleet?: string;
|
||||||
device?: string; // device UUID
|
device?: string; // device UUID
|
||||||
help: void;
|
help: void;
|
||||||
@ -101,11 +95,8 @@ export default class EnvAddCmd extends Command {
|
|||||||
public static usage = 'env add <name> [value]';
|
public static usage = 'env add <name> [value]';
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
...(isV13()
|
fleet: { ...cf.fleet, exclusive: ['device'] },
|
||||||
? {}
|
device: { ...cf.device, exclusive: ['fleet'] },
|
||||||
: { application: { ...cf.application, exclusive: ['fleet', 'device'] } }),
|
|
||||||
fleet: { ...cf.fleet, exclusive: ['application', 'device'] },
|
|
||||||
device: { ...cf.device, exclusive: ['application', 'fleet'] },
|
|
||||||
help: cf.help,
|
help: cf.help,
|
||||||
quiet: cf.quiet,
|
quiet: cf.quiet,
|
||||||
service: cf.service,
|
service: cf.service,
|
||||||
@ -117,11 +108,7 @@ export default class EnvAddCmd extends Command {
|
|||||||
);
|
);
|
||||||
const cmd = this;
|
const cmd = this;
|
||||||
|
|
||||||
if (options.application && process.stderr.isTTY) {
|
if (!options.fleet && !options.device) {
|
||||||
console.error(warnify(appToFleetFlagMsg));
|
|
||||||
}
|
|
||||||
options.application ||= options.fleet;
|
|
||||||
if (!options.application && !options.device) {
|
|
||||||
throw new ExpectedError(
|
throw new ExpectedError(
|
||||||
'Either the --fleet or the --device option must be specified',
|
'Either the --fleet or the --device option must be specified',
|
||||||
);
|
);
|
||||||
@ -163,11 +150,12 @@ export default class EnvAddCmd extends Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const varType = isConfigVar ? 'configVar' : 'envVar';
|
const varType = isConfigVar ? 'configVar' : 'envVar';
|
||||||
if (options.application) {
|
if (options.fleet) {
|
||||||
for (const app of options.application.split(',')) {
|
const { getFleetSlug } = await import('../../utils/sdk');
|
||||||
|
for (const app of options.fleet.split(',')) {
|
||||||
try {
|
try {
|
||||||
await balena.models.application[varType].set(
|
await balena.models.application[varType].set(
|
||||||
app,
|
await getFleetSlug(balena, app),
|
||||||
params.name,
|
params.name,
|
||||||
params.value,
|
params.value,
|
||||||
);
|
);
|
||||||
@ -201,8 +189,8 @@ async function setServiceVars(
|
|||||||
params: ArgsDef,
|
params: ArgsDef,
|
||||||
options: FlagsDef,
|
options: FlagsDef,
|
||||||
) {
|
) {
|
||||||
if (options.application) {
|
if (options.fleet) {
|
||||||
for (const app of options.application.split(',')) {
|
for (const app of options.fleet.split(',')) {
|
||||||
for (const service of options.service!.split(',')) {
|
for (const service of options.service!.split(',')) {
|
||||||
try {
|
try {
|
||||||
const serviceId = await getServiceIdForApp(sdk, app, service);
|
const serviceId = await getServiceIdForApp(sdk, app, service);
|
||||||
|
@ -21,42 +21,33 @@ import Command from '../command';
|
|||||||
import { ExpectedError } from '../errors';
|
import { ExpectedError } from '../errors';
|
||||||
import * as cf from '../utils/common-flags';
|
import * as cf from '../utils/common-flags';
|
||||||
import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy';
|
import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy';
|
||||||
import {
|
import { applicationIdInfo } from '../utils/messages';
|
||||||
applicationIdInfo,
|
|
||||||
appToFleetFlagMsg,
|
|
||||||
appToFleetOutputMsg,
|
|
||||||
warnify,
|
|
||||||
} from '../utils/messages';
|
|
||||||
import { isV13 } from '../utils/version';
|
|
||||||
|
|
||||||
interface FlagsDef {
|
interface FlagsDef {
|
||||||
application?: string;
|
|
||||||
fleet?: string;
|
fleet?: string;
|
||||||
config: boolean;
|
config: boolean;
|
||||||
device?: string; // device UUID
|
device?: string; // device UUID
|
||||||
json: boolean;
|
json: boolean;
|
||||||
help: void;
|
help: void;
|
||||||
service?: string; // service name
|
service?: string; // service name
|
||||||
verbose: boolean;
|
|
||||||
v13: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EnvironmentVariableInfo extends SDK.EnvironmentVariableBase {
|
interface EnvironmentVariableInfo extends SDK.EnvironmentVariableBase {
|
||||||
appName?: string | null; // application name
|
fleet?: string | null; // fleet slug
|
||||||
deviceUUID?: string; // device UUID
|
deviceUUID?: string; // device UUID
|
||||||
serviceName?: string; // service name
|
serviceName?: string; // service name
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DeviceServiceEnvironmentVariableInfo
|
interface DeviceServiceEnvironmentVariableInfo
|
||||||
extends SDK.DeviceServiceEnvironmentVariable {
|
extends SDK.DeviceServiceEnvironmentVariable {
|
||||||
appName?: string; // application name
|
fleet?: string; // fleet slug
|
||||||
deviceUUID?: string; // device UUID
|
deviceUUID?: string; // device UUID
|
||||||
serviceName?: string; // service name
|
serviceName?: string; // service name
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ServiceEnvironmentVariableInfo
|
interface ServiceEnvironmentVariableInfo
|
||||||
extends SDK.ServiceEnvironmentVariable {
|
extends SDK.ServiceEnvironmentVariable {
|
||||||
appName?: string; // application name
|
fleet?: string; // fleet slug
|
||||||
deviceUUID?: string; // device UUID
|
deviceUUID?: string; // device UUID
|
||||||
serviceName?: string; // service name
|
serviceName?: string; // service name
|
||||||
}
|
}
|
||||||
@ -96,8 +87,6 @@ export default class EnvsCmd extends Command {
|
|||||||
in case the current user was removed from the fleet by the fleet's owner).
|
in case the current user was removed from the fleet by the fleet's owner).
|
||||||
|
|
||||||
${applicationIdInfo.split('\n').join('\n\t\t')}
|
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||||
|
|
||||||
${appToFleetOutputMsg.split('\n').join('\n\t\t')}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
public static examples = [
|
public static examples = [
|
||||||
@ -115,57 +104,35 @@ export default class EnvsCmd extends Command {
|
|||||||
public static usage = 'envs';
|
public static usage = 'envs';
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
...(isV13()
|
fleet: { ...cf.fleet, exclusive: ['device'] },
|
||||||
? {}
|
|
||||||
: {
|
|
||||||
all: flags.boolean({
|
|
||||||
default: false,
|
|
||||||
description: 'No-op since balena CLI v12.0.0.',
|
|
||||||
hidden: true,
|
|
||||||
}),
|
|
||||||
application: {
|
|
||||||
exclusive: ['device', 'fleet', 'v13'],
|
|
||||||
...cf.application,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
fleet: { exclusive: ['device', 'application'], ...cf.fleet },
|
|
||||||
config: flags.boolean({
|
config: flags.boolean({
|
||||||
default: false,
|
default: false,
|
||||||
char: 'c',
|
char: 'c',
|
||||||
description: 'show configuration variables only',
|
description: 'show configuration variables only',
|
||||||
exclusive: ['service'],
|
exclusive: ['service'],
|
||||||
}),
|
}),
|
||||||
device: { exclusive: ['fleet', 'application'], ...cf.device },
|
device: { ...cf.device, exclusive: ['fleet'] },
|
||||||
help: cf.help,
|
help: cf.help,
|
||||||
json: cf.json,
|
json: cf.json,
|
||||||
verbose: cf.verbose,
|
service: { ...cf.service, exclusive: ['config'] },
|
||||||
service: { exclusive: ['config'], ...cf.service },
|
|
||||||
v13: cf.v13,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
protected useAppWord = false;
|
|
||||||
protected hasWarned = false;
|
|
||||||
|
|
||||||
public async run() {
|
public async run() {
|
||||||
const { flags: options } = this.parse<FlagsDef, {}>(EnvsCmd);
|
const { flags: options } = this.parse<FlagsDef, {}>(EnvsCmd);
|
||||||
this.useAppWord = !options.fleet && !options.v13 && !isV13();
|
|
||||||
|
|
||||||
const variables: EnvironmentVariableInfo[] = [];
|
const variables: EnvironmentVariableInfo[] = [];
|
||||||
|
|
||||||
await Command.checkLoggedIn();
|
await Command.checkLoggedIn();
|
||||||
|
|
||||||
if (options.application && !options.json && process.stderr.isTTY) {
|
if (!options.fleet && !options.device) {
|
||||||
this.hasWarned = true;
|
|
||||||
console.error(warnify(appToFleetFlagMsg));
|
|
||||||
}
|
|
||||||
options.application ||= options.fleet;
|
|
||||||
if (!options.application && !options.device) {
|
|
||||||
throw new ExpectedError('Missing --fleet or --device option');
|
throw new ExpectedError('Missing --fleet or --device option');
|
||||||
}
|
}
|
||||||
|
|
||||||
const balena = getBalenaSdk();
|
const balena = getBalenaSdk();
|
||||||
|
|
||||||
let appNameOrSlug = options.application;
|
let fleetSlug: string | undefined = options.fleet
|
||||||
|
? await (await import('../utils/sdk')).getFleetSlug(balena, options.fleet)
|
||||||
|
: undefined;
|
||||||
let fullUUID: string | undefined; // as oppposed to the short, 7-char UUID
|
let fullUUID: string | undefined; // as oppposed to the short, 7-char UUID
|
||||||
|
|
||||||
if (options.device) {
|
if (options.device) {
|
||||||
@ -178,23 +145,23 @@ export default class EnvsCmd extends Command {
|
|||||||
);
|
);
|
||||||
fullUUID = device.uuid;
|
fullUUID = device.uuid;
|
||||||
if (app) {
|
if (app) {
|
||||||
appNameOrSlug = app.slug;
|
fleetSlug = app.slug;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (appNameOrSlug && options.service) {
|
if (fleetSlug && options.service) {
|
||||||
await validateServiceName(balena, options.service, appNameOrSlug);
|
await validateServiceName(balena, options.service, fleetSlug);
|
||||||
}
|
}
|
||||||
variables.push(...(await getAppVars(balena, appNameOrSlug, options)));
|
variables.push(...(await getAppVars(balena, fleetSlug, options)));
|
||||||
if (fullUUID) {
|
if (fullUUID) {
|
||||||
variables.push(
|
variables.push(
|
||||||
...(await getDeviceVars(balena, fullUUID, appNameOrSlug, options)),
|
...(await getDeviceVars(balena, fullUUID, fleetSlug, options)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!options.json && variables.length === 0) {
|
if (!options.json && variables.length === 0) {
|
||||||
const target =
|
const target =
|
||||||
(options.service ? `service "${options.service}" of ` : '') +
|
(options.service ? `service "${options.service}" of ` : '') +
|
||||||
(options.application
|
(options.fleet
|
||||||
? `fleet "${options.application}"`
|
? `fleet "${options.fleet}"`
|
||||||
: `device "${options.device}"`);
|
: `device "${options.device}"`);
|
||||||
throw new ExpectedError(`No environment variables found for ${target}`);
|
throw new ExpectedError(`No environment variables found for ${target}`);
|
||||||
}
|
}
|
||||||
@ -206,24 +173,14 @@ export default class EnvsCmd extends Command {
|
|||||||
varArray: EnvironmentVariableInfo[],
|
varArray: EnvironmentVariableInfo[],
|
||||||
options: FlagsDef,
|
options: FlagsDef,
|
||||||
) {
|
) {
|
||||||
const fields = ['id', 'name', 'value'];
|
const fields = ['id', 'name', 'value', 'fleet'];
|
||||||
|
|
||||||
// Replace undefined app names with 'N/A' or null
|
// Replace undefined app names with 'N/A' or null
|
||||||
varArray = varArray.map((i: EnvironmentVariableInfo) => {
|
varArray = varArray.map((i: EnvironmentVariableInfo) => {
|
||||||
if (i.appName) {
|
i.fleet ||= options.json ? null : 'N/A';
|
||||||
// use slug in v13, app name in v12 for compatibility
|
|
||||||
i.appName = isV13()
|
|
||||||
? i.appName
|
|
||||||
: i.appName.substring(i.appName.indexOf('/') + 1);
|
|
||||||
} else {
|
|
||||||
i.appName = options.json ? null : 'N/A';
|
|
||||||
}
|
|
||||||
return i;
|
return i;
|
||||||
});
|
});
|
||||||
|
|
||||||
const jName = this.useAppWord ? 'appName' : 'fleetName';
|
|
||||||
const tName = this.useAppWord ? 'APPLICATION' : 'FLEET';
|
|
||||||
fields.push(options.json ? `appName => ${jName}` : `appName => ${tName}`);
|
|
||||||
if (options.device) {
|
if (options.device) {
|
||||||
fields.push(options.json ? 'deviceUUID' : 'deviceUUID => DEVICE');
|
fields.push(options.json ? 'deviceUUID' : 'deviceUUID => DEVICE');
|
||||||
}
|
}
|
||||||
@ -236,9 +193,6 @@ export default class EnvsCmd extends Command {
|
|||||||
const mapped = varArray.map((o) => pickAndRename(o, fields));
|
const mapped = varArray.map((o) => pickAndRename(o, fields));
|
||||||
this.log(JSON.stringify(mapped, null, 4));
|
this.log(JSON.stringify(mapped, null, 4));
|
||||||
} else {
|
} else {
|
||||||
if (!this.hasWarned && this.useAppWord && process.stderr.isTTY) {
|
|
||||||
console.error(warnify(appToFleetOutputMsg));
|
|
||||||
}
|
|
||||||
this.log(
|
this.log(
|
||||||
getVisuals().table.horizontal(
|
getVisuals().table.horizontal(
|
||||||
_.sortBy(varArray, (v: SDK.EnvironmentVariableBase) => v.name),
|
_.sortBy(varArray, (v: SDK.EnvironmentVariableBase) => v.name),
|
||||||
@ -252,14 +206,14 @@ export default class EnvsCmd extends Command {
|
|||||||
async function validateServiceName(
|
async function validateServiceName(
|
||||||
sdk: SDK.BalenaSDK,
|
sdk: SDK.BalenaSDK,
|
||||||
serviceName: string,
|
serviceName: string,
|
||||||
appName: string,
|
fleetSlug: string,
|
||||||
) {
|
) {
|
||||||
const services = await sdk.models.service.getAllByApplication(appName, {
|
const services = await sdk.models.service.getAllByApplication(fleetSlug, {
|
||||||
$filter: { service_name: serviceName },
|
$filter: { service_name: serviceName },
|
||||||
});
|
});
|
||||||
if (services.length === 0) {
|
if (services.length === 0) {
|
||||||
throw new ExpectedError(
|
throw new ExpectedError(
|
||||||
`Service "${serviceName}" not found for fleet "${appName}"`,
|
`Service "${serviceName}" not found for fleet "${fleetSlug}"`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -273,17 +227,17 @@ async function validateServiceName(
|
|||||||
*/
|
*/
|
||||||
async function getAppVars(
|
async function getAppVars(
|
||||||
sdk: SDK.BalenaSDK,
|
sdk: SDK.BalenaSDK,
|
||||||
appNameOrSlug: string | undefined,
|
fleetSlug: string | undefined,
|
||||||
options: FlagsDef,
|
options: FlagsDef,
|
||||||
): Promise<EnvironmentVariableInfo[]> {
|
): Promise<EnvironmentVariableInfo[]> {
|
||||||
const appVars: EnvironmentVariableInfo[] = [];
|
const appVars: EnvironmentVariableInfo[] = [];
|
||||||
if (!appNameOrSlug) {
|
if (!fleetSlug) {
|
||||||
return appVars;
|
return appVars;
|
||||||
}
|
}
|
||||||
const vars = await sdk.models.application[
|
const vars = await sdk.models.application[
|
||||||
options.config ? 'configVar' : 'envVar'
|
options.config ? 'configVar' : 'envVar'
|
||||||
].getAllByApplication(appNameOrSlug);
|
].getAllByApplication(fleetSlug);
|
||||||
fillInInfoFields(vars, appNameOrSlug);
|
fillInInfoFields(vars, fleetSlug);
|
||||||
appVars.push(...vars);
|
appVars.push(...vars);
|
||||||
if (!options.config) {
|
if (!options.config) {
|
||||||
const pineOpts: SDK.PineOptions<SDK.ServiceEnvironmentVariable> = {
|
const pineOpts: SDK.PineOptions<SDK.ServiceEnvironmentVariable> = {
|
||||||
@ -299,10 +253,10 @@ async function getAppVars(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
const serviceVars = await sdk.models.service.var.getAllByApplication(
|
const serviceVars = await sdk.models.service.var.getAllByApplication(
|
||||||
appNameOrSlug,
|
fleetSlug,
|
||||||
pineOpts,
|
pineOpts,
|
||||||
);
|
);
|
||||||
fillInInfoFields(serviceVars, appNameOrSlug);
|
fillInInfoFields(serviceVars, fleetSlug);
|
||||||
appVars.push(...serviceVars);
|
appVars.push(...serviceVars);
|
||||||
}
|
}
|
||||||
return appVars;
|
return appVars;
|
||||||
@ -315,7 +269,7 @@ async function getAppVars(
|
|||||||
async function getDeviceVars(
|
async function getDeviceVars(
|
||||||
sdk: SDK.BalenaSDK,
|
sdk: SDK.BalenaSDK,
|
||||||
fullUUID: string,
|
fullUUID: string,
|
||||||
appNameOrSlug: string | undefined,
|
fleetSlug: string | undefined,
|
||||||
options: FlagsDef,
|
options: FlagsDef,
|
||||||
): Promise<EnvironmentVariableInfo[]> {
|
): Promise<EnvironmentVariableInfo[]> {
|
||||||
const printedUUID = options.json ? fullUUID : options.device!;
|
const printedUUID = options.json ? fullUUID : options.device!;
|
||||||
@ -324,7 +278,7 @@ async function getDeviceVars(
|
|||||||
const deviceConfigVars = await sdk.models.device.configVar.getAllByDevice(
|
const deviceConfigVars = await sdk.models.device.configVar.getAllByDevice(
|
||||||
fullUUID,
|
fullUUID,
|
||||||
);
|
);
|
||||||
fillInInfoFields(deviceConfigVars, appNameOrSlug, printedUUID);
|
fillInInfoFields(deviceConfigVars, fleetSlug, printedUUID);
|
||||||
deviceVars.push(...deviceConfigVars);
|
deviceVars.push(...deviceConfigVars);
|
||||||
} else {
|
} else {
|
||||||
const pineOpts: SDK.PineOptions<SDK.DeviceServiceEnvironmentVariable> = {
|
const pineOpts: SDK.PineOptions<SDK.DeviceServiceEnvironmentVariable> = {
|
||||||
@ -345,13 +299,13 @@ async function getDeviceVars(
|
|||||||
fullUUID,
|
fullUUID,
|
||||||
pineOpts,
|
pineOpts,
|
||||||
);
|
);
|
||||||
fillInInfoFields(deviceServiceVars, appNameOrSlug, printedUUID);
|
fillInInfoFields(deviceServiceVars, fleetSlug, printedUUID);
|
||||||
deviceVars.push(...deviceServiceVars);
|
deviceVars.push(...deviceServiceVars);
|
||||||
|
|
||||||
const deviceEnvVars = await sdk.models.device.envVar.getAllByDevice(
|
const deviceEnvVars = await sdk.models.device.envVar.getAllByDevice(
|
||||||
fullUUID,
|
fullUUID,
|
||||||
);
|
);
|
||||||
fillInInfoFields(deviceEnvVars, appNameOrSlug, printedUUID);
|
fillInInfoFields(deviceEnvVars, fleetSlug, printedUUID);
|
||||||
deviceVars.push(...deviceEnvVars);
|
deviceVars.push(...deviceEnvVars);
|
||||||
}
|
}
|
||||||
return deviceVars;
|
return deviceVars;
|
||||||
@ -367,7 +321,7 @@ function fillInInfoFields(
|
|||||||
| EnvironmentVariableInfo[]
|
| EnvironmentVariableInfo[]
|
||||||
| DeviceServiceEnvironmentVariableInfo[]
|
| DeviceServiceEnvironmentVariableInfo[]
|
||||||
| ServiceEnvironmentVariableInfo[],
|
| ServiceEnvironmentVariableInfo[],
|
||||||
appNameOrSlug?: string,
|
fleetSlug?: string,
|
||||||
deviceUUID?: string,
|
deviceUUID?: string,
|
||||||
) {
|
) {
|
||||||
for (const envVar of varArray) {
|
for (const envVar of varArray) {
|
||||||
@ -381,7 +335,7 @@ function fillInInfoFields(
|
|||||||
?.installs__service as SDK.Service[]
|
?.installs__service as SDK.Service[]
|
||||||
)[0]?.service_name;
|
)[0]?.service_name;
|
||||||
}
|
}
|
||||||
envVar.appName = appNameOrSlug;
|
envVar.fleet = fleetSlug;
|
||||||
envVar.serviceName = envVar.serviceName || '*';
|
envVar.serviceName = envVar.serviceName || '*';
|
||||||
envVar.deviceUUID = deviceUUID || '*';
|
envVar.deviceUUID = deviceUUID || '*';
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* @license
|
* @license
|
||||||
* Copyright 2021 Balena Ltd.
|
* Copyright 2016-2021 Balena Ltd.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -15,6 +15,135 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { FleetCreateCmd } from '../app/create';
|
import { flags } from '@oclif/command';
|
||||||
|
import type { Application } from 'balena-sdk';
|
||||||
|
|
||||||
export default FleetCreateCmd;
|
import Command from '../../command';
|
||||||
|
import { ExpectedError } from '../../errors';
|
||||||
|
import * as cf from '../../utils/common-flags';
|
||||||
|
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||||
|
|
||||||
|
interface FlagsDef {
|
||||||
|
organization?: string;
|
||||||
|
type?: string; // application device type
|
||||||
|
help: void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ArgsDef {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class FleetCreateCmd extends Command {
|
||||||
|
public static description = stripIndent`
|
||||||
|
Create a fleet.
|
||||||
|
|
||||||
|
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 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.
|
||||||
|
|
||||||
|
Interactive dropdowns will be shown for selection if no device type or
|
||||||
|
organization is specified and there are multiple options to choose from.
|
||||||
|
If there is a single option to choose from, it will be chosen automatically.
|
||||||
|
This interactive behavior can be disabled by explicitly specifying a device
|
||||||
|
type and organization.
|
||||||
|
`;
|
||||||
|
|
||||||
|
public static examples = [
|
||||||
|
'$ 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: 'fleet name',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
public static usage = 'fleet create <name>';
|
||||||
|
|
||||||
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
|
organization: flags.string({
|
||||||
|
char: 'o',
|
||||||
|
description: 'handle of the organization the fleet should belong to',
|
||||||
|
}),
|
||||||
|
type: flags.string({
|
||||||
|
char: 't',
|
||||||
|
description:
|
||||||
|
'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<FlagsDef, ArgsDef>(
|
||||||
|
FleetCreateCmd,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ascertain device type
|
||||||
|
const deviceType =
|
||||||
|
options.type ||
|
||||||
|
(await (await import('../../utils/patterns')).selectDeviceType());
|
||||||
|
|
||||||
|
// Ascertain organization
|
||||||
|
const organization =
|
||||||
|
options.organization?.toLowerCase() || (await this.getOrganization());
|
||||||
|
|
||||||
|
// Create application
|
||||||
|
let application: Application;
|
||||||
|
try {
|
||||||
|
application = await getBalenaSdk().models.application.create({
|
||||||
|
name: params.name,
|
||||||
|
deviceType,
|
||||||
|
organization,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
if ((err.message || '').toLowerCase().includes('unique')) {
|
||||||
|
// BalenaRequestError: Request error: "organization" and "app_name" must be unique.
|
||||||
|
throw new ExpectedError(
|
||||||
|
`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 fleets in organization "${organization}".`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output
|
||||||
|
console.log(
|
||||||
|
`Fleet created: slug "${application.slug}", device type "${deviceType}"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getOrganization() {
|
||||||
|
const { getOwnOrganizations } = await import('../../utils/sdk');
|
||||||
|
const organizations = await getOwnOrganizations(getBalenaSdk());
|
||||||
|
|
||||||
|
if (organizations.length === 0) {
|
||||||
|
// User is not a member of any organizations (should not happen).
|
||||||
|
throw new Error('This account is not a member of any organizations');
|
||||||
|
} else if (organizations.length === 1) {
|
||||||
|
// User is a member of only one organization - use this.
|
||||||
|
return organizations[0].handle;
|
||||||
|
} else {
|
||||||
|
// User is a member of multiple organizations -
|
||||||
|
const { selectOrganization } = await import('../../utils/patterns');
|
||||||
|
return selectOrganization(organizations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* @license
|
* @license
|
||||||
* Copyright 2021 Balena Ltd.
|
* Copyright 2016-2021 Balena Ltd.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -15,6 +15,89 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { FleetCmd } from '../app';
|
import type { flags } from '@oclif/command';
|
||||||
|
import type { Release } from 'balena-sdk';
|
||||||
|
|
||||||
export default FleetCmd;
|
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 { isV14 } from '../../utils/version';
|
||||||
|
import type { DataOutputOptions } from '../../framework';
|
||||||
|
|
||||||
|
interface FlagsDef extends DataOutputOptions {
|
||||||
|
help: void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ArgsDef {
|
||||||
|
fleet: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class FleetCmd extends Command {
|
||||||
|
public static description = stripIndent`
|
||||||
|
Display information about a single fleet.
|
||||||
|
|
||||||
|
Display detailed information about a single fleet.
|
||||||
|
|
||||||
|
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||||
|
`;
|
||||||
|
public static examples = [
|
||||||
|
'$ balena fleet MyFleet',
|
||||||
|
'$ balena fleet myorg/myfleet',
|
||||||
|
];
|
||||||
|
|
||||||
|
public static args = [ca.fleetRequired];
|
||||||
|
|
||||||
|
public static usage = 'fleet <fleet>';
|
||||||
|
|
||||||
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
|
help: cf.help,
|
||||||
|
...(isV14() ? cf.dataOutputFlags : {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
public static authenticated = true;
|
||||||
|
public static primary = true;
|
||||||
|
|
||||||
|
public async run() {
|
||||||
|
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
|
||||||
|
FleetCmd,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { getApplication } = await import('../../utils/sdk');
|
||||||
|
|
||||||
|
const application = (await getApplication(getBalenaSdk(), params.fleet, {
|
||||||
|
$expand: {
|
||||||
|
is_for__device_type: { $select: 'slug' },
|
||||||
|
should_be_running__release: { $select: 'commit' },
|
||||||
|
},
|
||||||
|
})) as ApplicationWithDeviceType & {
|
||||||
|
should_be_running__release: [Release?];
|
||||||
|
// For display purposes:
|
||||||
|
device_type: string;
|
||||||
|
commit?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
application.device_type = application.is_for__device_type[0].slug;
|
||||||
|
application.commit = application.should_be_running__release[0]?.commit;
|
||||||
|
|
||||||
|
if (isV14()) {
|
||||||
|
await this.outputData(
|
||||||
|
application,
|
||||||
|
['app_name', 'id', 'device_type', 'slug', 'commit'],
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Emulate table.vertical title output, but avoid uppercasing and inserting spaces
|
||||||
|
console.log(`== ${application.slug}`);
|
||||||
|
console.log(
|
||||||
|
getVisuals().table.vertical(application, [
|
||||||
|
'id',
|
||||||
|
'device_type',
|
||||||
|
'slug',
|
||||||
|
'commit',
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* @license
|
* @license
|
||||||
* Copyright 2021 Balena Ltd.
|
* Copyright 2016-2020 Balena Ltd.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -15,6 +15,67 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { FleetPurgeCmd } from '../app/purge';
|
import type { flags } from '@oclif/command';
|
||||||
|
|
||||||
export default FleetPurgeCmd;
|
import Command from '../../command';
|
||||||
|
import * as cf from '../../utils/common-flags';
|
||||||
|
import * as ca from '../../utils/common-args';
|
||||||
|
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||||
|
import { applicationIdInfo } from '../../utils/messages';
|
||||||
|
|
||||||
|
interface FlagsDef {
|
||||||
|
help: void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ArgsDef {
|
||||||
|
fleet: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class FleetPurgeCmd extends Command {
|
||||||
|
public static description = stripIndent`
|
||||||
|
Purge data from a fleet.
|
||||||
|
|
||||||
|
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 fleet purge MyFleet',
|
||||||
|
'$ balena fleet purge myorg/myfleet',
|
||||||
|
];
|
||||||
|
|
||||||
|
public static args = [ca.fleetRequired];
|
||||||
|
|
||||||
|
public static usage = 'fleet purge <fleet>';
|
||||||
|
|
||||||
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
|
help: cf.help,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static authenticated = true;
|
||||||
|
|
||||||
|
public async run() {
|
||||||
|
const { args: params } = this.parse<FlagsDef, ArgsDef>(FleetPurgeCmd);
|
||||||
|
|
||||||
|
const { getApplication } = await import('../../utils/sdk');
|
||||||
|
|
||||||
|
const balena = getBalenaSdk();
|
||||||
|
|
||||||
|
// balena.models.application.purge only accepts a numeric id
|
||||||
|
// so we must first fetch the app to get it's id,
|
||||||
|
const application = await getApplication(balena, params.fleet);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await balena.models.application.purge(application.id);
|
||||||
|
} catch (e) {
|
||||||
|
if (e.message.toLowerCase().includes('no online device(s) found')) {
|
||||||
|
// application.purge throws an error if no devices are online
|
||||||
|
// ignore in this case.
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* @license
|
* @license
|
||||||
* Copyright 2021 Balena Ltd.
|
* Copyright 2016-2020 Balena Ltd.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -15,6 +15,135 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { FleetRenameCmd } from '../app/rename';
|
import type { flags } from '@oclif/command';
|
||||||
|
import type { ApplicationType } from 'balena-sdk';
|
||||||
|
|
||||||
export default FleetRenameCmd;
|
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';
|
||||||
|
|
||||||
|
interface FlagsDef {
|
||||||
|
help: void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ArgsDef {
|
||||||
|
fleet: string;
|
||||||
|
newName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class FleetRenameCmd extends Command {
|
||||||
|
public static description = stripIndent`
|
||||||
|
Rename a fleet.
|
||||||
|
|
||||||
|
Rename a fleet.
|
||||||
|
|
||||||
|
Note, if the \`newName\` parameter is omitted, it will be
|
||||||
|
prompted for interactively.
|
||||||
|
|
||||||
|
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||||
|
`;
|
||||||
|
|
||||||
|
public static examples = [
|
||||||
|
'$ balena fleet rename OldName',
|
||||||
|
'$ balena fleet rename OldName NewName',
|
||||||
|
'$ balena fleet rename myorg/oldname NewName',
|
||||||
|
];
|
||||||
|
|
||||||
|
public static args = [
|
||||||
|
ca.fleetRequired,
|
||||||
|
{
|
||||||
|
name: 'newName',
|
||||||
|
description: 'the new name for the fleet',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
public static usage = 'fleet rename <fleet> [newName]';
|
||||||
|
|
||||||
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
|
help: cf.help,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static authenticated = true;
|
||||||
|
|
||||||
|
public async run() {
|
||||||
|
const { args: params } = this.parse<FlagsDef, ArgsDef>(FleetRenameCmd);
|
||||||
|
|
||||||
|
const { validateApplicationName } = await import('../../utils/validation');
|
||||||
|
const { ExpectedError } = await import('../../errors');
|
||||||
|
|
||||||
|
const balena = getBalenaSdk();
|
||||||
|
|
||||||
|
// 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.fleet, {
|
||||||
|
$expand: {
|
||||||
|
application_type: {
|
||||||
|
$select: ['is_legacy'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check app exists
|
||||||
|
if (!application) {
|
||||||
|
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(
|
||||||
|
`Fleet ${params.fleet} is of 'legacy' type, and cannot be renamed.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ascertain new name
|
||||||
|
const newName =
|
||||||
|
params.newName ||
|
||||||
|
(await getCliForm().ask({
|
||||||
|
message: 'Please enter the new name for this fleet:',
|
||||||
|
type: 'input',
|
||||||
|
validate: validateApplicationName,
|
||||||
|
})) ||
|
||||||
|
'';
|
||||||
|
|
||||||
|
// Check they haven't used slug in new name
|
||||||
|
if (newName.includes('/')) {
|
||||||
|
throw new ExpectedError(
|
||||||
|
`New fleet name cannot include '/', please check that you are not specifying fleet slug.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename
|
||||||
|
try {
|
||||||
|
await balena.models.application.rename(application.id, newName);
|
||||||
|
} catch (e) {
|
||||||
|
// BalenaRequestError: Request error: "organization" and "app_name" must be unique.
|
||||||
|
if ((e.message || '').toLowerCase().includes('unique')) {
|
||||||
|
throw new ExpectedError(`Error: fleet ${newName} already exists.`);
|
||||||
|
}
|
||||||
|
// BalenaRequestError: Request error: App name may only contain [a-zA-Z0-9_-].
|
||||||
|
if ((e.message || '').toLowerCase().includes('name may only contain')) {
|
||||||
|
throw new ExpectedError(
|
||||||
|
`Error: new fleet name may only include characters [a-zA-Z0-9_-].`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get application again, to be sure of results
|
||||||
|
const renamedApplication = await balena.models.application.get(
|
||||||
|
application.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Output result
|
||||||
|
console.log(`Fleet renamed`);
|
||||||
|
console.log('From:');
|
||||||
|
console.log(`\tname: ${application.app_name}`);
|
||||||
|
console.log(`\tslug: ${application.slug}`);
|
||||||
|
console.log('To:');
|
||||||
|
console.log(`\tname: ${renamedApplication.app_name}`);
|
||||||
|
console.log(`\tslug: ${renamedApplication.slug}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* @license
|
* @license
|
||||||
* Copyright 2021 Balena Ltd.
|
* Copyright 2016-2020 Balena Ltd.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -15,6 +15,56 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { FleetRestartCmd } from '../app/restart';
|
import type { flags } from '@oclif/command';
|
||||||
|
|
||||||
export default FleetRestartCmd;
|
import Command from '../../command';
|
||||||
|
import * as cf from '../../utils/common-flags';
|
||||||
|
import * as ca from '../../utils/common-args';
|
||||||
|
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||||
|
import { applicationIdInfo } from '../../utils/messages';
|
||||||
|
|
||||||
|
interface FlagsDef {
|
||||||
|
help: void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ArgsDef {
|
||||||
|
fleet: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class FleetRestartCmd extends Command {
|
||||||
|
public static description = stripIndent`
|
||||||
|
Restart a fleet.
|
||||||
|
|
||||||
|
Restart all devices belonging to a fleet.
|
||||||
|
|
||||||
|
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||||
|
`;
|
||||||
|
|
||||||
|
public static examples = [
|
||||||
|
'$ balena fleet restart MyFleet',
|
||||||
|
'$ balena fleet restart myorg/myfleet',
|
||||||
|
];
|
||||||
|
|
||||||
|
public static args = [ca.fleetRequired];
|
||||||
|
|
||||||
|
public static usage = 'fleet restart <fleet>';
|
||||||
|
|
||||||
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
|
help: cf.help,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static authenticated = true;
|
||||||
|
|
||||||
|
public async run() {
|
||||||
|
const { args: params } = this.parse<FlagsDef, ArgsDef>(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.fleet);
|
||||||
|
|
||||||
|
await balena.models.application.restart(application.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* @license
|
* @license
|
||||||
* Copyright 2021 Balena Ltd.
|
* Copyright 2016-2020 Balena Ltd.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -15,6 +15,70 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { FleetRmCmd } from '../app/rm';
|
import type { flags } from '@oclif/command';
|
||||||
|
|
||||||
export default FleetRmCmd;
|
import Command from '../../command';
|
||||||
|
import * as cf from '../../utils/common-flags';
|
||||||
|
import * as ca from '../../utils/common-args';
|
||||||
|
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||||
|
import { applicationIdInfo } from '../../utils/messages';
|
||||||
|
|
||||||
|
interface FlagsDef {
|
||||||
|
yes: boolean;
|
||||||
|
help: void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ArgsDef {
|
||||||
|
fleet: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class FleetRmCmd extends Command {
|
||||||
|
public static description = stripIndent`
|
||||||
|
Remove a fleet.
|
||||||
|
|
||||||
|
Permanently remove a fleet.
|
||||||
|
|
||||||
|
The --yes option may be used to avoid interactive confirmation.
|
||||||
|
|
||||||
|
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||||
|
`;
|
||||||
|
|
||||||
|
public static examples = [
|
||||||
|
'$ balena fleet rm MyFleet',
|
||||||
|
'$ balena fleet rm MyFleet --yes',
|
||||||
|
'$ balena fleet rm myorg/myfleet',
|
||||||
|
];
|
||||||
|
|
||||||
|
public static args = [ca.fleetRequired];
|
||||||
|
|
||||||
|
public static usage = 'fleet rm <fleet>';
|
||||||
|
|
||||||
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
|
yes: cf.yes,
|
||||||
|
help: cf.help,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static authenticated = true;
|
||||||
|
|
||||||
|
public async run() {
|
||||||
|
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
|
||||||
|
FleetRmCmd,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { confirm } = await import('../../utils/patterns');
|
||||||
|
const { getApplication } = await import('../../utils/sdk');
|
||||||
|
const balena = getBalenaSdk();
|
||||||
|
|
||||||
|
// Confirm
|
||||||
|
await confirm(
|
||||||
|
options.yes ?? false,
|
||||||
|
`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.fleet);
|
||||||
|
|
||||||
|
// Remove
|
||||||
|
await balena.models.application.remove(application.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -15,6 +15,94 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { FleetsCmd } from './apps';
|
import { flags } from '@oclif/command';
|
||||||
|
|
||||||
export default FleetsCmd;
|
import Command from '../command';
|
||||||
|
import * as cf from '../utils/common-flags';
|
||||||
|
import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy';
|
||||||
|
import { isV14 } from '../utils/version';
|
||||||
|
import type { DataSetOutputOptions } from '../framework';
|
||||||
|
|
||||||
|
interface ExtendedApplication extends ApplicationWithDeviceType {
|
||||||
|
device_count: number;
|
||||||
|
online_devices: number;
|
||||||
|
device_type?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FlagsDef extends DataSetOutputOptions {
|
||||||
|
help: void;
|
||||||
|
verbose?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class FleetsCmd extends Command {
|
||||||
|
public static description = stripIndent`
|
||||||
|
List all fleets.
|
||||||
|
|
||||||
|
List all your balena fleets.
|
||||||
|
|
||||||
|
For detailed information on a particular fleet, use
|
||||||
|
\`balena fleet <fleet>\`
|
||||||
|
`;
|
||||||
|
|
||||||
|
public static examples = ['$ balena fleets'];
|
||||||
|
|
||||||
|
public static usage = 'fleets';
|
||||||
|
|
||||||
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
|
...(isV14() ? cf.dataSetOutputFlags : {}),
|
||||||
|
help: cf.help,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static authenticated = true;
|
||||||
|
public static primary = true;
|
||||||
|
|
||||||
|
public async run() {
|
||||||
|
const { flags: options } = this.parse<FlagsDef, {}>(FleetsCmd);
|
||||||
|
|
||||||
|
const balena = getBalenaSdk();
|
||||||
|
|
||||||
|
// Get applications
|
||||||
|
const applications =
|
||||||
|
(await balena.models.application.getAllDirectlyAccessible({
|
||||||
|
$select: ['id', 'app_name', 'slug'],
|
||||||
|
$expand: {
|
||||||
|
is_for__device_type: { $select: 'slug' },
|
||||||
|
owns__device: { $select: 'is_online' },
|
||||||
|
},
|
||||||
|
})) as ExtendedApplication[];
|
||||||
|
|
||||||
|
// Add extended properties
|
||||||
|
applications.forEach((application) => {
|
||||||
|
application.device_count = application.owns__device?.length ?? 0;
|
||||||
|
application.online_devices =
|
||||||
|
application.owns__device?.filter((d) => d.is_online).length || 0;
|
||||||
|
application.device_type = application.is_for__device_type[0].slug;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isV14()) {
|
||||||
|
await this.outputData(
|
||||||
|
applications,
|
||||||
|
[
|
||||||
|
'id',
|
||||||
|
'app_name',
|
||||||
|
'slug',
|
||||||
|
'device_type',
|
||||||
|
'device_count',
|
||||||
|
'online_devices',
|
||||||
|
],
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
getVisuals().table.horizontal(applications, [
|
||||||
|
'id',
|
||||||
|
'app_name => NAME',
|
||||||
|
'slug',
|
||||||
|
'device_type',
|
||||||
|
'online_devices',
|
||||||
|
'device_count',
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -21,10 +21,8 @@ import * as cf from '../utils/common-flags';
|
|||||||
import { getBalenaSdk, stripIndent } from '../utils/lazy';
|
import { getBalenaSdk, stripIndent } from '../utils/lazy';
|
||||||
import { applicationIdInfo } from '../utils/messages';
|
import { applicationIdInfo } from '../utils/messages';
|
||||||
import { parseAsLocalHostnameOrIp } from '../utils/validation';
|
import { parseAsLocalHostnameOrIp } from '../utils/validation';
|
||||||
import { isV13 } from '../utils/version';
|
|
||||||
|
|
||||||
interface FlagsDef {
|
interface FlagsDef {
|
||||||
application?: string;
|
|
||||||
fleet?: string;
|
fleet?: string;
|
||||||
pollInterval?: number;
|
pollInterval?: number;
|
||||||
help?: void;
|
help?: void;
|
||||||
@ -77,7 +75,6 @@ export default class JoinCmd extends Command {
|
|||||||
public static usage = 'join [deviceIpOrHostname]';
|
public static usage = 'join [deviceIpOrHostname]';
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
...(isV13() ? {} : { application: cf.application }),
|
|
||||||
fleet: cf.fleet,
|
fleet: cf.fleet,
|
||||||
pollInterval: flags.integer({
|
pollInterval: flags.integer({
|
||||||
description: 'the interval in minutes to check for updates',
|
description: 'the interval in minutes to check for updates',
|
||||||
@ -101,7 +98,7 @@ export default class JoinCmd extends Command {
|
|||||||
logger,
|
logger,
|
||||||
sdk,
|
sdk,
|
||||||
params.deviceIpOrHostname,
|
params.deviceIpOrHostname,
|
||||||
options.application || options.fleet,
|
options.fleet,
|
||||||
options.pollInterval,
|
options.pollInterval,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -23,19 +23,12 @@ import Command from '../../command';
|
|||||||
import { ExpectedError } from '../../errors';
|
import { ExpectedError } from '../../errors';
|
||||||
import * as cf from '../../utils/common-flags';
|
import * as cf from '../../utils/common-flags';
|
||||||
import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy';
|
import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy';
|
||||||
import {
|
import { applicationIdInfo } from '../../utils/messages';
|
||||||
applicationIdInfo,
|
|
||||||
appToFleetFlagMsg,
|
|
||||||
warnify,
|
|
||||||
} from '../../utils/messages';
|
|
||||||
import { isV13 } from '../../utils/version';
|
|
||||||
|
|
||||||
const CONNECTIONS_FOLDER = '/system-connections';
|
const CONNECTIONS_FOLDER = '/system-connections';
|
||||||
|
|
||||||
interface FlagsDef {
|
interface FlagsDef {
|
||||||
advanced?: boolean;
|
advanced?: boolean;
|
||||||
application?: string;
|
|
||||||
app?: string;
|
|
||||||
fleet?: string;
|
fleet?: string;
|
||||||
config?: string;
|
config?: string;
|
||||||
'config-app-update-poll-interval'?: number;
|
'config-app-update-poll-interval'?: number;
|
||||||
@ -43,7 +36,6 @@ interface FlagsDef {
|
|||||||
'config-wifi-key'?: string;
|
'config-wifi-key'?: string;
|
||||||
'config-wifi-ssid'?: string;
|
'config-wifi-ssid'?: string;
|
||||||
device?: string; // device UUID
|
device?: string; // device UUID
|
||||||
'device-api-key'?: string;
|
|
||||||
'device-type'?: string;
|
'device-type'?: string;
|
||||||
help?: void;
|
help?: void;
|
||||||
version?: string;
|
version?: string;
|
||||||
@ -66,10 +58,6 @@ interface Answers {
|
|||||||
provisioningKeyName?: string;
|
provisioningKeyName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const deviceApiKeyDeprecationMsg = stripIndent`
|
|
||||||
The --device-api-key option is deprecated and will be removed in a future release.
|
|
||||||
A suitable key is automatically generated or fetched if this option is omitted.`;
|
|
||||||
|
|
||||||
export default class OsConfigureCmd extends Command {
|
export default class OsConfigureCmd extends Command {
|
||||||
public static description = stripIndent`
|
public static description = stripIndent`
|
||||||
Configure a previously downloaded balenaOS image.
|
Configure a previously downloaded balenaOS image.
|
||||||
@ -93,8 +81,6 @@ export default class OsConfigureCmd extends Command {
|
|||||||
https://www.balena.io/docs/reference/OS/network/2.x/
|
https://www.balena.io/docs/reference/OS/network/2.x/
|
||||||
https://developer.gnome.org/NetworkManager/stable/ref-settings.html
|
https://developer.gnome.org/NetworkManager/stable/ref-settings.html
|
||||||
|
|
||||||
${deviceApiKeyDeprecationMsg.split('\n').join('\n\t\t')}
|
|
||||||
|
|
||||||
${applicationIdInfo.split('\n').join('\n\t\t')}
|
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||||
|
|
||||||
Note: This command is currently not supported on Windows natively. Windows users
|
Note: This command is currently not supported on Windows natively. Windows users
|
||||||
@ -105,7 +91,6 @@ export default class OsConfigureCmd extends Command {
|
|||||||
|
|
||||||
public static examples = [
|
public static examples = [
|
||||||
'$ balena os configure ../path/rpi3.img --device 7cf02a6',
|
'$ balena os configure ../path/rpi3.img --device 7cf02a6',
|
||||||
'$ balena os configure ../path/rpi3.img --device 7cf02a6 --device-api-key <existingDeviceKey>',
|
|
||||||
'$ balena os configure ../path/rpi3.img --fleet myorg/myfleet',
|
'$ 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 --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',
|
||||||
@ -128,22 +113,7 @@ export default class OsConfigureCmd extends Command {
|
|||||||
description:
|
description:
|
||||||
'ask advanced configuration questions (when in interactive mode)',
|
'ask advanced configuration questions (when in interactive mode)',
|
||||||
}),
|
}),
|
||||||
...(isV13()
|
fleet: { ...cf.fleet, exclusive: ['device'] },
|
||||||
? {}
|
|
||||||
: {
|
|
||||||
application: {
|
|
||||||
...cf.application,
|
|
||||||
exclusive: ['app', 'fleet', 'device'],
|
|
||||||
},
|
|
||||||
app: {
|
|
||||||
...cf.app,
|
|
||||||
exclusive: ['application', 'fleet', 'device'],
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
fleet: {
|
|
||||||
...cf.fleet,
|
|
||||||
exclusive: ['app', 'application', 'device'],
|
|
||||||
},
|
|
||||||
config: flags.string({
|
config: flags.string({
|
||||||
description:
|
description:
|
||||||
'path to a pre-generated config.json file to be injected in the OS image',
|
'path to a pre-generated config.json file to be injected in the OS image',
|
||||||
@ -163,15 +133,7 @@ export default class OsConfigureCmd extends Command {
|
|||||||
'config-wifi-ssid': flags.string({
|
'config-wifi-ssid': flags.string({
|
||||||
description: 'WiFi SSID (network name) (non-interactive configuration)',
|
description: 'WiFi SSID (network name) (non-interactive configuration)',
|
||||||
}),
|
}),
|
||||||
device: {
|
device: { ...cf.device, exclusive: ['fleet', 'provisioning-key-name'] },
|
||||||
exclusive: ['app', 'application', 'fleet', 'provisioning-key-name'],
|
|
||||||
...cf.device,
|
|
||||||
},
|
|
||||||
'device-api-key': flags.string({
|
|
||||||
char: 'k',
|
|
||||||
description:
|
|
||||||
'custom device API key (DEPRECATED and only supported with balenaOS 2.0.3+)',
|
|
||||||
}),
|
|
||||||
'device-type': flags.string({
|
'device-type': flags.string({
|
||||||
description:
|
description:
|
||||||
'device type slug (e.g. "raspberrypi3") to override the fleet device type',
|
'device type slug (e.g. "raspberrypi3") to override the fleet device type',
|
||||||
@ -203,10 +165,6 @@ export default class OsConfigureCmd extends Command {
|
|||||||
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
|
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
|
||||||
OsConfigureCmd,
|
OsConfigureCmd,
|
||||||
);
|
);
|
||||||
if ((options.application || options.app) && process.stderr.isTTY) {
|
|
||||||
console.error(warnify(appToFleetFlagMsg));
|
|
||||||
}
|
|
||||||
options.application ||= options.app || options.fleet;
|
|
||||||
|
|
||||||
await validateOptions(options);
|
await validateOptions(options);
|
||||||
|
|
||||||
@ -233,7 +191,7 @@ export default class OsConfigureCmd extends Command {
|
|||||||
};
|
};
|
||||||
deviceTypeSlug = device.is_of__device_type[0].slug;
|
deviceTypeSlug = device.is_of__device_type[0].slug;
|
||||||
} else {
|
} else {
|
||||||
app = (await getApplication(balena, options.application!, {
|
app = (await getApplication(balena, options.fleet!, {
|
||||||
$expand: {
|
$expand: {
|
||||||
is_for__device_type: { $select: 'slug' },
|
is_for__device_type: { $select: 'slug' },
|
||||||
},
|
},
|
||||||
@ -259,7 +217,7 @@ export default class OsConfigureCmd extends Command {
|
|||||||
options,
|
options,
|
||||||
configJson,
|
configJson,
|
||||||
);
|
);
|
||||||
if (options.application) {
|
if (options.fleet) {
|
||||||
answers.deviceType = deviceTypeSlug;
|
answers.deviceType = deviceTypeSlug;
|
||||||
}
|
}
|
||||||
answers.version =
|
answers.version =
|
||||||
@ -270,11 +228,7 @@ export default class OsConfigureCmd extends Command {
|
|||||||
|
|
||||||
if (_.isEmpty(configJson)) {
|
if (_.isEmpty(configJson)) {
|
||||||
if (device) {
|
if (device) {
|
||||||
configJson = await generateDeviceConfig(
|
configJson = await generateDeviceConfig(device, undefined, answers);
|
||||||
device,
|
|
||||||
options['device-api-key'],
|
|
||||||
answers,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
configJson = await generateApplicationConfig(app!, answers);
|
configJson = await generateApplicationConfig(app!, answers);
|
||||||
}
|
}
|
||||||
@ -335,23 +289,16 @@ export default class OsConfigureCmd extends Command {
|
|||||||
async function validateOptions(options: FlagsDef) {
|
async function validateOptions(options: FlagsDef) {
|
||||||
// The 'device' and 'application' options are declared "exclusive" in the oclif
|
// The 'device' and 'application' options are declared "exclusive" in the oclif
|
||||||
// flag definitions above, so oclif will enforce that they are not both used together.
|
// flag definitions above, so oclif will enforce that they are not both used together.
|
||||||
if (!options.device && !options.application) {
|
if (!options.device && !options.fleet) {
|
||||||
throw new ExpectedError(
|
throw new ExpectedError(
|
||||||
"Either the '--device' or the '--fleet' option must be provided",
|
"Either the '--device' or the '--fleet' option must be provided",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!options.application && options['device-type']) {
|
if (!options.fleet && options['device-type']) {
|
||||||
throw new ExpectedError(
|
throw new ExpectedError(
|
||||||
"The '--device-type' option can only be used in conjunction with the '--fleet' option",
|
"The '--device-type' option can only be used in conjunction with the '--fleet' option",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (options['device-api-key']) {
|
|
||||||
console.error(stripIndent`
|
|
||||||
-------------------------------------------------------------------------------------------
|
|
||||||
Warning: ${deviceApiKeyDeprecationMsg.split('\n').join('\n\t\t\t')}
|
|
||||||
-------------------------------------------------------------------------------------------
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
|
|
||||||
await Command.checkLoggedIn();
|
await Command.checkLoggedIn();
|
||||||
}
|
}
|
||||||
@ -401,7 +348,7 @@ async function checkDeviceTypeCompatibility(
|
|||||||
const helpers = await import('../../utils/helpers');
|
const helpers = await import('../../utils/helpers');
|
||||||
if (!helpers.areDeviceTypesCompatible(appDeviceType, optionDeviceType)) {
|
if (!helpers.areDeviceTypesCompatible(appDeviceType, optionDeviceType)) {
|
||||||
throw new ExpectedError(
|
throw new ExpectedError(
|
||||||
`Device type ${options['device-type']} is incompatible with fleet ${options.application}`,
|
`Device type ${options['device-type']} is incompatible with fleet ${options.fleet}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -426,7 +373,13 @@ async function askQuestionsForDeviceType(
|
|||||||
options: FlagsDef,
|
options: FlagsDef,
|
||||||
configJson?: import('../../utils/config').ImgConfig,
|
configJson?: import('../../utils/config').ImgConfig,
|
||||||
): Promise<Answers> {
|
): Promise<Answers> {
|
||||||
const answerSources: any[] = [camelifyConfigOptions(options)];
|
const answerSources: any[] = [
|
||||||
|
{
|
||||||
|
...camelifyConfigOptions(options),
|
||||||
|
app: options.fleet,
|
||||||
|
application: options.fleet,
|
||||||
|
},
|
||||||
|
];
|
||||||
const defaultAnswers: Partial<Answers> = {};
|
const defaultAnswers: Partial<Answers> = {};
|
||||||
const questions: any = deviceType.options;
|
const questions: any = deviceType.options;
|
||||||
let extraOpts: { override: object } | undefined;
|
let extraOpts: { override: object } | undefined;
|
||||||
|
@ -24,15 +24,10 @@ import {
|
|||||||
getVisuals,
|
getVisuals,
|
||||||
stripIndent,
|
stripIndent,
|
||||||
} from '../utils/lazy';
|
} from '../utils/lazy';
|
||||||
import {
|
import { applicationIdInfo } from '../utils/messages';
|
||||||
applicationIdInfo,
|
|
||||||
appToFleetFlagMsg,
|
|
||||||
warnify,
|
|
||||||
} from '../utils/messages';
|
|
||||||
import type { DockerConnectionCliFlags } from '../utils/docker';
|
import type { DockerConnectionCliFlags } from '../utils/docker';
|
||||||
import { dockerConnectionCliFlags } from '../utils/docker';
|
import { dockerConnectionCliFlags } from '../utils/docker';
|
||||||
import { parseAsInteger } from '../utils/validation';
|
import { parseAsInteger } from '../utils/validation';
|
||||||
import { isV13 } from '../utils/version';
|
|
||||||
|
|
||||||
import { flags } from '@oclif/command';
|
import { flags } from '@oclif/command';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
@ -40,7 +35,6 @@ import type { Application, BalenaSDK, PineExpand, Release } from 'balena-sdk';
|
|||||||
import type { Preloader } from 'balena-preload';
|
import type { Preloader } from 'balena-preload';
|
||||||
|
|
||||||
interface FlagsDef extends DockerConnectionCliFlags {
|
interface FlagsDef extends DockerConnectionCliFlags {
|
||||||
app?: string;
|
|
||||||
fleet?: string;
|
fleet?: string;
|
||||||
commit?: string;
|
commit?: string;
|
||||||
'splash-image'?: string;
|
'splash-image'?: string;
|
||||||
@ -99,7 +93,6 @@ export default class PreloadCmd extends Command {
|
|||||||
public static usage = 'preload <image>';
|
public static usage = 'preload <image>';
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
...(isV13() ? {} : { app: cf.application }),
|
|
||||||
fleet: cf.fleet,
|
fleet: cf.fleet,
|
||||||
commit: flags.string({
|
commit: flags.string({
|
||||||
description: `\
|
description: `\
|
||||||
@ -163,11 +156,6 @@ Can be repeated to add multiple certificates.\
|
|||||||
PreloadCmd,
|
PreloadCmd,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (options.app && process.stderr.isTTY) {
|
|
||||||
console.error(warnify(appToFleetFlagMsg));
|
|
||||||
}
|
|
||||||
options.app ||= options.fleet;
|
|
||||||
|
|
||||||
const balena = getBalenaSdk();
|
const balena = getBalenaSdk();
|
||||||
const balenaPreload = await import('balena-preload');
|
const balenaPreload = await import('balena-preload');
|
||||||
const visuals = getVisuals();
|
const visuals = getVisuals();
|
||||||
@ -194,15 +182,9 @@ Can be repeated to add multiple certificates.\
|
|||||||
|
|
||||||
// balena-preload currently does not work with numerical app IDs
|
// balena-preload currently does not work with numerical app IDs
|
||||||
// Load app here, and use app slug from hereon
|
// Load app here, and use app slug from hereon
|
||||||
if (options.app && !options.app.includes('/')) {
|
const fleetSlug: string | undefined = options.fleet
|
||||||
// Disambiguate application (if is a number, it could either be an ID or a numerical name)
|
? await (await import('../utils/sdk')).getFleetSlug(balena, options.fleet)
|
||||||
const { getApplication } = await import('../utils/sdk');
|
: undefined;
|
||||||
const application = await getApplication(balena, options.app);
|
|
||||||
if (!application) {
|
|
||||||
throw new ExpectedError(`Fleet not found: ${options.app}`);
|
|
||||||
}
|
|
||||||
options.app = application.slug;
|
|
||||||
}
|
|
||||||
|
|
||||||
const progressBars: {
|
const progressBars: {
|
||||||
[key: string]: ReturnType<typeof getVisuals>['Progress'];
|
[key: string]: ReturnType<typeof getVisuals>['Progress'];
|
||||||
@ -238,15 +220,12 @@ Can be repeated to add multiple certificates.\
|
|||||||
? 'latest'
|
? 'latest'
|
||||||
: options.commit;
|
: options.commit;
|
||||||
const image = params.image;
|
const image = params.image;
|
||||||
const appId = options.app;
|
|
||||||
|
|
||||||
const splashImage = options['splash-image'];
|
const splashImage = options['splash-image'];
|
||||||
const additionalSpace = options['additional-space'];
|
const additionalSpace = options['additional-space'];
|
||||||
|
|
||||||
const dontCheckArch = options['dont-check-arch'] || false;
|
const dontCheckArch = options['dont-check-arch'] || false;
|
||||||
const pinDevice = options['pin-device-to-release'] || false;
|
const pinDevice = options['pin-device-to-release'] || false;
|
||||||
|
|
||||||
if (dontCheckArch && !appId) {
|
if (dontCheckArch && !fleetSlug) {
|
||||||
throw new ExpectedError(
|
throw new ExpectedError(
|
||||||
'You need to specify a fleet if you disable the architecture check.',
|
'You need to specify a fleet if you disable the architecture check.',
|
||||||
);
|
);
|
||||||
@ -265,7 +244,7 @@ Can be repeated to add multiple certificates.\
|
|||||||
const preloader = new balenaPreload.Preloader(
|
const preloader = new balenaPreload.Preloader(
|
||||||
null,
|
null,
|
||||||
docker,
|
docker,
|
||||||
appId,
|
fleetSlug,
|
||||||
commit,
|
commit,
|
||||||
image,
|
image,
|
||||||
splashImage,
|
splashImage,
|
||||||
@ -309,7 +288,7 @@ Can be repeated to add multiple certificates.\
|
|||||||
preloader.on('error', reject);
|
preloader.on('error', reject);
|
||||||
resolve(
|
resolve(
|
||||||
this.prepareAndPreload(preloader, balena, {
|
this.prepareAndPreload(preloader, balena, {
|
||||||
appId,
|
appId: fleetSlug,
|
||||||
commit,
|
commit,
|
||||||
pinDevice,
|
pinDevice,
|
||||||
}),
|
}),
|
||||||
@ -364,8 +343,8 @@ Can be repeated to add multiple certificates.\
|
|||||||
} catch {
|
} catch {
|
||||||
throw new Error(`Device type "${deviceTypeSlug}" not found in API query`);
|
throw new Error(`Device type "${deviceTypeSlug}" not found in API query`);
|
||||||
}
|
}
|
||||||
return (await balena.models.application.getAll({
|
return (await balena.models.application.getAllDirectlyAccessible({
|
||||||
$select: ['id', 'app_name', 'should_track_latest_release'],
|
$select: ['id', 'slug', 'should_track_latest_release'],
|
||||||
$expand: this.applicationExpandOptions,
|
$expand: this.applicationExpandOptions,
|
||||||
$filter: {
|
$filter: {
|
||||||
// get the apps that are of the same arch as the device type of the image
|
// get the apps that are of the same arch as the device type of the image
|
||||||
@ -408,7 +387,7 @@ Can be repeated to add multiple certificates.\
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
$orderby: 'app_name asc',
|
$orderby: 'slug asc',
|
||||||
})) as Array<
|
})) as Array<
|
||||||
ApplicationWithDeviceType & {
|
ApplicationWithDeviceType & {
|
||||||
should_be_running__release: [Release?];
|
should_be_running__release: [Release?];
|
||||||
@ -437,7 +416,7 @@ Can be repeated to add multiple certificates.\
|
|||||||
message: 'Select a fleet',
|
message: 'Select a fleet',
|
||||||
type: 'list',
|
type: 'list',
|
||||||
choices: applications.map((app) => ({
|
choices: applications.map((app) => ({
|
||||||
name: app.app_name,
|
name: app.slug,
|
||||||
value: app,
|
value: app,
|
||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
@ -512,7 +491,7 @@ Would you like to disable automatic updates for this fleet now?\
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAppWithReleases(balenaSdk: BalenaSDK, appId: string | number) {
|
async getAppWithReleases(balenaSdk: BalenaSDK, appId: string) {
|
||||||
const { getApplication } = await import('../utils/sdk');
|
const { getApplication } = await import('../utils/sdk');
|
||||||
|
|
||||||
return (await getApplication(balenaSdk, appId, {
|
return (await getApplication(balenaSdk, appId, {
|
||||||
|
@ -22,7 +22,6 @@ import { getBalenaSdk, stripIndent } from '../utils/lazy';
|
|||||||
import { dockerignoreHelp, registrySecretsHelp } from '../utils/messages';
|
import { dockerignoreHelp, registrySecretsHelp } from '../utils/messages';
|
||||||
import type { BalenaSDK } from 'balena-sdk';
|
import type { BalenaSDK } from 'balena-sdk';
|
||||||
import { ExpectedError, instanceOf } from '../errors';
|
import { ExpectedError, instanceOf } from '../errors';
|
||||||
import { isV13 } from '../utils/version';
|
|
||||||
import { RegistrySecrets } from 'resin-multibuild';
|
import { RegistrySecrets } from 'resin-multibuild';
|
||||||
import { lowercaseIfSlug } from '../utils/normalization';
|
import { lowercaseIfSlug } from '../utils/normalization';
|
||||||
import {
|
import {
|
||||||
@ -47,14 +46,11 @@ interface FlagsDef {
|
|||||||
pull: boolean;
|
pull: boolean;
|
||||||
'noparent-check': boolean;
|
'noparent-check': boolean;
|
||||||
'registry-secrets'?: string;
|
'registry-secrets'?: string;
|
||||||
gitignore?: boolean; // v13: delete this flag
|
|
||||||
nogitignore?: boolean; // v13: delete this flag
|
|
||||||
nolive: boolean;
|
nolive: boolean;
|
||||||
detached: boolean;
|
detached: boolean;
|
||||||
service?: string[];
|
service?: string[];
|
||||||
system: boolean;
|
system: boolean;
|
||||||
env?: string[];
|
env?: string[];
|
||||||
'convert-eol'?: boolean;
|
|
||||||
'noconvert-eol': boolean;
|
'noconvert-eol': boolean;
|
||||||
'multi-dockerignore': boolean;
|
'multi-dockerignore': boolean;
|
||||||
'release-tag'?: string[];
|
'release-tag'?: string[];
|
||||||
@ -218,16 +214,6 @@ export default class PushCmd extends Command {
|
|||||||
`,
|
`,
|
||||||
multiple: true,
|
multiple: true,
|
||||||
}),
|
}),
|
||||||
...(isV13()
|
|
||||||
? {}
|
|
||||||
: {
|
|
||||||
'convert-eol': flags.boolean({
|
|
||||||
description: 'No-op and deprecated since balena CLI v12.0.0',
|
|
||||||
char: 'l',
|
|
||||||
hidden: true,
|
|
||||||
default: false,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
'noconvert-eol': flags.boolean({
|
'noconvert-eol': flags.boolean({
|
||||||
description: `Don't convert line endings from CRLF (Windows format) to LF (Unix format).`,
|
description: `Don't convert line endings from CRLF (Windows format) to LF (Unix format).`,
|
||||||
default: false,
|
default: false,
|
||||||
@ -237,28 +223,7 @@ export default class PushCmd extends Command {
|
|||||||
'Have each service use its own .dockerignore file. See "balena help push".',
|
'Have each service use its own .dockerignore file. See "balena help push".',
|
||||||
char: 'm',
|
char: 'm',
|
||||||
default: false,
|
default: false,
|
||||||
exclusive: ['gitignore'], // v13: delete this line
|
|
||||||
}),
|
}),
|
||||||
...(isV13()
|
|
||||||
? {}
|
|
||||||
: {
|
|
||||||
gitignore: flags.boolean({
|
|
||||||
description: stripIndent`
|
|
||||||
Consider .gitignore files in addition to the .dockerignore file. This reverts
|
|
||||||
to the CLI v11 behavior/implementation (deprecated) if compatibility is
|
|
||||||
required until your project can be adapted.`,
|
|
||||||
char: 'g',
|
|
||||||
default: false,
|
|
||||||
exclusive: ['multi-dockerignore'],
|
|
||||||
}),
|
|
||||||
nogitignore: flags.boolean({
|
|
||||||
description:
|
|
||||||
'No-op (default behavior) since balena CLI v12.0.0. See "balena help push".',
|
|
||||||
char: 'G',
|
|
||||||
hidden: true,
|
|
||||||
default: false,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
'release-tag': flags.string({
|
'release-tag': flags.string({
|
||||||
description: stripIndent`
|
description: stripIndent`
|
||||||
Set release tags if the image build is successful (balenaCloud only). Multiple
|
Set release tags if the image build is successful (balenaCloud only). Multiple
|
||||||
@ -378,7 +343,6 @@ export default class PushCmd extends Command {
|
|||||||
source: options.source,
|
source: options.source,
|
||||||
auth: token,
|
auth: token,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
nogitignore: !options.gitignore, // v13: delete this line
|
|
||||||
sdk,
|
sdk,
|
||||||
opts,
|
opts,
|
||||||
};
|
};
|
||||||
@ -422,7 +386,6 @@ export default class PushCmd extends Command {
|
|||||||
multiDockerignore: options['multi-dockerignore'],
|
multiDockerignore: options['multi-dockerignore'],
|
||||||
nocache: options.nocache,
|
nocache: options.nocache,
|
||||||
pull: options.pull,
|
pull: options.pull,
|
||||||
nogitignore: !options.gitignore, // v13: delete this line
|
|
||||||
noParentCheck: options['noparent-check'],
|
noParentCheck: options['noparent-check'],
|
||||||
nolive: options.nolive,
|
nolive: options.nolive,
|
||||||
detached: options.detached,
|
detached: options.detached,
|
||||||
|
@ -49,7 +49,7 @@ export default class ReleasesCmd extends Command {
|
|||||||
public static args = [
|
public static args = [
|
||||||
{
|
{
|
||||||
name: 'fleet',
|
name: 'fleet',
|
||||||
description: 'fleet name or slug',
|
description: 'fleet name or slug (preferred)',
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@ -69,9 +69,10 @@ export default class ReleasesCmd extends Command {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const balena = getBalenaSdk();
|
const balena = getBalenaSdk();
|
||||||
|
const { getFleetSlug } = await import('../utils/sdk');
|
||||||
|
|
||||||
const releases = await balena.models.release.getAllByApplication(
|
const releases = await balena.models.release.getAllByApplication(
|
||||||
params.fleet,
|
await getFleetSlug(balena, params.fleet),
|
||||||
{ $select: fields },
|
{ $select: fields },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -20,15 +20,9 @@ import Command from '../command';
|
|||||||
import { ExpectedError } from '../errors';
|
import { ExpectedError } from '../errors';
|
||||||
import * as cf from '../utils/common-flags';
|
import * as cf from '../utils/common-flags';
|
||||||
import { getBalenaSdk, getCliUx, stripIndent } from '../utils/lazy';
|
import { getBalenaSdk, getCliUx, stripIndent } from '../utils/lazy';
|
||||||
import {
|
import { applicationIdInfo } from '../utils/messages';
|
||||||
applicationIdInfo,
|
|
||||||
appToFleetFlagMsg,
|
|
||||||
warnify,
|
|
||||||
} from '../utils/messages';
|
|
||||||
import { isV13 } from '../utils/version';
|
|
||||||
|
|
||||||
interface FlagsDef {
|
interface FlagsDef {
|
||||||
application?: string;
|
|
||||||
fleet?: string;
|
fleet?: string;
|
||||||
device?: string;
|
device?: string;
|
||||||
duration?: string;
|
duration?: string;
|
||||||
@ -77,11 +71,10 @@ export default class SupportCmd extends Command {
|
|||||||
description: 'comma-separated list (no spaces) of device UUIDs',
|
description: 'comma-separated list (no spaces) of device UUIDs',
|
||||||
char: 'd',
|
char: 'd',
|
||||||
}),
|
}),
|
||||||
...(isV13() ? {} : { application: cf.application }),
|
|
||||||
fleet: {
|
fleet: {
|
||||||
...cf.fleet,
|
...cf.fleet,
|
||||||
description:
|
description:
|
||||||
'comma-separated list (no spaces) of fleet names or org/name slugs',
|
'comma-separated list (no spaces) of fleet names or slugs (preferred)',
|
||||||
},
|
},
|
||||||
duration: flags.string({
|
duration: flags.string({
|
||||||
description:
|
description:
|
||||||
@ -98,18 +91,13 @@ export default class SupportCmd extends Command {
|
|||||||
SupportCmd,
|
SupportCmd,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (options.application && process.stderr.isTTY) {
|
|
||||||
console.error(warnify(appToFleetFlagMsg));
|
|
||||||
}
|
|
||||||
options.application ||= options.fleet;
|
|
||||||
|
|
||||||
const balena = getBalenaSdk();
|
const balena = getBalenaSdk();
|
||||||
const ux = getCliUx();
|
const ux = getCliUx();
|
||||||
|
|
||||||
const enabling = params.action === 'enable';
|
const enabling = params.action === 'enable';
|
||||||
|
|
||||||
// Validation
|
// Validation
|
||||||
if (!options.device && !options.application) {
|
if (!options.device && !options.fleet) {
|
||||||
throw new ExpectedError('At least one device or fleet must be specified');
|
throw new ExpectedError('At least one device or fleet must be specified');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +113,7 @@ export default class SupportCmd extends Command {
|
|||||||
const expiryTs = Date.now() + this.parseDuration(duration);
|
const expiryTs = Date.now() + this.parseDuration(duration);
|
||||||
|
|
||||||
const deviceUuids = options.device?.split(',') || [];
|
const deviceUuids = options.device?.split(',') || [];
|
||||||
const appNames = options.application?.split(',') || [];
|
const appNames = options.fleet?.split(',') || [];
|
||||||
|
|
||||||
const enablingMessage = 'Enabling support access for';
|
const enablingMessage = 'Enabling support access for';
|
||||||
const disablingMessage = 'Disabling support access for';
|
const disablingMessage = 'Disabling support access for';
|
||||||
@ -142,14 +130,17 @@ export default class SupportCmd extends Command {
|
|||||||
ux.action.stop();
|
ux.action.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { getFleetSlug } = await import('../utils/sdk');
|
||||||
|
|
||||||
// Process applications
|
// Process applications
|
||||||
for (const appName of appNames) {
|
for (const appName of appNames) {
|
||||||
|
const slug = await getFleetSlug(balena, appName);
|
||||||
if (enabling) {
|
if (enabling) {
|
||||||
ux.action.start(`${enablingMessage} fleet ${appName}`);
|
ux.action.start(`${enablingMessage} fleet ${slug}`);
|
||||||
await balena.models.application.grantSupportAccess(appName, expiryTs);
|
await balena.models.application.grantSupportAccess(slug, expiryTs);
|
||||||
} else if (params.action === 'disable') {
|
} else if (params.action === 'disable') {
|
||||||
ux.action.start(`${disablingMessage} fleet ${appName}`);
|
ux.action.start(`${disablingMessage} fleet ${slug}`);
|
||||||
await balena.models.application.revokeSupportAccess(appName);
|
await balena.models.application.revokeSupportAccess(slug);
|
||||||
}
|
}
|
||||||
ux.action.stop();
|
ux.action.stop();
|
||||||
}
|
}
|
||||||
|
@ -19,16 +19,9 @@ import { flags } from '@oclif/command';
|
|||||||
import Command from '../../command';
|
import Command from '../../command';
|
||||||
import * as cf from '../../utils/common-flags';
|
import * as cf from '../../utils/common-flags';
|
||||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||||
import {
|
import { applicationIdInfo } from '../../utils/messages';
|
||||||
applicationIdInfo,
|
|
||||||
appToFleetFlagMsg,
|
|
||||||
warnify,
|
|
||||||
} from '../../utils/messages';
|
|
||||||
import { isV13 } from '../../utils/version';
|
|
||||||
|
|
||||||
interface FlagsDef {
|
interface FlagsDef {
|
||||||
app?: string;
|
|
||||||
application?: string;
|
|
||||||
fleet?: string;
|
fleet?: string;
|
||||||
device?: string;
|
device?: string;
|
||||||
release?: string;
|
release?: string;
|
||||||
@ -67,29 +60,17 @@ export default class TagRmCmd extends Command {
|
|||||||
public static usage = 'tag rm <tagKey>';
|
public static usage = 'tag rm <tagKey>';
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
...(isV13()
|
|
||||||
? {}
|
|
||||||
: {
|
|
||||||
application: {
|
|
||||||
...cf.application,
|
|
||||||
exclusive: ['app', 'fleet', 'device', 'release'],
|
|
||||||
},
|
|
||||||
app: {
|
|
||||||
...cf.app,
|
|
||||||
exclusive: ['application', 'fleet', 'device', 'release'],
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
fleet: {
|
fleet: {
|
||||||
...cf.fleet,
|
...cf.fleet,
|
||||||
exclusive: ['app', 'application', 'device', 'release'],
|
exclusive: ['device', 'release'],
|
||||||
},
|
},
|
||||||
device: {
|
device: {
|
||||||
...cf.device,
|
...cf.device,
|
||||||
exclusive: ['app', 'application', 'fleet', 'release'],
|
exclusive: ['fleet', 'release'],
|
||||||
},
|
},
|
||||||
release: {
|
release: {
|
||||||
...cf.release,
|
...cf.release,
|
||||||
exclusive: ['app', 'application', 'fleet', 'device'],
|
exclusive: ['fleet', 'device'],
|
||||||
},
|
},
|
||||||
help: cf.help,
|
help: cf.help,
|
||||||
};
|
};
|
||||||
@ -101,25 +82,20 @@ export default class TagRmCmd extends Command {
|
|||||||
TagRmCmd,
|
TagRmCmd,
|
||||||
);
|
);
|
||||||
|
|
||||||
if ((options.application || options.app) && process.stderr.isTTY) {
|
|
||||||
console.error(warnify(appToFleetFlagMsg));
|
|
||||||
}
|
|
||||||
options.application ||= options.app || options.fleet;
|
|
||||||
|
|
||||||
const balena = getBalenaSdk();
|
const balena = getBalenaSdk();
|
||||||
|
|
||||||
// Check user has specified one of application/device/release
|
// Check user has specified one of application/device/release
|
||||||
if (!options.application && !options.device && !options.release) {
|
if (!options.fleet && !options.device && !options.release) {
|
||||||
const { ExpectedError } = await import('../../errors');
|
const { ExpectedError } = await import('../../errors');
|
||||||
throw new ExpectedError(TagRmCmd.missingResourceMessage);
|
throw new ExpectedError(TagRmCmd.missingResourceMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { tryAsInteger } = await import('../../utils/validation');
|
const { tryAsInteger } = await import('../../utils/validation');
|
||||||
|
|
||||||
if (options.application) {
|
if (options.fleet) {
|
||||||
const { getTypedApplicationIdentifier } = await import('../../utils/sdk');
|
const { getFleetSlug } = await import('../../utils/sdk');
|
||||||
return balena.models.application.tags.remove(
|
return balena.models.application.tags.remove(
|
||||||
await getTypedApplicationIdentifier(balena, options.application),
|
await getFleetSlug(balena, options.fleet),
|
||||||
params.tagKey,
|
params.tagKey,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -19,16 +19,9 @@ import { flags } from '@oclif/command';
|
|||||||
import Command from '../../command';
|
import Command from '../../command';
|
||||||
import * as cf from '../../utils/common-flags';
|
import * as cf from '../../utils/common-flags';
|
||||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||||
import {
|
import { applicationIdInfo } from '../../utils/messages';
|
||||||
applicationIdInfo,
|
|
||||||
appToFleetFlagMsg,
|
|
||||||
warnify,
|
|
||||||
} from '../../utils/messages';
|
|
||||||
import { isV13 } from '../../utils/version';
|
|
||||||
|
|
||||||
interface FlagsDef {
|
interface FlagsDef {
|
||||||
app?: string;
|
|
||||||
application?: string;
|
|
||||||
fleet?: string;
|
fleet?: string;
|
||||||
device?: string;
|
device?: string;
|
||||||
release?: string;
|
release?: string;
|
||||||
@ -80,29 +73,17 @@ export default class TagSetCmd extends Command {
|
|||||||
public static usage = 'tag set <tagKey> [value]';
|
public static usage = 'tag set <tagKey> [value]';
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
...(isV13()
|
|
||||||
? {}
|
|
||||||
: {
|
|
||||||
application: {
|
|
||||||
...cf.application,
|
|
||||||
exclusive: ['app', 'fleet', 'device', 'release'],
|
|
||||||
},
|
|
||||||
app: {
|
|
||||||
...cf.app,
|
|
||||||
exclusive: ['application', 'fleet', 'device', 'release'],
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
fleet: {
|
fleet: {
|
||||||
...cf.fleet,
|
...cf.fleet,
|
||||||
exclusive: ['app', 'application', 'device', 'release'],
|
exclusive: ['device', 'release'],
|
||||||
},
|
},
|
||||||
device: {
|
device: {
|
||||||
...cf.device,
|
...cf.device,
|
||||||
exclusive: ['app', 'application', 'fleet', 'release'],
|
exclusive: ['fleet', 'release'],
|
||||||
},
|
},
|
||||||
release: {
|
release: {
|
||||||
...cf.release,
|
...cf.release,
|
||||||
exclusive: ['app', 'application', 'fleet', 'device'],
|
exclusive: ['fleet', 'device'],
|
||||||
},
|
},
|
||||||
help: cf.help,
|
help: cf.help,
|
||||||
};
|
};
|
||||||
@ -114,15 +95,10 @@ export default class TagSetCmd extends Command {
|
|||||||
TagSetCmd,
|
TagSetCmd,
|
||||||
);
|
);
|
||||||
|
|
||||||
if ((options.application || options.app) && process.stderr.isTTY) {
|
|
||||||
console.error(warnify(appToFleetFlagMsg));
|
|
||||||
}
|
|
||||||
options.application ||= options.app || options.fleet;
|
|
||||||
|
|
||||||
const balena = getBalenaSdk();
|
const balena = getBalenaSdk();
|
||||||
|
|
||||||
// Check user has specified one of application/device/release
|
// Check user has specified one of application/device/release
|
||||||
if (!options.application && !options.device && !options.release) {
|
if (!options.fleet && !options.device && !options.release) {
|
||||||
const { ExpectedError } = await import('../../errors');
|
const { ExpectedError } = await import('../../errors');
|
||||||
throw new ExpectedError(TagSetCmd.missingResourceMessage);
|
throw new ExpectedError(TagSetCmd.missingResourceMessage);
|
||||||
}
|
}
|
||||||
@ -131,10 +107,10 @@ export default class TagSetCmd extends Command {
|
|||||||
|
|
||||||
const { tryAsInteger } = await import('../../utils/validation');
|
const { tryAsInteger } = await import('../../utils/validation');
|
||||||
|
|
||||||
if (options.application) {
|
if (options.fleet) {
|
||||||
const { getTypedApplicationIdentifier } = await import('../../utils/sdk');
|
const { getFleetSlug } = await import('../../utils/sdk');
|
||||||
return balena.models.application.tags.set(
|
return balena.models.application.tags.set(
|
||||||
await getTypedApplicationIdentifier(balena, options.application),
|
await getFleetSlug(balena, options.fleet),
|
||||||
params.tagKey,
|
params.tagKey,
|
||||||
params.value,
|
params.value,
|
||||||
);
|
);
|
||||||
|
@ -20,16 +20,9 @@ import Command from '../command';
|
|||||||
import { ExpectedError } from '../errors';
|
import { ExpectedError } from '../errors';
|
||||||
import * as cf from '../utils/common-flags';
|
import * as cf from '../utils/common-flags';
|
||||||
import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy';
|
import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy';
|
||||||
import {
|
import { applicationIdInfo } from '../utils/messages';
|
||||||
applicationIdInfo,
|
|
||||||
appToFleetFlagMsg,
|
|
||||||
warnify,
|
|
||||||
} from '../utils/messages';
|
|
||||||
import { isV13 } from '../utils/version';
|
|
||||||
|
|
||||||
interface FlagsDef {
|
interface FlagsDef {
|
||||||
app?: string;
|
|
||||||
application?: string;
|
|
||||||
fleet?: string;
|
fleet?: string;
|
||||||
device?: string;
|
device?: string;
|
||||||
release?: string;
|
release?: string;
|
||||||
@ -56,29 +49,17 @@ export default class TagsCmd extends Command {
|
|||||||
public static usage = 'tags';
|
public static usage = 'tags';
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
...(isV13()
|
|
||||||
? {}
|
|
||||||
: {
|
|
||||||
application: {
|
|
||||||
...cf.application,
|
|
||||||
exclusive: ['app', 'fleet', 'device', 'release'],
|
|
||||||
},
|
|
||||||
app: {
|
|
||||||
...cf.app,
|
|
||||||
exclusive: ['application', 'fleet', 'device', 'release'],
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
fleet: {
|
fleet: {
|
||||||
...cf.fleet,
|
...cf.fleet,
|
||||||
exclusive: ['app', 'application', 'device', 'release'],
|
exclusive: ['device', 'release'],
|
||||||
},
|
},
|
||||||
device: {
|
device: {
|
||||||
...cf.device,
|
...cf.device,
|
||||||
exclusive: ['app', 'application', 'fleet', 'release'],
|
exclusive: ['fleet', 'release'],
|
||||||
},
|
},
|
||||||
release: {
|
release: {
|
||||||
...cf.release,
|
...cf.release,
|
||||||
exclusive: ['app', 'application', 'fleet', 'device'],
|
exclusive: ['fleet', 'device'],
|
||||||
},
|
},
|
||||||
help: cf.help,
|
help: cf.help,
|
||||||
};
|
};
|
||||||
@ -88,15 +69,10 @@ export default class TagsCmd extends Command {
|
|||||||
public async run() {
|
public async run() {
|
||||||
const { flags: options } = this.parse<FlagsDef, {}>(TagsCmd);
|
const { flags: options } = this.parse<FlagsDef, {}>(TagsCmd);
|
||||||
|
|
||||||
if ((options.application || options.app) && process.stderr.isTTY) {
|
|
||||||
console.error(warnify(appToFleetFlagMsg));
|
|
||||||
}
|
|
||||||
options.application ||= options.app || options.fleet;
|
|
||||||
|
|
||||||
const balena = getBalenaSdk();
|
const balena = getBalenaSdk();
|
||||||
|
|
||||||
// Check user has specified one of application/device/release
|
// Check user has specified one of application/device/release
|
||||||
if (!options.application && !options.device && !options.release) {
|
if (!options.fleet && !options.device && !options.release) {
|
||||||
throw new ExpectedError(this.missingResourceMessage);
|
throw new ExpectedError(this.missingResourceMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,10 +80,10 @@ export default class TagsCmd extends Command {
|
|||||||
|
|
||||||
let tags;
|
let tags;
|
||||||
|
|
||||||
if (options.application) {
|
if (options.fleet) {
|
||||||
const { getTypedApplicationIdentifier } = await import('../utils/sdk');
|
const { getFleetSlug } = await import('../utils/sdk');
|
||||||
tags = await balena.models.application.tags.getAllByApplication(
|
tags = await balena.models.application.tags.getAllByApplication(
|
||||||
await getTypedApplicationIdentifier(balena, options.application),
|
await getFleetSlug(balena, options.fleet),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (options.device) {
|
if (options.device) {
|
||||||
|
@ -131,6 +131,13 @@ Please use "balena ${alternative}" instead.`);
|
|||||||
'local scan': [replaced, 'scan', 'v11.0.0'],
|
'local scan': [replaced, 'scan', 'v11.0.0'],
|
||||||
'local ssh': [replaced, 'ssh', 'v11.0.0'],
|
'local ssh': [replaced, 'ssh', 'v11.0.0'],
|
||||||
'local stop': [removed, stopAlternative, 'v11.0.0'],
|
'local stop': [removed, stopAlternative, 'v11.0.0'],
|
||||||
|
app: [replaced, 'fleet', 'v13.0.0'],
|
||||||
|
apps: [replaced, 'fleets', 'v13.0.0'],
|
||||||
|
'app create': [replaced, 'fleet create', 'v13.0.0'],
|
||||||
|
'app purge': [replaced, 'fleet purge', 'v13.0.0'],
|
||||||
|
'app rename': [replaced, 'fleet rename', 'v13.0.0'],
|
||||||
|
'app restart': [replaced, 'fleet restart', 'v13.0.0'],
|
||||||
|
'app rm': [replaced, 'fleet rm', 'v13.0.0'],
|
||||||
};
|
};
|
||||||
let cmd: string | undefined;
|
let cmd: string | undefined;
|
||||||
if (argvSlice.length > 1) {
|
if (argvSlice.length > 1) {
|
||||||
|
@ -253,13 +253,13 @@ export async function getFormattedOsVersions(
|
|||||||
const sdk = getBalenaSdk();
|
const sdk = getBalenaSdk();
|
||||||
let slug = deviceType;
|
let slug = deviceType;
|
||||||
let versionsByDT: SDK.OsVersionsByDeviceType =
|
let versionsByDT: SDK.OsVersionsByDeviceType =
|
||||||
await sdk.models.hostapp.getAvailableOsVersions([slug]);
|
await sdk.models.os.getAvailableOsVersions([slug]);
|
||||||
// if slug is an alias, fetch the real slug
|
// if slug is an alias, fetch the real slug
|
||||||
if (!versionsByDT[slug]?.length) {
|
if (!versionsByDT[slug]?.length) {
|
||||||
// unaliasDeviceType() produces a nice error msg if slug is invalid
|
// unaliasDeviceType() produces a nice error msg if slug is invalid
|
||||||
slug = await unaliasDeviceType(sdk, slug);
|
slug = await unaliasDeviceType(sdk, slug);
|
||||||
if (slug !== deviceType) {
|
if (slug !== deviceType) {
|
||||||
versionsByDT = await sdk.models.hostapp.getAvailableOsVersions([slug]);
|
versionsByDT = await sdk.models.os.getAvailableOsVersions([slug]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const versions: SDK.OsVersion[] = (versionsByDT[slug] || [])
|
const versions: SDK.OsVersion[] = (versionsByDT[slug] || [])
|
||||||
|
@ -19,35 +19,13 @@ import { flags } from '@oclif/command';
|
|||||||
import { stripIndent } from './lazy';
|
import { stripIndent } from './lazy';
|
||||||
import { lowercaseIfSlug } from './normalization';
|
import { lowercaseIfSlug } from './normalization';
|
||||||
|
|
||||||
import { isV13 } from './version';
|
import { isV14 } from './version';
|
||||||
import type { IBooleanFlag } from '@oclif/parser/lib/flags';
|
import type { IBooleanFlag } from '@oclif/parser/lib/flags';
|
||||||
import type { DataOutputOptions, DataSetOutputOptions } from '../framework';
|
import type { DataOutputOptions, DataSetOutputOptions } from '../framework';
|
||||||
|
|
||||||
export const v13: IBooleanFlag<boolean> = 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: 'DEPRECATED alias for -f, --fleet',
|
|
||||||
parse: lowercaseIfSlug,
|
|
||||||
});
|
|
||||||
// TODO: Consider remove second alias 'app' when we can, to simplify.
|
|
||||||
export const app = flags.string({
|
|
||||||
description: 'DEPRECATED alias for -f, --fleet',
|
|
||||||
parse: lowercaseIfSlug,
|
|
||||||
});
|
|
||||||
export const fleet = flags.string({
|
export const fleet = flags.string({
|
||||||
char: 'f',
|
char: 'f',
|
||||||
description: isV13()
|
description: 'fleet name, slug (preferred), or numeric ID (deprecated)',
|
||||||
? '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,
|
parse: lowercaseIfSlug,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -114,12 +92,14 @@ export const deviceType = flags.string({
|
|||||||
required: true,
|
required: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const deviceTypeIgnored = flags.string({
|
export const deviceTypeIgnored = isV14()
|
||||||
description: 'ignored - no longer required',
|
? undefined
|
||||||
char: 't',
|
: flags.string({
|
||||||
required: false,
|
description: 'ignored - no longer required',
|
||||||
hidden: isV13(),
|
char: 't',
|
||||||
});
|
required: false,
|
||||||
|
hidden: true,
|
||||||
|
});
|
||||||
|
|
||||||
export const json: IBooleanFlag<boolean> = flags.boolean({
|
export const json: IBooleanFlag<boolean> = flags.boolean({
|
||||||
char: 'j',
|
char: 'j',
|
||||||
|
5
lib/utils/compose-types.d.ts
vendored
5
lib/utils/compose-types.d.ts
vendored
@ -51,7 +51,6 @@ export interface ComposeOpts {
|
|||||||
dockerfilePath?: string;
|
dockerfilePath?: string;
|
||||||
inlineLogs?: boolean;
|
inlineLogs?: boolean;
|
||||||
multiDockerignore: boolean;
|
multiDockerignore: boolean;
|
||||||
nogitignore: boolean; // v13: delete this line
|
|
||||||
noParentCheck: boolean;
|
noParentCheck: boolean;
|
||||||
projectName: string;
|
projectName: string;
|
||||||
projectPath: string;
|
projectPath: string;
|
||||||
@ -63,12 +62,9 @@ export interface ComposeCliFlags {
|
|||||||
dockerfile?: string;
|
dockerfile?: string;
|
||||||
logs: boolean;
|
logs: boolean;
|
||||||
nologs: boolean;
|
nologs: boolean;
|
||||||
gitignore?: boolean; // v13: delete this line
|
|
||||||
nogitignore?: boolean; // v13: delete this line
|
|
||||||
'multi-dockerignore': boolean;
|
'multi-dockerignore': boolean;
|
||||||
'noparent-check': boolean;
|
'noparent-check': boolean;
|
||||||
'registry-secrets'?: RegistrySecrets;
|
'registry-secrets'?: RegistrySecrets;
|
||||||
'convert-eol': boolean;
|
|
||||||
'noconvert-eol': boolean;
|
'noconvert-eol': boolean;
|
||||||
projectName?: string;
|
projectName?: string;
|
||||||
}
|
}
|
||||||
@ -102,6 +98,5 @@ interface TarDirectoryOptions {
|
|||||||
composition?: Composition;
|
composition?: Composition;
|
||||||
convertEol?: boolean;
|
convertEol?: boolean;
|
||||||
multiDockerignore?: boolean;
|
multiDockerignore?: boolean;
|
||||||
nogitignore: boolean; // v13: delete this line
|
|
||||||
preFinalizeCallback?: (pack: Pack) => void | Promise<void>;
|
preFinalizeCallback?: (pack: Pack) => void | Promise<void>;
|
||||||
}
|
}
|
||||||
|
@ -16,21 +16,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { ExpectedError } from '../errors';
|
|
||||||
import { getChalk } from './lazy';
|
import { getChalk } from './lazy';
|
||||||
import { isV13 } from './version';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns Promise<{import('./compose-types').ComposeOpts}>
|
* @returns Promise<{import('./compose-types').ComposeOpts}>
|
||||||
*/
|
*/
|
||||||
export function generateOpts(options) {
|
export function generateOpts(options) {
|
||||||
const { promises: fs } = require('fs');
|
const { promises: fs } = require('fs');
|
||||||
|
|
||||||
if (!isV13() && options.gitignore && options['multi-dockerignore']) {
|
|
||||||
throw new ExpectedError(
|
|
||||||
'The --gitignore and --multi-dockerignore options cannot be used together',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return fs.realpath(options.source || '.').then((projectPath) => ({
|
return fs.realpath(options.source || '.').then((projectPath) => ({
|
||||||
projectName: options.projectName,
|
projectName: options.projectName,
|
||||||
projectPath,
|
projectPath,
|
||||||
@ -38,7 +30,6 @@ export function generateOpts(options) {
|
|||||||
convertEol: !options['noconvert-eol'],
|
convertEol: !options['noconvert-eol'],
|
||||||
dockerfilePath: options.dockerfile,
|
dockerfilePath: options.dockerfile,
|
||||||
multiDockerignore: !!options['multi-dockerignore'],
|
multiDockerignore: !!options['multi-dockerignore'],
|
||||||
nogitignore: !options.gitignore, // v13: delete this line
|
|
||||||
noParentCheck: options['noparent-check'],
|
noParentCheck: options['noparent-check'],
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -89,92 +80,6 @@ export function createProject(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the CLI v10 / v11 "original" tarDirectory function. It is still
|
|
||||||
* around for the benefit of the `--gitignore` option, but is expected to be
|
|
||||||
* deleted in CLI v13.
|
|
||||||
* @param {string} dir Source directory
|
|
||||||
* @param {import('./compose-types').TarDirectoryOptions} param
|
|
||||||
* @returns {Promise<import('stream').Readable>}
|
|
||||||
*
|
|
||||||
* v13: delete this function
|
|
||||||
*/
|
|
||||||
export async function originalTarDirectory(dir, param) {
|
|
||||||
let {
|
|
||||||
preFinalizeCallback = null,
|
|
||||||
convertEol = false,
|
|
||||||
nogitignore = false,
|
|
||||||
} = param;
|
|
||||||
if (convertEol == null) {
|
|
||||||
convertEol = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Bluebird = require('bluebird');
|
|
||||||
const tar = require('tar-stream');
|
|
||||||
const klaw = require('klaw');
|
|
||||||
const { promises: fs } = require('fs');
|
|
||||||
const streamToPromise = require('stream-to-promise');
|
|
||||||
const { printGitignoreWarn } = require('./compose_ts');
|
|
||||||
const { FileIgnorer, IgnoreFileType } = require('./ignore');
|
|
||||||
const { toPosixPath } = require('resin-multibuild').PathUtils;
|
|
||||||
let readFile;
|
|
||||||
if (process.platform === 'win32') {
|
|
||||||
const { readFileWithEolConversion } = require('./eol-conversion');
|
|
||||||
readFile = (file) => readFileWithEolConversion(file, convertEol);
|
|
||||||
} else {
|
|
||||||
({ readFile } = fs);
|
|
||||||
}
|
|
||||||
|
|
||||||
const getFiles = () =>
|
|
||||||
Bluebird.resolve(streamToPromise(klaw(dir)))
|
|
||||||
// @ts-ignore
|
|
||||||
.filter((item) => !item.stats.isDirectory())
|
|
||||||
// @ts-ignore
|
|
||||||
.map((item) => item.path);
|
|
||||||
|
|
||||||
const ignore = new FileIgnorer(dir);
|
|
||||||
const pack = tar.pack();
|
|
||||||
const ignoreFiles = {};
|
|
||||||
return getFiles()
|
|
||||||
.each(function (file) {
|
|
||||||
const type = ignore.getIgnoreFileType(path.relative(dir, file));
|
|
||||||
if (type != null) {
|
|
||||||
ignoreFiles[type] = ignoreFiles[type] || [];
|
|
||||||
ignoreFiles[type].push(path.resolve(dir, file));
|
|
||||||
return ignore.addIgnoreFile(file, type);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.tap(() => {
|
|
||||||
if (!nogitignore) {
|
|
||||||
printGitignoreWarn(
|
|
||||||
(ignoreFiles[IgnoreFileType.DockerIgnore] || [])[0] || '',
|
|
||||||
ignoreFiles[IgnoreFileType.GitIgnore] || [],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(ignore.filter)
|
|
||||||
.map(function (file) {
|
|
||||||
const relPath = path.relative(path.resolve(dir), file);
|
|
||||||
return Promise.all([relPath, fs.stat(file), readFile(file)]).then(
|
|
||||||
([filename, stats, data]) =>
|
|
||||||
pack.entry(
|
|
||||||
{
|
|
||||||
name: toPosixPath(filename),
|
|
||||||
mtime: stats.mtime,
|
|
||||||
size: stats.size,
|
|
||||||
mode: stats.mode,
|
|
||||||
},
|
|
||||||
data,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.then(() => preFinalizeCallback?.(pack))
|
|
||||||
.then(function () {
|
|
||||||
pack.finalize();
|
|
||||||
return pack;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} apiEndpoint
|
* @param {string} apiEndpoint
|
||||||
* @param {string} auth
|
* @param {string} auth
|
||||||
|
@ -43,7 +43,6 @@ import {
|
|||||||
import type { DeviceInfo } from './device/api';
|
import type { DeviceInfo } from './device/api';
|
||||||
import { getBalenaSdk, getChalk, stripIndent } from './lazy';
|
import { getBalenaSdk, getChalk, stripIndent } from './lazy';
|
||||||
import Logger = require('./logger');
|
import Logger = require('./logger');
|
||||||
import { isV13 } from './version';
|
|
||||||
import { exists } from './which';
|
import { exists } from './which';
|
||||||
|
|
||||||
const allowedContractTypes = ['sw.application', 'sw.block'];
|
const allowedContractTypes = ['sw.application', 'sw.block'];
|
||||||
@ -105,8 +104,6 @@ export async function applyReleaseTagKeysAndValues(
|
|||||||
|
|
||||||
const LOG_LENGTH_MAX = 512 * 1024; // 512KB
|
const LOG_LENGTH_MAX = 512 * 1024; // 512KB
|
||||||
const compositionFileNames = ['docker-compose.yml', 'docker-compose.yaml'];
|
const compositionFileNames = ['docker-compose.yml', 'docker-compose.yaml'];
|
||||||
const hr =
|
|
||||||
'----------------------------------------------------------------------';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* high-level function resolving a project and creating a composition out
|
* high-level function resolving a project and creating a composition out
|
||||||
@ -257,7 +254,6 @@ export interface BuildProjectOpts {
|
|||||||
inlineLogs?: boolean;
|
inlineLogs?: boolean;
|
||||||
convertEol: boolean;
|
convertEol: boolean;
|
||||||
dockerfilePath?: string;
|
dockerfilePath?: string;
|
||||||
nogitignore: boolean; // v13: delete this line
|
|
||||||
multiDockerignore: boolean;
|
multiDockerignore: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -748,43 +744,19 @@ export function isBuildConfig(
|
|||||||
* Create a tar stream out of the local filesystem at the given directory,
|
* Create a tar stream out of the local filesystem at the given directory,
|
||||||
* while optionally applying file filters such as '.dockerignore' and
|
* while optionally applying file filters such as '.dockerignore' and
|
||||||
* optionally converting text file line endings (CRLF to LF).
|
* optionally converting text file line endings (CRLF to LF).
|
||||||
* @param dir Source directory
|
* @param dir Project directory (the '--source' command line option)
|
||||||
* @param param Options
|
* @param param TarDirectoryOptions
|
||||||
* @returns Readable stream
|
* @returns Readable stream (to be sent to the Docker Engine)
|
||||||
*/
|
*/
|
||||||
export async function tarDirectory(
|
export async function tarDirectory(
|
||||||
dir: string,
|
|
||||||
param: TarDirectoryOptions,
|
|
||||||
): Promise<import('stream').Readable> {
|
|
||||||
const { nogitignore = false } = param; // v13: delete this line
|
|
||||||
if (isV13() || nogitignore) {
|
|
||||||
return newTarDirectory(dir, param);
|
|
||||||
} else {
|
|
||||||
return (await import('./compose')).originalTarDirectory(dir, param);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a tar stream out of the local filesystem at the given directory,
|
|
||||||
* while optionally applying file filters such as '.dockerignore' and
|
|
||||||
* optionally converting text file line endings (CRLF to LF).
|
|
||||||
* @param dir Source directory
|
|
||||||
* @param param Options
|
|
||||||
* @returns Readable stream
|
|
||||||
*/
|
|
||||||
async function newTarDirectory(
|
|
||||||
dir: string,
|
dir: string,
|
||||||
{
|
{
|
||||||
composition,
|
composition,
|
||||||
convertEol = false,
|
convertEol = false,
|
||||||
multiDockerignore = false,
|
multiDockerignore = false,
|
||||||
nogitignore = false, // v13: delete this line
|
|
||||||
preFinalizeCallback,
|
preFinalizeCallback,
|
||||||
}: TarDirectoryOptions,
|
}: TarDirectoryOptions,
|
||||||
): Promise<import('stream').Readable> {
|
): Promise<import('stream').Readable> {
|
||||||
if (!isV13()) {
|
|
||||||
require('assert').strict.equal(nogitignore, true);
|
|
||||||
}
|
|
||||||
const { filterFilesWithDockerignore } = await import('./ignore');
|
const { filterFilesWithDockerignore } = await import('./ignore');
|
||||||
const { toPosixPath } = (await import('resin-multibuild')).PathUtils;
|
const { toPosixPath } = (await import('resin-multibuild')).PathUtils;
|
||||||
|
|
||||||
@ -905,48 +877,6 @@ function printDockerignoreWarn(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Print a deprecation warning if any '.gitignore' or '.dockerignore' file is
|
|
||||||
* found and the --gitignore (-g) option has been provided (v11 compatibility).
|
|
||||||
* @param dockerignoreFile Absolute path to a .dockerignore file
|
|
||||||
* @param gitignoreFiles Array of absolute paths to .gitginore files
|
|
||||||
*
|
|
||||||
* v13: delete this function
|
|
||||||
*/
|
|
||||||
export function printGitignoreWarn(
|
|
||||||
dockerignoreFile: string,
|
|
||||||
gitignoreFiles: string[],
|
|
||||||
) {
|
|
||||||
if (isV13()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const ignoreFiles = [dockerignoreFile, ...gitignoreFiles].filter((e) => e);
|
|
||||||
if (ignoreFiles.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const msg = [' ', hr, 'Using file ignore patterns from:'];
|
|
||||||
msg.push(...ignoreFiles.map((e) => `* ${e}`));
|
|
||||||
if (gitignoreFiles.length) {
|
|
||||||
msg.push(stripIndent`
|
|
||||||
.gitignore files are being considered because the --gitignore option was used.
|
|
||||||
This option is deprecated and will be removed in the next major version release.
|
|
||||||
For more information, see 'balena help ${Logger.command}'.
|
|
||||||
`);
|
|
||||||
msg.push(hr);
|
|
||||||
Logger.getLogger().logWarn(msg.join('\n'));
|
|
||||||
} else if (dockerignoreFile && process.platform === 'win32') {
|
|
||||||
msg.push(stripIndent`
|
|
||||||
The --gitignore option was used, but no .gitignore files were found.
|
|
||||||
The --gitignore option is deprecated and will be removed in the next major
|
|
||||||
version release. It prevents the use of a better dockerignore parser and
|
|
||||||
filter library that fixes several issues on Windows and improves compatibility
|
|
||||||
with 'docker build'. For more information, see 'balena help ${Logger.command}'.
|
|
||||||
`);
|
|
||||||
msg.push(hr);
|
|
||||||
Logger.getLogger().logWarn(msg.join('\n'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether the "build secrets" feature is being used and, if so,
|
* Check whether the "build secrets" feature is being used and, if so,
|
||||||
* verify that the target docker daemon is balenaEngine. If the
|
* verify that the target docker daemon is balenaEngine. If the
|
||||||
@ -1729,21 +1659,6 @@ export const composeCliFlags: flags.Input<ComposeCliFlags> = {
|
|||||||
description:
|
description:
|
||||||
'Hide the image build log output (produce less verbose output)',
|
'Hide the image build log output (produce less verbose output)',
|
||||||
}),
|
}),
|
||||||
...(isV13()
|
|
||||||
? {}
|
|
||||||
: {
|
|
||||||
gitignore: flags.boolean({
|
|
||||||
description: stripIndent`
|
|
||||||
Consider .gitignore files in addition to the .dockerignore file. This reverts
|
|
||||||
to the CLI v11 behavior/implementation (deprecated) if compatibility is required
|
|
||||||
until your project can be adapted.`,
|
|
||||||
char: 'g',
|
|
||||||
}),
|
|
||||||
nogitignore: flags.boolean({
|
|
||||||
description: `No-op (default behavior) since balena CLI v12.0.0. See "balena help build".`,
|
|
||||||
char: 'G',
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
'multi-dockerignore': flags.boolean({
|
'multi-dockerignore': flags.boolean({
|
||||||
description:
|
description:
|
||||||
'Have each service use its own .dockerignore file. See "balena help build".',
|
'Have each service use its own .dockerignore file. See "balena help build".',
|
||||||
@ -1758,10 +1673,6 @@ export const composeCliFlags: flags.Input<ComposeCliFlags> = {
|
|||||||
'Path to a YAML or JSON file with passwords for a private Docker registry',
|
'Path to a YAML or JSON file with passwords for a private Docker registry',
|
||||||
char: 'R',
|
char: 'R',
|
||||||
}),
|
}),
|
||||||
'convert-eol': flags.boolean({
|
|
||||||
description: 'No-op and deprecated since balena CLI v12.0.0',
|
|
||||||
char: 'l',
|
|
||||||
}),
|
|
||||||
'noconvert-eol': flags.boolean({
|
'noconvert-eol': flags.boolean({
|
||||||
description:
|
description:
|
||||||
"Don't convert line endings from CRLF (Windows format) to LF (Unix format).",
|
"Don't convert line endings from CRLF (Windows format) to LF (Unix format).",
|
||||||
|
@ -59,7 +59,6 @@ export interface DeviceDeployOptions {
|
|||||||
registrySecrets: RegistrySecrets;
|
registrySecrets: RegistrySecrets;
|
||||||
multiDockerignore: boolean;
|
multiDockerignore: boolean;
|
||||||
nocache: boolean;
|
nocache: boolean;
|
||||||
nogitignore: boolean; // v13: delete this line
|
|
||||||
noParentCheck: boolean;
|
noParentCheck: boolean;
|
||||||
nolive: boolean;
|
nolive: boolean;
|
||||||
pull: boolean;
|
pull: boolean;
|
||||||
@ -184,7 +183,6 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
|
|||||||
convertEol: opts.convertEol,
|
convertEol: opts.convertEol,
|
||||||
dockerfilePath: opts.dockerfilePath,
|
dockerfilePath: opts.dockerfilePath,
|
||||||
multiDockerignore: opts.multiDockerignore,
|
multiDockerignore: opts.multiDockerignore,
|
||||||
nogitignore: opts.nogitignore, // v13: delete this line
|
|
||||||
noParentCheck: opts.noParentCheck,
|
noParentCheck: opts.noParentCheck,
|
||||||
projectName: 'local',
|
projectName: 'local',
|
||||||
projectPath: opts.source,
|
projectPath: opts.source,
|
||||||
@ -204,7 +202,6 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
|
|||||||
composition: project.composition,
|
composition: project.composition,
|
||||||
convertEol: opts.convertEol,
|
convertEol: opts.convertEol,
|
||||||
multiDockerignore: opts.multiDockerignore,
|
multiDockerignore: opts.multiDockerignore,
|
||||||
nogitignore: opts.nogitignore, // v13: delete this line
|
|
||||||
});
|
});
|
||||||
globalLogger.logDebug(`Tarring complete in ${Date.now() - tarStartTime} ms`);
|
globalLogger.logDebug(`Tarring complete in ${Date.now() - tarStartTime} ms`);
|
||||||
|
|
||||||
@ -435,7 +432,6 @@ export async function rebuildSingleTask(
|
|||||||
composition,
|
composition,
|
||||||
convertEol: opts.convertEol,
|
convertEol: opts.convertEol,
|
||||||
multiDockerignore: opts.multiDockerignore,
|
multiDockerignore: opts.multiDockerignore,
|
||||||
nogitignore: opts.nogitignore, // v13: delete this line
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const task = _.find(
|
const task = _.find(
|
||||||
|
@ -151,26 +151,8 @@ export async function osProgressHandler(step: InitializeEmitter) {
|
|||||||
export async function getAppWithArch(
|
export async function getAppWithArch(
|
||||||
applicationName: string,
|
applicationName: string,
|
||||||
): Promise<ApplicationWithDeviceType & { arch: string }> {
|
): Promise<ApplicationWithDeviceType & { arch: string }> {
|
||||||
const app = await getApplication(applicationName);
|
const { getApplication } = await import('./sdk');
|
||||||
const { getExpanded } = await import('./pine');
|
const options: BalenaSdk.PineOptions<BalenaSdk.Application> = {
|
||||||
|
|
||||||
return {
|
|
||||||
...app,
|
|
||||||
arch: getExpanded(
|
|
||||||
getExpanded(app.is_for__device_type)!.is_of__cpu_architecture,
|
|
||||||
)!.slug,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Drop this. The sdk now has this baked in application.get().
|
|
||||||
function getApplication(
|
|
||||||
applicationName: string,
|
|
||||||
): Promise<ApplicationWithDeviceType> {
|
|
||||||
// Check for an app of the form `user/application`, and send
|
|
||||||
// that off to a special handler (before importing any modules)
|
|
||||||
const match = applicationName.split('/');
|
|
||||||
|
|
||||||
const extraOptions: BalenaSdk.PineOptions<BalenaSdk.Application> = {
|
|
||||||
$expand: {
|
$expand: {
|
||||||
application_type: {
|
application_type: {
|
||||||
$select: ['name', 'slug', 'supports_multicontainer', 'is_legacy'],
|
$select: ['name', 'slug', 'supports_multicontainer', 'is_legacy'],
|
||||||
@ -185,20 +167,20 @@ function getApplication(
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const balena = getBalenaSdk();
|
const balena = getBalenaSdk();
|
||||||
if (match.length > 1) {
|
const app = (await getApplication(
|
||||||
return balena.models.application.getAppByOwner(
|
balena,
|
||||||
match[1],
|
|
||||||
match[0],
|
|
||||||
extraOptions,
|
|
||||||
) as Promise<ApplicationWithDeviceType>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return balena.models.application.get(
|
|
||||||
applicationName,
|
applicationName,
|
||||||
extraOptions,
|
options,
|
||||||
) as Promise<ApplicationWithDeviceType>;
|
)) as ApplicationWithDeviceType;
|
||||||
|
const { getExpanded } = await import('./pine');
|
||||||
|
|
||||||
|
return {
|
||||||
|
...app,
|
||||||
|
arch: getExpanded(
|
||||||
|
getExpanded(app.is_for__device_type)!.is_of__cpu_architecture,
|
||||||
|
)!.slug,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const second = 1000; // 1000 milliseconds
|
const second = 1000; // 1000 milliseconds
|
||||||
@ -428,7 +410,7 @@ export function getProxyConfig(): ProxyConfig | undefined {
|
|||||||
|
|
||||||
export const expandForAppName = {
|
export const expandForAppName = {
|
||||||
$expand: {
|
$expand: {
|
||||||
belongs_to__application: { $select: 'app_name' },
|
belongs_to__application: { $select: ['app_name', 'slug'] as any },
|
||||||
is_of__device_type: { $select: 'slug' },
|
is_of__device_type: { $select: 'slug' },
|
||||||
is_running__release: { $select: 'commit' },
|
is_running__release: { $select: 'commit' },
|
||||||
},
|
},
|
||||||
|
@ -17,183 +17,11 @@
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import { promises as fs, Stats } from 'fs';
|
import { promises as fs, Stats } from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as MultiBuild from 'resin-multibuild';
|
|
||||||
|
|
||||||
import dockerIgnore = require('@zeit/dockerignore');
|
|
||||||
import ignore from 'ignore';
|
|
||||||
import type { Ignore } from '@balena/dockerignore';
|
import type { Ignore } from '@balena/dockerignore';
|
||||||
|
|
||||||
import { ExpectedError } from '../errors';
|
import { ExpectedError } from '../errors';
|
||||||
|
|
||||||
const { toPosixPath } = MultiBuild.PathUtils;
|
|
||||||
|
|
||||||
// v13: delete this enum
|
|
||||||
export enum IgnoreFileType {
|
|
||||||
DockerIgnore,
|
|
||||||
GitIgnore,
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IgnoreEntry {
|
|
||||||
pattern: string;
|
|
||||||
// The relative file path from the base path of the build context
|
|
||||||
filePath: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is used by the CLI v10 / v11 "original" tarDirectory function
|
|
||||||
* in `compose.js`. It is still around for the benefit of the `--gitignore`
|
|
||||||
* option, but is expected to be deleted in CLI v13.
|
|
||||||
*
|
|
||||||
* v13: delete this class
|
|
||||||
*/
|
|
||||||
export class FileIgnorer {
|
|
||||||
private dockerIgnoreEntries: IgnoreEntry[];
|
|
||||||
private gitIgnoreEntries: IgnoreEntry[];
|
|
||||||
|
|
||||||
private static ignoreFiles: Array<{
|
|
||||||
pattern: string;
|
|
||||||
type: IgnoreFileType;
|
|
||||||
allowSubdirs: boolean;
|
|
||||||
}> = [
|
|
||||||
{
|
|
||||||
pattern: '.gitignore',
|
|
||||||
type: IgnoreFileType.GitIgnore,
|
|
||||||
allowSubdirs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
pattern: '.dockerignore',
|
|
||||||
type: IgnoreFileType.DockerIgnore,
|
|
||||||
allowSubdirs: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
public constructor(public basePath: string) {
|
|
||||||
this.dockerIgnoreEntries = [];
|
|
||||||
this.gitIgnoreEntries = [];
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {string} relativePath
|
|
||||||
* The relative pathname from the build context, for example a root level .gitignore should be
|
|
||||||
* ./.gitignore
|
|
||||||
* @returns IgnoreFileType
|
|
||||||
* The type of ignore file, or null
|
|
||||||
*/
|
|
||||||
public getIgnoreFileType(relativePath: string): IgnoreFileType | null {
|
|
||||||
for (const { pattern, type, allowSubdirs } of FileIgnorer.ignoreFiles) {
|
|
||||||
if (
|
|
||||||
path.basename(relativePath) === pattern &&
|
|
||||||
(allowSubdirs || path.dirname(relativePath) === '.')
|
|
||||||
) {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {string} fullPath
|
|
||||||
* The full path on disk of the ignore file
|
|
||||||
* @param {IgnoreFileType} type
|
|
||||||
* @returns Promise
|
|
||||||
*/
|
|
||||||
public async addIgnoreFile(
|
|
||||||
fullPath: string,
|
|
||||||
type: IgnoreFileType,
|
|
||||||
): Promise<void> {
|
|
||||||
const contents = await fs.readFile(fullPath, 'utf8');
|
|
||||||
|
|
||||||
contents.split('\n').forEach((line) => {
|
|
||||||
// ignore empty lines and comments
|
|
||||||
if (/\s*#/.test(line) || _.isEmpty(line)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.addEntry(line, fullPath, type);
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass this function as a predicate to a filter function, and it will filter
|
|
||||||
// any ignored files
|
|
||||||
public filter = (filename: string): boolean => {
|
|
||||||
const relFile = path.relative(this.basePath, filename);
|
|
||||||
|
|
||||||
// Don't ignore any metadata files
|
|
||||||
// The regex below matches `.balena/qemu` and `myservice/.balena/qemu`
|
|
||||||
// but not `some.dir.for.balena/qemu`.
|
|
||||||
if (/(^|\/)\.(balena|resin)\//.test(toPosixPath(relFile))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't ignore Dockerfile (with or without extension) or docker-compose.yml
|
|
||||||
if (
|
|
||||||
/^Dockerfile$|^Dockerfile\.\S+/.test(path.basename(relFile)) ||
|
|
||||||
path.basename(relFile) === 'docker-compose.yml'
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dockerIgnoreHandle = dockerIgnore();
|
|
||||||
const gitIgnoreHandle = ignore();
|
|
||||||
|
|
||||||
interface IgnoreHandle {
|
|
||||||
add: (pattern: string) => void;
|
|
||||||
ignores: (file: string) => boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ignoreTypes: Array<{
|
|
||||||
handle: IgnoreHandle;
|
|
||||||
entries: IgnoreEntry[];
|
|
||||||
}> = [
|
|
||||||
{ handle: dockerIgnoreHandle, entries: this.dockerIgnoreEntries },
|
|
||||||
{ handle: gitIgnoreHandle, entries: this.gitIgnoreEntries },
|
|
||||||
];
|
|
||||||
|
|
||||||
_.each(ignoreTypes, ({ handle, entries }) => {
|
|
||||||
_.each(entries, ({ pattern, filePath }) => {
|
|
||||||
if (FileIgnorer.contains(path.posix.dirname(filePath), filename)) {
|
|
||||||
handle.add(pattern);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return !_.some(ignoreTypes, ({ handle }) => handle.ignores(relFile));
|
|
||||||
}; // tslint:disable-line:semicolon
|
|
||||||
|
|
||||||
private addEntry(
|
|
||||||
pattern: string,
|
|
||||||
filePath: string,
|
|
||||||
type: IgnoreFileType,
|
|
||||||
): void {
|
|
||||||
const entry: IgnoreEntry = { pattern, filePath };
|
|
||||||
switch (type) {
|
|
||||||
case IgnoreFileType.DockerIgnore:
|
|
||||||
this.dockerIgnoreEntries.push(entry);
|
|
||||||
break;
|
|
||||||
case IgnoreFileType.GitIgnore:
|
|
||||||
this.gitIgnoreEntries.push(entry);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given two paths, check whether the first contains the second
|
|
||||||
* @param path1 The potentially containing path
|
|
||||||
* @param path2 The potentially contained path
|
|
||||||
* @return A boolean indicating whether `path1` contains `path2`
|
|
||||||
*/
|
|
||||||
private static contains(path1: string, path2: string): boolean {
|
|
||||||
// First normalise the input, to remove any path weirdness
|
|
||||||
path1 = path.posix.normalize(path1);
|
|
||||||
path2 = path.posix.normalize(path2);
|
|
||||||
|
|
||||||
// Now test if the start of the relative path contains ../ ,
|
|
||||||
// which would tell us that path1 is not part of path2
|
|
||||||
return !/^\.\.\//.test(path.posix.relative(path1, path2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FileStats {
|
export interface FileStats {
|
||||||
filePath: string;
|
filePath: string;
|
||||||
relPath: string;
|
relPath: string;
|
||||||
|
@ -15,8 +15,6 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { isV13 } from './version';
|
|
||||||
|
|
||||||
export const reachingOut = `\
|
export const reachingOut = `\
|
||||||
For further help or support, visit:
|
For further help or support, visit:
|
||||||
https://www.balena.io/docs/reference/balena-cli/#support-faq-and-troubleshooting
|
https://www.balena.io/docs/reference/balena-cli/#support-faq-and-troubleshooting
|
||||||
@ -88,60 +86,7 @@ If the --registry-secrets option is not specified, and a secrets.yml or
|
|||||||
secrets.json file exists in the balena directory (usually $HOME/.balena),
|
secrets.json file exists in the balena directory (usually $HOME/.balena),
|
||||||
this file will be used instead.`;
|
this file will be used instead.`;
|
||||||
|
|
||||||
const dockerignoreHelpV12 =
|
export const dockerignoreHelp =
|
||||||
'DOCKERIGNORE AND GITIGNORE FILES \n' +
|
|
||||||
`By default, the balena CLI will use a single ".dockerignore" file (if any) at
|
|
||||||
the project root (--source directory) in order to decide which source files to
|
|
||||||
exclude from the "build context" (tar stream) sent to balenaCloud, Docker
|
|
||||||
daemon or balenaEngine. In a microservices (multicontainer) 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) 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
|
|
||||||
only the default .dockerignore patterns (see below) apply for that service
|
|
||||||
subdirectory.
|
|
||||||
|
|
||||||
When the --multi-dockerignore (-m) option is used, the .dockerignore file (if
|
|
||||||
any) defined at the overall project root will be used to filter files and
|
|
||||||
subdirectories other than service subdirectories. It will not have any effect
|
|
||||||
on service subdirectories, whether or not a service subdirectory defines its
|
|
||||||
own .dockerignore file. Multiple .dockerignore files are not merged or added
|
|
||||||
together, and cannot override or extend other files. This behavior maximizes
|
|
||||||
compatibility with the standard docker-compose tool, while still allowing a
|
|
||||||
root .dockerignore file (at the overall project root) to filter files and
|
|
||||||
folders that are outside service subdirectories.
|
|
||||||
|
|
||||||
balena CLI releases older than v12.0.0 also took .gitignore files into account.
|
|
||||||
This behavior is deprecated, but may still be enabled with the --gitignore (-g)
|
|
||||||
option if compatibility is required. This option is mutually exclusive with
|
|
||||||
--multi-dockerignore (-m) and will be removed in the CLI's next major version
|
|
||||||
release (v13).
|
|
||||||
|
|
||||||
Default .dockerignore patterns \n` +
|
|
||||||
`When --gitignore (-g) is NOT used (i.e. when not in v11 compatibility mode), a
|
|
||||||
few default/hardcoded dockerignore patterns are "merged" (in memory) with the
|
|
||||||
patterns found in the applicable .dockerignore files, in the following order:
|
|
||||||
\`\`\`
|
|
||||||
**/.git
|
|
||||||
< user's patterns from the applicable '.dockerignore' file, if any >
|
|
||||||
!**/.balena
|
|
||||||
!**/.resin
|
|
||||||
!**/Dockerfile
|
|
||||||
!**/Dockerfile.*
|
|
||||||
!**/docker-compose.yml
|
|
||||||
\`\`\`
|
|
||||||
These patterns always apply, whether or not .dockerignore files exist in the
|
|
||||||
project. If necessary, the effect of the \`**/.git\` pattern may be modified by
|
|
||||||
adding counter patterns to the applicable .dockerignore file(s), for example
|
|
||||||
\`!mysubmodule/.git\`. For documentation on pattern format, see:
|
|
||||||
- https://docs.docker.com/engine/reference/builder/#dockerignore-file
|
|
||||||
- https://www.npmjs.com/package/@balena/dockerignore`;
|
|
||||||
|
|
||||||
const dockerignoreHelpV13 =
|
|
||||||
'DOCKERIGNORE AND GITIGNORE FILES \n' +
|
'DOCKERIGNORE AND GITIGNORE FILES \n' +
|
||||||
`By default, the balena CLI will use a single ".dockerignore" file (if any) at
|
`By default, the balena CLI will use a single ".dockerignore" file (if any) at
|
||||||
the project root (--source directory) in order to decide which source files to
|
the project root (--source directory) in order to decide which source files to
|
||||||
@ -191,10 +136,6 @@ adding exception patterns to the applicable .dockerignore file(s), for example
|
|||||||
- https://docs.docker.com/engine/reference/builder/#dockerignore-file
|
- https://docs.docker.com/engine/reference/builder/#dockerignore-file
|
||||||
- https://www.npmjs.com/package/@balena/dockerignore`;
|
- https://www.npmjs.com/package/@balena/dockerignore`;
|
||||||
|
|
||||||
export const dockerignoreHelp = isV13()
|
|
||||||
? dockerignoreHelpV13
|
|
||||||
: dockerignoreHelpV12;
|
|
||||||
|
|
||||||
export const applicationIdInfo = `\
|
export const applicationIdInfo = `\
|
||||||
Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are
|
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
|
the recommended option, as they are unique and unambiguous. Slugs can be
|
||||||
@ -234,30 +175,6 @@ 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/
|
secrets, please contact us via support or the forums: https://forums.balena.io/
|
||||||
\n`;
|
\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`;
|
|
||||||
|
|
||||||
export function getNodeEngineVersionWarn(
|
export function getNodeEngineVersionWarn(
|
||||||
version: string,
|
version: string,
|
||||||
validVersions: string,
|
validVersions: string,
|
||||||
|
@ -13,15 +13,14 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
import type * as BalenaSdk from 'balena-sdk';
|
|
||||||
|
import type { Application, BalenaSDK, Device, Organization } from 'balena-sdk';
|
||||||
import _ = require('lodash');
|
import _ = require('lodash');
|
||||||
|
|
||||||
import { instanceOf, NotLoggedInError, ExpectedError } from '../errors';
|
import { instanceOf, NotLoggedInError, ExpectedError } from '../errors';
|
||||||
import { getBalenaSdk, getVisuals, stripIndent, getCliForm } from './lazy';
|
import { getBalenaSdk, getVisuals, stripIndent, getCliForm } from './lazy';
|
||||||
import validation = require('./validation');
|
import validation = require('./validation');
|
||||||
import { delay } from './helpers';
|
import { delay } from './helpers';
|
||||||
import { isV13 } from './version';
|
|
||||||
import type { Application, Device, Organization } from 'balena-sdk';
|
|
||||||
|
|
||||||
export function authenticate(options: {}): Promise<void> {
|
export function authenticate(options: {}): Promise<void> {
|
||||||
const balena = getBalenaSdk();
|
const balena = getBalenaSdk();
|
||||||
@ -142,11 +141,7 @@ export async function confirm(
|
|||||||
) {
|
) {
|
||||||
if (yesOption) {
|
if (yesOption) {
|
||||||
if (yesMessage) {
|
if (yesMessage) {
|
||||||
if (isV13()) {
|
console.log(yesMessage);
|
||||||
console.error(yesMessage);
|
|
||||||
} else {
|
|
||||||
console.log(yesMessage);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -174,7 +169,7 @@ export function selectApplication(
|
|||||||
throw new ExpectedError('No fleets found');
|
throw new ExpectedError('No fleets found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const apps = (await balena.models.application.getAll({
|
const apps = (await balena.models.application.getAllDirectlyAccessible({
|
||||||
$expand: {
|
$expand: {
|
||||||
is_for__device_type: {
|
is_for__device_type: {
|
||||||
$select: 'slug',
|
$select: 'slug',
|
||||||
@ -326,7 +321,7 @@ export function inferOrSelectDevice(preferredUuid: string) {
|
|||||||
* TODO: Modify this when app IDs dropped.
|
* TODO: Modify this when app IDs dropped.
|
||||||
*/
|
*/
|
||||||
export async function getOnlineTargetDeviceUuid(
|
export async function getOnlineTargetDeviceUuid(
|
||||||
sdk: BalenaSdk.BalenaSDK,
|
sdk: BalenaSDK,
|
||||||
applicationOrDevice: string,
|
applicationOrDevice: string,
|
||||||
) {
|
) {
|
||||||
const logger = (await import('../utils/logger')).getLogger();
|
const logger = (await import('../utils/logger')).getLogger();
|
||||||
|
@ -235,7 +235,7 @@ async function getOrSelectApplication(
|
|||||||
name = appName;
|
name = appName;
|
||||||
}
|
}
|
||||||
|
|
||||||
const applications = (await sdk.models.application.getAll(
|
const applications = (await sdk.models.application.getAllDirectlyAccessible(
|
||||||
options,
|
options,
|
||||||
)) as ApplicationWithDeviceType[];
|
)) as ApplicationWithDeviceType[];
|
||||||
|
|
||||||
@ -273,7 +273,7 @@ async function createOrSelectApp(
|
|||||||
deviceType: string,
|
deviceType: string,
|
||||||
): Promise<ApplicationWithDeviceType> {
|
): Promise<ApplicationWithDeviceType> {
|
||||||
// No fleet specified, show a list to select one.
|
// No fleet specified, show a list to select one.
|
||||||
const applications = (await sdk.models.application.getAll({
|
const applications = (await sdk.models.application.getAllDirectlyAccessible({
|
||||||
$expand: { is_for__device_type: { $select: 'slug' } },
|
$expand: { is_for__device_type: { $select: 'slug' } },
|
||||||
$filter: {
|
$filter: {
|
||||||
is_for__device_type: {
|
is_for__device_type: {
|
||||||
@ -322,10 +322,13 @@ async function createApplication(
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await sdk.models.application.get(appName, {
|
await sdk.models.application.getDirectlyAccessible(appName, {
|
||||||
$filter: {
|
$filter: {
|
||||||
$or: [
|
$or: [
|
||||||
{ slug: { $startswith: `${username!.toLowerCase()}/` } },
|
{ slug: { $startswith: `${username!.toLowerCase()}/` } },
|
||||||
|
// TODO: do we still need the following filter? Is it for
|
||||||
|
// old openBalena instances where slugs were equal to the
|
||||||
|
// app name and did not contain the slash character?
|
||||||
{ $not: { slug: { $contains: '/' } } },
|
{ $not: { slug: { $contains: '/' } } },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -50,7 +50,6 @@ export interface RemoteBuild {
|
|||||||
source: string;
|
source: string;
|
||||||
auth: string;
|
auth: string;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
nogitignore: boolean; // v13: delete this line
|
|
||||||
opts: BuildOpts;
|
opts: BuildOpts;
|
||||||
sdk: BalenaSDK;
|
sdk: BalenaSDK;
|
||||||
// For internal use
|
// For internal use
|
||||||
@ -323,7 +322,6 @@ async function getTarStream(build: RemoteBuild): Promise<Stream.Readable> {
|
|||||||
preFinalizeCallback: preFinalizeCb,
|
preFinalizeCallback: preFinalizeCb,
|
||||||
convertEol: build.opts.convertEol,
|
convertEol: build.opts.convertEol,
|
||||||
multiDockerignore: build.opts.multiDockerignore,
|
multiDockerignore: build.opts.multiDockerignore,
|
||||||
nogitignore: build.nogitignore, // v13: delete this line
|
|
||||||
});
|
});
|
||||||
globalLogger.logDebug(
|
globalLogger.logDebug(
|
||||||
`Tarring complete in ${Date.now() - tarStartTime} ms`,
|
`Tarring complete in ${Date.now() - tarStartTime} ms`,
|
||||||
|
@ -23,21 +23,36 @@ import type {
|
|||||||
} from 'balena-sdk';
|
} from 'balena-sdk';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps the sdk application.get method,
|
* Get a fleet object, disambiguating the fleet identifier which may be a
|
||||||
* adding disambiguation in cases where the provided
|
* a fleet slug, name or numeric database ID (as a string).
|
||||||
* identifier could be interpreted in multiple valid ways.
|
* TODO: add support for fleet UUIDs.
|
||||||
*/
|
*/
|
||||||
export async function getApplication(
|
export async function getApplication(
|
||||||
sdk: BalenaSDK,
|
sdk: BalenaSDK,
|
||||||
nameOrSlugOrId: string | number,
|
nameOrSlugOrId: string | number,
|
||||||
options?: PineOptions<Application>,
|
options?: PineOptions<Application>,
|
||||||
): Promise<Application> {
|
): Promise<Application> {
|
||||||
const { looksLikeInteger } = await import('./validation');
|
const { looksLikeFleetSlug, looksLikeInteger } = await import('./validation');
|
||||||
if (looksLikeInteger(nameOrSlugOrId as string)) {
|
if (
|
||||||
|
typeof nameOrSlugOrId === 'string' &&
|
||||||
|
looksLikeFleetSlug(nameOrSlugOrId)
|
||||||
|
) {
|
||||||
|
return await sdk.models.application.getDirectlyAccessible(
|
||||||
|
nameOrSlugOrId,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (typeof nameOrSlugOrId === 'number' || looksLikeInteger(nameOrSlugOrId)) {
|
||||||
try {
|
try {
|
||||||
// Test for existence of app with this numerical ID
|
// Test for existence of app with this numerical ID
|
||||||
return await sdk.models.application.get(Number(nameOrSlugOrId), options);
|
return await sdk.models.application.getDirectlyAccessible(
|
||||||
|
Number(nameOrSlugOrId),
|
||||||
|
options,
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (typeof nameOrSlugOrId === 'number') {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
const { instanceOf } = await import('../errors');
|
const { instanceOf } = await import('../errors');
|
||||||
const { BalenaApplicationNotFound } = await import('balena-errors');
|
const { BalenaApplicationNotFound } = await import('balena-errors');
|
||||||
if (!instanceOf(e, BalenaApplicationNotFound)) {
|
if (!instanceOf(e, BalenaApplicationNotFound)) {
|
||||||
@ -46,47 +61,34 @@ export async function getApplication(
|
|||||||
// App with this numerical ID not found, but there may be an app with this numerical name.
|
// App with this numerical ID not found, but there may be an app with this numerical name.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sdk.models.application.get(nameOrSlugOrId, options);
|
// Not a slug and not a numeric database ID: must be an app name.
|
||||||
|
// TODO: revisit this logic when we add support for fleet UUIDs.
|
||||||
|
return await sdk.models.application.getAppByName(
|
||||||
|
nameOrSlugOrId,
|
||||||
|
options,
|
||||||
|
'directly_accessible',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an string representation of an application identifier,
|
* Given a fleet name, slug or numeric database ID, return its slug.
|
||||||
* which could be one of:
|
* This function conditionally makes an async SDK/API call to retrieve the
|
||||||
* - name (including numeric names)
|
* application object, which can be wasteful if the application object is
|
||||||
* - slug
|
* required before or after the call to this function. If this is the case,
|
||||||
* - numerical id
|
* consider calling `getApplication()` and reusing the application object.
|
||||||
* disambiguate and return a properly typed identifier.
|
|
||||||
*
|
|
||||||
* Attempts to minimise the number of API calls required.
|
|
||||||
* TODO: Remove this once support for numeric App IDs is removed.
|
|
||||||
*/
|
*/
|
||||||
export async function getTypedApplicationIdentifier(
|
export async function getFleetSlug(
|
||||||
sdk: BalenaSDK,
|
sdk: BalenaSDK,
|
||||||
nameOrSlugOrId: string,
|
nameOrSlugOrId: string | number,
|
||||||
) {
|
): Promise<string> {
|
||||||
const { looksLikeInteger } = await import('./validation');
|
const { looksLikeFleetSlug } = await import('./validation');
|
||||||
// If there's no possible ambiguity,
|
if (
|
||||||
// return the passed identifier unchanged
|
typeof nameOrSlugOrId === 'string' &&
|
||||||
if (!looksLikeInteger(nameOrSlugOrId)) {
|
looksLikeFleetSlug(nameOrSlugOrId)
|
||||||
return nameOrSlugOrId;
|
) {
|
||||||
|
return nameOrSlugOrId.toLowerCase();
|
||||||
}
|
}
|
||||||
|
return (await getApplication(sdk, nameOrSlugOrId)).slug;
|
||||||
// Resolve ambiguity
|
|
||||||
try {
|
|
||||||
// Test for existence of app with this numerical ID,
|
|
||||||
// and return typed id if found
|
|
||||||
return (await sdk.models.application.get(Number(nameOrSlugOrId))).id;
|
|
||||||
} catch (e) {
|
|
||||||
const { instanceOf } = await import('../errors');
|
|
||||||
const { BalenaApplicationNotFound } = await import('balena-errors');
|
|
||||||
if (!instanceOf(e, BalenaApplicationNotFound)) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// App with this numerical id not found
|
|
||||||
// return the passed identifier unchanged
|
|
||||||
return nameOrSlugOrId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -118,7 +118,6 @@ export function parseAsLocalHostnameOrIp(input: string, paramName?: string) {
|
|||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function looksLikeAppSlug(input: string) {
|
export function looksLikeFleetSlug(input: string) {
|
||||||
// One or more non whitespace chars, /, 4 or more non whitespace chars
|
return input.includes('/');
|
||||||
return /[\S]+\/[\S]{4,}/.test(input);
|
|
||||||
}
|
}
|
||||||
|
@ -22,12 +22,12 @@ export function isVersionGTE(v: string): boolean {
|
|||||||
return semver.gte(process.env.BALENA_CLI_VERSION_OVERRIDE || version, v);
|
return semver.gte(process.env.BALENA_CLI_VERSION_OVERRIDE || version, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
let v13: boolean;
|
let v14: boolean;
|
||||||
|
|
||||||
/** Feature switch for the next major version of the CLI */
|
/** Feature switch for the next major version of the CLI */
|
||||||
export function isV13(): boolean {
|
export function isV14(): boolean {
|
||||||
if (v13 === undefined) {
|
if (v14 === undefined) {
|
||||||
v13 = isVersionGTE('13.0.0');
|
v14 = isVersionGTE('14.0.0');
|
||||||
}
|
}
|
||||||
return v13;
|
return v14;
|
||||||
}
|
}
|
||||||
|
180
npm-shrinkwrap.json
generated
180
npm-shrinkwrap.json
generated
@ -2445,6 +2445,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/jsesc/-/jsesc-2.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/jsesc/-/jsesc-2.5.1.tgz",
|
||||||
"integrity": "sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw=="
|
"integrity": "sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw=="
|
||||||
},
|
},
|
||||||
|
"@types/json-schema": {
|
||||||
|
"version": "7.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
|
||||||
|
"integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ=="
|
||||||
|
},
|
||||||
"@types/jsonstream": {
|
"@types/jsonstream": {
|
||||||
"version": "0.8.30",
|
"version": "0.8.30",
|
||||||
"resolved": "https://registry.npmjs.org/@types/jsonstream/-/jsonstream-0.8.30.tgz",
|
"resolved": "https://registry.npmjs.org/@types/jsonstream/-/jsonstream-0.8.30.tgz",
|
||||||
@ -2807,9 +2812,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/uuid": {
|
"@types/uuid": {
|
||||||
"version": "8.3.1",
|
"version": "8.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.3.tgz",
|
||||||
"integrity": "sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg=="
|
"integrity": "sha512-0LbEEx1zxrYB3pgpd1M5lEhLcXjKJnYghvhTRgaBeUivLHMDM1TzF3IJ6hXU2+8uA4Xz+5BA63mtZo5DjVT8iA=="
|
||||||
},
|
},
|
||||||
"@types/which": {
|
"@types/which": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
@ -2921,11 +2926,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
|
||||||
"integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ=="
|
"integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ=="
|
||||||
},
|
},
|
||||||
"@zeit/dockerignore": {
|
|
||||||
"version": "0.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@zeit/dockerignore/-/dockerignore-0.0.3.tgz",
|
|
||||||
"integrity": "sha512-tO000RCJxEbRBhQpWEaTF+aAIkMRSuQVnC1g7Y1l/duhxjp6CotepHcUerQBFa5lgjVesh/zlblq2t1GvfAEbQ=="
|
|
||||||
},
|
|
||||||
"JSONStream": {
|
"JSONStream": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
|
||||||
@ -3675,10 +3675,54 @@
|
|||||||
"rimraf": "^3.0.2"
|
"rimraf": "^3.0.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"balena-sdk": {
|
||||||
|
"version": "15.59.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/balena-sdk/-/balena-sdk-15.59.2.tgz",
|
||||||
|
"integrity": "sha512-pMNwqqnqtets6PoYxRQhOHBnk7Say4gNhInAvS69UlEhraCR0Xau8rJk9G2IthWDA4tpR2In3u4SQJMIHYj84w==",
|
||||||
|
"requires": {
|
||||||
|
"@balena/es-version": "^1.0.0",
|
||||||
|
"@types/lodash": "^4.14.168",
|
||||||
|
"@types/memoizee": "^0.4.5",
|
||||||
|
"@types/node": "^10.17.55",
|
||||||
|
"abortcontroller-polyfill": "^1.7.1",
|
||||||
|
"balena-auth": "^4.1.0",
|
||||||
|
"balena-errors": "^4.7.1",
|
||||||
|
"balena-hup-action-utils": "~4.0.2",
|
||||||
|
"balena-pine": "^12.4.0",
|
||||||
|
"balena-register-device": "^7.1.0",
|
||||||
|
"balena-request": "^11.5.0",
|
||||||
|
"balena-semver": "^2.3.0",
|
||||||
|
"balena-settings-client": "^4.0.6",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"memoizee": "^0.4.15",
|
||||||
|
"moment": "^2.29.1",
|
||||||
|
"ndjson": "^2.0.0",
|
||||||
|
"semver": "^7.3.4",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"mkdirp": {
|
"mkdirp": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
|
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
|
||||||
|
},
|
||||||
|
"moment": {
|
||||||
|
"version": "2.29.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
|
||||||
|
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
|
||||||
|
},
|
||||||
|
"semver": {
|
||||||
|
"version": "7.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
|
||||||
|
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
|
||||||
|
"requires": {
|
||||||
|
"lru-cache": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3730,6 +3774,32 @@
|
|||||||
"zip-stream": "^2.1.2"
|
"zip-stream": "^2.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"balena-sdk": {
|
||||||
|
"version": "15.59.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/balena-sdk/-/balena-sdk-15.59.2.tgz",
|
||||||
|
"integrity": "sha512-pMNwqqnqtets6PoYxRQhOHBnk7Say4gNhInAvS69UlEhraCR0Xau8rJk9G2IthWDA4tpR2In3u4SQJMIHYj84w==",
|
||||||
|
"requires": {
|
||||||
|
"@balena/es-version": "^1.0.0",
|
||||||
|
"@types/lodash": "^4.14.168",
|
||||||
|
"@types/memoizee": "^0.4.5",
|
||||||
|
"@types/node": "^10.17.55",
|
||||||
|
"abortcontroller-polyfill": "^1.7.1",
|
||||||
|
"balena-auth": "^4.1.0",
|
||||||
|
"balena-errors": "^4.7.1",
|
||||||
|
"balena-hup-action-utils": "~4.0.2",
|
||||||
|
"balena-pine": "^12.4.0",
|
||||||
|
"balena-register-device": "^7.1.0",
|
||||||
|
"balena-request": "^11.5.0",
|
||||||
|
"balena-semver": "^2.3.0",
|
||||||
|
"balena-settings-client": "^4.0.6",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"memoizee": "^0.4.15",
|
||||||
|
"moment": "^2.29.1",
|
||||||
|
"ndjson": "^2.0.0",
|
||||||
|
"semver": "^7.3.4",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"compress-commons": {
|
"compress-commons": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-2.1.1.tgz",
|
||||||
@ -3766,6 +3836,11 @@
|
|||||||
"readable-stream": "^3.4.0"
|
"readable-stream": "^3.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"moment": {
|
||||||
|
"version": "2.29.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
|
||||||
|
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
|
||||||
|
},
|
||||||
"readable-stream": {
|
"readable-stream": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||||
@ -3776,6 +3851,14 @@
|
|||||||
"util-deprecate": "^1.0.1"
|
"util-deprecate": "^1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"semver": {
|
||||||
|
"version": "7.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
|
||||||
|
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
|
||||||
|
"requires": {
|
||||||
|
"lru-cache": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tar-fs": {
|
"tar-fs": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
|
||||||
@ -3801,6 +3884,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||||
|
},
|
||||||
"zip-stream": {
|
"zip-stream": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-2.1.3.tgz",
|
||||||
@ -3853,9 +3941,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"balena-request": {
|
"balena-request": {
|
||||||
"version": "11.4.2",
|
"version": "11.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/balena-request/-/balena-request-11.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/balena-request/-/balena-request-11.5.0.tgz",
|
||||||
"integrity": "sha512-J4SrFBUR4AB2Y3afsX2QAMZ7H/zjysXjOyEhEqvjNTsBfe5ReCmf17vzRQ8q2URCm00nUhQgfQtlJUq6miB1/g==",
|
"integrity": "sha512-mLf5NQU2qqGKyjZAmz8fFOzyUjwF9uptcDPUe56ADAQfLvQYML8izW7/9Q7DBRGpvlZrBZ/OnjWLK3vUs1tNdA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@balena/node-web-streams": "^0.2.3",
|
"@balena/node-web-streams": "^0.2.3",
|
||||||
"balena-errors": "^4.7.1",
|
"balena-errors": "^4.7.1",
|
||||||
@ -3867,11 +3955,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"balena-sdk": {
|
"balena-sdk": {
|
||||||
"version": "15.51.1",
|
"version": "16.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/balena-sdk/-/balena-sdk-15.51.1.tgz",
|
"resolved": "https://registry.npmjs.org/balena-sdk/-/balena-sdk-16.8.0.tgz",
|
||||||
"integrity": "sha512-EMCQruytqyvpfxvjq9Zd/wWnnOAIl/Wd1majqv6hqa+z104UUTjnRCQaUji4mo8YtrFLn7aUUZFFHIKiv/3sTg==",
|
"integrity": "sha512-kch7hhB9PksFsyVOAFyylsQMlv976V11DOwhb41w2JykVKjR9KSNFBfy1wYwYY1Wcp8PWXvj9WxtB0Sd/SW9bQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@balena/es-version": "^1.0.0",
|
"@balena/es-version": "^1.0.0",
|
||||||
|
"@types/json-schema": "^7.0.9",
|
||||||
"@types/lodash": "^4.14.168",
|
"@types/lodash": "^4.14.168",
|
||||||
"@types/memoizee": "^0.4.5",
|
"@types/memoizee": "^0.4.5",
|
||||||
"@types/node": "^10.17.55",
|
"@types/node": "^10.17.55",
|
||||||
@ -3881,14 +3970,13 @@
|
|||||||
"balena-hup-action-utils": "~4.0.2",
|
"balena-hup-action-utils": "~4.0.2",
|
||||||
"balena-pine": "^12.4.0",
|
"balena-pine": "^12.4.0",
|
||||||
"balena-register-device": "^7.1.0",
|
"balena-register-device": "^7.1.0",
|
||||||
"balena-request": "^11.4.2",
|
"balena-request": "^11.5.0",
|
||||||
"balena-semver": "^2.3.0",
|
"balena-semver": "^2.3.0",
|
||||||
"balena-settings-client": "^4.0.6",
|
"balena-settings-client": "^4.0.6",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"memoizee": "^0.4.15",
|
"memoizee": "^0.4.15",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"ndjson": "^2.0.0",
|
"ndjson": "^2.0.0",
|
||||||
"semver": "^7.3.4",
|
|
||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -3897,14 +3985,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
|
||||||
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
|
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
|
||||||
},
|
},
|
||||||
"semver": {
|
|
||||||
"version": "7.3.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
|
|
||||||
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
|
|
||||||
"requires": {
|
|
||||||
"lru-cache": "^6.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tslib": {
|
"tslib": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||||
@ -3990,6 +4070,39 @@
|
|||||||
"typed-error": "^2.0.0"
|
"typed-error": "^2.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"balena-sdk": {
|
||||||
|
"version": "15.59.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/balena-sdk/-/balena-sdk-15.59.2.tgz",
|
||||||
|
"integrity": "sha512-pMNwqqnqtets6PoYxRQhOHBnk7Say4gNhInAvS69UlEhraCR0Xau8rJk9G2IthWDA4tpR2In3u4SQJMIHYj84w==",
|
||||||
|
"requires": {
|
||||||
|
"@balena/es-version": "^1.0.0",
|
||||||
|
"@types/lodash": "^4.14.168",
|
||||||
|
"@types/memoizee": "^0.4.5",
|
||||||
|
"@types/node": "^10.17.55",
|
||||||
|
"abortcontroller-polyfill": "^1.7.1",
|
||||||
|
"balena-auth": "^4.1.0",
|
||||||
|
"balena-errors": "^4.7.1",
|
||||||
|
"balena-hup-action-utils": "~4.0.2",
|
||||||
|
"balena-pine": "^12.4.0",
|
||||||
|
"balena-register-device": "^7.1.0",
|
||||||
|
"balena-request": "^11.5.0",
|
||||||
|
"balena-semver": "^2.3.0",
|
||||||
|
"balena-settings-client": "^4.0.6",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"memoizee": "^0.4.15",
|
||||||
|
"moment": "^2.29.1",
|
||||||
|
"ndjson": "^2.0.0",
|
||||||
|
"semver": "^7.3.4",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"chalk": {
|
"chalk": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||||
@ -4013,6 +4126,19 @@
|
|||||||
"esprima": "^4.0.0"
|
"esprima": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"moment": {
|
||||||
|
"version": "2.29.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
|
||||||
|
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
|
||||||
|
},
|
||||||
|
"semver": {
|
||||||
|
"version": "7.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
|
||||||
|
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
|
||||||
|
"requires": {
|
||||||
|
"lru-cache": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"supports-color": {
|
"supports-color": {
|
||||||
"version": "7.1.0",
|
"version": "7.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
|
||||||
@ -18212,9 +18338,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"web-streams-polyfill": {
|
"web-streams-polyfill": {
|
||||||
"version": "3.1.1",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz",
|
||||||
"integrity": "sha512-Czi3fG883e96T4DLEPRvufrF2ydhOOW1+1a6c3gNjH2aIh50DNFBdfwh2AKoOf1rXvpvavAoA11Qdq9+BKjE0Q=="
|
"integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA=="
|
||||||
},
|
},
|
||||||
"which": {
|
"which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
|
@ -90,7 +90,7 @@
|
|||||||
"author": "Balena Inc. (https://balena.io/)",
|
"author": "Balena Inc. (https://balena.io/)",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.20.0 <13.0.0",
|
"node": ">=12.0.0 <13.0.0",
|
||||||
"npm": "<7.0.0"
|
"npm": "<7.0.0"
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
@ -200,7 +200,6 @@
|
|||||||
"@sentry/node": "^6.13.2",
|
"@sentry/node": "^6.13.2",
|
||||||
"@types/fast-levenshtein": "0.0.1",
|
"@types/fast-levenshtein": "0.0.1",
|
||||||
"@types/update-notifier": "^4.1.1",
|
"@types/update-notifier": "^4.1.1",
|
||||||
"@zeit/dockerignore": "0.0.3",
|
|
||||||
"JSONStream": "^1.0.3",
|
"JSONStream": "^1.0.3",
|
||||||
"balena-config-json": "^4.2.0",
|
"balena-config-json": "^4.2.0",
|
||||||
"balena-device-init": "^6.0.0",
|
"balena-device-init": "^6.0.0",
|
||||||
@ -209,7 +208,7 @@
|
|||||||
"balena-image-manager": "^7.1.1",
|
"balena-image-manager": "^7.1.1",
|
||||||
"balena-preload": "^11.0.0",
|
"balena-preload": "^11.0.0",
|
||||||
"balena-release": "^3.2.0",
|
"balena-release": "^3.2.0",
|
||||||
"balena-sdk": "^15.51.1",
|
"balena-sdk": "^16.8.0",
|
||||||
"balena-semver": "^2.3.0",
|
"balena-semver": "^2.3.0",
|
||||||
"balena-settings-client": "^4.0.7",
|
"balena-settings-client": "^4.0.7",
|
||||||
"balena-settings-storage": "^7.0.0",
|
"balena-settings-storage": "^7.0.0",
|
||||||
@ -241,7 +240,6 @@
|
|||||||
"global-tunnel-ng": "^2.1.1",
|
"global-tunnel-ng": "^2.1.1",
|
||||||
"got": "^11.8.2",
|
"got": "^11.8.2",
|
||||||
"humanize": "0.0.9",
|
"humanize": "0.0.9",
|
||||||
"ignore": "^5.1.8",
|
|
||||||
"inquirer": "^7.3.3",
|
"inquirer": "^7.3.3",
|
||||||
"is-elevated": "^3.0.0",
|
"is-elevated": "^3.0.0",
|
||||||
"is-root": "^2.1.0",
|
"is-root": "^2.1.0",
|
||||||
|
@ -22,7 +22,6 @@ import { promises as fs } from 'fs';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { stripIndent } from '../../build/utils/lazy';
|
import { stripIndent } from '../../build/utils/lazy';
|
||||||
import { isV13 } from '../../build/utils/version';
|
|
||||||
import { BalenaAPIMock } from '../nock/balena-api-mock';
|
import { BalenaAPIMock } from '../nock/balena-api-mock';
|
||||||
import { expectStreamNoCRLF, testDockerBuildStream } from '../docker-build';
|
import { expectStreamNoCRLF, testDockerBuildStream } from '../docker-build';
|
||||||
import { DockerMock, dockerResponsePath } from '../nock/docker-mock';
|
import { DockerMock, dockerResponsePath } from '../nock/docker-mock';
|
||||||
@ -127,23 +126,14 @@ describe('balena build', function () {
|
|||||||
];
|
];
|
||||||
if (isWindows) {
|
if (isWindows) {
|
||||||
const fname = path.join(projectPath, 'src', 'windows-crlf.sh');
|
const fname = path.join(projectPath, 'src', 'windows-crlf.sh');
|
||||||
if (isWindows) {
|
expectedResponseLines.push(
|
||||||
expectedResponseLines.push(
|
`[Info] Converting line endings CRLF -> LF for file: ${fname}`,
|
||||||
`[Info] Converting line endings CRLF -> LF for file: ${fname}`,
|
);
|
||||||
);
|
|
||||||
} else {
|
|
||||||
expectedResponseLines.push(
|
|
||||||
`[Warn] CRLF (Windows) line endings detected in file: ${fname}`,
|
|
||||||
'[Warn] Windows-format line endings were detected in some files. Consider using the `--convert-eol` option.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
docker.expectGetInfo({});
|
docker.expectGetInfo({});
|
||||||
docker.expectGetManifestBusybox();
|
docker.expectGetManifestBusybox();
|
||||||
await testDockerBuildStream({
|
await testDockerBuildStream({
|
||||||
commandLine: `build ${projectPath} --deviceType nuc --arch amd64 ${
|
commandLine: `build ${projectPath} --deviceType nuc --arch amd64`,
|
||||||
isV13() ? '' : '-g'
|
|
||||||
}`,
|
|
||||||
dockerMock: docker,
|
dockerMock: docker,
|
||||||
expectedFilesByService: { main: expectedFiles },
|
expectedFilesByService: { main: expectedFiles },
|
||||||
expectedQueryParamsByService: {
|
expectedQueryParamsByService: {
|
||||||
@ -192,16 +182,9 @@ describe('balena build', function () {
|
|||||||
];
|
];
|
||||||
if (isWindows) {
|
if (isWindows) {
|
||||||
const fname = path.join(projectPath, 'src', 'windows-crlf.sh');
|
const fname = path.join(projectPath, 'src', 'windows-crlf.sh');
|
||||||
if (isWindows) {
|
expectedResponseLines.push(
|
||||||
expectedResponseLines.push(
|
`[Info] Converting line endings CRLF -> LF for file: ${fname}`,
|
||||||
`[Info] Converting line endings CRLF -> LF for file: ${fname}`,
|
);
|
||||||
);
|
|
||||||
} else {
|
|
||||||
expectedResponseLines.push(
|
|
||||||
`[Warn] CRLF (Windows) line endings detected in file: ${fname}`,
|
|
||||||
'[Warn] Windows-format line endings were detected in some files. Consider using the `--convert-eol` option.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
docker.expectGetInfo({});
|
docker.expectGetInfo({});
|
||||||
docker.expectGetManifestBusybox();
|
docker.expectGetManifestBusybox();
|
||||||
@ -297,9 +280,7 @@ describe('balena build', function () {
|
|||||||
docker.expectGetInfo({ OperatingSystem: 'balenaOS 2.44.0+rev1' });
|
docker.expectGetInfo({ OperatingSystem: 'balenaOS 2.44.0+rev1' });
|
||||||
docker.expectGetManifestBusybox();
|
docker.expectGetManifestBusybox();
|
||||||
await testDockerBuildStream({
|
await testDockerBuildStream({
|
||||||
commandLine: `build ${projectPath} --emulated --deviceType ${deviceType} --arch ${arch} ${
|
commandLine: `build ${projectPath} --emulated --deviceType ${deviceType} --arch ${arch}`,
|
||||||
isV13() ? '' : '--nogitignore'
|
|
||||||
}`,
|
|
||||||
dockerMock: docker,
|
dockerMock: docker,
|
||||||
expectedFilesByService: { main: expectedFiles },
|
expectedFilesByService: { main: expectedFiles },
|
||||||
expectedQueryParamsByService: {
|
expectedQueryParamsByService: {
|
||||||
@ -317,7 +298,7 @@ describe('balena build', function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create the expected tar stream (single container, --[no]convert-eol, --multi-dockerignore)', async () => {
|
it('should create the expected tar stream (single container, --noconvert-eol, --multi-dockerignore)', async () => {
|
||||||
const projectPath = path.join(projectsPath, 'no-docker-compose', 'basic');
|
const projectPath = path.join(projectsPath, 'no-docker-compose', 'basic');
|
||||||
const expectedFiles: ExpectedTarStreamFiles = {
|
const expectedFiles: ExpectedTarStreamFiles = {
|
||||||
'src/.dockerignore': { fileSize: 16, type: 'file' },
|
'src/.dockerignore': { fileSize: 16, type: 'file' },
|
||||||
@ -448,9 +429,7 @@ describe('balena build', function () {
|
|||||||
docker.expectGetManifestNucAlpine();
|
docker.expectGetManifestNucAlpine();
|
||||||
docker.expectGetManifestBusybox();
|
docker.expectGetManifestBusybox();
|
||||||
await testDockerBuildStream({
|
await testDockerBuildStream({
|
||||||
commandLine: `build ${projectPath} --deviceType nuc --arch amd64 --convert-eol ${
|
commandLine: `build ${projectPath} --deviceType nuc --arch amd64 -B COMPOSE_ARG=A -B barg=b --cache-from my/img1,my/img2`,
|
||||||
isV13() ? '' : '-G'
|
|
||||||
} -B COMPOSE_ARG=A -B barg=b --cache-from my/img1,my/img2`,
|
|
||||||
dockerMock: docker,
|
dockerMock: docker,
|
||||||
expectedFilesByService,
|
expectedFilesByService,
|
||||||
expectedQueryParamsByService,
|
expectedQueryParamsByService,
|
||||||
@ -533,7 +512,7 @@ describe('balena build', function () {
|
|||||||
docker.expectGetManifestNucAlpine();
|
docker.expectGetManifestNucAlpine();
|
||||||
|
|
||||||
await testDockerBuildStream({
|
await testDockerBuildStream({
|
||||||
commandLine: `build ${projectPath} --deviceType nuc --arch amd64 --convert-eol -m`,
|
commandLine: `build ${projectPath} --deviceType nuc --arch amd64 -m`,
|
||||||
dockerMock: docker,
|
dockerMock: docker,
|
||||||
expectedFilesByService,
|
expectedFilesByService,
|
||||||
expectedQueryParamsByService,
|
expectedQueryParamsByService,
|
||||||
@ -618,7 +597,7 @@ describe('balena build', function () {
|
|||||||
docker.expectGetManifestNucAlpine();
|
docker.expectGetManifestNucAlpine();
|
||||||
|
|
||||||
await testDockerBuildStream({
|
await testDockerBuildStream({
|
||||||
commandLine: `build ${projectPath} --deviceType nuc --arch amd64 --convert-eol -m --tag ${tag} --projectName ${projectName}`,
|
commandLine: `build ${projectPath} --deviceType nuc --arch amd64 -m --tag ${tag} --projectName ${projectName}`,
|
||||||
dockerMock: docker,
|
dockerMock: docker,
|
||||||
expectedFilesByService,
|
expectedFilesByService,
|
||||||
expectedQueryParamsByService,
|
expectedQueryParamsByService,
|
||||||
|
@ -23,7 +23,6 @@ import * as nock from 'nock';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as sinon from 'sinon';
|
import * as sinon from 'sinon';
|
||||||
|
|
||||||
import { isV13 } from '../../build/utils/version';
|
|
||||||
import { BalenaAPIMock } from '../nock/balena-api-mock';
|
import { BalenaAPIMock } from '../nock/balena-api-mock';
|
||||||
import { expectStreamNoCRLF, testDockerBuildStream } from '../docker-build';
|
import { expectStreamNoCRLF, testDockerBuildStream } from '../docker-build';
|
||||||
import { DockerMock, dockerResponsePath } from '../nock/docker-mock';
|
import { DockerMock, dockerResponsePath } from '../nock/docker-mock';
|
||||||
@ -149,9 +148,7 @@ describe('balena deploy', function () {
|
|||||||
docker.expectGetManifestBusybox();
|
docker.expectGetManifestBusybox();
|
||||||
|
|
||||||
await testDockerBuildStream({
|
await testDockerBuildStream({
|
||||||
commandLine: `deploy testApp --build --source ${projectPath} ${
|
commandLine: `deploy testApp --build --source ${projectPath}`,
|
||||||
isV13() ? '' : '-G'
|
|
||||||
}`,
|
|
||||||
dockerMock: docker,
|
dockerMock: docker,
|
||||||
expectedFilesByService: { main: expectedFiles },
|
expectedFilesByService: { main: expectedFiles },
|
||||||
expectedQueryParamsByService: { main: commonQueryParams },
|
expectedQueryParamsByService: { main: commonQueryParams },
|
||||||
@ -315,9 +312,7 @@ describe('balena deploy', function () {
|
|||||||
sinon.stub(process, 'exit');
|
sinon.stub(process, 'exit');
|
||||||
|
|
||||||
await testDockerBuildStream({
|
await testDockerBuildStream({
|
||||||
commandLine: `deploy testApp --build --source ${projectPath} --noconvert-eol ${
|
commandLine: `deploy testApp --build --source ${projectPath} --noconvert-eol`,
|
||||||
isV13() ? '' : '-G'
|
|
||||||
}`,
|
|
||||||
dockerMock: docker,
|
dockerMock: docker,
|
||||||
expectedFilesByService: { main: expectedFiles },
|
expectedFilesByService: { main: expectedFiles },
|
||||||
expectedQueryParamsByService: { main: commonQueryParams },
|
expectedQueryParamsByService: { main: commonQueryParams },
|
||||||
|
@ -21,9 +21,6 @@ import * as path from 'path';
|
|||||||
import { apiResponsePath, BalenaAPIMock } from '../../nock/balena-api-mock';
|
import { apiResponsePath, BalenaAPIMock } from '../../nock/balena-api-mock';
|
||||||
import { cleanOutput, runCommand } from '../../helpers';
|
import { cleanOutput, runCommand } from '../../helpers';
|
||||||
|
|
||||||
import { appToFleetOutputMsg, warnify } from '../../../build/utils/messages';
|
|
||||||
import { isV13 } from '../../../build/utils/version';
|
|
||||||
|
|
||||||
describe('balena device', function () {
|
describe('balena device', function () {
|
||||||
let api: BalenaAPIMock;
|
let api: BalenaAPIMock;
|
||||||
|
|
||||||
@ -38,13 +35,6 @@ describe('balena device', function () {
|
|||||||
api.done();
|
api.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
const expectedWarn =
|
|
||||||
!isV13() &&
|
|
||||||
process.stderr.isTTY &&
|
|
||||||
process.env.BALENA_CLI_TEST_TYPE !== 'standalone'
|
|
||||||
? warnify(appToFleetOutputMsg) + '\n'
|
|
||||||
: '';
|
|
||||||
|
|
||||||
it('should error if uuid not provided', async () => {
|
it('should error if uuid not provided', async () => {
|
||||||
const { out, err } = await runCommand('device');
|
const { out, err } = await runCommand('device');
|
||||||
const errLines = cleanOutput(err);
|
const errLines = cleanOutput(err);
|
||||||
@ -57,27 +47,25 @@ describe('balena device', function () {
|
|||||||
it('should list device details for provided uuid', async () => {
|
it('should list device details for provided uuid', async () => {
|
||||||
api.scope
|
api.scope
|
||||||
.get(
|
.get(
|
||||||
/^\/v6\/device\?.+&\$expand=belongs_to__application\(\$select=app_name\)/,
|
/^\/v6\/device\?.+&\$expand=belongs_to__application\(\$select=app_name,slug\)/,
|
||||||
)
|
)
|
||||||
.replyWithFile(200, path.join(apiResponsePath, 'device.json'), {
|
.replyWithFile(200, path.join(apiResponsePath, 'device.json'), {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
});
|
});
|
||||||
|
|
||||||
const { out, err } = await runCommand('device 27fda508c');
|
const { out } = await runCommand('device 27fda508c');
|
||||||
|
|
||||||
const lines = cleanOutput(out);
|
const lines = cleanOutput(out);
|
||||||
|
|
||||||
expect(lines).to.have.lengthOf(25);
|
expect(lines).to.have.lengthOf(25);
|
||||||
expect(lines[0]).to.equal('== SPARKLING WOOD');
|
expect(lines[0]).to.equal('== SPARKLING WOOD');
|
||||||
expect(lines[6].split(':')[1].trim()).to.equal('test app');
|
expect(lines[6].split(':')[1].trim()).to.equal('org/test app');
|
||||||
|
|
||||||
expect(err.join('')).to.eql(expectedWarn);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('correctly handles devices with missing fields', async () => {
|
it.skip('correctly handles devices with missing fields', async () => {
|
||||||
api.scope
|
api.scope
|
||||||
.get(
|
.get(
|
||||||
/^\/v6\/device\?.+&\$expand=belongs_to__application\(\$select=app_name\)/,
|
/^\/v6\/device\?.+&\$expand=belongs_to__application\(\$select=app_name,slug\)/,
|
||||||
)
|
)
|
||||||
.replyWithFile(
|
.replyWithFile(
|
||||||
200,
|
200,
|
||||||
@ -87,15 +75,13 @@ describe('balena device', function () {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const { out, err } = await runCommand('device 27fda508c');
|
const { out } = await runCommand('device 27fda508c');
|
||||||
|
|
||||||
const lines = cleanOutput(out);
|
const lines = cleanOutput(out);
|
||||||
|
|
||||||
expect(lines).to.have.lengthOf(14);
|
expect(lines).to.have.lengthOf(14);
|
||||||
expect(lines[0]).to.equal('== SPARKLING WOOD');
|
expect(lines[0]).to.equal('== SPARKLING WOOD');
|
||||||
expect(lines[6].split(':')[1].trim()).to.equal('test app');
|
expect(lines[6].split(':')[1].trim()).to.equal('org/test app');
|
||||||
|
|
||||||
expect(err.join('')).to.eql(expectedWarn);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('correctly handles devices with missing application', async () => {
|
it('correctly handles devices with missing application', async () => {
|
||||||
@ -103,7 +89,7 @@ describe('balena device', function () {
|
|||||||
// e.g. When user has a device associated with app that user is no longer a collaborator of.
|
// e.g. When user has a device associated with app that user is no longer a collaborator of.
|
||||||
api.scope
|
api.scope
|
||||||
.get(
|
.get(
|
||||||
/^\/v6\/device\?.+&\$expand=belongs_to__application\(\$select=app_name\)/,
|
/^\/v6\/device\?.+&\$expand=belongs_to__application\(\$select=app_name,slug\)/,
|
||||||
)
|
)
|
||||||
.replyWithFile(
|
.replyWithFile(
|
||||||
200,
|
200,
|
||||||
@ -113,14 +99,12 @@ describe('balena device', function () {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const { out, err } = await runCommand('device 27fda508c');
|
const { out } = await runCommand('device 27fda508c');
|
||||||
|
|
||||||
const lines = cleanOutput(out);
|
const lines = cleanOutput(out);
|
||||||
|
|
||||||
expect(lines).to.have.lengthOf(25);
|
expect(lines).to.have.lengthOf(25);
|
||||||
expect(lines[0]).to.equal('== SPARKLING WOOD');
|
expect(lines[0]).to.equal('== SPARKLING WOOD');
|
||||||
expect(lines[6].split(':')[1].trim()).to.equal('N/a');
|
expect(lines[6].split(':')[1].trim()).to.equal('N/a');
|
||||||
|
|
||||||
expect(err.join('')).to.eql(expectedWarn);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -21,9 +21,6 @@ import * as path from 'path';
|
|||||||
import { apiResponsePath, BalenaAPIMock } from '../../nock/balena-api-mock';
|
import { apiResponsePath, BalenaAPIMock } from '../../nock/balena-api-mock';
|
||||||
import { cleanOutput, runCommand } from '../../helpers';
|
import { cleanOutput, runCommand } from '../../helpers';
|
||||||
|
|
||||||
import { appToFleetOutputMsg, warnify } from '../../../build/utils/messages';
|
|
||||||
import { isV13 } from '../../../build/utils/version';
|
|
||||||
|
|
||||||
describe('balena devices', function () {
|
describe('balena devices', function () {
|
||||||
let api: BalenaAPIMock;
|
let api: BalenaAPIMock;
|
||||||
|
|
||||||
@ -38,39 +35,28 @@ describe('balena devices', function () {
|
|||||||
api.done();
|
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 () => {
|
it('should list devices from own and collaborator apps', async () => {
|
||||||
api.scope
|
api.scope
|
||||||
.get(
|
.get(
|
||||||
'/v6/device?$orderby=device_name%20asc&$expand=belongs_to__application($select=app_name),is_of__device_type($select=slug),is_running__release($select=commit)',
|
'/v6/device?$orderby=device_name%20asc&$expand=belongs_to__application($select=app_name,slug),is_of__device_type($select=slug),is_running__release($select=commit)',
|
||||||
)
|
)
|
||||||
.replyWithFile(200, path.join(apiResponsePath, 'devices.json'), {
|
.replyWithFile(200, path.join(apiResponsePath, 'devices.json'), {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
});
|
});
|
||||||
|
|
||||||
const { out, err } = await runCommand('devices');
|
const { out } = await runCommand('devices');
|
||||||
|
|
||||||
const lines = cleanOutput(out);
|
const lines = cleanOutput(out);
|
||||||
|
|
||||||
expect(lines[0].replace(/ +/g, ' ')).to.equal(
|
expect(lines[0].replace(/ +/g, ' ')).to.equal(
|
||||||
isV13()
|
'ID UUID DEVICE NAME DEVICE TYPE FLEET STATUS IS ONLINE SUPERVISOR VERSION OS VERSION DASHBOARD URL',
|
||||||
? 'ID UUID DEVICE NAME DEVICE TYPE FLEET STATUS IS ONLINE SUPERVISOR VERSION OS VERSION DASHBOARD URL'
|
|
||||||
: 'ID UUID DEVICE NAME DEVICE TYPE APPLICATION NAME STATUS IS ONLINE SUPERVISOR VERSION OS VERSION DASHBOARD URL',
|
|
||||||
);
|
);
|
||||||
expect(lines).to.have.lengthOf.at.least(2);
|
expect(lines).to.have.lengthOf.at.least(2);
|
||||||
|
|
||||||
expect(lines.some((l) => l.includes('test app'))).to.be.true;
|
expect(lines.some((l) => l.includes('org/test app'))).to.be.true;
|
||||||
|
|
||||||
// Devices with missing applications will have application name set to `N/a`.
|
// Devices with missing applications will have application name set to `N/a`.
|
||||||
// e.g. When user has a device associated with app that user is no longer a collaborator of.
|
// 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(lines.some((l) => l.includes('N/a'))).to.be.true;
|
||||||
|
|
||||||
expect(err.join('')).to.eql(expectedWarn);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -20,8 +20,6 @@ import { expect } from 'chai';
|
|||||||
import { BalenaAPIMock } from '../../nock/balena-api-mock';
|
import { BalenaAPIMock } from '../../nock/balena-api-mock';
|
||||||
import { cleanOutput, runCommand } from '../../helpers';
|
import { cleanOutput, runCommand } from '../../helpers';
|
||||||
|
|
||||||
import { isV13 } from '../../../build/utils/version';
|
|
||||||
|
|
||||||
describe('balena devices supported', function () {
|
describe('balena devices supported', function () {
|
||||||
let api: BalenaAPIMock;
|
let api: BalenaAPIMock;
|
||||||
|
|
||||||
@ -48,34 +46,16 @@ describe('balena devices supported', function () {
|
|||||||
api.expectGetDeviceTypes();
|
api.expectGetDeviceTypes();
|
||||||
api.expectGetConfigDeviceTypes();
|
api.expectGetConfigDeviceTypes();
|
||||||
|
|
||||||
const { out, err } = await runCommand('devices supported -v');
|
const { out, err } = await runCommand('devices supported');
|
||||||
|
|
||||||
const lines = cleanOutput(out, true);
|
const lines = cleanOutput(out, true);
|
||||||
|
|
||||||
expect(lines[0]).to.equal(
|
expect(lines[0]).to.equal('SLUG ALIASES ARCH NAME');
|
||||||
isV13() ? 'SLUG ALIASES ARCH NAME' : 'SLUG ALIASES ARCH STATE NAME',
|
|
||||||
);
|
|
||||||
expect(lines).to.have.lengthOf.at.least(2);
|
expect(lines).to.have.lengthOf.at.least(2);
|
||||||
|
expect(lines).to.contain('intel-nuc nuc amd64 Intel NUC');
|
||||||
expect(lines).to.contain(
|
expect(lines).to.contain(
|
||||||
isV13()
|
'odroid-xu4 odroid-ux3, odroid-u3+ armv7hf ODROID-XU4',
|
||||||
? 'intel-nuc nuc amd64 Intel NUC'
|
|
||||||
: 'intel-nuc nuc amd64 RELEASED Intel NUC',
|
|
||||||
);
|
);
|
||||||
expect(lines).to.contain(
|
|
||||||
isV13()
|
|
||||||
? 'odroid-xu4 odroid-ux3, odroid-u3+ armv7hf ODROID-XU4'
|
|
||||||
: 'odroid-xu4 odroid-ux3, odroid-u3+ armv7hf RELEASED ODROID-XU4',
|
|
||||||
);
|
|
||||||
|
|
||||||
// Discontinued devices should be filtered out from results
|
|
||||||
expect(lines.some((l) => l.includes('DISCONTINUED'))).to.be.false;
|
|
||||||
|
|
||||||
// Beta devices should be listed as new
|
|
||||||
expect(lines.some((l) => l.includes('BETA'))).to.be.false;
|
|
||||||
expect(lines.some((l) => l.includes('NEW'))).to.equal(
|
|
||||||
isV13() ? false : true,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(err).to.eql([]);
|
expect(err).to.eql([]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
212
tests/commands/env/envs.spec.ts
vendored
212
tests/commands/env/envs.spec.ts
vendored
@ -21,9 +21,6 @@ import { stripIndent } from '../../../build/utils/lazy';
|
|||||||
import { BalenaAPIMock } from '../../nock/balena-api-mock';
|
import { BalenaAPIMock } from '../../nock/balena-api-mock';
|
||||||
import { runCommand } from '../../helpers';
|
import { runCommand } from '../../helpers';
|
||||||
|
|
||||||
import { appToFleetOutputMsg, warnify } from '../../../build/utils/messages';
|
|
||||||
import { isV13 } from '../../../build/utils/version';
|
|
||||||
|
|
||||||
describe('balena envs', function () {
|
describe('balena envs', function () {
|
||||||
const appName = 'test';
|
const appName = 'test';
|
||||||
let fullUUID: string;
|
let fullUUID: string;
|
||||||
@ -44,13 +41,6 @@ describe('balena envs', function () {
|
|||||||
api.done();
|
api.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
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 () => {
|
it('should successfully list env vars for a test fleet', async () => {
|
||||||
api.expectGetApplication();
|
api.expectGetApplication();
|
||||||
api.expectGetAppEnvVars();
|
api.expectGetAppEnvVars();
|
||||||
@ -60,11 +50,11 @@ describe('balena envs', function () {
|
|||||||
|
|
||||||
expect(out.join('')).to.equal(
|
expect(out.join('')).to.equal(
|
||||||
stripIndent`
|
stripIndent`
|
||||||
ID NAME VALUE FLEET SERVICE
|
ID NAME VALUE FLEET SERVICE
|
||||||
120110 svar1 svar1-value test service1
|
120110 svar1 svar1-value gh_user/testApp service1
|
||||||
120111 svar2 svar2-value test service2
|
120111 svar2 svar2-value gh_user/testApp service2
|
||||||
120101 var1 var1-val test *
|
120101 var1 var1-val gh_user/testApp *
|
||||||
120102 var2 22 test *
|
120102 var2 22 gh_user/testApp *
|
||||||
` + '\n',
|
` + '\n',
|
||||||
);
|
);
|
||||||
expect(err.join('')).to.equal('');
|
expect(err.join('')).to.equal('');
|
||||||
@ -79,7 +69,7 @@ describe('balena envs', function () {
|
|||||||
expect(out.join('')).to.equal(
|
expect(out.join('')).to.equal(
|
||||||
stripIndent`
|
stripIndent`
|
||||||
ID NAME VALUE FLEET
|
ID NAME VALUE FLEET
|
||||||
120300 RESIN_SUPERVISOR_NATIVE_LOGGER false test
|
120300 RESIN_SUPERVISOR_NATIVE_LOGGER false gh_user/testApp
|
||||||
` + '\n',
|
` + '\n',
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -94,7 +84,7 @@ describe('balena envs', function () {
|
|||||||
|
|
||||||
expect(JSON.parse(out.join(''))).to.deep.equal([
|
expect(JSON.parse(out.join(''))).to.deep.equal([
|
||||||
{
|
{
|
||||||
fleetName: 'test',
|
fleet: 'gh_user/testApp',
|
||||||
id: 120300,
|
id: 120300,
|
||||||
name: 'RESIN_SUPERVISOR_NATIVE_LOGGER',
|
name: 'RESIN_SUPERVISOR_NATIVE_LOGGER',
|
||||||
value: 'false',
|
value: 'false',
|
||||||
@ -116,10 +106,10 @@ describe('balena envs', function () {
|
|||||||
|
|
||||||
expect(out.join('')).to.equal(
|
expect(out.join('')).to.equal(
|
||||||
stripIndent`
|
stripIndent`
|
||||||
ID NAME VALUE FLEET SERVICE
|
ID NAME VALUE FLEET SERVICE
|
||||||
120111 svar2 svar2-value test service2
|
120111 svar2 svar2-value gh_user/testApp service2
|
||||||
120101 var1 var1-val test *
|
120101 var1 var1-val gh_user/testApp *
|
||||||
120102 var2 22 test *
|
120102 var2 22 gh_user/testApp *
|
||||||
` + '\n',
|
` + '\n',
|
||||||
);
|
);
|
||||||
expect(err.join('')).to.equal('');
|
expect(err.join('')).to.equal('');
|
||||||
@ -138,10 +128,10 @@ describe('balena envs', function () {
|
|||||||
|
|
||||||
expect(out.join('')).to.equal(
|
expect(out.join('')).to.equal(
|
||||||
stripIndent`
|
stripIndent`
|
||||||
ID NAME VALUE FLEET SERVICE
|
ID NAME VALUE FLEET SERVICE
|
||||||
120110 svar1 svar1-value test ${serviceName}
|
120110 svar1 svar1-value gh_user/testApp ${serviceName}
|
||||||
120101 var1 var1-val test *
|
120101 var1 var1-val gh_user/testApp *
|
||||||
120102 var2 22 test *
|
120102 var2 22 gh_user/testApp *
|
||||||
` + '\n',
|
` + '\n',
|
||||||
);
|
);
|
||||||
expect(err.join('')).to.equal('');
|
expect(err.join('')).to.equal('');
|
||||||
@ -157,29 +147,24 @@ describe('balena envs', function () {
|
|||||||
|
|
||||||
const uuid = shortUUID;
|
const uuid = shortUUID;
|
||||||
const result = await runCommand(`envs -d ${uuid}`);
|
const result = await runCommand(`envs -d ${uuid}`);
|
||||||
const { err } = result;
|
|
||||||
let { out } = result;
|
let { out } = result;
|
||||||
let expected =
|
let expected =
|
||||||
stripIndent`
|
stripIndent`
|
||||||
ID NAME VALUE APPLICATION DEVICE SERVICE
|
ID NAME VALUE FLEET DEVICE SERVICE
|
||||||
120110 svar1 svar1-value test * service1
|
120110 svar1 svar1-value org/test * service1
|
||||||
120111 svar2 svar2-value test * service2
|
120111 svar2 svar2-value org/test * service2
|
||||||
120120 svar3 svar3-value test ${uuid} service1
|
120120 svar3 svar3-value org/test ${uuid} service1
|
||||||
120121 svar4 svar4-value test ${uuid} service2
|
120121 svar4 svar4-value org/test ${uuid} service2
|
||||||
120101 var1 var1-val test * *
|
120101 var1 var1-val org/test * *
|
||||||
120102 var2 22 test * *
|
120102 var2 22 org/test * *
|
||||||
120203 var3 var3-val test ${uuid} *
|
120203 var3 var3-val org/test ${uuid} *
|
||||||
120204 var4 44 test ${uuid} *
|
120204 var4 44 org/test ${uuid} *
|
||||||
` + '\n';
|
` + '\n';
|
||||||
if (isV13()) {
|
|
||||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||||
expected = expected
|
expected = expected.replace(/ +/g, ' ');
|
||||||
.replace(/ +/g, ' ')
|
|
||||||
.replace(' APPLICATION ', ' FLEET ')
|
|
||||||
.replace(/ test /g, ' org/test ');
|
|
||||||
}
|
|
||||||
expect(out.join('')).to.equal(expected);
|
expect(out.join('')).to.equal(expected);
|
||||||
expect(err.join('')).to.equal(appToFleetOutputWarn);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should successfully list env variables for a test device (JSON output)', async () => {
|
it('should successfully list env variables for a test device (JSON output)', async () => {
|
||||||
@ -191,22 +176,17 @@ describe('balena envs', function () {
|
|||||||
api.expectGetDeviceServiceVars();
|
api.expectGetDeviceServiceVars();
|
||||||
|
|
||||||
const { out, err } = await runCommand(`envs -jd ${shortUUID}`);
|
const { out, err } = await runCommand(`envs -jd ${shortUUID}`);
|
||||||
let expected = `[
|
const expected = `[
|
||||||
{ "id": 120101, "appName": "test", "deviceUUID": "*", "name": "var1", "value": "var1-val", "serviceName": "*" },
|
{ "id": 120101, "fleet": "org/test", "deviceUUID": "*", "name": "var1", "value": "var1-val", "serviceName": "*" },
|
||||||
{ "id": 120102, "appName": "test", "deviceUUID": "*", "name": "var2", "value": "22", "serviceName": "*" },
|
{ "id": 120102, "fleet": "org/test", "deviceUUID": "*", "name": "var2", "value": "22", "serviceName": "*" },
|
||||||
{ "id": 120110, "appName": "test", "deviceUUID": "*", "name": "svar1", "value": "svar1-value", "serviceName": "service1" },
|
{ "id": 120110, "fleet": "org/test", "deviceUUID": "*", "name": "svar1", "value": "svar1-value", "serviceName": "service1" },
|
||||||
{ "id": 120111, "appName": "test", "deviceUUID": "*", "name": "svar2", "value": "svar2-value", "serviceName": "service2" },
|
{ "id": 120111, "fleet": "org/test", "deviceUUID": "*", "name": "svar2", "value": "svar2-value", "serviceName": "service2" },
|
||||||
{ "id": 120120, "appName": "test", "deviceUUID": "${fullUUID}", "name": "svar3", "value": "svar3-value", "serviceName": "service1" },
|
{ "id": 120120, "fleet": "org/test", "deviceUUID": "${fullUUID}", "name": "svar3", "value": "svar3-value", "serviceName": "service1" },
|
||||||
{ "id": 120121, "appName": "test", "deviceUUID": "${fullUUID}", "name": "svar4", "value": "svar4-value", "serviceName": "service2" },
|
{ "id": 120121, "fleet": "org/test", "deviceUUID": "${fullUUID}", "name": "svar4", "value": "svar4-value", "serviceName": "service2" },
|
||||||
{ "id": 120203, "appName": "test", "deviceUUID": "${fullUUID}", "name": "var3", "value": "var3-val", "serviceName": "*" },
|
{ "id": 120203, "fleet": "org/test", "deviceUUID": "${fullUUID}", "name": "var3", "value": "var3-val", "serviceName": "*" },
|
||||||
{ "id": 120204, "appName": "test", "deviceUUID": "${fullUUID}", "name": "var4", "value": "44", "serviceName": "*" }
|
{ "id": 120204, "fleet": "org/test", "deviceUUID": "${fullUUID}", "name": "var4", "value": "44", "serviceName": "*" }
|
||||||
]`;
|
]`;
|
||||||
if (isV13()) {
|
|
||||||
expected = expected.replace(
|
|
||||||
/"appName": "test"/g,
|
|
||||||
'"fleetName": "org/test"',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
expect(JSON.parse(out.join(''))).to.deep.equal(JSON.parse(expected));
|
expect(JSON.parse(out.join(''))).to.deep.equal(JSON.parse(expected));
|
||||||
expect(err.join('')).to.equal('');
|
expect(err.join('')).to.equal('');
|
||||||
});
|
});
|
||||||
@ -218,23 +198,18 @@ describe('balena envs', function () {
|
|||||||
api.expectGetAppConfigVars();
|
api.expectGetAppConfigVars();
|
||||||
|
|
||||||
const result = await runCommand(`envs -d ${shortUUID} --config`);
|
const result = await runCommand(`envs -d ${shortUUID} --config`);
|
||||||
const { err } = result;
|
|
||||||
let { out } = result;
|
let { out } = result;
|
||||||
let expected =
|
let expected =
|
||||||
stripIndent`
|
stripIndent`
|
||||||
ID NAME VALUE APPLICATION DEVICE
|
ID NAME VALUE FLEET DEVICE
|
||||||
120300 RESIN_SUPERVISOR_NATIVE_LOGGER false test *
|
120300 RESIN_SUPERVISOR_NATIVE_LOGGER false org/test *
|
||||||
120400 RESIN_SUPERVISOR_POLL_INTERVAL 900900 test ${shortUUID}
|
120400 RESIN_SUPERVISOR_POLL_INTERVAL 900900 org/test ${shortUUID}
|
||||||
` + '\n';
|
` + '\n';
|
||||||
if (isV13()) {
|
|
||||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||||
expected = expected
|
expected = expected.replace(/ +/g, ' ');
|
||||||
.replace(/ +/g, ' ')
|
|
||||||
.replace(' APPLICATION ', ' FLEET ')
|
|
||||||
.replace(/ test /g, ' org/test ');
|
|
||||||
}
|
|
||||||
expect(out.join('')).to.equal(expected);
|
expect(out.join('')).to.equal(expected);
|
||||||
expect(err.join('')).to.equal(appToFleetOutputWarn);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should successfully list service variables for a test device (-s flag)', async () => {
|
it('should successfully list service variables for a test device (-s flag)', async () => {
|
||||||
@ -249,27 +224,22 @@ describe('balena envs', function () {
|
|||||||
|
|
||||||
const uuid = shortUUID;
|
const uuid = shortUUID;
|
||||||
const result = await runCommand(`envs -d ${uuid} -s ${serviceName}`);
|
const result = await runCommand(`envs -d ${uuid} -s ${serviceName}`);
|
||||||
const { err } = result;
|
|
||||||
let { out } = result;
|
let { out } = result;
|
||||||
let expected =
|
let expected =
|
||||||
stripIndent`
|
stripIndent`
|
||||||
ID NAME VALUE APPLICATION DEVICE SERVICE
|
ID NAME VALUE FLEET DEVICE SERVICE
|
||||||
120111 svar2 svar2-value test * service2
|
120111 svar2 svar2-value org/test * service2
|
||||||
120121 svar4 svar4-value test ${uuid} service2
|
120121 svar4 svar4-value org/test ${uuid} service2
|
||||||
120101 var1 var1-val test * *
|
120101 var1 var1-val org/test * *
|
||||||
120102 var2 22 test * *
|
120102 var2 22 org/test * *
|
||||||
120203 var3 var3-val test ${uuid} *
|
120203 var3 var3-val org/test ${uuid} *
|
||||||
120204 var4 44 test ${uuid} *
|
120204 var4 44 org/test ${uuid} *
|
||||||
` + '\n';
|
` + '\n';
|
||||||
if (isV13()) {
|
|
||||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||||
expected = expected
|
expected = expected.replace(/ +/g, ' ');
|
||||||
.replace(/ +/g, ' ')
|
|
||||||
.replace(' APPLICATION ', ' FLEET ')
|
|
||||||
.replace(/ test /g, ' org/test ');
|
|
||||||
}
|
|
||||||
expect(out.join('')).to.equal(expected);
|
expect(out.join('')).to.equal(expected);
|
||||||
expect(err.join('')).to.equal(appToFleetOutputWarn);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should successfully list env and service variables for a test device (unknown fleet)', async () => {
|
it('should successfully list env and service variables for a test device (unknown fleet)', async () => {
|
||||||
@ -279,24 +249,20 @@ describe('balena envs', function () {
|
|||||||
|
|
||||||
const uuid = shortUUID;
|
const uuid = shortUUID;
|
||||||
const result = await runCommand(`envs -d ${uuid}`);
|
const result = await runCommand(`envs -d ${uuid}`);
|
||||||
const { err } = result;
|
|
||||||
let { out } = result;
|
let { out } = result;
|
||||||
let expected =
|
let expected =
|
||||||
stripIndent`
|
stripIndent`
|
||||||
ID NAME VALUE APPLICATION DEVICE SERVICE
|
ID NAME VALUE FLEET DEVICE SERVICE
|
||||||
120120 svar3 svar3-value N/A ${uuid} service1
|
120120 svar3 svar3-value N/A ${uuid} service1
|
||||||
120121 svar4 svar4-value N/A ${uuid} service2
|
120121 svar4 svar4-value N/A ${uuid} service2
|
||||||
120203 var3 var3-val N/A ${uuid} *
|
120203 var3 var3-val N/A ${uuid} *
|
||||||
120204 var4 44 N/A ${uuid} *
|
120204 var4 44 N/A ${uuid} *
|
||||||
` + '\n';
|
` + '\n';
|
||||||
if (isV13()) {
|
|
||||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||||
expected = expected
|
expected = expected.replace(/ +/g, ' ');
|
||||||
.replace(/ +/g, ' ')
|
|
||||||
.replace(' APPLICATION ', ' FLEET ');
|
|
||||||
}
|
|
||||||
expect(out.join('')).to.equal(expected);
|
expect(out.join('')).to.equal(expected);
|
||||||
expect(err.join('')).to.equal(appToFleetOutputWarn);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should successfully list env and service vars for a test device (-s flags)', async () => {
|
it('should successfully list env and service vars for a test device (-s flags)', async () => {
|
||||||
@ -311,27 +277,22 @@ describe('balena envs', function () {
|
|||||||
|
|
||||||
const uuid = shortUUID;
|
const uuid = shortUUID;
|
||||||
const result = await runCommand(`envs -d ${uuid} -s ${serviceName}`);
|
const result = await runCommand(`envs -d ${uuid} -s ${serviceName}`);
|
||||||
const { err } = result;
|
|
||||||
let { out } = result;
|
let { out } = result;
|
||||||
let expected =
|
let expected =
|
||||||
stripIndent`
|
stripIndent`
|
||||||
ID NAME VALUE APPLICATION DEVICE SERVICE
|
ID NAME VALUE FLEET DEVICE SERVICE
|
||||||
120110 svar1 svar1-value test * ${serviceName}
|
120110 svar1 svar1-value org/test * ${serviceName}
|
||||||
120120 svar3 svar3-value test ${uuid} ${serviceName}
|
120120 svar3 svar3-value org/test ${uuid} ${serviceName}
|
||||||
120101 var1 var1-val test * *
|
120101 var1 var1-val org/test * *
|
||||||
120102 var2 22 test * *
|
120102 var2 22 org/test * *
|
||||||
120203 var3 var3-val test ${uuid} *
|
120203 var3 var3-val org/test ${uuid} *
|
||||||
120204 var4 44 test ${uuid} *
|
120204 var4 44 org/test ${uuid} *
|
||||||
` + '\n';
|
` + '\n';
|
||||||
if (isV13()) {
|
|
||||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||||
expected = expected
|
expected = expected.replace(/ +/g, ' ');
|
||||||
.replace(/ +/g, ' ')
|
|
||||||
.replace(' APPLICATION ', ' FLEET ')
|
|
||||||
.replace(/ test /g, ' org/test ');
|
|
||||||
}
|
|
||||||
expect(out.join('')).to.equal(expected);
|
expect(out.join('')).to.equal(expected);
|
||||||
expect(err.join('')).to.equal(appToFleetOutputWarn);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should successfully list env and service vars for a test device (-js flags)', async () => {
|
it('should successfully list env and service vars for a test device (-js flags)', async () => {
|
||||||
@ -347,20 +308,15 @@ describe('balena envs', function () {
|
|||||||
const { out, err } = await runCommand(
|
const { out, err } = await runCommand(
|
||||||
`envs -d ${shortUUID} -js ${serviceName}`,
|
`envs -d ${shortUUID} -js ${serviceName}`,
|
||||||
);
|
);
|
||||||
let expected = `[
|
const expected = `[
|
||||||
{ "id": 120101, "appName": "test", "deviceUUID": "*", "name": "var1", "value": "var1-val", "serviceName": "*" },
|
{ "id": 120101, "fleet": "org/test", "deviceUUID": "*", "name": "var1", "value": "var1-val", "serviceName": "*" },
|
||||||
{ "id": 120102, "appName": "test", "deviceUUID": "*", "name": "var2", "value": "22", "serviceName": "*" },
|
{ "id": 120102, "fleet": "org/test", "deviceUUID": "*", "name": "var2", "value": "22", "serviceName": "*" },
|
||||||
{ "id": 120110, "appName": "test", "deviceUUID": "*", "name": "svar1", "value": "svar1-value", "serviceName": "${serviceName}" },
|
{ "id": 120110, "fleet": "org/test", "deviceUUID": "*", "name": "svar1", "value": "svar1-value", "serviceName": "${serviceName}" },
|
||||||
{ "id": 120120, "appName": "test", "deviceUUID": "${fullUUID}", "name": "svar3", "value": "svar3-value", "serviceName": "${serviceName}" },
|
{ "id": 120120, "fleet": "org/test", "deviceUUID": "${fullUUID}", "name": "svar3", "value": "svar3-value", "serviceName": "${serviceName}" },
|
||||||
{ "id": 120203, "appName": "test", "deviceUUID": "${fullUUID}", "name": "var3", "value": "var3-val", "serviceName": "*" },
|
{ "id": 120203, "fleet": "org/test", "deviceUUID": "${fullUUID}", "name": "var3", "value": "var3-val", "serviceName": "*" },
|
||||||
{ "id": 120204, "appName": "test", "deviceUUID": "${fullUUID}", "name": "var4", "value": "44", "serviceName": "*" }
|
{ "id": 120204, "fleet": "org/test", "deviceUUID": "${fullUUID}", "name": "var4", "value": "44", "serviceName": "*" }
|
||||||
]`;
|
]`;
|
||||||
if (isV13()) {
|
|
||||||
expected = expected.replace(
|
|
||||||
/"appName": "test"/g,
|
|
||||||
'"fleetName": "org/test"',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
expect(JSON.parse(out.join(''))).to.deep.equal(JSON.parse(expected));
|
expect(JSON.parse(out.join(''))).to.deep.equal(JSON.parse(expected));
|
||||||
expect(err.join('')).to.equal('');
|
expect(err.join('')).to.equal('');
|
||||||
});
|
});
|
||||||
|
@ -19,7 +19,6 @@ import { expect } from 'chai';
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { isV13 } from '../../build/utils/version';
|
|
||||||
import { BalenaAPIMock } from '../nock/balena-api-mock';
|
import { BalenaAPIMock } from '../nock/balena-api-mock';
|
||||||
import { BuilderMock, builderResponsePath } from '../nock/builder-mock';
|
import { BuilderMock, builderResponsePath } from '../nock/builder-mock';
|
||||||
import { expectStreamNoCRLF, testPushBuildStream } from '../docker-build';
|
import { expectStreamNoCRLF, testPushBuildStream } from '../docker-build';
|
||||||
@ -37,7 +36,6 @@ import {
|
|||||||
const repoPath = path.normalize(path.join(__dirname, '..', '..'));
|
const repoPath = path.normalize(path.join(__dirname, '..', '..'));
|
||||||
const projectsPath = path.join(repoPath, 'tests', 'test-data', 'projects');
|
const projectsPath = path.join(repoPath, 'tests', 'test-data', 'projects');
|
||||||
|
|
||||||
const itNoV13 = isV13() ? it.skip : it;
|
|
||||||
const itNoWin = process.platform === 'win32' ? it.skip : it;
|
const itNoWin = process.platform === 'win32' ? it.skip : it;
|
||||||
|
|
||||||
const commonResponseLines = {
|
const commonResponseLines = {
|
||||||
@ -82,9 +80,6 @@ const commonQueryParams = [
|
|||||||
['isdraft', 'false'],
|
['isdraft', 'false'],
|
||||||
];
|
];
|
||||||
|
|
||||||
const hr =
|
|
||||||
'----------------------------------------------------------------------';
|
|
||||||
|
|
||||||
describe('balena push', function () {
|
describe('balena push', function () {
|
||||||
let api: BalenaAPIMock;
|
let api: BalenaAPIMock;
|
||||||
let builder: BuilderMock;
|
let builder: BuilderMock;
|
||||||
@ -195,7 +190,7 @@ describe('balena push', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create the expected tar stream (single container, --[no]convert-eol)', async () => {
|
it('should create the expected tar stream (single container, --noconvert-eol)', async () => {
|
||||||
const projectPath = path.join(projectsPath, 'no-docker-compose', 'basic');
|
const projectPath = path.join(projectsPath, 'no-docker-compose', 'basic');
|
||||||
const expectedFiles: ExpectedTarStreamFiles = {
|
const expectedFiles: ExpectedTarStreamFiles = {
|
||||||
'src/.dockerignore': { fileSize: 16, type: 'file' },
|
'src/.dockerignore': { fileSize: 16, type: 'file' },
|
||||||
@ -240,76 +235,7 @@ describe('balena push', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
itNoV13(
|
it('should create the expected tar stream (single container, dockerignore1)', async () => {
|
||||||
'should create the expected tar stream (single container, --gitignore)',
|
|
||||||
async () => {
|
|
||||||
const projectPath = path.join(
|
|
||||||
projectsPath,
|
|
||||||
'no-docker-compose',
|
|
||||||
'dockerignore1',
|
|
||||||
);
|
|
||||||
const expectedFiles: ExpectedTarStreamFiles = {
|
|
||||||
'.balena/balena.yml': { fileSize: 12, type: 'file' },
|
|
||||||
'.dockerignore': { fileSize: 438, type: 'file' },
|
|
||||||
'.gitignore': { fileSize: 20, type: 'file' },
|
|
||||||
'.git/bar.txt': { fileSize: 4, type: 'file' },
|
|
||||||
'.git/foo.txt': { fileSize: 4, type: 'file' },
|
|
||||||
'c.txt': { fileSize: 1, type: 'file' },
|
|
||||||
Dockerfile: { fileSize: 13, type: 'file' },
|
|
||||||
'src/.balena/balena.yml': { fileSize: 16, type: 'file' },
|
|
||||||
'src/.gitignore': { fileSize: 10, type: 'file' },
|
|
||||||
'vendor/.git/vendor-git-contents': { fileSize: 20, type: 'file' },
|
|
||||||
// When --gitignore (-g) is provided for v11 compatibility, the old
|
|
||||||
// `zeit/dockerignore` npm package is still used but it is broken on
|
|
||||||
// Windows (reason why we created `@balena/dockerignore`).
|
|
||||||
...(isWindows
|
|
||||||
? {
|
|
||||||
'src/src-b.txt': { fileSize: 5, type: 'file' },
|
|
||||||
'dot.git/bar.txt': { fileSize: 4, type: 'file' },
|
|
||||||
'dot.git/foo.txt': { fileSize: 4, type: 'file' },
|
|
||||||
'vendor/dot.git/vendor-git-contents': {
|
|
||||||
fileSize: 20,
|
|
||||||
type: 'file',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
};
|
|
||||||
|
|
||||||
const regSecretsPath = await addRegSecretsEntries(expectedFiles);
|
|
||||||
const responseFilename = 'build-POST-v3.json';
|
|
||||||
const responseBody = await fs.readFile(
|
|
||||||
path.join(builderResponsePath, responseFilename),
|
|
||||||
'utf8',
|
|
||||||
);
|
|
||||||
const expectedResponseLines = [
|
|
||||||
...[
|
|
||||||
`[Warn] ${hr}`,
|
|
||||||
'[Warn] Using file ignore patterns from:',
|
|
||||||
`[Warn] * ${path.join(projectPath, '.dockerignore')}`,
|
|
||||||
`[Warn] * ${path.join(projectPath, '.gitignore')}`,
|
|
||||||
`[Warn] * ${path.join(projectPath, 'src', '.gitignore')}`,
|
|
||||||
'[Warn] .gitignore files are being considered because the --gitignore option was used.',
|
|
||||||
'[Warn] This option is deprecated and will be removed in the next major version release.',
|
|
||||||
"[Warn] For more information, see 'balena help push'.",
|
|
||||||
`[Warn] ${hr}`,
|
|
||||||
],
|
|
||||||
...commonResponseLines[responseFilename],
|
|
||||||
];
|
|
||||||
|
|
||||||
await testPushBuildStream({
|
|
||||||
builderMock: builder,
|
|
||||||
commandLine: `push testApp -s ${projectPath} -R ${regSecretsPath} -g`,
|
|
||||||
expectedFiles,
|
|
||||||
expectedQueryParams: commonQueryParams,
|
|
||||||
expectedResponseLines,
|
|
||||||
projectPath,
|
|
||||||
responseBody,
|
|
||||||
responseCode: 200,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
it('should create the expected tar stream (single container, --nogitignore)', async () => {
|
|
||||||
const projectPath = path.join(
|
const projectPath = path.join(
|
||||||
projectsPath,
|
projectsPath,
|
||||||
'no-docker-compose',
|
'no-docker-compose',
|
||||||
@ -352,7 +278,7 @@ describe('balena push', function () {
|
|||||||
// (with a mismatched fileSize 13 vs 5 for 'symlink-a.txt'), ensure that the
|
// (with a mismatched fileSize 13 vs 5 for 'symlink-a.txt'), ensure that the
|
||||||
// `core.symlinks` property is set to `true` in the `.git/config` file. Ref:
|
// `core.symlinks` property is set to `true` in the `.git/config` file. Ref:
|
||||||
// https://git-scm.com/docs/git-config#Documentation/git-config.txt-coresymlinks
|
// https://git-scm.com/docs/git-config#Documentation/git-config.txt-coresymlinks
|
||||||
it('should create the expected tar stream (single container, symbolic links, --gitignore)', async () => {
|
it('should create the expected tar stream (single container, symbolic links)', async () => {
|
||||||
const projectPath = path.join(
|
const projectPath = path.join(
|
||||||
projectsPath,
|
projectsPath,
|
||||||
'no-docker-compose',
|
'no-docker-compose',
|
||||||
@ -366,12 +292,6 @@ describe('balena push', function () {
|
|||||||
'lib/src-b.txt': { fileSize: 5, type: 'file' },
|
'lib/src-b.txt': { fileSize: 5, type: 'file' },
|
||||||
'src/src-b.txt': { fileSize: 5, type: 'file' },
|
'src/src-b.txt': { fileSize: 5, type: 'file' },
|
||||||
'symlink-a.txt': { fileSize: 5, type: 'file' },
|
'symlink-a.txt': { fileSize: 5, type: 'file' },
|
||||||
...(isWindows
|
|
||||||
? {
|
|
||||||
'lib/src-a.txt': { fileSize: 5, type: 'file' },
|
|
||||||
'src/src-a.txt': { fileSize: 5, type: 'file' },
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
};
|
};
|
||||||
const regSecretsPath = await addRegSecretsEntries(expectedFiles);
|
const regSecretsPath = await addRegSecretsEntries(expectedFiles);
|
||||||
const responseFilename = 'build-POST-v3.json';
|
const responseFilename = 'build-POST-v3.json';
|
||||||
@ -379,27 +299,11 @@ describe('balena push', function () {
|
|||||||
path.join(builderResponsePath, responseFilename),
|
path.join(builderResponsePath, responseFilename),
|
||||||
'utf8',
|
'utf8',
|
||||||
);
|
);
|
||||||
const expectedResponseLines =
|
const expectedResponseLines = commonResponseLines[responseFilename];
|
||||||
!isV13() && isWindows
|
|
||||||
? [
|
|
||||||
`[Warn] ${hr}`,
|
|
||||||
'[Warn] Using file ignore patterns from:',
|
|
||||||
`[Warn] * ${path.join(projectPath, '.dockerignore')}`,
|
|
||||||
'[Warn] The --gitignore option was used, but no .gitignore files were found.',
|
|
||||||
'[Warn] The --gitignore option is deprecated and will be removed in the next major',
|
|
||||||
'[Warn] version release. It prevents the use of a better dockerignore parser and',
|
|
||||||
'[Warn] filter library that fixes several issues on Windows and improves compatibility',
|
|
||||||
"[Warn] with 'docker build'. For more information, see 'balena help push'.",
|
|
||||||
`[Warn] ${hr}`,
|
|
||||||
...commonResponseLines[responseFilename],
|
|
||||||
]
|
|
||||||
: commonResponseLines[responseFilename];
|
|
||||||
|
|
||||||
await testPushBuildStream({
|
await testPushBuildStream({
|
||||||
builderMock: builder,
|
builderMock: builder,
|
||||||
commandLine: `push testApp -s ${projectPath} -R ${regSecretsPath} ${
|
commandLine: `push testApp -s ${projectPath} -R ${regSecretsPath}`,
|
||||||
isV13() ? '' : '--gitignore'
|
|
||||||
}`,
|
|
||||||
expectedFiles,
|
expectedFiles,
|
||||||
expectedQueryParams: commonQueryParams,
|
expectedQueryParams: commonQueryParams,
|
||||||
expectedResponseLines,
|
expectedResponseLines,
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
"belongs_to__application": [
|
"belongs_to__application": [
|
||||||
{
|
{
|
||||||
"app_name": "test app",
|
"app_name": "test app",
|
||||||
|
"slug": "org/test app",
|
||||||
"__metadata": {}
|
"__metadata": {}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
"belongs_to__application": [
|
"belongs_to__application": [
|
||||||
{
|
{
|
||||||
"app_name": "test app",
|
"app_name": "test app",
|
||||||
|
"slug": "org/test app",
|
||||||
"__metadata": {}
|
"__metadata": {}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
"belongs_to__application": [
|
"belongs_to__application": [
|
||||||
{
|
{
|
||||||
"app_name": "test app",
|
"app_name": "test app",
|
||||||
|
"slug": "org/test app",
|
||||||
"__metadata": {}
|
"__metadata": {}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -1,101 +0,0 @@
|
|||||||
import { expect } from 'chai';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
import * as path from 'path';
|
|
||||||
import { FileIgnorer, IgnoreFileType } from '../../build/utils/ignore';
|
|
||||||
|
|
||||||
// Note that brack notation is used intentionally when accessing private members
|
|
||||||
// of the FileIgnorer class to prevent a Typescript compilation error (this
|
|
||||||
// behavior is by design: see
|
|
||||||
// https://github.com/microsoft/TypeScript/issues/19335 )
|
|
||||||
//
|
|
||||||
// v13: delete this file
|
|
||||||
//
|
|
||||||
describe('File ignorer', function () {
|
|
||||||
it('should detect ignore files', function () {
|
|
||||||
const f = new FileIgnorer(`.${path.sep}`);
|
|
||||||
expect(f.getIgnoreFileType('.gitignore')).to.equal(
|
|
||||||
IgnoreFileType.GitIgnore,
|
|
||||||
);
|
|
||||||
expect(f.getIgnoreFileType('.dockerignore')).to.equal(
|
|
||||||
IgnoreFileType.DockerIgnore,
|
|
||||||
);
|
|
||||||
expect(f.getIgnoreFileType('./.gitignore')).to.equal(
|
|
||||||
IgnoreFileType.GitIgnore,
|
|
||||||
);
|
|
||||||
expect(f.getIgnoreFileType('./.dockerignore')).to.equal(
|
|
||||||
IgnoreFileType.DockerIgnore,
|
|
||||||
);
|
|
||||||
|
|
||||||
// gitignore files can appear in subdirectories, but dockerignore files cannot
|
|
||||||
expect(f.getIgnoreFileType('./subdir/.gitignore')).to.equal(
|
|
||||||
IgnoreFileType.GitIgnore,
|
|
||||||
);
|
|
||||||
expect(f.getIgnoreFileType('./subdir/.dockerignore')).to.equal(null);
|
|
||||||
expect(f.getIgnoreFileType('./subdir/subdir2/.gitignore')).to.equal(
|
|
||||||
IgnoreFileType.GitIgnore,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(f.getIgnoreFileType('file')).to.equal(null);
|
|
||||||
return expect(f.getIgnoreFileType('./file')).to.equal(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should filter files from the root directory', function () {
|
|
||||||
const ignore = new FileIgnorer(`.${path.sep}`);
|
|
||||||
ignore['gitIgnoreEntries'] = [
|
|
||||||
{ pattern: '*.ignore', filePath: '.gitignore' },
|
|
||||||
];
|
|
||||||
ignore['dockerIgnoreEntries'] = [
|
|
||||||
{ pattern: '*.ignore2', filePath: '.dockerignore' },
|
|
||||||
];
|
|
||||||
const files = [
|
|
||||||
'a',
|
|
||||||
'a/b',
|
|
||||||
'a/b/c',
|
|
||||||
'file.ignore',
|
|
||||||
'file2.ignore',
|
|
||||||
'file.ignore2',
|
|
||||||
'file2.ignore',
|
|
||||||
];
|
|
||||||
|
|
||||||
return expect(_.filter(files, ignore.filter.bind(ignore))).to.deep.equal([
|
|
||||||
'a',
|
|
||||||
'a/b',
|
|
||||||
'a/b/c',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
return it('should filter files from subdirectories', function () {
|
|
||||||
const ignore = new FileIgnorer(`.${path.sep}`);
|
|
||||||
ignore['gitIgnoreEntries'] = [
|
|
||||||
{ pattern: '*.ignore', filePath: 'lib/.gitignore' },
|
|
||||||
];
|
|
||||||
let files = [
|
|
||||||
'test.ignore',
|
|
||||||
'root.ignore',
|
|
||||||
'lib/normal-file',
|
|
||||||
'lib/should.ignore',
|
|
||||||
'lib/thistoo.ignore',
|
|
||||||
];
|
|
||||||
expect(_.filter(files, ignore.filter.bind(ignore))).to.deep.equal([
|
|
||||||
'test.ignore',
|
|
||||||
'root.ignore',
|
|
||||||
'lib/normal-file',
|
|
||||||
]);
|
|
||||||
|
|
||||||
ignore['gitIgnoreEntries'] = [
|
|
||||||
{ pattern: '*.ignore', filePath: './lib/.gitignore' },
|
|
||||||
];
|
|
||||||
files = [
|
|
||||||
'test.ignore',
|
|
||||||
'root.ignore',
|
|
||||||
'lib/normal-file',
|
|
||||||
'lib/should.ignore',
|
|
||||||
'lib/thistoo.ignore',
|
|
||||||
];
|
|
||||||
return expect(_.filter(files, ignore.filter.bind(ignore))).to.deep.equal([
|
|
||||||
'test.ignore',
|
|
||||||
'root.ignore',
|
|
||||||
'lib/normal-file',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
@ -33,8 +33,6 @@ interface TarFiles {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const itSkipWindows = process.platform === 'win32' ? it.skip : it;
|
|
||||||
|
|
||||||
describe('compare new and old tarDirectory implementations', function () {
|
describe('compare new and old tarDirectory implementations', function () {
|
||||||
const extraContent = 'extra';
|
const extraContent = 'extra';
|
||||||
const extraEntry: tar.Headers = {
|
const extraEntry: tar.Headers = {
|
||||||
@ -82,7 +80,6 @@ describe('compare new and old tarDirectory implementations', function () {
|
|||||||
|
|
||||||
const tarPack = await tarDirectory(dockerignoreProjDir, {
|
const tarPack = await tarDirectory(dockerignoreProjDir, {
|
||||||
preFinalizeCallback,
|
preFinalizeCallback,
|
||||||
nogitignore: true,
|
|
||||||
});
|
});
|
||||||
const fileList = await getTarPackFiles(tarPack);
|
const fileList = await getTarPackFiles(tarPack);
|
||||||
|
|
||||||
@ -105,67 +102,11 @@ describe('compare new and old tarDirectory implementations', function () {
|
|||||||
'symlink-a.txt': { fileSize: 5, type: 'file' },
|
'symlink-a.txt': { fileSize: 5, type: 'file' },
|
||||||
};
|
};
|
||||||
|
|
||||||
const tarPack = await tarDirectory(projectPath, { nogitignore: true });
|
const tarPack = await tarDirectory(projectPath, {});
|
||||||
const fileList = await getTarPackFiles(tarPack);
|
const fileList = await getTarPackFiles(tarPack);
|
||||||
|
|
||||||
expect(fileList).to.deep.equal(expectedFiles);
|
expect(fileList).to.deep.equal(expectedFiles);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Skip Windows because the old tarDirectory() implementation (still used when
|
|
||||||
// '--gitignore' is provided) uses the old `zeit/dockerignore` npm package
|
|
||||||
// that is broken on Windows (reason why we created `@balena/dockerignore`).
|
|
||||||
itSkipWindows('should produce a compatible tar stream', async function () {
|
|
||||||
const dockerignoreProjDir = path.join(
|
|
||||||
projectsPath,
|
|
||||||
'no-docker-compose',
|
|
||||||
'dockerignore1',
|
|
||||||
);
|
|
||||||
const oldTarPack = await tarDirectory(dockerignoreProjDir, {
|
|
||||||
preFinalizeCallback,
|
|
||||||
nogitignore: false,
|
|
||||||
});
|
|
||||||
const oldFileList = await getTarPackFiles(oldTarPack);
|
|
||||||
|
|
||||||
const newTarPack = await tarDirectory(dockerignoreProjDir, {
|
|
||||||
preFinalizeCallback,
|
|
||||||
nogitignore: true,
|
|
||||||
});
|
|
||||||
const newFileList = await getTarPackFiles(newTarPack);
|
|
||||||
|
|
||||||
const gitIgnored = ['a.txt', 'src/src-a.txt', 'src/src-c.txt'];
|
|
||||||
|
|
||||||
expect({
|
|
||||||
...newFileList,
|
|
||||||
..._.pick(oldFileList, ['.git/bar.txt']),
|
|
||||||
}).to.deep.equal({
|
|
||||||
...oldFileList,
|
|
||||||
..._.pick(newFileList, gitIgnored),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
itSkipWindows(
|
|
||||||
'should produce a compatible tar stream (symbolic links)',
|
|
||||||
async function () {
|
|
||||||
const dockerignoreProjDir = path.join(
|
|
||||||
projectsPath,
|
|
||||||
'no-docker-compose',
|
|
||||||
'dockerignore2',
|
|
||||||
);
|
|
||||||
const oldTarPack = await tarDirectory(dockerignoreProjDir, {
|
|
||||||
preFinalizeCallback,
|
|
||||||
nogitignore: false,
|
|
||||||
});
|
|
||||||
const oldFileList = await getTarPackFiles(oldTarPack);
|
|
||||||
|
|
||||||
const newTarPack = await tarDirectory(dockerignoreProjDir, {
|
|
||||||
preFinalizeCallback,
|
|
||||||
nogitignore: true,
|
|
||||||
});
|
|
||||||
const newFileList = await getTarPackFiles(newTarPack);
|
|
||||||
|
|
||||||
expect(newFileList).to.deep.equal(oldFileList);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
async function getTarPackFiles(
|
async function getTarPackFiles(
|
||||||
|
Loading…
Reference in New Issue
Block a user