mirror of
https://github.com/balena-io/balena-cli.git
synced 2024-12-30 02:28:51 +00:00
commit
66608b32e9
@ -267,4 +267,3 @@ gotchas to bear in mind:
|
||||
replace: `spec: 'tests/**/*.spec.ts',`
|
||||
|
||||
with: `spec: ['tests/auth/*.spec.ts', 'tests/**/deploy.spec.ts'],`
|
||||
|
||||
|
@ -36,19 +36,12 @@ const capitanoDoc = {
|
||||
{
|
||||
title: 'Fleet',
|
||||
files: [
|
||||
'build/commands/apps.js',
|
||||
'build/commands/fleets.js',
|
||||
'build/commands/app/index.js',
|
||||
'build/commands/fleet/index.js',
|
||||
'build/commands/app/create.js',
|
||||
'build/commands/fleet/create.js',
|
||||
'build/commands/app/purge.js',
|
||||
'build/commands/fleet/purge.js',
|
||||
'build/commands/app/rename.js',
|
||||
'build/commands/fleet/rename.js',
|
||||
'build/commands/app/restart.js',
|
||||
'build/commands/fleet/restart.js',
|
||||
'build/commands/app/rm.js',
|
||||
'build/commands/fleet/rm.js',
|
||||
],
|
||||
},
|
||||
|
@ -8,10 +8,9 @@ _balena() {
|
||||
local context state line curcontext="$curcontext"
|
||||
|
||||
# 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
|
||||
api_key_cmds=( generate )
|
||||
app_cmds=( create purge rename restart rm )
|
||||
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 )
|
||||
devices_cmds=( supported )
|
||||
@ -44,9 +43,6 @@ _balena_sec_cmds() {
|
||||
"api-key")
|
||||
_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")
|
||||
_describe -t config_cmds 'config_cmd' config_cmds "$@" && ret=0
|
||||
;;
|
||||
|
@ -7,10 +7,9 @@ _balena_complete()
|
||||
local cur prev
|
||||
|
||||
# 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
|
||||
api_key_cmds="generate"
|
||||
app_cmds="create purge rename restart rm"
|
||||
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"
|
||||
devices_cmds="supported"
|
||||
@ -38,9 +37,6 @@ _balena_complete()
|
||||
api-key)
|
||||
COMPREPLY=( $(compgen -W "$api_key_cmds" -- $cur) )
|
||||
;;
|
||||
app)
|
||||
COMPREPLY=( $(compgen -W "$app_cmds" -- $cur) )
|
||||
;;
|
||||
config)
|
||||
COMPREPLY=( $(compgen -W "$config_cmds" -- $cur) )
|
||||
;;
|
||||
|
@ -166,19 +166,12 @@ are encouraged to regularly update the balena CLI to the latest version.
|
||||
|
||||
- Fleet
|
||||
|
||||
- [apps](#apps)
|
||||
- [fleets](#fleets)
|
||||
- [app <fleet>](#app-fleet)
|
||||
- [fleet <fleet>](#fleet-fleet)
|
||||
- [app create <name>](#app-create-name)
|
||||
- [fleet create <name>](#fleet-create-name)
|
||||
- [app purge <fleet>](#app-purge-fleet)
|
||||
- [fleet purge <fleet>](#fleet-purge-fleet)
|
||||
- [app rename <fleet> [newname]](#app-rename-fleet-newname)
|
||||
- [fleet rename <fleet> [newname]](#fleet-rename-fleet-newname)
|
||||
- [app restart <fleet>](#app-restart-fleet)
|
||||
- [fleet restart <fleet>](#fleet-restart-fleet)
|
||||
- [app rm <fleet>](#app-rm-fleet)
|
||||
- [fleet rm <fleet>](#fleet-rm-fleet)
|
||||
|
||||
- Authentication
|
||||
@ -327,22 +320,6 @@ the API key name
|
||||
|
||||
# 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
|
||||
|
||||
List all your balena fleets.
|
||||
@ -356,28 +333,6 @@ Examples:
|
||||
|
||||
### 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>
|
||||
|
||||
Display detailed information about a single fleet.
|
||||
@ -407,32 +362,6 @@ fleet name, slug (preferred), or numeric ID (deprecated)
|
||||
|
||||
### 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>
|
||||
|
||||
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`)
|
||||
|
||||
## 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>
|
||||
|
||||
Purge data from all devices belonging to a fleet.
|
||||
@ -522,28 +433,6 @@ fleet name, slug (preferred), or numeric ID (deprecated)
|
||||
|
||||
### Options
|
||||
|
||||
## app rename <fleet> [newName]
|
||||
|
||||
Renaming notice: The 'app' command was renamed to 'fleet', and 'app'
|
||||
is now an alias. THE ALIAS WILL BE REMOVED in the next major version
|
||||
of the balena CLI (so that a different 'app' command can be implemented
|
||||
in the future). Use 'fleet' instead of 'app' to avoid this warning.
|
||||
Find out more at: https://git.io/JRuZr
|
||||
|
||||
For command usage, see 'balena help fleet rename'
|
||||
|
||||
### Arguments
|
||||
|
||||
#### FLEET
|
||||
|
||||
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||
|
||||
#### NEWNAME
|
||||
|
||||
the new name for the fleet
|
||||
|
||||
### Options
|
||||
|
||||
## fleet rename <fleet> [newName]
|
||||
|
||||
Rename a fleet.
|
||||
@ -581,24 +470,6 @@ the new name for the fleet
|
||||
|
||||
### 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>
|
||||
|
||||
Restart all devices belonging to a fleet.
|
||||
@ -628,28 +499,6 @@ fleet name, slug (preferred), or numeric ID (deprecated)
|
||||
|
||||
### Options
|
||||
|
||||
## app rm <fleet>
|
||||
|
||||
Renaming notice: The 'app' command was renamed to 'fleet', and 'app'
|
||||
is now an alias. THE ALIAS WILL BE REMOVED in the next major version
|
||||
of the balena CLI (so that a different 'app' command can be implemented
|
||||
in the future). Use 'fleet' instead of 'app' to avoid this warning.
|
||||
Find out more at: https://git.io/JRuZr
|
||||
|
||||
For command usage, see 'balena help fleet rm'
|
||||
|
||||
### Arguments
|
||||
|
||||
#### FLEET
|
||||
|
||||
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||
|
||||
### Options
|
||||
|
||||
#### -y, --yes
|
||||
|
||||
answer "yes" to all questions (non interactive use)
|
||||
|
||||
## fleet rm <fleet>
|
||||
|
||||
Permanently remove a fleet.
|
||||
@ -795,37 +644,18 @@ Examples:
|
||||
|
||||
### Options
|
||||
|
||||
#### -a, --application APPLICATION
|
||||
|
||||
DEPRECATED alias for -f, --fleet
|
||||
|
||||
#### --app APP
|
||||
|
||||
DEPRECATED alias for -f, --fleet
|
||||
|
||||
#### -f, --fleet FLEET
|
||||
|
||||
fleet name, slug (preferred), or numeric ID
|
||||
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||
|
||||
#### -j, --json
|
||||
|
||||
produce JSON output instead of tabular output
|
||||
|
||||
#### --v13
|
||||
|
||||
enable selected balena CLI v13 pre-release features, like the renaming
|
||||
from "application" to "fleet" in command output
|
||||
|
||||
## devices supported
|
||||
|
||||
List the supported device types (like 'raspberrypi3' or 'intel-nuc').
|
||||
|
||||
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,
|
||||
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
|
||||
@ -835,23 +665,14 @@ list of zero or more values). The 'jq' utility may be helpful in shell scripts
|
||||
Examples:
|
||||
|
||||
$ balena devices supported
|
||||
$ balena devices supported --verbose
|
||||
$ balena devices supported -vj
|
||||
$ balena devices supported --json
|
||||
|
||||
### Options
|
||||
|
||||
#### --discontinued
|
||||
|
||||
include "discontinued" device types (DEPRECATED)
|
||||
|
||||
#### -j, --json
|
||||
|
||||
produce JSON output instead of tabular output
|
||||
|
||||
#### -v, --verbose
|
||||
|
||||
add extra columns in the tabular output (DEPRECATED)
|
||||
|
||||
## device <uuid>
|
||||
|
||||
Show information about a single device.
|
||||
@ -868,11 +689,6 @@ the device uuid
|
||||
|
||||
### Options
|
||||
|
||||
#### --v13
|
||||
|
||||
enable selected balena CLI v13 pre-release features, like the renaming
|
||||
from "application" to "fleet" in command output
|
||||
|
||||
## device deactivate <uuid>
|
||||
|
||||
Deactivate a device.
|
||||
@ -961,17 +777,9 @@ Examples:
|
||||
|
||||
### Options
|
||||
|
||||
#### -a, --application APPLICATION
|
||||
|
||||
DEPRECATED alias for -f, --fleet
|
||||
|
||||
#### --app APP
|
||||
|
||||
DEPRECATED alias for -f, --fleet
|
||||
|
||||
#### -f, --fleet FLEET
|
||||
|
||||
fleet name, slug (preferred), or numeric ID
|
||||
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||
|
||||
#### -y, --yes
|
||||
|
||||
@ -1068,17 +876,9 @@ comma-separated list (no blank spaces) of device UUIDs to be moved
|
||||
|
||||
### Options
|
||||
|
||||
#### --app APP
|
||||
|
||||
DEPRECATED alias for -f, --fleet
|
||||
|
||||
#### -a, --application APPLICATION
|
||||
|
||||
DEPRECATED alias for -f, --fleet
|
||||
|
||||
#### -f, --fleet FLEET
|
||||
|
||||
fleet name, slug (preferred), or numeric ID
|
||||
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||
|
||||
## device os-update <uuid>
|
||||
|
||||
@ -1348,7 +1148,7 @@ Examples:
|
||||
|
||||
#### FLEET
|
||||
|
||||
fleet name or slug
|
||||
fleet name or slug (preferred)
|
||||
|
||||
### 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
|
||||
numeric IDs at some point in the future.
|
||||
|
||||
Renaming notice: The 'app' or 'application' words in table headers
|
||||
or in JSON object keys/properties will be replaced with 'fleet' in
|
||||
the next major version of the CLI (v13). The --v13 option may be used
|
||||
to enable the new names already now, and suppress a warning message.
|
||||
(The --v13 option will be silently ignored in CLI v13.)
|
||||
Find out more at: https://git.io/JRuZr
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena envs --fleet myorg/myfleet
|
||||
@ -1466,17 +1259,9 @@ Examples:
|
||||
|
||||
### Options
|
||||
|
||||
#### --all
|
||||
|
||||
No-op since balena CLI v12.0.0.
|
||||
|
||||
#### -a, --application APPLICATION
|
||||
|
||||
DEPRECATED alias for -f, --fleet
|
||||
|
||||
#### -f, --fleet FLEET
|
||||
|
||||
fleet name, slug (preferred), or numeric ID
|
||||
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||
|
||||
#### -c, --config
|
||||
|
||||
@ -1490,19 +1275,10 @@ device UUID
|
||||
|
||||
produce JSON output instead of tabular output
|
||||
|
||||
#### -v, --verbose
|
||||
|
||||
produce verbose output
|
||||
|
||||
#### -s, --service SERVICE
|
||||
|
||||
service name
|
||||
|
||||
#### --v13
|
||||
|
||||
enable selected balena CLI v13 pre-release features, like the renaming
|
||||
from "application" to "fleet" in command output
|
||||
|
||||
## env rm <id>
|
||||
|
||||
Remove a configuration or environment variable from a fleet, device
|
||||
@ -1631,13 +1407,9 @@ variable value; if omitted, use value from this process' environment
|
||||
|
||||
### Options
|
||||
|
||||
#### -a, --application APPLICATION
|
||||
|
||||
DEPRECATED alias for -f, --fleet
|
||||
|
||||
#### -f, --fleet FLEET
|
||||
|
||||
fleet name, slug (preferred), or numeric ID
|
||||
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||
|
||||
#### -d, --device DEVICE
|
||||
|
||||
@ -1742,17 +1514,9 @@ Examples:
|
||||
|
||||
### Options
|
||||
|
||||
#### -a, --application APPLICATION
|
||||
|
||||
DEPRECATED alias for -f, --fleet
|
||||
|
||||
#### --app APP
|
||||
|
||||
DEPRECATED alias for -f, --fleet
|
||||
|
||||
#### -f, --fleet FLEET
|
||||
|
||||
fleet name, slug (preferred), or numeric ID
|
||||
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||
|
||||
#### -d, --device DEVICE
|
||||
|
||||
@ -1794,17 +1558,9 @@ the key string of the tag
|
||||
|
||||
### Options
|
||||
|
||||
#### -a, --application APPLICATION
|
||||
|
||||
DEPRECATED alias for -f, --fleet
|
||||
|
||||
#### --app APP
|
||||
|
||||
DEPRECATED alias for -f, --fleet
|
||||
|
||||
#### -f, --fleet FLEET
|
||||
|
||||
fleet name, slug (preferred), or numeric ID
|
||||
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||
|
||||
#### -d, --device DEVICE
|
||||
|
||||
@ -1857,17 +1613,9 @@ the optional value associated with the tag
|
||||
|
||||
### Options
|
||||
|
||||
#### -a, --application APPLICATION
|
||||
|
||||
DEPRECATED alias for -f, --fleet
|
||||
|
||||
#### --app APP
|
||||
|
||||
DEPRECATED alias for -f, --fleet
|
||||
|
||||
#### -f, --fleet FLEET
|
||||
|
||||
fleet name, slug (preferred), or numeric ID
|
||||
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||
|
||||
#### -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://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
|
||||
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
|
||||
@ -2411,7 +2156,6 @@ https://docs.microsoft.com/en-us/windows/wsl/about
|
||||
Examples:
|
||||
|
||||
$ 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 MyFleet --version 2.12.7
|
||||
$ 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)
|
||||
|
||||
#### -a, --application APPLICATION
|
||||
|
||||
DEPRECATED alias for -f, --fleet
|
||||
|
||||
#### --app APP
|
||||
|
||||
DEPRECATED alias for -f, --fleet
|
||||
|
||||
#### -f, --fleet FLEET
|
||||
|
||||
fleet name, slug (preferred), or numeric ID
|
||||
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||
|
||||
#### --config CONFIG
|
||||
|
||||
@ -2465,10 +2201,6 @@ WiFi SSID (network name) (non-interactive configuration)
|
||||
|
||||
device UUID
|
||||
|
||||
#### -k, --device-api-key DEVICE-API-KEY
|
||||
|
||||
custom device API key (DEPRECATED and only supported with balenaOS 2.0.3+)
|
||||
|
||||
#### --device-type DEVICE-TYPE
|
||||
|
||||
device type slug (e.g. "raspberrypi3") to override the fleet device type
|
||||
@ -2568,21 +2300,9 @@ Examples:
|
||||
|
||||
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
|
||||
|
||||
fleet name, slug (preferred), or numeric ID
|
||||
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||
|
||||
#### -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)
|
||||
|
||||
#### --appUpdatePollInterval APPUPDATEPOLLINTERVAL
|
||||
|
||||
supervisor cloud polling interval in minutes (e.g. for device variables)
|
||||
|
||||
#### --provisioning-key-name PROVISIONING-KEY-NAME
|
||||
|
||||
custom key name assigned to generated provisioning api key
|
||||
@ -2796,13 +2520,9 @@ the image file path
|
||||
|
||||
### Options
|
||||
|
||||
#### -a, --app APP
|
||||
|
||||
DEPRECATED alias for -f, --fleet
|
||||
|
||||
#### -f, --fleet FLEET
|
||||
|
||||
fleet name, slug (preferred), or numeric ID
|
||||
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||
|
||||
#### -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
|
||||
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).
|
||||
balena CLI v11 also took .gitignore files into account. This behavior was
|
||||
deprecated in CLI v12 and removed in CLI v13. Please use .dockerignore files
|
||||
instead.
|
||||
|
||||
Default .dockerignore patterns
|
||||
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
|
||||
A few default/hardcoded dockerignore patterns are "merged" (in memory) with the
|
||||
patterns found in the applicable .dockerignore files, in the following order:
|
||||
```
|
||||
**/.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
|
||||
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:
|
||||
- https://docs.docker.com/engine/reference/builder/#dockerignore-file
|
||||
- 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
|
||||
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
|
||||
|
||||
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".
|
||||
|
||||
#### -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
|
||||
|
||||
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
|
||||
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).
|
||||
balena CLI v11 also took .gitignore files into account. This behavior was
|
||||
deprecated in CLI v12 and removed in CLI v13. Please use .dockerignore files
|
||||
instead.
|
||||
|
||||
Default .dockerignore patterns
|
||||
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
|
||||
A few default/hardcoded dockerignore patterns are "merged" (in memory) with the
|
||||
patterns found in the applicable .dockerignore files, in the following order:
|
||||
```
|
||||
**/.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
|
||||
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:
|
||||
- https://docs.docker.com/engine/reference/builder/#dockerignore-file
|
||||
- https://www.npmjs.com/package/@balena/dockerignore
|
||||
@ -3279,13 +2979,9 @@ the architecture to build for
|
||||
|
||||
the type of device this build is for
|
||||
|
||||
#### -a, --application APPLICATION
|
||||
|
||||
DEPRECATED alias for -f, --fleet
|
||||
|
||||
#### -f, --fleet FLEET
|
||||
|
||||
fleet name, slug (preferred), or numeric ID
|
||||
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||
|
||||
#### -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)
|
||||
|
||||
#### -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
|
||||
|
||||
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
|
||||
|
||||
#### -l, --convert-eol
|
||||
|
||||
No-op and deprecated since balena CLI v12.0.0
|
||||
|
||||
#### --noconvert-eol
|
||||
|
||||
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
|
||||
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).
|
||||
balena CLI v11 also took .gitignore files into account. This behavior was
|
||||
deprecated in CLI v12 and removed in CLI v13. Please use .dockerignore files
|
||||
instead.
|
||||
|
||||
Default .dockerignore patterns
|
||||
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
|
||||
A few default/hardcoded dockerignore patterns are "merged" (in memory) with the
|
||||
patterns found in the applicable .dockerignore files, in the following order:
|
||||
```
|
||||
**/.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
|
||||
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:
|
||||
- https://docs.docker.com/engine/reference/builder/#dockerignore-file
|
||||
- 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)
|
||||
|
||||
#### -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
|
||||
|
||||
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
|
||||
|
||||
#### -l, --convert-eol
|
||||
|
||||
No-op and deprecated since balena CLI v12.0.0
|
||||
|
||||
#### --noconvert-eol
|
||||
|
||||
Don't convert line endings from CRLF (Windows format) to LF (Unix format).
|
||||
@ -3676,13 +3341,9 @@ the IP or hostname of device
|
||||
|
||||
### Options
|
||||
|
||||
#### -a, --application APPLICATION
|
||||
|
||||
DEPRECATED alias for -f, --fleet
|
||||
|
||||
#### -f, --fleet FLEET
|
||||
|
||||
fleet name, slug (preferred), or numeric ID
|
||||
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||
|
||||
#### -i, --pollInterval POLLINTERVAL
|
||||
|
||||
@ -3768,13 +3429,9 @@ enable|disable support access
|
||||
|
||||
comma-separated list (no spaces) of device UUIDs
|
||||
|
||||
#### -a, --application APPLICATION
|
||||
|
||||
DEPRECATED alias for -f, --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
|
||||
|
||||
|
@ -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 type { Application, ApplicationType, BalenaSDK } from 'balena-sdk';
|
||||
import {
|
||||
appToFleetFlagMsg,
|
||||
buildArgDeprecation,
|
||||
dockerignoreHelp,
|
||||
registrySecretsHelp,
|
||||
warnify,
|
||||
} from '../utils/messages';
|
||||
import type { ComposeCliFlags, ComposeOpts } from '../utils/compose-types';
|
||||
import { buildProject, composeCliFlags } from '../utils/compose_ts';
|
||||
import type { BuildOpts, DockerCliFlags } from '../utils/docker';
|
||||
import { dockerCliFlags } from '../utils/docker';
|
||||
import { isV13 } from '../utils/version';
|
||||
|
||||
interface FlagsDef extends ComposeCliFlags, DockerCliFlags {
|
||||
arch?: string;
|
||||
deviceType?: string;
|
||||
application?: string;
|
||||
fleet?: string;
|
||||
source?: string; // Not part of command profile - source param copied here.
|
||||
help: void;
|
||||
@ -96,7 +92,6 @@ ${dockerignoreHelp}
|
||||
description: 'the type of device this build is for',
|
||||
char: 'd',
|
||||
}),
|
||||
...(isV13() ? {} : { application: cf.application }),
|
||||
fleet: cf.fleet,
|
||||
...composeCliFlags,
|
||||
...dockerCliFlags,
|
||||
@ -112,12 +107,7 @@ ${dockerignoreHelp}
|
||||
BuildCmd,
|
||||
);
|
||||
|
||||
if (options.application && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
options.application ||= options.fleet;
|
||||
|
||||
await Command.checkLoggedInIf(!!options.application);
|
||||
await Command.checkLoggedInIf(!!options.fleet);
|
||||
|
||||
(await import('events')).defaultMaxListeners = 1000;
|
||||
|
||||
@ -161,10 +151,8 @@ ${dockerignoreHelp}
|
||||
protected async validateOptions(opts: FlagsDef, sdk: BalenaSDK) {
|
||||
// Validate option combinations
|
||||
if (
|
||||
(opts.application == null &&
|
||||
(opts.arch == null || opts.deviceType == null)) ||
|
||||
(opts.application != null &&
|
||||
(opts.arch != null || opts.deviceType != null))
|
||||
(opts.fleet == null && (opts.arch == null || opts.deviceType == null)) ||
|
||||
(opts.fleet != null && (opts.arch != null || opts.deviceType != null))
|
||||
) {
|
||||
const { ExpectedError } = await import('../errors');
|
||||
throw new ExpectedError(
|
||||
@ -189,9 +177,9 @@ ${dockerignoreHelp}
|
||||
}
|
||||
|
||||
protected async getAppAndResolveArch(opts: FlagsDef) {
|
||||
if (opts.application) {
|
||||
if (opts.fleet) {
|
||||
const { getAppWithArch } = await import('../utils/helpers');
|
||||
const app = await getAppWithArch(opts.application);
|
||||
const app = await getAppWithArch(opts.fleet);
|
||||
opts.arch = app.arch;
|
||||
opts.deviceType = app.is_for__device_type[0].slug;
|
||||
return app;
|
||||
@ -271,7 +259,6 @@ ${dockerignoreHelp}
|
||||
inlineLogs: composeOpts.inlineLogs,
|
||||
convertEol: composeOpts.convertEol,
|
||||
dockerfilePath: composeOpts.dockerfilePath,
|
||||
nogitignore: composeOpts.nogitignore, // v13: delete this line
|
||||
multiDockerignore: composeOpts.multiDockerignore,
|
||||
});
|
||||
}
|
||||
|
@ -19,18 +19,11 @@ import { flags } from '@oclif/command';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, getCliForm, stripIndent } from '../../utils/lazy';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
warnify,
|
||||
} from '../../utils/messages';
|
||||
import { isV13 } from '../../utils/version';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
import type { PineDeferred } from 'balena-sdk';
|
||||
|
||||
interface FlagsDef {
|
||||
version: string; // OS version
|
||||
application?: string;
|
||||
app?: string; // application alias
|
||||
fleet?: string;
|
||||
device?: string;
|
||||
deviceApiKey?: string;
|
||||
@ -82,22 +75,10 @@ export default class ConfigGenerateCmd extends Command {
|
||||
description: 'a balenaOS version',
|
||||
required: true,
|
||||
}),
|
||||
...(isV13()
|
||||
? {}
|
||||
: {
|
||||
application: {
|
||||
...cf.application,
|
||||
exclusive: ['app', 'fleet', 'device'],
|
||||
},
|
||||
app: { ...cf.app, exclusive: ['application', 'fleet', 'device'] },
|
||||
appUpdatePollInterval: flags.string({
|
||||
description: 'DEPRECATED alias for --updatePollInterval',
|
||||
}),
|
||||
}),
|
||||
fleet: { ...cf.fleet, exclusive: ['application', 'app', 'device'] },
|
||||
fleet: { ...cf.fleet, exclusive: ['device'] },
|
||||
device: {
|
||||
...cf.device,
|
||||
exclusive: ['application', 'app', 'fleet', 'provisioning-key-name'],
|
||||
exclusive: ['fleet', 'provisioning-key-name'],
|
||||
},
|
||||
deviceApiKey: flags.string({
|
||||
description:
|
||||
@ -130,7 +111,7 @@ export default class ConfigGenerateCmd extends Command {
|
||||
}),
|
||||
appUpdatePollInterval: flags.string({
|
||||
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({
|
||||
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;
|
||||
} else {
|
||||
// Disambiguate application (if is a number, it could either be an ID or a numerical name)
|
||||
application = (await getApplication(balena, options.application!, {
|
||||
application = (await getApplication(balena, options.fleet!, {
|
||||
$expand: {
|
||||
is_for__device_type: { $select: 'slug' },
|
||||
},
|
||||
@ -188,7 +169,7 @@ export default class ConfigGenerateCmd extends Command {
|
||||
);
|
||||
|
||||
// Check compatibility if application and deviceType provided
|
||||
if (options.application && options.deviceType) {
|
||||
if (options.fleet && options.deviceType) {
|
||||
const appDeviceManifest = await balena.models.device.getManifestBySlug(
|
||||
resourceDeviceType,
|
||||
);
|
||||
@ -198,7 +179,7 @@ export default class ConfigGenerateCmd extends Command {
|
||||
!helpers.areDeviceTypesCompatible(appDeviceManifest, deviceManifest)
|
||||
) {
|
||||
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
|
||||
// required option, that value is used (and the corresponding question is not asked)
|
||||
const answers = await getCliForm().run(deviceManifest.options, {
|
||||
override: options,
|
||||
override: { ...options, app: options.fleet, application: options.fleet },
|
||||
});
|
||||
answers.version = options.version;
|
||||
answers.provisioningKeyName = options['provisioning-key-name'];
|
||||
@ -253,18 +234,11 @@ export default class ConfigGenerateCmd extends Command {
|
||||
protected async validateOptions(options: FlagsDef) {
|
||||
const { ExpectedError } = await import('../../errors');
|
||||
|
||||
if ((options.application || options.app) && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
options.application ||= options.app || options.fleet;
|
||||
// Prefer options.application over options.app
|
||||
delete options.app;
|
||||
|
||||
if (options.device == null && options.application == null) {
|
||||
if (options.device == null && options.fleet == null) {
|
||||
throw new ExpectedError(this.missingDeviceOrAppMessage);
|
||||
}
|
||||
|
||||
if (!options.application && options.deviceType) {
|
||||
if (!options.fleet && options.deviceType) {
|
||||
throw new ExpectedError(this.deviceTypeNotAllowedMessage);
|
||||
}
|
||||
}
|
||||
|
@ -319,7 +319,6 @@ ${dockerignoreHelp}
|
||||
inlineLogs: composeOpts.inlineLogs,
|
||||
convertEol: composeOpts.convertEol,
|
||||
dockerfilePath: composeOpts.dockerfilePath,
|
||||
nogitignore: composeOpts.nogitignore, // v13: delete this line
|
||||
multiDockerignore: composeOpts.multiDockerignore,
|
||||
});
|
||||
builtImagesByService = _.keyBy(builtImages, 'serviceName');
|
||||
|
@ -21,15 +21,13 @@ import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { expandForAppName } from '../../utils/helpers';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
||||
import { appToFleetOutputMsg, warnify } from '../../utils/messages';
|
||||
import { tryAsInteger } from '../../utils/validation';
|
||||
import { isV13 } from '../../utils/version';
|
||||
|
||||
import type { Application, Release } from 'balena-sdk';
|
||||
|
||||
interface ExtendedDevice extends DeviceWithDeviceType {
|
||||
dashboard_url?: string;
|
||||
application_name?: string;
|
||||
fleet: string; // 'org/name' slug
|
||||
device_type?: string;
|
||||
commit?: string;
|
||||
last_seen?: string;
|
||||
@ -46,7 +44,6 @@ interface ExtendedDevice extends DeviceWithDeviceType {
|
||||
|
||||
interface FlagsDef {
|
||||
help: void;
|
||||
v13: boolean;
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
@ -74,17 +71,13 @@ export default class DeviceCmd extends Command {
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
help: cf.help,
|
||||
v13: cf.v13,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
public static primary = true;
|
||||
|
||||
public async run() {
|
||||
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
|
||||
DeviceCmd,
|
||||
);
|
||||
const useAppWord = !options.v13 && !isV13();
|
||||
const { args: params } = this.parse<FlagsDef, ArgsDef>(DeviceCmd);
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
@ -121,8 +114,8 @@ export default class DeviceCmd extends Command {
|
||||
|
||||
const belongsToApplication =
|
||||
device.belongs_to__application as Application[];
|
||||
device.application_name = belongsToApplication?.[0]
|
||||
? belongsToApplication[0].app_name
|
||||
device.fleet = belongsToApplication?.[0]
|
||||
? belongsToApplication[0].slug
|
||||
: 'N/a';
|
||||
|
||||
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(
|
||||
getVisuals().table.vertical(device, [
|
||||
`$${device.device_name}$`,
|
||||
@ -184,7 +173,7 @@ export default class DeviceCmd extends Command {
|
||||
'ip_address',
|
||||
'public_address',
|
||||
'mac_address',
|
||||
useAppWord ? 'application_name' : 'application_name => FLEET',
|
||||
'fleet',
|
||||
'last_seen',
|
||||
'uuid',
|
||||
'commit',
|
||||
|
@ -19,17 +19,10 @@ import { flags } from '@oclif/command';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
warnify,
|
||||
} from '../../utils/messages';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
import { runCommand } from '../../utils/helpers';
|
||||
import { isV13 } from '../../utils/version';
|
||||
|
||||
interface FlagsDef {
|
||||
application?: string;
|
||||
app?: string;
|
||||
fleet?: string;
|
||||
yes: boolean;
|
||||
advanced: boolean;
|
||||
@ -82,12 +75,6 @@ export default class DeviceInitCmd extends Command {
|
||||
public static usage = 'device init';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
...(isV13()
|
||||
? {}
|
||||
: {
|
||||
application: cf.application,
|
||||
app: cf.app,
|
||||
}),
|
||||
fleet: cf.fleet,
|
||||
yes: cf.yes,
|
||||
advanced: flags.boolean({
|
||||
@ -130,17 +117,10 @@ export default class DeviceInitCmd extends Command {
|
||||
const logger = await Command.getLogger();
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
if ((options.application || options.app) && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
// Consolidate application options
|
||||
options.application ||= options.app || options.fleet;
|
||||
delete options.app;
|
||||
|
||||
// Get application and
|
||||
const application = (await getApplication(
|
||||
balena,
|
||||
options['application'] ||
|
||||
options.fleet ||
|
||||
(
|
||||
await (await import('../../utils/patterns')).selectApplication()
|
||||
).id,
|
||||
@ -155,7 +135,7 @@ export default class DeviceInitCmd extends Command {
|
||||
|
||||
// Register new device
|
||||
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);
|
||||
const device = await balena.models.device.get(deviceUuid);
|
||||
|
||||
|
@ -27,12 +27,7 @@ import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { ExpectedError } from '../../errors';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
warnify,
|
||||
} from '../../utils/messages';
|
||||
import { isV13 } from '../../utils/version';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
|
||||
type ExtendedDevice = PineTypedResult<
|
||||
Device,
|
||||
@ -42,8 +37,6 @@ type ExtendedDevice = PineTypedResult<
|
||||
};
|
||||
|
||||
interface FlagsDef {
|
||||
application?: string;
|
||||
app?: string;
|
||||
fleet?: string;
|
||||
help: void;
|
||||
}
|
||||
@ -82,7 +75,6 @@ export default class DeviceMoveCmd extends Command {
|
||||
public static usage = 'device move <uuid(s)>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
...(isV13() ? {} : { app: cf.app, application: cf.application }),
|
||||
fleet: cf.fleet,
|
||||
help: cf.help,
|
||||
};
|
||||
@ -94,11 +86,6 @@ export default class DeviceMoveCmd extends Command {
|
||||
DeviceMoveCmd,
|
||||
);
|
||||
|
||||
if ((options.application || options.app) && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
options.application ||= options.app || options.fleet;
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
const { tryAsInteger } = await import('../../utils/validation');
|
||||
@ -132,8 +119,8 @@ export default class DeviceMoveCmd extends Command {
|
||||
const { getApplication } = await import('../../utils/sdk');
|
||||
|
||||
// Get destination application
|
||||
const application = options.application
|
||||
? await getApplication(balena, options.application)
|
||||
const application = options.fleet
|
||||
? await getApplication(balena, options.fleet)
|
||||
: await this.interactivelySelectApplication(balena, devices);
|
||||
|
||||
// Move each device
|
||||
|
@ -75,7 +75,7 @@ export default class DeviceRegisterCmd extends Command {
|
||||
const application = await getApplication(balena, params.fleet);
|
||||
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);
|
||||
|
||||
|
@ -20,30 +20,20 @@ import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { expandForAppName } from '../../utils/helpers';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
appToFleetOutputMsg,
|
||||
jsonInfo,
|
||||
warnify,
|
||||
} from '../../utils/messages';
|
||||
import { isV13 } from '../../utils/version';
|
||||
import { applicationIdInfo, jsonInfo } from '../../utils/messages';
|
||||
|
||||
import type { Application } from 'balena-sdk';
|
||||
|
||||
interface ExtendedDevice extends DeviceWithDeviceType {
|
||||
dashboard_url?: string;
|
||||
application_name?: string | null;
|
||||
fleet?: string | null; // 'org/name' slug
|
||||
device_type?: string | null;
|
||||
}
|
||||
|
||||
interface FlagsDef {
|
||||
application?: string;
|
||||
app?: string;
|
||||
fleet?: string;
|
||||
help: void;
|
||||
json: boolean;
|
||||
v13: boolean;
|
||||
}
|
||||
|
||||
export default class DevicesCmd extends Command {
|
||||
@ -67,50 +57,25 @@ export default class DevicesCmd extends Command {
|
||||
public static usage = 'devices';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
...(isV13()
|
||||
? {}
|
||||
: {
|
||||
application: {
|
||||
...cf.application,
|
||||
exclusive: ['app', 'fleet', 'v13'],
|
||||
},
|
||||
app: { ...cf.app, exclusive: ['application', 'fleet', 'v13'] },
|
||||
}),
|
||||
fleet: { ...cf.fleet, exclusive: ['app', 'application'] },
|
||||
fleet: cf.fleet,
|
||||
json: cf.json,
|
||||
help: cf.help,
|
||||
v13: cf.v13,
|
||||
};
|
||||
|
||||
public static primary = true;
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
protected useAppWord = false;
|
||||
protected hasWarned = false;
|
||||
|
||||
public async run() {
|
||||
const { flags: options } = this.parse<FlagsDef, {}>(DevicesCmd);
|
||||
this.useAppWord = !options.fleet && !options.v13 && !isV13();
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
if (
|
||||
(options.application || options.app) &&
|
||||
!options.json &&
|
||||
process.stderr.isTTY
|
||||
) {
|
||||
this.hasWarned = true;
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
// Consolidate application options
|
||||
options.application ||= options.app || options.fleet;
|
||||
|
||||
let devices;
|
||||
|
||||
if (options.application != null) {
|
||||
if (options.fleet != null) {
|
||||
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(
|
||||
application.id,
|
||||
expandForAppName,
|
||||
@ -126,7 +91,7 @@ export default class DevicesCmd extends Command {
|
||||
|
||||
const belongsToApplication =
|
||||
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);
|
||||
|
||||
@ -134,16 +99,12 @@ export default class DevicesCmd extends Command {
|
||||
return device;
|
||||
});
|
||||
|
||||
const jName = this.useAppWord ? 'application_name' : 'fleet_name';
|
||||
const tName = this.useAppWord ? 'APPLICATION NAME' : 'FLEET';
|
||||
const fields = [
|
||||
'id',
|
||||
'uuid',
|
||||
'device_name',
|
||||
'device_type',
|
||||
options.json
|
||||
? `application_name => ${jName}`
|
||||
: `application_name => ${tName}`,
|
||||
'fleet',
|
||||
'status',
|
||||
'is_online',
|
||||
'supervisor_version',
|
||||
@ -156,9 +117,6 @@ export default class DevicesCmd extends Command {
|
||||
const mapped = devices.map((device) => pickAndRename(device, fields));
|
||||
console.log(JSON.stringify(mapped, null, 4));
|
||||
} else {
|
||||
if (!this.hasWarned && this.useAppWord && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetOutputMsg));
|
||||
}
|
||||
const _ = await import('lodash');
|
||||
console.log(
|
||||
getVisuals().table.horizontal(
|
||||
|
@ -21,33 +21,18 @@ import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
||||
import { CommandHelp } from '../../utils/oclif-utils';
|
||||
import { isV13 } from '../../utils/version';
|
||||
|
||||
interface FlagsDef {
|
||||
discontinued: boolean;
|
||||
help: void;
|
||||
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 {
|
||||
public static description = stripIndent`
|
||||
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,
|
||||
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
|
||||
@ -56,8 +41,7 @@ export default class DevicesSupportedCmd extends Command {
|
||||
`;
|
||||
public static examples = [
|
||||
'$ balena devices supported',
|
||||
'$ balena devices supported --verbose',
|
||||
'$ balena devices supported -vj',
|
||||
'$ balena devices supported --json',
|
||||
];
|
||||
|
||||
public static usage = (
|
||||
@ -66,22 +50,11 @@ export default class DevicesSupportedCmd extends Command {
|
||||
).trim();
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
discontinued: flags.boolean({
|
||||
description: isV13()
|
||||
? 'No effect (DEPRECATED)'
|
||||
: 'include "discontinued" device types (DEPRECATED)',
|
||||
}),
|
||||
help: cf.help,
|
||||
json: flags.boolean({
|
||||
char: 'j',
|
||||
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() {
|
||||
@ -95,53 +68,28 @@ export default class DevicesSupportedCmd extends Command {
|
||||
]);
|
||||
const dtsBySlug = _.keyBy(dts, (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 {
|
||||
slug: string;
|
||||
aliases: string[];
|
||||
arch: string;
|
||||
state?: string; // to be removed in CLI v13
|
||||
name: string;
|
||||
}
|
||||
let deviceTypes: DT[] = [];
|
||||
for (const slug of slugsOfInterest) {
|
||||
for (const slug of Object.keys(dtsBySlug)) {
|
||||
const configDT: Partial<typeof configDTs[0]> =
|
||||
configDTsBySlug[slug] || {};
|
||||
if (configDT.state === 'DISCONTINUED' && !options.discontinued) {
|
||||
continue;
|
||||
}
|
||||
const dt: Partial<typeof dts[0]> = dtsBySlug[slug] || {};
|
||||
const aliases = (configDT.aliases || []).filter(
|
||||
(alias) => alias !== slug,
|
||||
);
|
||||
const dt: Partial<typeof dts[0]> = dtsBySlug[slug] || {};
|
||||
deviceTypes.push({
|
||||
slug,
|
||||
aliases: options.json ? aliases : [aliases.join(', ')],
|
||||
arch:
|
||||
(dt.is_of__cpu_architecture as any)?.[0]?.slug ||
|
||||
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',
|
||||
arch: (dt.is_of__cpu_architecture as any)?.[0]?.slug || 'n/a',
|
||||
name: dt.name || 'N/A',
|
||||
});
|
||||
}
|
||||
const fields =
|
||||
options.verbose && !isV13()
|
||||
? ['slug', 'aliases', 'arch', 'state', 'name']
|
||||
: ['slug', 'aliases', 'arch', 'name'];
|
||||
const fields = ['slug', 'aliases', 'arch', 'name'];
|
||||
deviceTypes = _.sortBy(deviceTypes, fields);
|
||||
if (options.json) {
|
||||
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 * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
warnify,
|
||||
} from '../../utils/messages';
|
||||
import { isV13 } from '../../utils/version';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
|
||||
interface FlagsDef {
|
||||
application?: string;
|
||||
fleet?: string;
|
||||
device?: string; // device UUID
|
||||
help: void;
|
||||
@ -101,11 +95,8 @@ export default class EnvAddCmd extends Command {
|
||||
public static usage = 'env add <name> [value]';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
...(isV13()
|
||||
? {}
|
||||
: { application: { ...cf.application, exclusive: ['fleet', 'device'] } }),
|
||||
fleet: { ...cf.fleet, exclusive: ['application', 'device'] },
|
||||
device: { ...cf.device, exclusive: ['application', 'fleet'] },
|
||||
fleet: { ...cf.fleet, exclusive: ['device'] },
|
||||
device: { ...cf.device, exclusive: ['fleet'] },
|
||||
help: cf.help,
|
||||
quiet: cf.quiet,
|
||||
service: cf.service,
|
||||
@ -117,11 +108,7 @@ export default class EnvAddCmd extends Command {
|
||||
);
|
||||
const cmd = this;
|
||||
|
||||
if (options.application && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
options.application ||= options.fleet;
|
||||
if (!options.application && !options.device) {
|
||||
if (!options.fleet && !options.device) {
|
||||
throw new ExpectedError(
|
||||
'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';
|
||||
if (options.application) {
|
||||
for (const app of options.application.split(',')) {
|
||||
if (options.fleet) {
|
||||
const { getFleetSlug } = await import('../../utils/sdk');
|
||||
for (const app of options.fleet.split(',')) {
|
||||
try {
|
||||
await balena.models.application[varType].set(
|
||||
app,
|
||||
await getFleetSlug(balena, app),
|
||||
params.name,
|
||||
params.value,
|
||||
);
|
||||
@ -201,8 +189,8 @@ async function setServiceVars(
|
||||
params: ArgsDef,
|
||||
options: FlagsDef,
|
||||
) {
|
||||
if (options.application) {
|
||||
for (const app of options.application.split(',')) {
|
||||
if (options.fleet) {
|
||||
for (const app of options.fleet.split(',')) {
|
||||
for (const service of options.service!.split(',')) {
|
||||
try {
|
||||
const serviceId = await getServiceIdForApp(sdk, app, service);
|
||||
|
@ -21,42 +21,33 @@ import Command from '../command';
|
||||
import { ExpectedError } from '../errors';
|
||||
import * as cf from '../utils/common-flags';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
appToFleetOutputMsg,
|
||||
warnify,
|
||||
} from '../utils/messages';
|
||||
import { isV13 } from '../utils/version';
|
||||
import { applicationIdInfo } from '../utils/messages';
|
||||
|
||||
interface FlagsDef {
|
||||
application?: string;
|
||||
fleet?: string;
|
||||
config: boolean;
|
||||
device?: string; // device UUID
|
||||
json: boolean;
|
||||
help: void;
|
||||
service?: string; // service name
|
||||
verbose: boolean;
|
||||
v13: boolean;
|
||||
}
|
||||
|
||||
interface EnvironmentVariableInfo extends SDK.EnvironmentVariableBase {
|
||||
appName?: string | null; // application name
|
||||
fleet?: string | null; // fleet slug
|
||||
deviceUUID?: string; // device UUID
|
||||
serviceName?: string; // service name
|
||||
}
|
||||
|
||||
interface DeviceServiceEnvironmentVariableInfo
|
||||
extends SDK.DeviceServiceEnvironmentVariable {
|
||||
appName?: string; // application name
|
||||
fleet?: string; // fleet slug
|
||||
deviceUUID?: string; // device UUID
|
||||
serviceName?: string; // service name
|
||||
}
|
||||
|
||||
interface ServiceEnvironmentVariableInfo
|
||||
extends SDK.ServiceEnvironmentVariable {
|
||||
appName?: string; // application name
|
||||
fleet?: string; // fleet slug
|
||||
deviceUUID?: string; // device UUID
|
||||
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).
|
||||
|
||||
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||
|
||||
${appToFleetOutputMsg.split('\n').join('\n\t\t')}
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
@ -115,57 +104,35 @@ export default class EnvsCmd extends Command {
|
||||
public static usage = 'envs';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
...(isV13()
|
||||
? {}
|
||||
: {
|
||||
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 },
|
||||
fleet: { ...cf.fleet, exclusive: ['device'] },
|
||||
config: flags.boolean({
|
||||
default: false,
|
||||
char: 'c',
|
||||
description: 'show configuration variables only',
|
||||
exclusive: ['service'],
|
||||
}),
|
||||
device: { exclusive: ['fleet', 'application'], ...cf.device },
|
||||
device: { ...cf.device, exclusive: ['fleet'] },
|
||||
help: cf.help,
|
||||
json: cf.json,
|
||||
verbose: cf.verbose,
|
||||
service: { exclusive: ['config'], ...cf.service },
|
||||
v13: cf.v13,
|
||||
service: { ...cf.service, exclusive: ['config'] },
|
||||
};
|
||||
|
||||
protected useAppWord = false;
|
||||
protected hasWarned = false;
|
||||
|
||||
public async run() {
|
||||
const { flags: options } = this.parse<FlagsDef, {}>(EnvsCmd);
|
||||
this.useAppWord = !options.fleet && !options.v13 && !isV13();
|
||||
|
||||
const variables: EnvironmentVariableInfo[] = [];
|
||||
|
||||
await Command.checkLoggedIn();
|
||||
|
||||
if (options.application && !options.json && process.stderr.isTTY) {
|
||||
this.hasWarned = true;
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
options.application ||= options.fleet;
|
||||
if (!options.application && !options.device) {
|
||||
if (!options.fleet && !options.device) {
|
||||
throw new ExpectedError('Missing --fleet or --device option');
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
if (options.device) {
|
||||
@ -178,23 +145,23 @@ export default class EnvsCmd extends Command {
|
||||
);
|
||||
fullUUID = device.uuid;
|
||||
if (app) {
|
||||
appNameOrSlug = app.slug;
|
||||
fleetSlug = app.slug;
|
||||
}
|
||||
}
|
||||
if (appNameOrSlug && options.service) {
|
||||
await validateServiceName(balena, options.service, appNameOrSlug);
|
||||
if (fleetSlug && options.service) {
|
||||
await validateServiceName(balena, options.service, fleetSlug);
|
||||
}
|
||||
variables.push(...(await getAppVars(balena, appNameOrSlug, options)));
|
||||
variables.push(...(await getAppVars(balena, fleetSlug, options)));
|
||||
if (fullUUID) {
|
||||
variables.push(
|
||||
...(await getDeviceVars(balena, fullUUID, appNameOrSlug, options)),
|
||||
...(await getDeviceVars(balena, fullUUID, fleetSlug, options)),
|
||||
);
|
||||
}
|
||||
if (!options.json && variables.length === 0) {
|
||||
const target =
|
||||
(options.service ? `service "${options.service}" of ` : '') +
|
||||
(options.application
|
||||
? `fleet "${options.application}"`
|
||||
(options.fleet
|
||||
? `fleet "${options.fleet}"`
|
||||
: `device "${options.device}"`);
|
||||
throw new ExpectedError(`No environment variables found for ${target}`);
|
||||
}
|
||||
@ -206,24 +173,14 @@ export default class EnvsCmd extends Command {
|
||||
varArray: EnvironmentVariableInfo[],
|
||||
options: FlagsDef,
|
||||
) {
|
||||
const fields = ['id', 'name', 'value'];
|
||||
const fields = ['id', 'name', 'value', 'fleet'];
|
||||
|
||||
// Replace undefined app names with 'N/A' or null
|
||||
varArray = varArray.map((i: EnvironmentVariableInfo) => {
|
||||
if (i.appName) {
|
||||
// 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';
|
||||
}
|
||||
i.fleet ||= options.json ? null : 'N/A';
|
||||
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) {
|
||||
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));
|
||||
this.log(JSON.stringify(mapped, null, 4));
|
||||
} else {
|
||||
if (!this.hasWarned && this.useAppWord && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetOutputMsg));
|
||||
}
|
||||
this.log(
|
||||
getVisuals().table.horizontal(
|
||||
_.sortBy(varArray, (v: SDK.EnvironmentVariableBase) => v.name),
|
||||
@ -252,14 +206,14 @@ export default class EnvsCmd extends Command {
|
||||
async function validateServiceName(
|
||||
sdk: SDK.BalenaSDK,
|
||||
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 },
|
||||
});
|
||||
if (services.length === 0) {
|
||||
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(
|
||||
sdk: SDK.BalenaSDK,
|
||||
appNameOrSlug: string | undefined,
|
||||
fleetSlug: string | undefined,
|
||||
options: FlagsDef,
|
||||
): Promise<EnvironmentVariableInfo[]> {
|
||||
const appVars: EnvironmentVariableInfo[] = [];
|
||||
if (!appNameOrSlug) {
|
||||
if (!fleetSlug) {
|
||||
return appVars;
|
||||
}
|
||||
const vars = await sdk.models.application[
|
||||
options.config ? 'configVar' : 'envVar'
|
||||
].getAllByApplication(appNameOrSlug);
|
||||
fillInInfoFields(vars, appNameOrSlug);
|
||||
].getAllByApplication(fleetSlug);
|
||||
fillInInfoFields(vars, fleetSlug);
|
||||
appVars.push(...vars);
|
||||
if (!options.config) {
|
||||
const pineOpts: SDK.PineOptions<SDK.ServiceEnvironmentVariable> = {
|
||||
@ -299,10 +253,10 @@ async function getAppVars(
|
||||
};
|
||||
}
|
||||
const serviceVars = await sdk.models.service.var.getAllByApplication(
|
||||
appNameOrSlug,
|
||||
fleetSlug,
|
||||
pineOpts,
|
||||
);
|
||||
fillInInfoFields(serviceVars, appNameOrSlug);
|
||||
fillInInfoFields(serviceVars, fleetSlug);
|
||||
appVars.push(...serviceVars);
|
||||
}
|
||||
return appVars;
|
||||
@ -315,7 +269,7 @@ async function getAppVars(
|
||||
async function getDeviceVars(
|
||||
sdk: SDK.BalenaSDK,
|
||||
fullUUID: string,
|
||||
appNameOrSlug: string | undefined,
|
||||
fleetSlug: string | undefined,
|
||||
options: FlagsDef,
|
||||
): Promise<EnvironmentVariableInfo[]> {
|
||||
const printedUUID = options.json ? fullUUID : options.device!;
|
||||
@ -324,7 +278,7 @@ async function getDeviceVars(
|
||||
const deviceConfigVars = await sdk.models.device.configVar.getAllByDevice(
|
||||
fullUUID,
|
||||
);
|
||||
fillInInfoFields(deviceConfigVars, appNameOrSlug, printedUUID);
|
||||
fillInInfoFields(deviceConfigVars, fleetSlug, printedUUID);
|
||||
deviceVars.push(...deviceConfigVars);
|
||||
} else {
|
||||
const pineOpts: SDK.PineOptions<SDK.DeviceServiceEnvironmentVariable> = {
|
||||
@ -345,13 +299,13 @@ async function getDeviceVars(
|
||||
fullUUID,
|
||||
pineOpts,
|
||||
);
|
||||
fillInInfoFields(deviceServiceVars, appNameOrSlug, printedUUID);
|
||||
fillInInfoFields(deviceServiceVars, fleetSlug, printedUUID);
|
||||
deviceVars.push(...deviceServiceVars);
|
||||
|
||||
const deviceEnvVars = await sdk.models.device.envVar.getAllByDevice(
|
||||
fullUUID,
|
||||
);
|
||||
fillInInfoFields(deviceEnvVars, appNameOrSlug, printedUUID);
|
||||
fillInInfoFields(deviceEnvVars, fleetSlug, printedUUID);
|
||||
deviceVars.push(...deviceEnvVars);
|
||||
}
|
||||
return deviceVars;
|
||||
@ -367,7 +321,7 @@ function fillInInfoFields(
|
||||
| EnvironmentVariableInfo[]
|
||||
| DeviceServiceEnvironmentVariableInfo[]
|
||||
| ServiceEnvironmentVariableInfo[],
|
||||
appNameOrSlug?: string,
|
||||
fleetSlug?: string,
|
||||
deviceUUID?: string,
|
||||
) {
|
||||
for (const envVar of varArray) {
|
||||
@ -381,7 +335,7 @@ function fillInInfoFields(
|
||||
?.installs__service as SDK.Service[]
|
||||
)[0]?.service_name;
|
||||
}
|
||||
envVar.appName = appNameOrSlug;
|
||||
envVar.fleet = fleetSlug;
|
||||
envVar.serviceName = envVar.serviceName || '*';
|
||||
envVar.deviceUUID = deviceUUID || '*';
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2021 Balena Ltd.
|
||||
* Copyright 2016-2021 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -15,6 +15,135 @@
|
||||
* 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
|
||||
* Copyright 2021 Balena Ltd.
|
||||
* Copyright 2016-2021 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -15,6 +15,89 @@
|
||||
* 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
|
||||
* Copyright 2021 Balena Ltd.
|
||||
* 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.
|
||||
@ -15,6 +15,67 @@
|
||||
* 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
|
||||
* Copyright 2021 Balena Ltd.
|
||||
* 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.
|
||||
@ -15,6 +15,135 @@
|
||||
* 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
|
||||
* Copyright 2021 Balena Ltd.
|
||||
* 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.
|
||||
@ -15,6 +15,56 @@
|
||||
* 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
|
||||
* Copyright 2021 Balena Ltd.
|
||||
* 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.
|
||||
@ -15,6 +15,70 @@
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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 { applicationIdInfo } from '../utils/messages';
|
||||
import { parseAsLocalHostnameOrIp } from '../utils/validation';
|
||||
import { isV13 } from '../utils/version';
|
||||
|
||||
interface FlagsDef {
|
||||
application?: string;
|
||||
fleet?: string;
|
||||
pollInterval?: number;
|
||||
help?: void;
|
||||
@ -77,7 +75,6 @@ export default class JoinCmd extends Command {
|
||||
public static usage = 'join [deviceIpOrHostname]';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
...(isV13() ? {} : { application: cf.application }),
|
||||
fleet: cf.fleet,
|
||||
pollInterval: flags.integer({
|
||||
description: 'the interval in minutes to check for updates',
|
||||
@ -101,7 +98,7 @@ export default class JoinCmd extends Command {
|
||||
logger,
|
||||
sdk,
|
||||
params.deviceIpOrHostname,
|
||||
options.application || options.fleet,
|
||||
options.fleet,
|
||||
options.pollInterval,
|
||||
);
|
||||
}
|
||||
|
@ -23,19 +23,12 @@ import Command from '../../command';
|
||||
import { ExpectedError } from '../../errors';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
warnify,
|
||||
} from '../../utils/messages';
|
||||
import { isV13 } from '../../utils/version';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
|
||||
const CONNECTIONS_FOLDER = '/system-connections';
|
||||
|
||||
interface FlagsDef {
|
||||
advanced?: boolean;
|
||||
application?: string;
|
||||
app?: string;
|
||||
fleet?: string;
|
||||
config?: string;
|
||||
'config-app-update-poll-interval'?: number;
|
||||
@ -43,7 +36,6 @@ interface FlagsDef {
|
||||
'config-wifi-key'?: string;
|
||||
'config-wifi-ssid'?: string;
|
||||
device?: string; // device UUID
|
||||
'device-api-key'?: string;
|
||||
'device-type'?: string;
|
||||
help?: void;
|
||||
version?: string;
|
||||
@ -66,10 +58,6 @@ interface Answers {
|
||||
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 {
|
||||
public static description = stripIndent`
|
||||
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://developer.gnome.org/NetworkManager/stable/ref-settings.html
|
||||
|
||||
${deviceApiKeyDeprecationMsg.split('\n').join('\n\t\t')}
|
||||
|
||||
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||
|
||||
Note: This command is currently not supported on Windows natively. Windows users
|
||||
@ -105,7 +91,6 @@ export default class OsConfigureCmd extends Command {
|
||||
|
||||
public static examples = [
|
||||
'$ balena os configure ../path/rpi3.img --device 7cf02a6',
|
||||
'$ balena os configure ../path/rpi3.img --device 7cf02a6 --device-api-key <existingDeviceKey>',
|
||||
'$ balena os configure ../path/rpi3.img --fleet myorg/myfleet',
|
||||
'$ balena os configure ../path/rpi3.img --fleet MyFleet --version 2.12.7',
|
||||
'$ balena os configure ../path/rpi3.img -f MyFinFleet --device-type raspberrypi3',
|
||||
@ -128,22 +113,7 @@ export default class OsConfigureCmd extends Command {
|
||||
description:
|
||||
'ask advanced configuration questions (when in interactive mode)',
|
||||
}),
|
||||
...(isV13()
|
||||
? {}
|
||||
: {
|
||||
application: {
|
||||
...cf.application,
|
||||
exclusive: ['app', 'fleet', 'device'],
|
||||
},
|
||||
app: {
|
||||
...cf.app,
|
||||
exclusive: ['application', 'fleet', 'device'],
|
||||
},
|
||||
}),
|
||||
fleet: {
|
||||
...cf.fleet,
|
||||
exclusive: ['app', 'application', 'device'],
|
||||
},
|
||||
fleet: { ...cf.fleet, exclusive: ['device'] },
|
||||
config: flags.string({
|
||||
description:
|
||||
'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({
|
||||
description: 'WiFi SSID (network name) (non-interactive configuration)',
|
||||
}),
|
||||
device: {
|
||||
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: { ...cf.device, exclusive: ['fleet', 'provisioning-key-name'] },
|
||||
'device-type': flags.string({
|
||||
description:
|
||||
'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>(
|
||||
OsConfigureCmd,
|
||||
);
|
||||
if ((options.application || options.app) && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
options.application ||= options.app || options.fleet;
|
||||
|
||||
await validateOptions(options);
|
||||
|
||||
@ -233,7 +191,7 @@ export default class OsConfigureCmd extends Command {
|
||||
};
|
||||
deviceTypeSlug = device.is_of__device_type[0].slug;
|
||||
} else {
|
||||
app = (await getApplication(balena, options.application!, {
|
||||
app = (await getApplication(balena, options.fleet!, {
|
||||
$expand: {
|
||||
is_for__device_type: { $select: 'slug' },
|
||||
},
|
||||
@ -259,7 +217,7 @@ export default class OsConfigureCmd extends Command {
|
||||
options,
|
||||
configJson,
|
||||
);
|
||||
if (options.application) {
|
||||
if (options.fleet) {
|
||||
answers.deviceType = deviceTypeSlug;
|
||||
}
|
||||
answers.version =
|
||||
@ -270,11 +228,7 @@ export default class OsConfigureCmd extends Command {
|
||||
|
||||
if (_.isEmpty(configJson)) {
|
||||
if (device) {
|
||||
configJson = await generateDeviceConfig(
|
||||
device,
|
||||
options['device-api-key'],
|
||||
answers,
|
||||
);
|
||||
configJson = await generateDeviceConfig(device, undefined, answers);
|
||||
} else {
|
||||
configJson = await generateApplicationConfig(app!, answers);
|
||||
}
|
||||
@ -335,23 +289,16 @@ export default class OsConfigureCmd extends Command {
|
||||
async function validateOptions(options: FlagsDef) {
|
||||
// The 'device' and 'application' options are declared "exclusive" in the oclif
|
||||
// flag definitions above, so oclif will enforce that they are not both used together.
|
||||
if (!options.device && !options.application) {
|
||||
if (!options.device && !options.fleet) {
|
||||
throw new ExpectedError(
|
||||
"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(
|
||||
"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();
|
||||
}
|
||||
@ -401,7 +348,7 @@ async function checkDeviceTypeCompatibility(
|
||||
const helpers = await import('../../utils/helpers');
|
||||
if (!helpers.areDeviceTypesCompatible(appDeviceType, optionDeviceType)) {
|
||||
throw new ExpectedError(
|
||||
`Device type ${options['device-type']} is incompatible with fleet ${options.application}`,
|
||||
`Device type ${options['device-type']} is incompatible with fleet ${options.fleet}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -426,7 +373,13 @@ async function askQuestionsForDeviceType(
|
||||
options: FlagsDef,
|
||||
configJson?: import('../../utils/config').ImgConfig,
|
||||
): Promise<Answers> {
|
||||
const answerSources: any[] = [camelifyConfigOptions(options)];
|
||||
const answerSources: any[] = [
|
||||
{
|
||||
...camelifyConfigOptions(options),
|
||||
app: options.fleet,
|
||||
application: options.fleet,
|
||||
},
|
||||
];
|
||||
const defaultAnswers: Partial<Answers> = {};
|
||||
const questions: any = deviceType.options;
|
||||
let extraOpts: { override: object } | undefined;
|
||||
|
@ -24,15 +24,10 @@ import {
|
||||
getVisuals,
|
||||
stripIndent,
|
||||
} from '../utils/lazy';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
warnify,
|
||||
} from '../utils/messages';
|
||||
import { applicationIdInfo } from '../utils/messages';
|
||||
import type { DockerConnectionCliFlags } from '../utils/docker';
|
||||
import { dockerConnectionCliFlags } from '../utils/docker';
|
||||
import { parseAsInteger } from '../utils/validation';
|
||||
import { isV13 } from '../utils/version';
|
||||
|
||||
import { flags } from '@oclif/command';
|
||||
import * as _ from 'lodash';
|
||||
@ -40,7 +35,6 @@ import type { Application, BalenaSDK, PineExpand, Release } from 'balena-sdk';
|
||||
import type { Preloader } from 'balena-preload';
|
||||
|
||||
interface FlagsDef extends DockerConnectionCliFlags {
|
||||
app?: string;
|
||||
fleet?: string;
|
||||
commit?: string;
|
||||
'splash-image'?: string;
|
||||
@ -99,7 +93,6 @@ export default class PreloadCmd extends Command {
|
||||
public static usage = 'preload <image>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
...(isV13() ? {} : { app: cf.application }),
|
||||
fleet: cf.fleet,
|
||||
commit: flags.string({
|
||||
description: `\
|
||||
@ -163,11 +156,6 @@ Can be repeated to add multiple certificates.\
|
||||
PreloadCmd,
|
||||
);
|
||||
|
||||
if (options.app && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
options.app ||= options.fleet;
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
const balenaPreload = await import('balena-preload');
|
||||
const visuals = getVisuals();
|
||||
@ -194,15 +182,9 @@ Can be repeated to add multiple certificates.\
|
||||
|
||||
// balena-preload currently does not work with numerical app IDs
|
||||
// Load app here, and use app slug from hereon
|
||||
if (options.app && !options.app.includes('/')) {
|
||||
// Disambiguate application (if is a number, it could either be an ID or a numerical name)
|
||||
const { getApplication } = await import('../utils/sdk');
|
||||
const application = await getApplication(balena, options.app);
|
||||
if (!application) {
|
||||
throw new ExpectedError(`Fleet not found: ${options.app}`);
|
||||
}
|
||||
options.app = application.slug;
|
||||
}
|
||||
const fleetSlug: string | undefined = options.fleet
|
||||
? await (await import('../utils/sdk')).getFleetSlug(balena, options.fleet)
|
||||
: undefined;
|
||||
|
||||
const progressBars: {
|
||||
[key: string]: ReturnType<typeof getVisuals>['Progress'];
|
||||
@ -238,15 +220,12 @@ Can be repeated to add multiple certificates.\
|
||||
? 'latest'
|
||||
: options.commit;
|
||||
const image = params.image;
|
||||
const appId = options.app;
|
||||
|
||||
const splashImage = options['splash-image'];
|
||||
const additionalSpace = options['additional-space'];
|
||||
|
||||
const dontCheckArch = options['dont-check-arch'] || false;
|
||||
const pinDevice = options['pin-device-to-release'] || false;
|
||||
|
||||
if (dontCheckArch && !appId) {
|
||||
if (dontCheckArch && !fleetSlug) {
|
||||
throw new ExpectedError(
|
||||
'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(
|
||||
null,
|
||||
docker,
|
||||
appId,
|
||||
fleetSlug,
|
||||
commit,
|
||||
image,
|
||||
splashImage,
|
||||
@ -309,7 +288,7 @@ Can be repeated to add multiple certificates.\
|
||||
preloader.on('error', reject);
|
||||
resolve(
|
||||
this.prepareAndPreload(preloader, balena, {
|
||||
appId,
|
||||
appId: fleetSlug,
|
||||
commit,
|
||||
pinDevice,
|
||||
}),
|
||||
@ -364,8 +343,8 @@ Can be repeated to add multiple certificates.\
|
||||
} catch {
|
||||
throw new Error(`Device type "${deviceTypeSlug}" not found in API query`);
|
||||
}
|
||||
return (await balena.models.application.getAll({
|
||||
$select: ['id', 'app_name', 'should_track_latest_release'],
|
||||
return (await balena.models.application.getAllDirectlyAccessible({
|
||||
$select: ['id', 'slug', 'should_track_latest_release'],
|
||||
$expand: this.applicationExpandOptions,
|
||||
$filter: {
|
||||
// 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<
|
||||
ApplicationWithDeviceType & {
|
||||
should_be_running__release: [Release?];
|
||||
@ -437,7 +416,7 @@ Can be repeated to add multiple certificates.\
|
||||
message: 'Select a fleet',
|
||||
type: 'list',
|
||||
choices: applications.map((app) => ({
|
||||
name: app.app_name,
|
||||
name: app.slug,
|
||||
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');
|
||||
|
||||
return (await getApplication(balenaSdk, appId, {
|
||||
|
@ -22,7 +22,6 @@ import { getBalenaSdk, stripIndent } from '../utils/lazy';
|
||||
import { dockerignoreHelp, registrySecretsHelp } from '../utils/messages';
|
||||
import type { BalenaSDK } from 'balena-sdk';
|
||||
import { ExpectedError, instanceOf } from '../errors';
|
||||
import { isV13 } from '../utils/version';
|
||||
import { RegistrySecrets } from 'resin-multibuild';
|
||||
import { lowercaseIfSlug } from '../utils/normalization';
|
||||
import {
|
||||
@ -47,14 +46,11 @@ interface FlagsDef {
|
||||
pull: boolean;
|
||||
'noparent-check': boolean;
|
||||
'registry-secrets'?: string;
|
||||
gitignore?: boolean; // v13: delete this flag
|
||||
nogitignore?: boolean; // v13: delete this flag
|
||||
nolive: boolean;
|
||||
detached: boolean;
|
||||
service?: string[];
|
||||
system: boolean;
|
||||
env?: string[];
|
||||
'convert-eol'?: boolean;
|
||||
'noconvert-eol': boolean;
|
||||
'multi-dockerignore': boolean;
|
||||
'release-tag'?: string[];
|
||||
@ -218,16 +214,6 @@ export default class PushCmd extends Command {
|
||||
`,
|
||||
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({
|
||||
description: `Don't convert line endings from CRLF (Windows format) to LF (Unix format).`,
|
||||
default: false,
|
||||
@ -237,28 +223,7 @@ export default class PushCmd extends Command {
|
||||
'Have each service use its own .dockerignore file. See "balena help push".',
|
||||
char: 'm',
|
||||
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({
|
||||
description: stripIndent`
|
||||
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,
|
||||
auth: token,
|
||||
baseUrl,
|
||||
nogitignore: !options.gitignore, // v13: delete this line
|
||||
sdk,
|
||||
opts,
|
||||
};
|
||||
@ -422,7 +386,6 @@ export default class PushCmd extends Command {
|
||||
multiDockerignore: options['multi-dockerignore'],
|
||||
nocache: options.nocache,
|
||||
pull: options.pull,
|
||||
nogitignore: !options.gitignore, // v13: delete this line
|
||||
noParentCheck: options['noparent-check'],
|
||||
nolive: options.nolive,
|
||||
detached: options.detached,
|
||||
|
@ -49,7 +49,7 @@ export default class ReleasesCmd extends Command {
|
||||
public static args = [
|
||||
{
|
||||
name: 'fleet',
|
||||
description: 'fleet name or slug',
|
||||
description: 'fleet name or slug (preferred)',
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
@ -69,9 +69,10 @@ export default class ReleasesCmd extends Command {
|
||||
];
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
const { getFleetSlug } = await import('../utils/sdk');
|
||||
|
||||
const releases = await balena.models.release.getAllByApplication(
|
||||
params.fleet,
|
||||
await getFleetSlug(balena, params.fleet),
|
||||
{ $select: fields },
|
||||
);
|
||||
|
||||
|
@ -20,15 +20,9 @@ import Command from '../command';
|
||||
import { ExpectedError } from '../errors';
|
||||
import * as cf from '../utils/common-flags';
|
||||
import { getBalenaSdk, getCliUx, stripIndent } from '../utils/lazy';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
warnify,
|
||||
} from '../utils/messages';
|
||||
import { isV13 } from '../utils/version';
|
||||
import { applicationIdInfo } from '../utils/messages';
|
||||
|
||||
interface FlagsDef {
|
||||
application?: string;
|
||||
fleet?: string;
|
||||
device?: string;
|
||||
duration?: string;
|
||||
@ -77,11 +71,10 @@ export default class SupportCmd extends Command {
|
||||
description: 'comma-separated list (no spaces) of device UUIDs',
|
||||
char: 'd',
|
||||
}),
|
||||
...(isV13() ? {} : { application: cf.application }),
|
||||
fleet: {
|
||||
...cf.fleet,
|
||||
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({
|
||||
description:
|
||||
@ -98,18 +91,13 @@ export default class SupportCmd extends Command {
|
||||
SupportCmd,
|
||||
);
|
||||
|
||||
if (options.application && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
options.application ||= options.fleet;
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
const ux = getCliUx();
|
||||
|
||||
const enabling = params.action === 'enable';
|
||||
|
||||
// Validation
|
||||
if (!options.device && !options.application) {
|
||||
if (!options.device && !options.fleet) {
|
||||
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 deviceUuids = options.device?.split(',') || [];
|
||||
const appNames = options.application?.split(',') || [];
|
||||
const appNames = options.fleet?.split(',') || [];
|
||||
|
||||
const enablingMessage = 'Enabling support access for';
|
||||
const disablingMessage = 'Disabling support access for';
|
||||
@ -142,14 +130,17 @@ export default class SupportCmd extends Command {
|
||||
ux.action.stop();
|
||||
}
|
||||
|
||||
const { getFleetSlug } = await import('../utils/sdk');
|
||||
|
||||
// Process applications
|
||||
for (const appName of appNames) {
|
||||
const slug = await getFleetSlug(balena, appName);
|
||||
if (enabling) {
|
||||
ux.action.start(`${enablingMessage} fleet ${appName}`);
|
||||
await balena.models.application.grantSupportAccess(appName, expiryTs);
|
||||
ux.action.start(`${enablingMessage} fleet ${slug}`);
|
||||
await balena.models.application.grantSupportAccess(slug, expiryTs);
|
||||
} else if (params.action === 'disable') {
|
||||
ux.action.start(`${disablingMessage} fleet ${appName}`);
|
||||
await balena.models.application.revokeSupportAccess(appName);
|
||||
ux.action.start(`${disablingMessage} fleet ${slug}`);
|
||||
await balena.models.application.revokeSupportAccess(slug);
|
||||
}
|
||||
ux.action.stop();
|
||||
}
|
||||
|
@ -19,16 +19,9 @@ import { flags } from '@oclif/command';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
warnify,
|
||||
} from '../../utils/messages';
|
||||
import { isV13 } from '../../utils/version';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
|
||||
interface FlagsDef {
|
||||
app?: string;
|
||||
application?: string;
|
||||
fleet?: string;
|
||||
device?: string;
|
||||
release?: string;
|
||||
@ -67,29 +60,17 @@ export default class TagRmCmd extends Command {
|
||||
public static usage = 'tag rm <tagKey>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
...(isV13()
|
||||
? {}
|
||||
: {
|
||||
application: {
|
||||
...cf.application,
|
||||
exclusive: ['app', 'fleet', 'device', 'release'],
|
||||
},
|
||||
app: {
|
||||
...cf.app,
|
||||
exclusive: ['application', 'fleet', 'device', 'release'],
|
||||
},
|
||||
}),
|
||||
fleet: {
|
||||
...cf.fleet,
|
||||
exclusive: ['app', 'application', 'device', 'release'],
|
||||
exclusive: ['device', 'release'],
|
||||
},
|
||||
device: {
|
||||
...cf.device,
|
||||
exclusive: ['app', 'application', 'fleet', 'release'],
|
||||
exclusive: ['fleet', 'release'],
|
||||
},
|
||||
release: {
|
||||
...cf.release,
|
||||
exclusive: ['app', 'application', 'fleet', 'device'],
|
||||
exclusive: ['fleet', 'device'],
|
||||
},
|
||||
help: cf.help,
|
||||
};
|
||||
@ -101,25 +82,20 @@ export default class TagRmCmd extends Command {
|
||||
TagRmCmd,
|
||||
);
|
||||
|
||||
if ((options.application || options.app) && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
options.application ||= options.app || options.fleet;
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
// 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');
|
||||
throw new ExpectedError(TagRmCmd.missingResourceMessage);
|
||||
}
|
||||
|
||||
const { tryAsInteger } = await import('../../utils/validation');
|
||||
|
||||
if (options.application) {
|
||||
const { getTypedApplicationIdentifier } = await import('../../utils/sdk');
|
||||
if (options.fleet) {
|
||||
const { getFleetSlug } = await import('../../utils/sdk');
|
||||
return balena.models.application.tags.remove(
|
||||
await getTypedApplicationIdentifier(balena, options.application),
|
||||
await getFleetSlug(balena, options.fleet),
|
||||
params.tagKey,
|
||||
);
|
||||
}
|
||||
|
@ -19,16 +19,9 @@ import { flags } from '@oclif/command';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
warnify,
|
||||
} from '../../utils/messages';
|
||||
import { isV13 } from '../../utils/version';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
|
||||
interface FlagsDef {
|
||||
app?: string;
|
||||
application?: string;
|
||||
fleet?: string;
|
||||
device?: string;
|
||||
release?: string;
|
||||
@ -80,29 +73,17 @@ export default class TagSetCmd extends Command {
|
||||
public static usage = 'tag set <tagKey> [value]';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
...(isV13()
|
||||
? {}
|
||||
: {
|
||||
application: {
|
||||
...cf.application,
|
||||
exclusive: ['app', 'fleet', 'device', 'release'],
|
||||
},
|
||||
app: {
|
||||
...cf.app,
|
||||
exclusive: ['application', 'fleet', 'device', 'release'],
|
||||
},
|
||||
}),
|
||||
fleet: {
|
||||
...cf.fleet,
|
||||
exclusive: ['app', 'application', 'device', 'release'],
|
||||
exclusive: ['device', 'release'],
|
||||
},
|
||||
device: {
|
||||
...cf.device,
|
||||
exclusive: ['app', 'application', 'fleet', 'release'],
|
||||
exclusive: ['fleet', 'release'],
|
||||
},
|
||||
release: {
|
||||
...cf.release,
|
||||
exclusive: ['app', 'application', 'fleet', 'device'],
|
||||
exclusive: ['fleet', 'device'],
|
||||
},
|
||||
help: cf.help,
|
||||
};
|
||||
@ -114,15 +95,10 @@ export default class TagSetCmd extends Command {
|
||||
TagSetCmd,
|
||||
);
|
||||
|
||||
if ((options.application || options.app) && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
options.application ||= options.app || options.fleet;
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
// 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');
|
||||
throw new ExpectedError(TagSetCmd.missingResourceMessage);
|
||||
}
|
||||
@ -131,10 +107,10 @@ export default class TagSetCmd extends Command {
|
||||
|
||||
const { tryAsInteger } = await import('../../utils/validation');
|
||||
|
||||
if (options.application) {
|
||||
const { getTypedApplicationIdentifier } = await import('../../utils/sdk');
|
||||
if (options.fleet) {
|
||||
const { getFleetSlug } = await import('../../utils/sdk');
|
||||
return balena.models.application.tags.set(
|
||||
await getTypedApplicationIdentifier(balena, options.application),
|
||||
await getFleetSlug(balena, options.fleet),
|
||||
params.tagKey,
|
||||
params.value,
|
||||
);
|
||||
|
@ -20,16 +20,9 @@ import Command from '../command';
|
||||
import { ExpectedError } from '../errors';
|
||||
import * as cf from '../utils/common-flags';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
warnify,
|
||||
} from '../utils/messages';
|
||||
import { isV13 } from '../utils/version';
|
||||
import { applicationIdInfo } from '../utils/messages';
|
||||
|
||||
interface FlagsDef {
|
||||
app?: string;
|
||||
application?: string;
|
||||
fleet?: string;
|
||||
device?: string;
|
||||
release?: string;
|
||||
@ -56,29 +49,17 @@ export default class TagsCmd extends Command {
|
||||
public static usage = 'tags';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
...(isV13()
|
||||
? {}
|
||||
: {
|
||||
application: {
|
||||
...cf.application,
|
||||
exclusive: ['app', 'fleet', 'device', 'release'],
|
||||
},
|
||||
app: {
|
||||
...cf.app,
|
||||
exclusive: ['application', 'fleet', 'device', 'release'],
|
||||
},
|
||||
}),
|
||||
fleet: {
|
||||
...cf.fleet,
|
||||
exclusive: ['app', 'application', 'device', 'release'],
|
||||
exclusive: ['device', 'release'],
|
||||
},
|
||||
device: {
|
||||
...cf.device,
|
||||
exclusive: ['app', 'application', 'fleet', 'release'],
|
||||
exclusive: ['fleet', 'release'],
|
||||
},
|
||||
release: {
|
||||
...cf.release,
|
||||
exclusive: ['app', 'application', 'fleet', 'device'],
|
||||
exclusive: ['fleet', 'device'],
|
||||
},
|
||||
help: cf.help,
|
||||
};
|
||||
@ -88,15 +69,10 @@ export default class TagsCmd extends Command {
|
||||
public async run() {
|
||||
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();
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@ -104,10 +80,10 @@ export default class TagsCmd extends Command {
|
||||
|
||||
let tags;
|
||||
|
||||
if (options.application) {
|
||||
const { getTypedApplicationIdentifier } = await import('../utils/sdk');
|
||||
if (options.fleet) {
|
||||
const { getFleetSlug } = await import('../utils/sdk');
|
||||
tags = await balena.models.application.tags.getAllByApplication(
|
||||
await getTypedApplicationIdentifier(balena, options.application),
|
||||
await getFleetSlug(balena, options.fleet),
|
||||
);
|
||||
}
|
||||
if (options.device) {
|
||||
|
@ -131,6 +131,13 @@ Please use "balena ${alternative}" instead.`);
|
||||
'local scan': [replaced, 'scan', 'v11.0.0'],
|
||||
'local ssh': [replaced, 'ssh', '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;
|
||||
if (argvSlice.length > 1) {
|
||||
|
@ -253,13 +253,13 @@ export async function getFormattedOsVersions(
|
||||
const sdk = getBalenaSdk();
|
||||
let slug = deviceType;
|
||||
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 (!versionsByDT[slug]?.length) {
|
||||
// unaliasDeviceType() produces a nice error msg if slug is invalid
|
||||
slug = await unaliasDeviceType(sdk, slug);
|
||||
if (slug !== deviceType) {
|
||||
versionsByDT = await sdk.models.hostapp.getAvailableOsVersions([slug]);
|
||||
versionsByDT = await sdk.models.os.getAvailableOsVersions([slug]);
|
||||
}
|
||||
}
|
||||
const versions: SDK.OsVersion[] = (versionsByDT[slug] || [])
|
||||
|
@ -19,35 +19,13 @@ import { flags } from '@oclif/command';
|
||||
import { stripIndent } from './lazy';
|
||||
import { lowercaseIfSlug } from './normalization';
|
||||
|
||||
import { isV13 } from './version';
|
||||
import { isV14 } from './version';
|
||||
import type { IBooleanFlag } from '@oclif/parser/lib/flags';
|
||||
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({
|
||||
char: 'f',
|
||||
description: isV13()
|
||||
? 'fleet name, slug (preferred), or numeric ID (deprecated)'
|
||||
: // avoid the '(deprecated)' remark in v12 while cf.application and
|
||||
// cf.app are also described as deprecated, to avoid the impression
|
||||
// that cf.fleet is deprecated as well.
|
||||
'fleet name, slug (preferred), or numeric ID',
|
||||
description: 'fleet name, slug (preferred), or numeric ID (deprecated)',
|
||||
parse: lowercaseIfSlug,
|
||||
});
|
||||
|
||||
@ -114,12 +92,14 @@ export const deviceType = flags.string({
|
||||
required: true,
|
||||
});
|
||||
|
||||
export const deviceTypeIgnored = flags.string({
|
||||
description: 'ignored - no longer required',
|
||||
char: 't',
|
||||
required: false,
|
||||
hidden: isV13(),
|
||||
});
|
||||
export const deviceTypeIgnored = isV14()
|
||||
? undefined
|
||||
: flags.string({
|
||||
description: 'ignored - no longer required',
|
||||
char: 't',
|
||||
required: false,
|
||||
hidden: true,
|
||||
});
|
||||
|
||||
export const json: IBooleanFlag<boolean> = flags.boolean({
|
||||
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;
|
||||
inlineLogs?: boolean;
|
||||
multiDockerignore: boolean;
|
||||
nogitignore: boolean; // v13: delete this line
|
||||
noParentCheck: boolean;
|
||||
projectName: string;
|
||||
projectPath: string;
|
||||
@ -63,12 +62,9 @@ export interface ComposeCliFlags {
|
||||
dockerfile?: string;
|
||||
logs: boolean;
|
||||
nologs: boolean;
|
||||
gitignore?: boolean; // v13: delete this line
|
||||
nogitignore?: boolean; // v13: delete this line
|
||||
'multi-dockerignore': boolean;
|
||||
'noparent-check': boolean;
|
||||
'registry-secrets'?: RegistrySecrets;
|
||||
'convert-eol': boolean;
|
||||
'noconvert-eol': boolean;
|
||||
projectName?: string;
|
||||
}
|
||||
@ -102,6 +98,5 @@ interface TarDirectoryOptions {
|
||||
composition?: Composition;
|
||||
convertEol?: boolean;
|
||||
multiDockerignore?: boolean;
|
||||
nogitignore: boolean; // v13: delete this line
|
||||
preFinalizeCallback?: (pack: Pack) => void | Promise<void>;
|
||||
}
|
||||
|
@ -16,21 +16,13 @@
|
||||
*/
|
||||
|
||||
import * as path from 'path';
|
||||
import { ExpectedError } from '../errors';
|
||||
import { getChalk } from './lazy';
|
||||
import { isV13 } from './version';
|
||||
|
||||
/**
|
||||
* @returns Promise<{import('./compose-types').ComposeOpts}>
|
||||
*/
|
||||
export function generateOpts(options) {
|
||||
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) => ({
|
||||
projectName: options.projectName,
|
||||
projectPath,
|
||||
@ -38,7 +30,6 @@ export function generateOpts(options) {
|
||||
convertEol: !options['noconvert-eol'],
|
||||
dockerfilePath: options.dockerfile,
|
||||
multiDockerignore: !!options['multi-dockerignore'],
|
||||
nogitignore: !options.gitignore, // v13: delete this line
|
||||
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} auth
|
||||
|
@ -43,7 +43,6 @@ import {
|
||||
import type { DeviceInfo } from './device/api';
|
||||
import { getBalenaSdk, getChalk, stripIndent } from './lazy';
|
||||
import Logger = require('./logger');
|
||||
import { isV13 } from './version';
|
||||
import { exists } from './which';
|
||||
|
||||
const allowedContractTypes = ['sw.application', 'sw.block'];
|
||||
@ -105,8 +104,6 @@ export async function applyReleaseTagKeysAndValues(
|
||||
|
||||
const LOG_LENGTH_MAX = 512 * 1024; // 512KB
|
||||
const compositionFileNames = ['docker-compose.yml', 'docker-compose.yaml'];
|
||||
const hr =
|
||||
'----------------------------------------------------------------------';
|
||||
|
||||
/**
|
||||
* high-level function resolving a project and creating a composition out
|
||||
@ -257,7 +254,6 @@ export interface BuildProjectOpts {
|
||||
inlineLogs?: boolean;
|
||||
convertEol: boolean;
|
||||
dockerfilePath?: string;
|
||||
nogitignore: boolean; // v13: delete this line
|
||||
multiDockerignore: boolean;
|
||||
}
|
||||
|
||||
@ -748,43 +744,19 @@ export function isBuildConfig(
|
||||
* 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
|
||||
* @param dir Project directory (the '--source' command line option)
|
||||
* @param param TarDirectoryOptions
|
||||
* @returns Readable stream (to be sent to the Docker Engine)
|
||||
*/
|
||||
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,
|
||||
{
|
||||
composition,
|
||||
convertEol = false,
|
||||
multiDockerignore = false,
|
||||
nogitignore = false, // v13: delete this line
|
||||
preFinalizeCallback,
|
||||
}: TarDirectoryOptions,
|
||||
): Promise<import('stream').Readable> {
|
||||
if (!isV13()) {
|
||||
require('assert').strict.equal(nogitignore, true);
|
||||
}
|
||||
const { filterFilesWithDockerignore } = await import('./ignore');
|
||||
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,
|
||||
* verify that the target docker daemon is balenaEngine. If the
|
||||
@ -1729,21 +1659,6 @@ export const composeCliFlags: flags.Input<ComposeCliFlags> = {
|
||||
description:
|
||||
'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({
|
||||
description:
|
||||
'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',
|
||||
char: 'R',
|
||||
}),
|
||||
'convert-eol': flags.boolean({
|
||||
description: 'No-op and deprecated since balena CLI v12.0.0',
|
||||
char: 'l',
|
||||
}),
|
||||
'noconvert-eol': flags.boolean({
|
||||
description:
|
||||
"Don't convert line endings from CRLF (Windows format) to LF (Unix format).",
|
||||
|
@ -59,7 +59,6 @@ export interface DeviceDeployOptions {
|
||||
registrySecrets: RegistrySecrets;
|
||||
multiDockerignore: boolean;
|
||||
nocache: boolean;
|
||||
nogitignore: boolean; // v13: delete this line
|
||||
noParentCheck: boolean;
|
||||
nolive: boolean;
|
||||
pull: boolean;
|
||||
@ -184,7 +183,6 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
|
||||
convertEol: opts.convertEol,
|
||||
dockerfilePath: opts.dockerfilePath,
|
||||
multiDockerignore: opts.multiDockerignore,
|
||||
nogitignore: opts.nogitignore, // v13: delete this line
|
||||
noParentCheck: opts.noParentCheck,
|
||||
projectName: 'local',
|
||||
projectPath: opts.source,
|
||||
@ -204,7 +202,6 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
|
||||
composition: project.composition,
|
||||
convertEol: opts.convertEol,
|
||||
multiDockerignore: opts.multiDockerignore,
|
||||
nogitignore: opts.nogitignore, // v13: delete this line
|
||||
});
|
||||
globalLogger.logDebug(`Tarring complete in ${Date.now() - tarStartTime} ms`);
|
||||
|
||||
@ -435,7 +432,6 @@ export async function rebuildSingleTask(
|
||||
composition,
|
||||
convertEol: opts.convertEol,
|
||||
multiDockerignore: opts.multiDockerignore,
|
||||
nogitignore: opts.nogitignore, // v13: delete this line
|
||||
});
|
||||
|
||||
const task = _.find(
|
||||
|
@ -151,26 +151,8 @@ export async function osProgressHandler(step: InitializeEmitter) {
|
||||
export async function getAppWithArch(
|
||||
applicationName: string,
|
||||
): Promise<ApplicationWithDeviceType & { arch: string }> {
|
||||
const app = await getApplication(applicationName);
|
||||
const { getExpanded } = await import('./pine');
|
||||
|
||||
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> = {
|
||||
const { getApplication } = await import('./sdk');
|
||||
const options: BalenaSdk.PineOptions<BalenaSdk.Application> = {
|
||||
$expand: {
|
||||
application_type: {
|
||||
$select: ['name', 'slug', 'supports_multicontainer', 'is_legacy'],
|
||||
@ -185,20 +167,20 @@ function getApplication(
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
if (match.length > 1) {
|
||||
return balena.models.application.getAppByOwner(
|
||||
match[1],
|
||||
match[0],
|
||||
extraOptions,
|
||||
) as Promise<ApplicationWithDeviceType>;
|
||||
}
|
||||
|
||||
return balena.models.application.get(
|
||||
const app = (await getApplication(
|
||||
balena,
|
||||
applicationName,
|
||||
extraOptions,
|
||||
) as Promise<ApplicationWithDeviceType>;
|
||||
options,
|
||||
)) 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
|
||||
@ -428,7 +410,7 @@ export function getProxyConfig(): ProxyConfig | undefined {
|
||||
|
||||
export const expandForAppName = {
|
||||
$expand: {
|
||||
belongs_to__application: { $select: 'app_name' },
|
||||
belongs_to__application: { $select: ['app_name', 'slug'] as any },
|
||||
is_of__device_type: { $select: 'slug' },
|
||||
is_running__release: { $select: 'commit' },
|
||||
},
|
||||
|
@ -17,183 +17,11 @@
|
||||
import * as _ from 'lodash';
|
||||
import { promises as fs, Stats } from 'fs';
|
||||
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 { 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 {
|
||||
filePath: string;
|
||||
relPath: string;
|
||||
|
@ -15,8 +15,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { isV13 } from './version';
|
||||
|
||||
export const reachingOut = `\
|
||||
For further help or support, visit:
|
||||
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),
|
||||
this file will be used instead.`;
|
||||
|
||||
const dockerignoreHelpV12 =
|
||||
'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 =
|
||||
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
|
||||
@ -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://www.npmjs.com/package/@balena/dockerignore`;
|
||||
|
||||
export const dockerignoreHelp = isV13()
|
||||
? dockerignoreHelpV13
|
||||
: dockerignoreHelpV12;
|
||||
|
||||
export const applicationIdInfo = `\
|
||||
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
|
||||
@ -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/
|
||||
\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(
|
||||
version: 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
|
||||
limitations under the License.
|
||||
*/
|
||||
import type * as BalenaSdk from 'balena-sdk';
|
||||
|
||||
import type { Application, BalenaSDK, Device, Organization } from 'balena-sdk';
|
||||
import _ = require('lodash');
|
||||
|
||||
import { instanceOf, NotLoggedInError, ExpectedError } from '../errors';
|
||||
import { getBalenaSdk, getVisuals, stripIndent, getCliForm } from './lazy';
|
||||
import validation = require('./validation');
|
||||
import { delay } from './helpers';
|
||||
import { isV13 } from './version';
|
||||
import type { Application, Device, Organization } from 'balena-sdk';
|
||||
|
||||
export function authenticate(options: {}): Promise<void> {
|
||||
const balena = getBalenaSdk();
|
||||
@ -142,11 +141,7 @@ export async function confirm(
|
||||
) {
|
||||
if (yesOption) {
|
||||
if (yesMessage) {
|
||||
if (isV13()) {
|
||||
console.error(yesMessage);
|
||||
} else {
|
||||
console.log(yesMessage);
|
||||
}
|
||||
console.log(yesMessage);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -174,7 +169,7 @@ export function selectApplication(
|
||||
throw new ExpectedError('No fleets found');
|
||||
}
|
||||
|
||||
const apps = (await balena.models.application.getAll({
|
||||
const apps = (await balena.models.application.getAllDirectlyAccessible({
|
||||
$expand: {
|
||||
is_for__device_type: {
|
||||
$select: 'slug',
|
||||
@ -326,7 +321,7 @@ export function inferOrSelectDevice(preferredUuid: string) {
|
||||
* TODO: Modify this when app IDs dropped.
|
||||
*/
|
||||
export async function getOnlineTargetDeviceUuid(
|
||||
sdk: BalenaSdk.BalenaSDK,
|
||||
sdk: BalenaSDK,
|
||||
applicationOrDevice: string,
|
||||
) {
|
||||
const logger = (await import('../utils/logger')).getLogger();
|
||||
|
@ -235,7 +235,7 @@ async function getOrSelectApplication(
|
||||
name = appName;
|
||||
}
|
||||
|
||||
const applications = (await sdk.models.application.getAll(
|
||||
const applications = (await sdk.models.application.getAllDirectlyAccessible(
|
||||
options,
|
||||
)) as ApplicationWithDeviceType[];
|
||||
|
||||
@ -273,7 +273,7 @@ async function createOrSelectApp(
|
||||
deviceType: string,
|
||||
): Promise<ApplicationWithDeviceType> {
|
||||
// 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' } },
|
||||
$filter: {
|
||||
is_for__device_type: {
|
||||
@ -322,10 +322,13 @@ async function createApplication(
|
||||
});
|
||||
|
||||
try {
|
||||
await sdk.models.application.get(appName, {
|
||||
await sdk.models.application.getDirectlyAccessible(appName, {
|
||||
$filter: {
|
||||
$or: [
|
||||
{ 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: '/' } } },
|
||||
],
|
||||
},
|
||||
|
@ -50,7 +50,6 @@ export interface RemoteBuild {
|
||||
source: string;
|
||||
auth: string;
|
||||
baseUrl: string;
|
||||
nogitignore: boolean; // v13: delete this line
|
||||
opts: BuildOpts;
|
||||
sdk: BalenaSDK;
|
||||
// For internal use
|
||||
@ -323,7 +322,6 @@ async function getTarStream(build: RemoteBuild): Promise<Stream.Readable> {
|
||||
preFinalizeCallback: preFinalizeCb,
|
||||
convertEol: build.opts.convertEol,
|
||||
multiDockerignore: build.opts.multiDockerignore,
|
||||
nogitignore: build.nogitignore, // v13: delete this line
|
||||
});
|
||||
globalLogger.logDebug(
|
||||
`Tarring complete in ${Date.now() - tarStartTime} ms`,
|
||||
|
@ -23,21 +23,36 @@ import type {
|
||||
} from 'balena-sdk';
|
||||
|
||||
/**
|
||||
* Wraps the sdk application.get method,
|
||||
* adding disambiguation in cases where the provided
|
||||
* identifier could be interpreted in multiple valid ways.
|
||||
* Get a fleet object, disambiguating the fleet identifier which may be a
|
||||
* a fleet slug, name or numeric database ID (as a string).
|
||||
* TODO: add support for fleet UUIDs.
|
||||
*/
|
||||
export async function getApplication(
|
||||
sdk: BalenaSDK,
|
||||
nameOrSlugOrId: string | number,
|
||||
options?: PineOptions<Application>,
|
||||
): Promise<Application> {
|
||||
const { looksLikeInteger } = await import('./validation');
|
||||
if (looksLikeInteger(nameOrSlugOrId as string)) {
|
||||
const { looksLikeFleetSlug, looksLikeInteger } = await import('./validation');
|
||||
if (
|
||||
typeof nameOrSlugOrId === 'string' &&
|
||||
looksLikeFleetSlug(nameOrSlugOrId)
|
||||
) {
|
||||
return await sdk.models.application.getDirectlyAccessible(
|
||||
nameOrSlugOrId,
|
||||
options,
|
||||
);
|
||||
}
|
||||
if (typeof nameOrSlugOrId === 'number' || looksLikeInteger(nameOrSlugOrId)) {
|
||||
try {
|
||||
// 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) {
|
||||
if (typeof nameOrSlugOrId === 'number') {
|
||||
throw e;
|
||||
}
|
||||
const { instanceOf } = await import('../errors');
|
||||
const { BalenaApplicationNotFound } = await import('balena-errors');
|
||||
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.
|
||||
}
|
||||
}
|
||||
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,
|
||||
* which could be one of:
|
||||
* - name (including numeric names)
|
||||
* - slug
|
||||
* - numerical id
|
||||
* 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.
|
||||
* Given a fleet name, slug or numeric database ID, return its slug.
|
||||
* This function conditionally makes an async SDK/API call to retrieve the
|
||||
* application object, which can be wasteful if the application object is
|
||||
* required before or after the call to this function. If this is the case,
|
||||
* consider calling `getApplication()` and reusing the application object.
|
||||
*/
|
||||
export async function getTypedApplicationIdentifier(
|
||||
export async function getFleetSlug(
|
||||
sdk: BalenaSDK,
|
||||
nameOrSlugOrId: string,
|
||||
) {
|
||||
const { looksLikeInteger } = await import('./validation');
|
||||
// If there's no possible ambiguity,
|
||||
// return the passed identifier unchanged
|
||||
if (!looksLikeInteger(nameOrSlugOrId)) {
|
||||
return nameOrSlugOrId;
|
||||
nameOrSlugOrId: string | number,
|
||||
): Promise<string> {
|
||||
const { looksLikeFleetSlug } = await import('./validation');
|
||||
if (
|
||||
typeof nameOrSlugOrId === 'string' &&
|
||||
looksLikeFleetSlug(nameOrSlugOrId)
|
||||
) {
|
||||
return nameOrSlugOrId.toLowerCase();
|
||||
}
|
||||
|
||||
// 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;
|
||||
return (await getApplication(sdk, nameOrSlugOrId)).slug;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -118,7 +118,6 @@ export function parseAsLocalHostnameOrIp(input: string, paramName?: string) {
|
||||
return input;
|
||||
}
|
||||
|
||||
export function looksLikeAppSlug(input: string) {
|
||||
// One or more non whitespace chars, /, 4 or more non whitespace chars
|
||||
return /[\S]+\/[\S]{4,}/.test(input);
|
||||
export function looksLikeFleetSlug(input: string) {
|
||||
return input.includes('/');
|
||||
}
|
||||
|
@ -22,12 +22,12 @@ export function isVersionGTE(v: string): boolean {
|
||||
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 */
|
||||
export function isV13(): boolean {
|
||||
if (v13 === undefined) {
|
||||
v13 = isVersionGTE('13.0.0');
|
||||
export function isV14(): boolean {
|
||||
if (v14 === undefined) {
|
||||
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",
|
||||
"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": {
|
||||
"version": "0.8.30",
|
||||
"resolved": "https://registry.npmjs.org/@types/jsonstream/-/jsonstream-0.8.30.tgz",
|
||||
@ -2807,9 +2812,9 @@
|
||||
}
|
||||
},
|
||||
"@types/uuid": {
|
||||
"version": "8.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.1.tgz",
|
||||
"integrity": "sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg=="
|
||||
"version": "8.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.3.tgz",
|
||||
"integrity": "sha512-0LbEEx1zxrYB3pgpd1M5lEhLcXjKJnYghvhTRgaBeUivLHMDM1TzF3IJ6hXU2+8uA4Xz+5BA63mtZo5DjVT8iA=="
|
||||
},
|
||||
"@types/which": {
|
||||
"version": "2.0.1",
|
||||
@ -2921,11 +2926,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
|
||||
"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": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
|
||||
@ -3675,10 +3675,54 @@
|
||||
"rimraf": "^3.0.2"
|
||||
},
|
||||
"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": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-2.1.1.tgz",
|
||||
@ -3766,6 +3836,11 @@
|
||||
"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": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
@ -3776,6 +3851,14 @@
|
||||
"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": {
|
||||
"version": "2.1.1",
|
||||
"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": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-2.1.3.tgz",
|
||||
@ -3853,9 +3941,9 @@
|
||||
}
|
||||
},
|
||||
"balena-request": {
|
||||
"version": "11.4.2",
|
||||
"resolved": "https://registry.npmjs.org/balena-request/-/balena-request-11.4.2.tgz",
|
||||
"integrity": "sha512-J4SrFBUR4AB2Y3afsX2QAMZ7H/zjysXjOyEhEqvjNTsBfe5ReCmf17vzRQ8q2URCm00nUhQgfQtlJUq6miB1/g==",
|
||||
"version": "11.5.0",
|
||||
"resolved": "https://registry.npmjs.org/balena-request/-/balena-request-11.5.0.tgz",
|
||||
"integrity": "sha512-mLf5NQU2qqGKyjZAmz8fFOzyUjwF9uptcDPUe56ADAQfLvQYML8izW7/9Q7DBRGpvlZrBZ/OnjWLK3vUs1tNdA==",
|
||||
"requires": {
|
||||
"@balena/node-web-streams": "^0.2.3",
|
||||
"balena-errors": "^4.7.1",
|
||||
@ -3867,11 +3955,12 @@
|
||||
}
|
||||
},
|
||||
"balena-sdk": {
|
||||
"version": "15.51.1",
|
||||
"resolved": "https://registry.npmjs.org/balena-sdk/-/balena-sdk-15.51.1.tgz",
|
||||
"integrity": "sha512-EMCQruytqyvpfxvjq9Zd/wWnnOAIl/Wd1majqv6hqa+z104UUTjnRCQaUji4mo8YtrFLn7aUUZFFHIKiv/3sTg==",
|
||||
"version": "16.8.0",
|
||||
"resolved": "https://registry.npmjs.org/balena-sdk/-/balena-sdk-16.8.0.tgz",
|
||||
"integrity": "sha512-kch7hhB9PksFsyVOAFyylsQMlv976V11DOwhb41w2JykVKjR9KSNFBfy1wYwYY1Wcp8PWXvj9WxtB0Sd/SW9bQ==",
|
||||
"requires": {
|
||||
"@balena/es-version": "^1.0.0",
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"@types/lodash": "^4.14.168",
|
||||
"@types/memoizee": "^0.4.5",
|
||||
"@types/node": "^10.17.55",
|
||||
@ -3881,14 +3970,13 @@
|
||||
"balena-hup-action-utils": "~4.0.2",
|
||||
"balena-pine": "^12.4.0",
|
||||
"balena-register-device": "^7.1.0",
|
||||
"balena-request": "^11.4.2",
|
||||
"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": {
|
||||
@ -3897,14 +3985,6 @@
|
||||
"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",
|
||||
@ -3990,6 +4070,39 @@
|
||||
"typed-error": "^2.0.0"
|
||||
},
|
||||
"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": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||
@ -4013,6 +4126,19 @@
|
||||
"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": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
|
||||
@ -18212,9 +18338,9 @@
|
||||
}
|
||||
},
|
||||
"web-streams-polyfill": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.1.1.tgz",
|
||||
"integrity": "sha512-Czi3fG883e96T4DLEPRvufrF2ydhOOW1+1a6c3gNjH2aIh50DNFBdfwh2AKoOf1rXvpvavAoA11Qdq9+BKjE0Q=="
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz",
|
||||
"integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA=="
|
||||
},
|
||||
"which": {
|
||||
"version": "2.0.2",
|
||||
|
@ -90,7 +90,7 @@
|
||||
"author": "Balena Inc. (https://balena.io/)",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=10.20.0 <13.0.0",
|
||||
"node": ">=12.0.0 <13.0.0",
|
||||
"npm": "<7.0.0"
|
||||
},
|
||||
"husky": {
|
||||
@ -200,7 +200,6 @@
|
||||
"@sentry/node": "^6.13.2",
|
||||
"@types/fast-levenshtein": "0.0.1",
|
||||
"@types/update-notifier": "^4.1.1",
|
||||
"@zeit/dockerignore": "0.0.3",
|
||||
"JSONStream": "^1.0.3",
|
||||
"balena-config-json": "^4.2.0",
|
||||
"balena-device-init": "^6.0.0",
|
||||
@ -209,7 +208,7 @@
|
||||
"balena-image-manager": "^7.1.1",
|
||||
"balena-preload": "^11.0.0",
|
||||
"balena-release": "^3.2.0",
|
||||
"balena-sdk": "^15.51.1",
|
||||
"balena-sdk": "^16.8.0",
|
||||
"balena-semver": "^2.3.0",
|
||||
"balena-settings-client": "^4.0.7",
|
||||
"balena-settings-storage": "^7.0.0",
|
||||
@ -241,7 +240,6 @@
|
||||
"global-tunnel-ng": "^2.1.1",
|
||||
"got": "^11.8.2",
|
||||
"humanize": "0.0.9",
|
||||
"ignore": "^5.1.8",
|
||||
"inquirer": "^7.3.3",
|
||||
"is-elevated": "^3.0.0",
|
||||
"is-root": "^2.1.0",
|
||||
|
@ -22,7 +22,6 @@ import { promises as fs } from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { stripIndent } from '../../build/utils/lazy';
|
||||
import { isV13 } from '../../build/utils/version';
|
||||
import { BalenaAPIMock } from '../nock/balena-api-mock';
|
||||
import { expectStreamNoCRLF, testDockerBuildStream } from '../docker-build';
|
||||
import { DockerMock, dockerResponsePath } from '../nock/docker-mock';
|
||||
@ -127,23 +126,14 @@ describe('balena build', function () {
|
||||
];
|
||||
if (isWindows) {
|
||||
const fname = path.join(projectPath, 'src', 'windows-crlf.sh');
|
||||
if (isWindows) {
|
||||
expectedResponseLines.push(
|
||||
`[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.',
|
||||
);
|
||||
}
|
||||
expectedResponseLines.push(
|
||||
`[Info] Converting line endings CRLF -> LF for file: ${fname}`,
|
||||
);
|
||||
}
|
||||
docker.expectGetInfo({});
|
||||
docker.expectGetManifestBusybox();
|
||||
await testDockerBuildStream({
|
||||
commandLine: `build ${projectPath} --deviceType nuc --arch amd64 ${
|
||||
isV13() ? '' : '-g'
|
||||
}`,
|
||||
commandLine: `build ${projectPath} --deviceType nuc --arch amd64`,
|
||||
dockerMock: docker,
|
||||
expectedFilesByService: { main: expectedFiles },
|
||||
expectedQueryParamsByService: {
|
||||
@ -192,16 +182,9 @@ describe('balena build', function () {
|
||||
];
|
||||
if (isWindows) {
|
||||
const fname = path.join(projectPath, 'src', 'windows-crlf.sh');
|
||||
if (isWindows) {
|
||||
expectedResponseLines.push(
|
||||
`[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.',
|
||||
);
|
||||
}
|
||||
expectedResponseLines.push(
|
||||
`[Info] Converting line endings CRLF -> LF for file: ${fname}`,
|
||||
);
|
||||
}
|
||||
docker.expectGetInfo({});
|
||||
docker.expectGetManifestBusybox();
|
||||
@ -297,9 +280,7 @@ describe('balena build', function () {
|
||||
docker.expectGetInfo({ OperatingSystem: 'balenaOS 2.44.0+rev1' });
|
||||
docker.expectGetManifestBusybox();
|
||||
await testDockerBuildStream({
|
||||
commandLine: `build ${projectPath} --emulated --deviceType ${deviceType} --arch ${arch} ${
|
||||
isV13() ? '' : '--nogitignore'
|
||||
}`,
|
||||
commandLine: `build ${projectPath} --emulated --deviceType ${deviceType} --arch ${arch}`,
|
||||
dockerMock: docker,
|
||||
expectedFilesByService: { main: expectedFiles },
|
||||
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 expectedFiles: ExpectedTarStreamFiles = {
|
||||
'src/.dockerignore': { fileSize: 16, type: 'file' },
|
||||
@ -448,9 +429,7 @@ describe('balena build', function () {
|
||||
docker.expectGetManifestNucAlpine();
|
||||
docker.expectGetManifestBusybox();
|
||||
await testDockerBuildStream({
|
||||
commandLine: `build ${projectPath} --deviceType nuc --arch amd64 --convert-eol ${
|
||||
isV13() ? '' : '-G'
|
||||
} -B COMPOSE_ARG=A -B barg=b --cache-from my/img1,my/img2`,
|
||||
commandLine: `build ${projectPath} --deviceType nuc --arch amd64 -B COMPOSE_ARG=A -B barg=b --cache-from my/img1,my/img2`,
|
||||
dockerMock: docker,
|
||||
expectedFilesByService,
|
||||
expectedQueryParamsByService,
|
||||
@ -533,7 +512,7 @@ describe('balena build', function () {
|
||||
docker.expectGetManifestNucAlpine();
|
||||
|
||||
await testDockerBuildStream({
|
||||
commandLine: `build ${projectPath} --deviceType nuc --arch amd64 --convert-eol -m`,
|
||||
commandLine: `build ${projectPath} --deviceType nuc --arch amd64 -m`,
|
||||
dockerMock: docker,
|
||||
expectedFilesByService,
|
||||
expectedQueryParamsByService,
|
||||
@ -618,7 +597,7 @@ describe('balena build', function () {
|
||||
docker.expectGetManifestNucAlpine();
|
||||
|
||||
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,
|
||||
expectedFilesByService,
|
||||
expectedQueryParamsByService,
|
||||
|
@ -23,7 +23,6 @@ import * as nock from 'nock';
|
||||
import * as path from 'path';
|
||||
import * as sinon from 'sinon';
|
||||
|
||||
import { isV13 } from '../../build/utils/version';
|
||||
import { BalenaAPIMock } from '../nock/balena-api-mock';
|
||||
import { expectStreamNoCRLF, testDockerBuildStream } from '../docker-build';
|
||||
import { DockerMock, dockerResponsePath } from '../nock/docker-mock';
|
||||
@ -149,9 +148,7 @@ describe('balena deploy', function () {
|
||||
docker.expectGetManifestBusybox();
|
||||
|
||||
await testDockerBuildStream({
|
||||
commandLine: `deploy testApp --build --source ${projectPath} ${
|
||||
isV13() ? '' : '-G'
|
||||
}`,
|
||||
commandLine: `deploy testApp --build --source ${projectPath}`,
|
||||
dockerMock: docker,
|
||||
expectedFilesByService: { main: expectedFiles },
|
||||
expectedQueryParamsByService: { main: commonQueryParams },
|
||||
@ -315,9 +312,7 @@ describe('balena deploy', function () {
|
||||
sinon.stub(process, 'exit');
|
||||
|
||||
await testDockerBuildStream({
|
||||
commandLine: `deploy testApp --build --source ${projectPath} --noconvert-eol ${
|
||||
isV13() ? '' : '-G'
|
||||
}`,
|
||||
commandLine: `deploy testApp --build --source ${projectPath} --noconvert-eol`,
|
||||
dockerMock: docker,
|
||||
expectedFilesByService: { main: expectedFiles },
|
||||
expectedQueryParamsByService: { main: commonQueryParams },
|
||||
|
@ -21,9 +21,6 @@ import * as path from 'path';
|
||||
import { apiResponsePath, BalenaAPIMock } from '../../nock/balena-api-mock';
|
||||
import { cleanOutput, runCommand } from '../../helpers';
|
||||
|
||||
import { appToFleetOutputMsg, warnify } from '../../../build/utils/messages';
|
||||
import { isV13 } from '../../../build/utils/version';
|
||||
|
||||
describe('balena device', function () {
|
||||
let api: BalenaAPIMock;
|
||||
|
||||
@ -38,13 +35,6 @@ describe('balena device', function () {
|
||||
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 () => {
|
||||
const { out, err } = await runCommand('device');
|
||||
const errLines = cleanOutput(err);
|
||||
@ -57,27 +47,25 @@ describe('balena device', function () {
|
||||
it('should list device details for provided uuid', async () => {
|
||||
api.scope
|
||||
.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'), {
|
||||
'Content-Type': 'application/json',
|
||||
});
|
||||
|
||||
const { out, err } = await runCommand('device 27fda508c');
|
||||
const { out } = await runCommand('device 27fda508c');
|
||||
|
||||
const lines = cleanOutput(out);
|
||||
|
||||
expect(lines).to.have.lengthOf(25);
|
||||
expect(lines[0]).to.equal('== SPARKLING WOOD');
|
||||
expect(lines[6].split(':')[1].trim()).to.equal('test app');
|
||||
|
||||
expect(err.join('')).to.eql(expectedWarn);
|
||||
expect(lines[6].split(':')[1].trim()).to.equal('org/test app');
|
||||
});
|
||||
|
||||
it.skip('correctly handles devices with missing fields', async () => {
|
||||
api.scope
|
||||
.get(
|
||||
/^\/v6\/device\?.+&\$expand=belongs_to__application\(\$select=app_name\)/,
|
||||
/^\/v6\/device\?.+&\$expand=belongs_to__application\(\$select=app_name,slug\)/,
|
||||
)
|
||||
.replyWithFile(
|
||||
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);
|
||||
|
||||
expect(lines).to.have.lengthOf(14);
|
||||
expect(lines[0]).to.equal('== SPARKLING WOOD');
|
||||
expect(lines[6].split(':')[1].trim()).to.equal('test app');
|
||||
|
||||
expect(err.join('')).to.eql(expectedWarn);
|
||||
expect(lines[6].split(':')[1].trim()).to.equal('org/test app');
|
||||
});
|
||||
|
||||
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.
|
||||
api.scope
|
||||
.get(
|
||||
/^\/v6\/device\?.+&\$expand=belongs_to__application\(\$select=app_name\)/,
|
||||
/^\/v6\/device\?.+&\$expand=belongs_to__application\(\$select=app_name,slug\)/,
|
||||
)
|
||||
.replyWithFile(
|
||||
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);
|
||||
|
||||
expect(lines).to.have.lengthOf(25);
|
||||
expect(lines[0]).to.equal('== SPARKLING WOOD');
|
||||
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 { cleanOutput, runCommand } from '../../helpers';
|
||||
|
||||
import { appToFleetOutputMsg, warnify } from '../../../build/utils/messages';
|
||||
import { isV13 } from '../../../build/utils/version';
|
||||
|
||||
describe('balena devices', function () {
|
||||
let api: BalenaAPIMock;
|
||||
|
||||
@ -38,39 +35,28 @@ describe('balena devices', function () {
|
||||
api.done();
|
||||
});
|
||||
|
||||
const expectedWarn =
|
||||
!isV13() &&
|
||||
process.stderr.isTTY &&
|
||||
process.env.BALENA_CLI_TEST_TYPE !== 'standalone'
|
||||
? warnify(appToFleetOutputMsg) + '\n'
|
||||
: '';
|
||||
|
||||
it('should list devices from own and collaborator apps', async () => {
|
||||
api.scope
|
||||
.get(
|
||||
'/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'), {
|
||||
'Content-Type': 'application/json',
|
||||
});
|
||||
|
||||
const { out, err } = await runCommand('devices');
|
||||
const { out } = await runCommand('devices');
|
||||
|
||||
const lines = cleanOutput(out);
|
||||
|
||||
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 APPLICATION NAME 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',
|
||||
);
|
||||
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`.
|
||||
// e.g. When user has a device associated with app that user is no longer a collaborator of.
|
||||
expect(lines.some((l) => l.includes('N/a'))).to.be.true;
|
||||
|
||||
expect(err.join('')).to.eql(expectedWarn);
|
||||
});
|
||||
});
|
||||
|
@ -20,8 +20,6 @@ import { expect } from 'chai';
|
||||
import { BalenaAPIMock } from '../../nock/balena-api-mock';
|
||||
import { cleanOutput, runCommand } from '../../helpers';
|
||||
|
||||
import { isV13 } from '../../../build/utils/version';
|
||||
|
||||
describe('balena devices supported', function () {
|
||||
let api: BalenaAPIMock;
|
||||
|
||||
@ -48,34 +46,16 @@ describe('balena devices supported', function () {
|
||||
api.expectGetDeviceTypes();
|
||||
api.expectGetConfigDeviceTypes();
|
||||
|
||||
const { out, err } = await runCommand('devices supported -v');
|
||||
const { out, err } = await runCommand('devices supported');
|
||||
|
||||
const lines = cleanOutput(out, true);
|
||||
|
||||
expect(lines[0]).to.equal(
|
||||
isV13() ? 'SLUG ALIASES ARCH NAME' : 'SLUG ALIASES ARCH STATE NAME',
|
||||
);
|
||||
expect(lines[0]).to.equal('SLUG ALIASES ARCH NAME');
|
||||
expect(lines).to.have.lengthOf.at.least(2);
|
||||
expect(lines).to.contain('intel-nuc nuc amd64 Intel NUC');
|
||||
expect(lines).to.contain(
|
||||
isV13()
|
||||
? 'intel-nuc nuc amd64 Intel NUC'
|
||||
: 'intel-nuc nuc amd64 RELEASED Intel NUC',
|
||||
'odroid-xu4 odroid-ux3, odroid-u3+ armv7hf ODROID-XU4',
|
||||
);
|
||||
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([]);
|
||||
});
|
||||
});
|
||||
|
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 { runCommand } from '../../helpers';
|
||||
|
||||
import { appToFleetOutputMsg, warnify } from '../../../build/utils/messages';
|
||||
import { isV13 } from '../../../build/utils/version';
|
||||
|
||||
describe('balena envs', function () {
|
||||
const appName = 'test';
|
||||
let fullUUID: string;
|
||||
@ -44,13 +41,6 @@ describe('balena envs', function () {
|
||||
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 () => {
|
||||
api.expectGetApplication();
|
||||
api.expectGetAppEnvVars();
|
||||
@ -60,11 +50,11 @@ describe('balena envs', function () {
|
||||
|
||||
expect(out.join('')).to.equal(
|
||||
stripIndent`
|
||||
ID NAME VALUE FLEET SERVICE
|
||||
120110 svar1 svar1-value test service1
|
||||
120111 svar2 svar2-value test service2
|
||||
120101 var1 var1-val test *
|
||||
120102 var2 22 test *
|
||||
ID NAME VALUE FLEET SERVICE
|
||||
120110 svar1 svar1-value gh_user/testApp service1
|
||||
120111 svar2 svar2-value gh_user/testApp service2
|
||||
120101 var1 var1-val gh_user/testApp *
|
||||
120102 var2 22 gh_user/testApp *
|
||||
` + '\n',
|
||||
);
|
||||
expect(err.join('')).to.equal('');
|
||||
@ -79,7 +69,7 @@ describe('balena envs', function () {
|
||||
expect(out.join('')).to.equal(
|
||||
stripIndent`
|
||||
ID NAME VALUE FLEET
|
||||
120300 RESIN_SUPERVISOR_NATIVE_LOGGER false test
|
||||
120300 RESIN_SUPERVISOR_NATIVE_LOGGER false gh_user/testApp
|
||||
` + '\n',
|
||||
);
|
||||
|
||||
@ -94,7 +84,7 @@ describe('balena envs', function () {
|
||||
|
||||
expect(JSON.parse(out.join(''))).to.deep.equal([
|
||||
{
|
||||
fleetName: 'test',
|
||||
fleet: 'gh_user/testApp',
|
||||
id: 120300,
|
||||
name: 'RESIN_SUPERVISOR_NATIVE_LOGGER',
|
||||
value: 'false',
|
||||
@ -116,10 +106,10 @@ describe('balena envs', function () {
|
||||
|
||||
expect(out.join('')).to.equal(
|
||||
stripIndent`
|
||||
ID NAME VALUE FLEET SERVICE
|
||||
120111 svar2 svar2-value test service2
|
||||
120101 var1 var1-val test *
|
||||
120102 var2 22 test *
|
||||
ID NAME VALUE FLEET SERVICE
|
||||
120111 svar2 svar2-value gh_user/testApp service2
|
||||
120101 var1 var1-val gh_user/testApp *
|
||||
120102 var2 22 gh_user/testApp *
|
||||
` + '\n',
|
||||
);
|
||||
expect(err.join('')).to.equal('');
|
||||
@ -138,10 +128,10 @@ describe('balena envs', function () {
|
||||
|
||||
expect(out.join('')).to.equal(
|
||||
stripIndent`
|
||||
ID NAME VALUE FLEET SERVICE
|
||||
120110 svar1 svar1-value test ${serviceName}
|
||||
120101 var1 var1-val test *
|
||||
120102 var2 22 test *
|
||||
ID NAME VALUE FLEET SERVICE
|
||||
120110 svar1 svar1-value gh_user/testApp ${serviceName}
|
||||
120101 var1 var1-val gh_user/testApp *
|
||||
120102 var2 22 gh_user/testApp *
|
||||
` + '\n',
|
||||
);
|
||||
expect(err.join('')).to.equal('');
|
||||
@ -157,29 +147,24 @@ describe('balena envs', function () {
|
||||
|
||||
const uuid = shortUUID;
|
||||
const result = await runCommand(`envs -d ${uuid}`);
|
||||
const { err } = result;
|
||||
let { out } = result;
|
||||
let expected =
|
||||
stripIndent`
|
||||
ID NAME VALUE APPLICATION DEVICE SERVICE
|
||||
120110 svar1 svar1-value test * service1
|
||||
120111 svar2 svar2-value test * service2
|
||||
120120 svar3 svar3-value test ${uuid} service1
|
||||
120121 svar4 svar4-value test ${uuid} service2
|
||||
120101 var1 var1-val test * *
|
||||
120102 var2 22 test * *
|
||||
120203 var3 var3-val test ${uuid} *
|
||||
120204 var4 44 test ${uuid} *
|
||||
ID NAME VALUE FLEET DEVICE SERVICE
|
||||
120110 svar1 svar1-value org/test * service1
|
||||
120111 svar2 svar2-value org/test * service2
|
||||
120120 svar3 svar3-value org/test ${uuid} service1
|
||||
120121 svar4 svar4-value org/test ${uuid} service2
|
||||
120101 var1 var1-val org/test * *
|
||||
120102 var2 22 org/test * *
|
||||
120203 var3 var3-val org/test ${uuid} *
|
||||
120204 var4 44 org/test ${uuid} *
|
||||
` + '\n';
|
||||
if (isV13()) {
|
||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||
expected = expected
|
||||
.replace(/ +/g, ' ')
|
||||
.replace(' APPLICATION ', ' FLEET ')
|
||||
.replace(/ test /g, ' org/test ');
|
||||
}
|
||||
|
||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||
expected = expected.replace(/ +/g, ' ');
|
||||
|
||||
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 () => {
|
||||
@ -191,22 +176,17 @@ describe('balena envs', function () {
|
||||
api.expectGetDeviceServiceVars();
|
||||
|
||||
const { out, err } = await runCommand(`envs -jd ${shortUUID}`);
|
||||
let expected = `[
|
||||
{ "id": 120101, "appName": "test", "deviceUUID": "*", "name": "var1", "value": "var1-val", "serviceName": "*" },
|
||||
{ "id": 120102, "appName": "test", "deviceUUID": "*", "name": "var2", "value": "22", "serviceName": "*" },
|
||||
{ "id": 120110, "appName": "test", "deviceUUID": "*", "name": "svar1", "value": "svar1-value", "serviceName": "service1" },
|
||||
{ "id": 120111, "appName": "test", "deviceUUID": "*", "name": "svar2", "value": "svar2-value", "serviceName": "service2" },
|
||||
{ "id": 120120, "appName": "test", "deviceUUID": "${fullUUID}", "name": "svar3", "value": "svar3-value", "serviceName": "service1" },
|
||||
{ "id": 120121, "appName": "test", "deviceUUID": "${fullUUID}", "name": "svar4", "value": "svar4-value", "serviceName": "service2" },
|
||||
{ "id": 120203, "appName": "test", "deviceUUID": "${fullUUID}", "name": "var3", "value": "var3-val", "serviceName": "*" },
|
||||
{ "id": 120204, "appName": "test", "deviceUUID": "${fullUUID}", "name": "var4", "value": "44", "serviceName": "*" }
|
||||
const expected = `[
|
||||
{ "id": 120101, "fleet": "org/test", "deviceUUID": "*", "name": "var1", "value": "var1-val", "serviceName": "*" },
|
||||
{ "id": 120102, "fleet": "org/test", "deviceUUID": "*", "name": "var2", "value": "22", "serviceName": "*" },
|
||||
{ "id": 120110, "fleet": "org/test", "deviceUUID": "*", "name": "svar1", "value": "svar1-value", "serviceName": "service1" },
|
||||
{ "id": 120111, "fleet": "org/test", "deviceUUID": "*", "name": "svar2", "value": "svar2-value", "serviceName": "service2" },
|
||||
{ "id": 120120, "fleet": "org/test", "deviceUUID": "${fullUUID}", "name": "svar3", "value": "svar3-value", "serviceName": "service1" },
|
||||
{ "id": 120121, "fleet": "org/test", "deviceUUID": "${fullUUID}", "name": "svar4", "value": "svar4-value", "serviceName": "service2" },
|
||||
{ "id": 120203, "fleet": "org/test", "deviceUUID": "${fullUUID}", "name": "var3", "value": "var3-val", "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(err.join('')).to.equal('');
|
||||
});
|
||||
@ -218,23 +198,18 @@ describe('balena envs', function () {
|
||||
api.expectGetAppConfigVars();
|
||||
|
||||
const result = await runCommand(`envs -d ${shortUUID} --config`);
|
||||
const { err } = result;
|
||||
let { out } = result;
|
||||
let expected =
|
||||
stripIndent`
|
||||
ID NAME VALUE APPLICATION DEVICE
|
||||
120300 RESIN_SUPERVISOR_NATIVE_LOGGER false test *
|
||||
120400 RESIN_SUPERVISOR_POLL_INTERVAL 900900 test ${shortUUID}
|
||||
ID NAME VALUE FLEET DEVICE
|
||||
120300 RESIN_SUPERVISOR_NATIVE_LOGGER false org/test *
|
||||
120400 RESIN_SUPERVISOR_POLL_INTERVAL 900900 org/test ${shortUUID}
|
||||
` + '\n';
|
||||
if (isV13()) {
|
||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||
expected = expected
|
||||
.replace(/ +/g, ' ')
|
||||
.replace(' APPLICATION ', ' FLEET ')
|
||||
.replace(/ test /g, ' org/test ');
|
||||
}
|
||||
|
||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||
expected = expected.replace(/ +/g, ' ');
|
||||
|
||||
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 () => {
|
||||
@ -249,27 +224,22 @@ describe('balena envs', function () {
|
||||
|
||||
const uuid = shortUUID;
|
||||
const result = await runCommand(`envs -d ${uuid} -s ${serviceName}`);
|
||||
const { err } = result;
|
||||
let { out } = result;
|
||||
let expected =
|
||||
stripIndent`
|
||||
ID NAME VALUE APPLICATION DEVICE SERVICE
|
||||
120111 svar2 svar2-value test * service2
|
||||
120121 svar4 svar4-value test ${uuid} service2
|
||||
120101 var1 var1-val test * *
|
||||
120102 var2 22 test * *
|
||||
120203 var3 var3-val test ${uuid} *
|
||||
120204 var4 44 test ${uuid} *
|
||||
ID NAME VALUE FLEET DEVICE SERVICE
|
||||
120111 svar2 svar2-value org/test * service2
|
||||
120121 svar4 svar4-value org/test ${uuid} service2
|
||||
120101 var1 var1-val org/test * *
|
||||
120102 var2 22 org/test * *
|
||||
120203 var3 var3-val org/test ${uuid} *
|
||||
120204 var4 44 org/test ${uuid} *
|
||||
` + '\n';
|
||||
if (isV13()) {
|
||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||
expected = expected
|
||||
.replace(/ +/g, ' ')
|
||||
.replace(' APPLICATION ', ' FLEET ')
|
||||
.replace(/ test /g, ' org/test ');
|
||||
}
|
||||
|
||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||
expected = expected.replace(/ +/g, ' ');
|
||||
|
||||
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 () => {
|
||||
@ -279,24 +249,20 @@ describe('balena envs', function () {
|
||||
|
||||
const uuid = shortUUID;
|
||||
const result = await runCommand(`envs -d ${uuid}`);
|
||||
const { err } = result;
|
||||
let { out } = result;
|
||||
let expected =
|
||||
stripIndent`
|
||||
ID NAME VALUE APPLICATION DEVICE SERVICE
|
||||
120120 svar3 svar3-value N/A ${uuid} service1
|
||||
120121 svar4 svar4-value N/A ${uuid} service2
|
||||
120203 var3 var3-val N/A ${uuid} *
|
||||
120204 var4 44 N/A ${uuid} *
|
||||
ID NAME VALUE FLEET DEVICE SERVICE
|
||||
120120 svar3 svar3-value N/A ${uuid} service1
|
||||
120121 svar4 svar4-value N/A ${uuid} service2
|
||||
120203 var3 var3-val N/A ${uuid} *
|
||||
120204 var4 44 N/A ${uuid} *
|
||||
` + '\n';
|
||||
if (isV13()) {
|
||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||
expected = expected
|
||||
.replace(/ +/g, ' ')
|
||||
.replace(' APPLICATION ', ' FLEET ');
|
||||
}
|
||||
|
||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||
expected = expected.replace(/ +/g, ' ');
|
||||
|
||||
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 () => {
|
||||
@ -311,27 +277,22 @@ describe('balena envs', function () {
|
||||
|
||||
const uuid = shortUUID;
|
||||
const result = await runCommand(`envs -d ${uuid} -s ${serviceName}`);
|
||||
const { err } = result;
|
||||
let { out } = result;
|
||||
let expected =
|
||||
stripIndent`
|
||||
ID NAME VALUE APPLICATION DEVICE SERVICE
|
||||
120110 svar1 svar1-value test * ${serviceName}
|
||||
120120 svar3 svar3-value test ${uuid} ${serviceName}
|
||||
120101 var1 var1-val test * *
|
||||
120102 var2 22 test * *
|
||||
120203 var3 var3-val test ${uuid} *
|
||||
120204 var4 44 test ${uuid} *
|
||||
ID NAME VALUE FLEET DEVICE SERVICE
|
||||
120110 svar1 svar1-value org/test * ${serviceName}
|
||||
120120 svar3 svar3-value org/test ${uuid} ${serviceName}
|
||||
120101 var1 var1-val org/test * *
|
||||
120102 var2 22 org/test * *
|
||||
120203 var3 var3-val org/test ${uuid} *
|
||||
120204 var4 44 org/test ${uuid} *
|
||||
` + '\n';
|
||||
if (isV13()) {
|
||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||
expected = expected
|
||||
.replace(/ +/g, ' ')
|
||||
.replace(' APPLICATION ', ' FLEET ')
|
||||
.replace(/ test /g, ' org/test ');
|
||||
}
|
||||
|
||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||
expected = expected.replace(/ +/g, ' ');
|
||||
|
||||
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 () => {
|
||||
@ -347,20 +308,15 @@ describe('balena envs', function () {
|
||||
const { out, err } = await runCommand(
|
||||
`envs -d ${shortUUID} -js ${serviceName}`,
|
||||
);
|
||||
let expected = `[
|
||||
{ "id": 120101, "appName": "test", "deviceUUID": "*", "name": "var1", "value": "var1-val", "serviceName": "*" },
|
||||
{ "id": 120102, "appName": "test", "deviceUUID": "*", "name": "var2", "value": "22", "serviceName": "*" },
|
||||
{ "id": 120110, "appName": "test", "deviceUUID": "*", "name": "svar1", "value": "svar1-value", "serviceName": "${serviceName}" },
|
||||
{ "id": 120120, "appName": "test", "deviceUUID": "${fullUUID}", "name": "svar3", "value": "svar3-value", "serviceName": "${serviceName}" },
|
||||
{ "id": 120203, "appName": "test", "deviceUUID": "${fullUUID}", "name": "var3", "value": "var3-val", "serviceName": "*" },
|
||||
{ "id": 120204, "appName": "test", "deviceUUID": "${fullUUID}", "name": "var4", "value": "44", "serviceName": "*" }
|
||||
const expected = `[
|
||||
{ "id": 120101, "fleet": "org/test", "deviceUUID": "*", "name": "var1", "value": "var1-val", "serviceName": "*" },
|
||||
{ "id": 120102, "fleet": "org/test", "deviceUUID": "*", "name": "var2", "value": "22", "serviceName": "*" },
|
||||
{ "id": 120110, "fleet": "org/test", "deviceUUID": "*", "name": "svar1", "value": "svar1-value", "serviceName": "${serviceName}" },
|
||||
{ "id": 120120, "fleet": "org/test", "deviceUUID": "${fullUUID}", "name": "svar3", "value": "svar3-value", "serviceName": "${serviceName}" },
|
||||
{ "id": 120203, "fleet": "org/test", "deviceUUID": "${fullUUID}", "name": "var3", "value": "var3-val", "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(err.join('')).to.equal('');
|
||||
});
|
||||
|
@ -19,7 +19,6 @@ import { expect } from 'chai';
|
||||
import { promises as fs } from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { isV13 } from '../../build/utils/version';
|
||||
import { BalenaAPIMock } from '../nock/balena-api-mock';
|
||||
import { BuilderMock, builderResponsePath } from '../nock/builder-mock';
|
||||
import { expectStreamNoCRLF, testPushBuildStream } from '../docker-build';
|
||||
@ -37,7 +36,6 @@ import {
|
||||
const repoPath = path.normalize(path.join(__dirname, '..', '..'));
|
||||
const projectsPath = path.join(repoPath, 'tests', 'test-data', 'projects');
|
||||
|
||||
const itNoV13 = isV13() ? it.skip : it;
|
||||
const itNoWin = process.platform === 'win32' ? it.skip : it;
|
||||
|
||||
const commonResponseLines = {
|
||||
@ -82,9 +80,6 @@ const commonQueryParams = [
|
||||
['isdraft', 'false'],
|
||||
];
|
||||
|
||||
const hr =
|
||||
'----------------------------------------------------------------------';
|
||||
|
||||
describe('balena push', function () {
|
||||
let api: BalenaAPIMock;
|
||||
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 expectedFiles: ExpectedTarStreamFiles = {
|
||||
'src/.dockerignore': { fileSize: 16, type: 'file' },
|
||||
@ -240,76 +235,7 @@ describe('balena push', function () {
|
||||
});
|
||||
});
|
||||
|
||||
itNoV13(
|
||||
'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 () => {
|
||||
it('should create the expected tar stream (single container, dockerignore1)', async () => {
|
||||
const projectPath = path.join(
|
||||
projectsPath,
|
||||
'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
|
||||
// `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
|
||||
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(
|
||||
projectsPath,
|
||||
'no-docker-compose',
|
||||
@ -366,12 +292,6 @@ describe('balena push', function () {
|
||||
'lib/src-b.txt': { fileSize: 5, type: 'file' },
|
||||
'src/src-b.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 responseFilename = 'build-POST-v3.json';
|
||||
@ -379,27 +299,11 @@ describe('balena push', function () {
|
||||
path.join(builderResponsePath, responseFilename),
|
||||
'utf8',
|
||||
);
|
||||
const expectedResponseLines =
|
||||
!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];
|
||||
const expectedResponseLines = commonResponseLines[responseFilename];
|
||||
|
||||
await testPushBuildStream({
|
||||
builderMock: builder,
|
||||
commandLine: `push testApp -s ${projectPath} -R ${regSecretsPath} ${
|
||||
isV13() ? '' : '--gitignore'
|
||||
}`,
|
||||
commandLine: `push testApp -s ${projectPath} -R ${regSecretsPath}`,
|
||||
expectedFiles,
|
||||
expectedQueryParams: commonQueryParams,
|
||||
expectedResponseLines,
|
||||
|
@ -4,6 +4,7 @@
|
||||
"belongs_to__application": [
|
||||
{
|
||||
"app_name": "test app",
|
||||
"slug": "org/test app",
|
||||
"__metadata": {}
|
||||
}
|
||||
],
|
||||
|
@ -4,6 +4,7 @@
|
||||
"belongs_to__application": [
|
||||
{
|
||||
"app_name": "test app",
|
||||
"slug": "org/test app",
|
||||
"__metadata": {}
|
||||
}
|
||||
],
|
||||
|
@ -4,6 +4,7 @@
|
||||
"belongs_to__application": [
|
||||
{
|
||||
"app_name": "test app",
|
||||
"slug": "org/test app",
|
||||
"__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 () {
|
||||
const extraContent = 'extra';
|
||||
const extraEntry: tar.Headers = {
|
||||
@ -82,7 +80,6 @@ describe('compare new and old tarDirectory implementations', function () {
|
||||
|
||||
const tarPack = await tarDirectory(dockerignoreProjDir, {
|
||||
preFinalizeCallback,
|
||||
nogitignore: true,
|
||||
});
|
||||
const fileList = await getTarPackFiles(tarPack);
|
||||
|
||||
@ -105,67 +102,11 @@ describe('compare new and old tarDirectory implementations', function () {
|
||||
'symlink-a.txt': { fileSize: 5, type: 'file' },
|
||||
};
|
||||
|
||||
const tarPack = await tarDirectory(projectPath, { nogitignore: true });
|
||||
const tarPack = await tarDirectory(projectPath, {});
|
||||
const fileList = await getTarPackFiles(tarPack);
|
||||
|
||||
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(
|
||||
|
Loading…
Reference in New Issue
Block a user