mirror of
https://github.com/balena-io/balena-cli.git
synced 2025-06-24 18:45:07 +00:00
Compare commits
17 Commits
v13.1.8
...
output-fra
Author | SHA1 | Date | |
---|---|---|---|
ab1d8aa6ba | |||
d2330f9ed1 | |||
cc19b00998 | |||
ed5ac75a10 | |||
465b8a1b5e | |||
eccadbdcb9 | |||
31eb734af1 | |||
fa7b59d64f | |||
1e42bfa0d5 | |||
5464e550e7 | |||
c0f27a663d | |||
d1c61c62ab | |||
a9691bff57 | |||
f5d09a43cd | |||
d11e547e11 | |||
bd462aee02 | |||
f633c0468b |
File diff suppressed because it is too large
Load Diff
241
CHANGELOG.md
241
CHANGELOG.md
@ -4,6 +4,247 @@ All notable changes to this project will be documented in this file
|
||||
automatically by Versionist. DO NOT EDIT THIS FILE MANUALLY!
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## 13.1.13 - 2022-02-10
|
||||
|
||||
* Drop unused awaitDevice utility function [Lucian Buzzo]
|
||||
|
||||
## 13.1.12 - 2022-02-09
|
||||
|
||||
|
||||
<details>
|
||||
<summary> preload: Stop using the deprecated /device-types/v1 API endpoints [Thodoris Greasidis] </summary>
|
||||
|
||||
> ### balena-preload-12.0.0 - 2022-01-27
|
||||
>
|
||||
> * Improve types [Thodoris Greasidis]
|
||||
> * Stop relying on the /device-types/v1 endpoints [Thodoris Greasidis]
|
||||
> * Bump TypeScript to v4.5 [Thodoris Greasidis]
|
||||
>
|
||||
> <details>
|
||||
> <summary> Bump balena-sdk to v16 [Thodoris Greasidis] </summary>
|
||||
>
|
||||
>> #### balena-sdk-16.0.0 - 2021-11-28
|
||||
>>
|
||||
>> * **BREAKING**: Merge the hostApp model into the OS model [Thodoris Greasidis]
|
||||
>> * **BREAKING** Drop os.getSupportedVersions() method in favor of hostapp.getAvailableOsVersions() [Thodoris Greasidis]
|
||||
>> * os.getMaxSatisfyingVersion: Add optional param to choose OS line type [Thodoris Greasidis]
|
||||
>> * os.getMaxSatisfyingVersion: Include ESR versions [Thodoris Greasidis]
|
||||
>> * os.getMaxSatisfyingVersion: Switch to use hostApps [Thodoris Greasidis]
|
||||
>> * hostapp.getAvailableOsVersions: Add single device type argument overload [Thodoris Greasidis]
|
||||
>> * hostapp.getAllOsVersions: Add single device type argument overload [Thodoris Greasidis]
|
||||
>> * models.hostapp: Add a getAvailableOsVersions() convenience method [Thodoris Greasidis]
|
||||
>> * Support optional extra PineOptions in hostapp.getAllOsVersions() [Thodoris Greasidis]
|
||||
>> * **BREAKING** Include invalidated versions in hostapp.getAllOsVersions() [Thodoris Greasidis]
|
||||
>> * models/application: Add getDirectlyAccessible & getAllDirectlyAccessible [Thodoris Greasidis]
|
||||
>> * application.get: Add 'directly_accessible' convenience filter param [Thodoris Greasidis]
|
||||
>> * application.getAll: Add 'directly_accessible' convenience filter param [Thodoris Greasidis]
|
||||
>> * **BREAKING** Change application.getAll to include public apps [Thodoris Greasidis]
|
||||
>> * **BREAKING** Drop targeting/retrieving apps by name in favor of slugs [Thodoris Greasidis]
|
||||
>> * Bump minimum supported Typescript to v4.5.2 [Thodoris Greasidis]
|
||||
>> * **BREAKING**: Stop actively supporting node 10 [Thodoris Greasidis]
|
||||
>> * **BREAKING** Drop application.getAllWithDeviceServiceDetails() [Thodoris Greasidis]
|
||||
>> * **BREAKING** Change apiKey.getAll() to return all key variants [Thodoris Greasidis]
|
||||
>> * types: Drop is_in_local_mode from the Device model [Thodoris Greasidis]
|
||||
>> * types: Drop user__is_member_of__application in favor of the term form [Thodoris Greasidis]
|
||||
>> * typings: Drop Subscription's discounts__plan_addon property [Thodoris Greasidis]
|
||||
>> * typings: Stop extending the JWTUser type in the User model [Thodoris Greasidis]
|
||||
>> * models/config: Change the BETA device type state to NEW [Thodoris Greasidis]
|
||||
>> * typings: Drop the PineWithSelectOnGet type [Thodoris Greasidis]
|
||||
>> * Remove my_application from the supported resources [Thodoris Greasidis]
|
||||
>> * typings: Properly type some Device properties [Thodoris Greasidis]
|
||||
>> * typings: Drop the DeviceWithImageInstalls type [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.59.2 - 2021-11-28
|
||||
>>
|
||||
>>
|
||||
>> <details>
|
||||
>> <summary> Update balena-request to 11.5.0 [Thodoris Greasidis] </summary>
|
||||
>>
|
||||
>>> ##### balena-request-11.5.0 - 2021-11-28
|
||||
>>>
|
||||
>>> * Convert tests to JavaScript and drop coffeescript [Thodoris Greasidis]
|
||||
>>> * Fix the jsdoc generation [Thodoris Greasidis]
|
||||
>>> * Convert to typescript and publish typings [Thodoris Greasidis]
|
||||
>>>
|
||||
>> </details>
|
||||
>>
|
||||
>>
|
||||
>> #### balena-sdk-15.59.1 - 2021-11-28
|
||||
>>
|
||||
>> * Fix the typings of the Image contract field [Thodoris Greasidis]
|
||||
>> * Fix the typings for the Release contract field [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.59.0 - 2021-11-24
|
||||
>>
|
||||
>> * Add release setIsInvalidated function [Matthew Yarmolinsky]
|
||||
>>
|
||||
>> #### balena-sdk-15.58.1 - 2021-11-17
|
||||
>>
|
||||
>> * Update typescript to 4.5.2 [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.58.0 - 2021-11-16
|
||||
>>
|
||||
>> * models/release: Add note() method [Thodoris Greasidis]
|
||||
>> * typings: Add the release.invalidation_reason property [Thodoris Greasidis]
|
||||
>> * typings: Add the release.note property [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.57.2 - 2021-11-15
|
||||
>>
|
||||
>> * tests/logs: Increase the wait time for retrieving the subscribed logs [Thodoris Greasidis]
|
||||
>> * tests/logs: Refactor to async-await [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.57.1 - 2021-11-11
|
||||
>>
|
||||
>> * typings: Fix $filters for resources with non numeric ids [Thodoris Greasidis]
|
||||
>> * typings: Add application.can_use__application_as_host ReverseNavigation [Thodoris Greasidis]
|
||||
>> * Add missing apiKey.getDeviceApiKeysByDevice docs [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.57.0 - 2021-11-05
|
||||
>>
|
||||
>> * models/api-key: Change update() & revoke() to work with all key variants [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.56.0 - 2021-11-04
|
||||
>>
|
||||
>> * models/apiKey: Add getDeviceApiKeysByDevice() method [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.55.0 - 2021-11-01
|
||||
>>
|
||||
>> * typings: Add the release.raw_version property [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.54.2 - 2021-10-25
|
||||
>>
|
||||
>> * application/create: Rely on the hostApps for detecting discontinued DTs [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.54.1 - 2021-10-22
|
||||
>>
|
||||
>> * tests/device: Async-await conversions & abstraction on multi-field tests [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.54.0 - 2021-10-20
|
||||
>>
|
||||
>> * tests: Register devices in chunks of 10 to avoid uuid conflicts in node [Thodoris Greasidis]
|
||||
>> * Add known issue check on release isReccomanded logic [JSReds]
|
||||
>> * Add known_issue_list to hostApp.getOsVersions() [JSReds]
|
||||
>>
|
||||
>> #### balena-sdk-15.53.0 - 2021-10-07
|
||||
>>
|
||||
>> * Add support for batch device supervisor updates [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.52.0 - 2021-10-06
|
||||
>>
|
||||
>> * Add support for batch device pinning to release [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.51.4 - 2021-09-28
|
||||
>>
|
||||
>> * auth.isLoggedIn: Treat BalenaExpiredToken errors as logged out indicator [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.51.3 - 2021-09-28
|
||||
>>
|
||||
>> * Convert application spec to TypeScript [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.51.2 - 2021-09-28
|
||||
>>
|
||||
>> * application.trackLatestRelease: Fix using draft/invalidated releases [Thodoris Greasidis]
|
||||
>> * application.isTrackingLatestRelease: Exclude draft&invalidated releases [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.51.1 - 2021-09-20
|
||||
>>
|
||||
>>
|
||||
>> <details>
|
||||
>> <summary> Update balena-request to v11.4.2 [Kyle Harding] </summary>
|
||||
>>
|
||||
>>> ##### balena-request-11.4.2 - 2021-09-20
|
||||
>>>
|
||||
>>> * Allow overriding the default zlib flush setting [Kyle Harding]
|
||||
>>>
|
||||
>> </details>
|
||||
>>
|
||||
>>
|
||||
>> #### balena-sdk-15.51.0 - 2021-09-16
|
||||
>>
|
||||
>> * os.getConfig: Add typings for the provisioningKeyName option [Nitish Agarwal]
|
||||
>>
|
||||
>> #### balena-sdk-15.50.1 - 2021-09-13
|
||||
>>
|
||||
>> * models/os: Always first normalize the device type slug [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.50.0 - 2021-09-10
|
||||
>>
|
||||
>> * Add release.finalize to promote draft releases to final [toochevere]
|
||||
>>
|
||||
>> #### balena-sdk-15.49.1 - 2021-09-10
|
||||
>>
|
||||
>> * typings: Drop the v5-model-only application_type.is_host_os [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.49.0 - 2021-09-06
|
||||
>>
|
||||
>> * os.getSupportedOsUpdateVersions: Use the hostApp releases [Thodoris Greasidis]
|
||||
>> * os.download: Use the hostApp for finding the latest release [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.48.3 - 2021-08-27
|
||||
>>
|
||||
>>
|
||||
>> <details>
|
||||
>> <summary> Update balena-request to 11.4.1 [Kyle Harding] </summary>
|
||||
>>
|
||||
>>> ##### balena-request-11.4.1 - 2021-08-27
|
||||
>>>
|
||||
>>> * Allow more lenient gzip decompression [Kyle Harding]
|
||||
>>>
|
||||
>> </details>
|
||||
>>
|
||||
>>
|
||||
>> #### balena-sdk-15.48.2 - 2021-08-27
|
||||
>>
|
||||
>> * Improve hostapp.getAllOsVersions performance & reduce fetched data [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.48.1 - 2021-08-27
|
||||
>>
|
||||
>> * Update typescript to 4.4.2 [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.48.0 - 2021-08-15
|
||||
>>
|
||||
>> * Deprecate the release.release_version property [Thodoris Greasidis]
|
||||
>> * typings: Add the release versioning properties [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.47.1 - 2021-08-10
|
||||
>>
|
||||
>> * Run browser tests using the minified browser bundle [Thodoris Greasidis]
|
||||
>> * Move to uglify-js to fix const assignment bug in minified build [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.47.0 - 2021-08-09
|
||||
>>
|
||||
>> * typings: Add the release.is_final & is_finalized_at__date properties [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.46.1 - 2021-07-28
|
||||
>>
|
||||
>> * apiKey.getAll: Return only NamedUserApiKeys for backwards compatibility [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-sdk-15.46.0 - 2021-07-27
|
||||
>>
|
||||
>> * Add email verification & email request methods [Nitish Agarwal]
|
||||
>>
|
||||
>> #### balena-sdk-15.45.0 - 2021-07-26
|
||||
>>
|
||||
>> * Update generateProvisioningKey to include keyName [Nitish Agarwal]
|
||||
>>
|
||||
> </details>
|
||||
>
|
||||
>
|
||||
</details>
|
||||
|
||||
## 13.1.11 - 2022-01-19
|
||||
|
||||
* chore: lib/auth/utils.ts: Replace deprecated url.resolve, use async/await [Paulo Castro]
|
||||
* chore: Update @types/node to v12.20.42 [Paulo Castro]
|
||||
|
||||
## 13.1.10 - 2022-01-16
|
||||
|
||||
* Update docs and package.json re min Node.js supported version (12.8.0) [Paulo Castro]
|
||||
|
||||
## 13.1.9 - 2022-01-14
|
||||
|
||||
* Update packages in response to `colors` package issues [Scott Lowe]
|
||||
|
||||
## 13.1.8 - 2022-01-11
|
||||
|
||||
* local push: Fix "invalid character '/' looking for beginning of value" [Paulo Castro]
|
||||
|
@ -78,7 +78,7 @@ If you are a Node.js developer, you may wish to install the balena CLI via [npm]
|
||||
The npm installation involves building native (platform-specific) binary modules, which require
|
||||
some development tools to be installed first, as follows.
|
||||
|
||||
> **The balena CLI currently requires Node.js version 10 (min 10.20.0) or 12.**
|
||||
> **The balena CLI currently requires Node.js version 12 (min 12.8.0).**
|
||||
> **Versions 13 and later are not yet fully supported.**
|
||||
|
||||
### Install development tools
|
||||
|
@ -36,8 +36,8 @@ const run = async (cmd: string) => {
|
||||
}
|
||||
resolve({ stdout, stderr });
|
||||
});
|
||||
p.stdout.pipe(process.stdout);
|
||||
p.stderr.pipe(process.stderr);
|
||||
p.stdout?.pipe(process.stdout);
|
||||
p.stderr?.pipe(process.stderr);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -117,7 +117,7 @@ export async function which(program: string): Promise<string> {
|
||||
*/
|
||||
export async function whichSpawn(
|
||||
programName: string,
|
||||
args?: string[],
|
||||
args: string[] = [],
|
||||
): Promise<void> {
|
||||
const program = await which(programName);
|
||||
let error: Error | undefined;
|
||||
|
@ -333,6 +333,30 @@ Examples:
|
||||
|
||||
### Options
|
||||
|
||||
#### --fields FIELDS
|
||||
|
||||
only show provided fields (comma-separated)
|
||||
|
||||
#### -j, --json
|
||||
|
||||
output in json format
|
||||
|
||||
#### --filter FILTER
|
||||
|
||||
filter results by substring matching of a given field, eg: --filter field=foo
|
||||
|
||||
#### --no-header
|
||||
|
||||
hide table header from output
|
||||
|
||||
#### --no-truncate
|
||||
|
||||
do not truncate output to fit screen
|
||||
|
||||
#### --sort SORT
|
||||
|
||||
field to sort by (prepend '-' for descending order)
|
||||
|
||||
## fleet <fleet>
|
||||
|
||||
Display detailed information about a single fleet.
|
||||
@ -362,6 +386,14 @@ fleet name, slug (preferred), or numeric ID (deprecated)
|
||||
|
||||
### Options
|
||||
|
||||
#### --fields FIELDS
|
||||
|
||||
only show provided fields (comma-separated)
|
||||
|
||||
#### -j, --json
|
||||
|
||||
output in json format
|
||||
|
||||
## fleet create <name>
|
||||
|
||||
Create a new balena fleet.
|
||||
@ -648,9 +680,29 @@ Examples:
|
||||
|
||||
fleet name, slug (preferred), or numeric ID (deprecated)
|
||||
|
||||
#### --fields FIELDS
|
||||
|
||||
only show provided fields (comma-separated)
|
||||
|
||||
#### -j, --json
|
||||
|
||||
produce JSON output instead of tabular output
|
||||
output in json format
|
||||
|
||||
#### --filter FILTER
|
||||
|
||||
filter results by substring matching of a given field, eg: --filter field=foo
|
||||
|
||||
#### --no-header
|
||||
|
||||
hide table header from output
|
||||
|
||||
#### --no-truncate
|
||||
|
||||
do not truncate output to fit screen
|
||||
|
||||
#### --sort SORT
|
||||
|
||||
field to sort by (prepend '-' for descending order)
|
||||
|
||||
## devices supported
|
||||
|
||||
@ -669,9 +721,29 @@ Examples:
|
||||
|
||||
### Options
|
||||
|
||||
#### --fields FIELDS
|
||||
|
||||
only show provided fields (comma-separated)
|
||||
|
||||
#### -j, --json
|
||||
|
||||
produce JSON output instead of tabular output
|
||||
output in json format
|
||||
|
||||
#### --filter FILTER
|
||||
|
||||
filter results by substring matching of a given field, eg: --filter field=foo
|
||||
|
||||
#### --no-header
|
||||
|
||||
hide table header from output
|
||||
|
||||
#### --no-truncate
|
||||
|
||||
do not truncate output to fit screen
|
||||
|
||||
#### --sort SORT
|
||||
|
||||
field to sort by (prepend '-' for descending order)
|
||||
|
||||
## device <uuid>
|
||||
|
||||
@ -689,6 +761,14 @@ the device uuid
|
||||
|
||||
### Options
|
||||
|
||||
#### --fields FIELDS
|
||||
|
||||
only show provided fields (comma-separated)
|
||||
|
||||
#### -j, --json
|
||||
|
||||
output in json format
|
||||
|
||||
## device deactivate <uuid>
|
||||
|
||||
Deactivate a device.
|
||||
@ -1152,6 +1232,14 @@ fleet name or slug (preferred)
|
||||
|
||||
### Options
|
||||
|
||||
#### --fields FIELDS
|
||||
|
||||
only show provided fields (comma-separated)
|
||||
|
||||
#### -j, --json
|
||||
|
||||
output in json format
|
||||
|
||||
## release <commitOrId>
|
||||
|
||||
|
||||
@ -1173,6 +1261,14 @@ the commit or ID of the release to get information
|
||||
|
||||
Return the release composition
|
||||
|
||||
#### --fields FIELDS
|
||||
|
||||
only show provided fields (comma-separated)
|
||||
|
||||
#### -j, --json
|
||||
|
||||
output in json format
|
||||
|
||||
## release finalize <commitOrId>
|
||||
|
||||
Finalize a release. Releases can be "draft" or "final", and this command
|
||||
@ -1271,9 +1367,29 @@ show configuration variables only
|
||||
|
||||
device UUID
|
||||
|
||||
#### --fields FIELDS
|
||||
|
||||
only show provided fields (comma-separated)
|
||||
|
||||
#### -j, --json
|
||||
|
||||
produce JSON output instead of tabular output
|
||||
output in json format
|
||||
|
||||
#### --filter FILTER
|
||||
|
||||
filter results by substring matching of a given field, eg: --filter field=foo
|
||||
|
||||
#### --no-header
|
||||
|
||||
hide table header from output
|
||||
|
||||
#### --no-truncate
|
||||
|
||||
do not truncate output to fit screen
|
||||
|
||||
#### --sort SORT
|
||||
|
||||
field to sort by (prepend '-' for descending order)
|
||||
|
||||
#### -s, --service SERVICE
|
||||
|
||||
@ -1526,6 +1642,30 @@ device UUID
|
||||
|
||||
release id
|
||||
|
||||
#### --fields FIELDS
|
||||
|
||||
only show provided fields (comma-separated)
|
||||
|
||||
#### -j, --json
|
||||
|
||||
output in json format
|
||||
|
||||
#### --filter FILTER
|
||||
|
||||
filter results by substring matching of a given field, eg: --filter field=foo
|
||||
|
||||
#### --no-header
|
||||
|
||||
hide table header from output
|
||||
|
||||
#### --no-truncate
|
||||
|
||||
do not truncate output to fit screen
|
||||
|
||||
#### --sort SORT
|
||||
|
||||
field to sort by (prepend '-' for descending order)
|
||||
|
||||
## tag rm <tagKey>
|
||||
|
||||
Remove a tag from a fleet, device or release.
|
||||
@ -1694,6 +1834,30 @@ Examples:
|
||||
|
||||
### Options
|
||||
|
||||
#### --fields FIELDS
|
||||
|
||||
only show provided fields (comma-separated)
|
||||
|
||||
#### -j, --json
|
||||
|
||||
output in json format
|
||||
|
||||
#### --filter FILTER
|
||||
|
||||
filter results by substring matching of a given field, eg: --filter field=foo
|
||||
|
||||
#### --no-header
|
||||
|
||||
hide table header from output
|
||||
|
||||
#### --no-truncate
|
||||
|
||||
do not truncate output to fit screen
|
||||
|
||||
#### --sort SORT
|
||||
|
||||
field to sort by (prepend '-' for descending order)
|
||||
|
||||
## key <id>
|
||||
|
||||
Display a single SSH key registered in balenaCloud for the logged in user.
|
||||
@ -1710,6 +1874,14 @@ balenaCloud ID for the SSH key
|
||||
|
||||
### Options
|
||||
|
||||
#### --fields FIELDS
|
||||
|
||||
only show provided fields (comma-separated)
|
||||
|
||||
#### -j, --json
|
||||
|
||||
output in json format
|
||||
|
||||
## key add <name> [path]
|
||||
|
||||
Add an SSH key to the balenaCloud account of the logged in user.
|
||||
@ -2394,10 +2566,6 @@ the path to the config.json file to inject
|
||||
|
||||
### Options
|
||||
|
||||
#### -t, --type TYPE
|
||||
|
||||
ignored - no longer required
|
||||
|
||||
#### -d, --drive DRIVE
|
||||
|
||||
path to OS image file (e.g. balena.img) or block device (e.g. /dev/disk2)
|
||||
@ -2418,10 +2586,6 @@ Examples:
|
||||
|
||||
### Options
|
||||
|
||||
#### -t, --type TYPE
|
||||
|
||||
ignored - no longer required
|
||||
|
||||
#### -d, --drive DRIVE
|
||||
|
||||
path to OS image file (e.g. balena.img) or block device (e.g. /dev/disk2)
|
||||
@ -2449,10 +2613,6 @@ Examples:
|
||||
|
||||
### Options
|
||||
|
||||
#### -t, --type TYPE
|
||||
|
||||
ignored - no longer required
|
||||
|
||||
#### -d, --drive DRIVE
|
||||
|
||||
path to OS image file (e.g. balena.img) or block device (e.g. /dev/disk2)
|
||||
@ -2491,10 +2651,6 @@ the value of the config parameter to write
|
||||
|
||||
### Options
|
||||
|
||||
#### -t, --type TYPE
|
||||
|
||||
ignored - no longer required
|
||||
|
||||
#### -d, --drive DRIVE
|
||||
|
||||
path to OS image file (e.g. balena.img) or block device (e.g. /dev/disk2)
|
||||
@ -2843,6 +2999,14 @@ Examples:
|
||||
|
||||
### Options
|
||||
|
||||
#### --fields FIELDS
|
||||
|
||||
only show provided fields (comma-separated)
|
||||
|
||||
#### -j, --json
|
||||
|
||||
output in json format
|
||||
|
||||
# Local
|
||||
|
||||
## local configure <target>
|
||||
|
@ -14,57 +14,44 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import * as _ from 'lodash';
|
||||
import * as url from 'url';
|
||||
import { getBalenaSdk } from '../utils/lazy';
|
||||
|
||||
/**
|
||||
* @summary Get dashboard CLI login URL
|
||||
* @function
|
||||
* @protected
|
||||
* Get dashboard CLI login URL
|
||||
*
|
||||
* @param {String} callbackUrl - callback url
|
||||
* @fulfil {String} - dashboard login url
|
||||
* @returns {Promise}
|
||||
*
|
||||
* @example
|
||||
* utils.getDashboardLoginURL('http://127.0.0.1:3000').then (url) ->
|
||||
* console.log(url)
|
||||
* @param callbackUrl - Callback url, e.g. 'http://127.0.0.1:3000'
|
||||
* @returns Dashboard login URL, e.g.:
|
||||
* 'https://dashboard.balena-cloud.com/login/cli/http%253A%252F%252F127.0.0.1%253A59581%252Fauth'
|
||||
*/
|
||||
export const getDashboardLoginURL = (callbackUrl: string) => {
|
||||
export async function getDashboardLoginURL(
|
||||
callbackUrl: string,
|
||||
): Promise<string> {
|
||||
// Encode percentages signs from the escaped url
|
||||
// characters to avoid angular getting confused.
|
||||
callbackUrl = encodeURIComponent(callbackUrl).replace(/%/g, '%25');
|
||||
|
||||
return getBalenaSdk()
|
||||
.settings.get('dashboardUrl')
|
||||
.then((dashboardUrl) =>
|
||||
url.resolve(dashboardUrl, `/login/cli/${callbackUrl}`),
|
||||
);
|
||||
};
|
||||
const [{ URL }, dashboardUrl] = await Promise.all([
|
||||
import('url'),
|
||||
getBalenaSdk().settings.get('dashboardUrl'),
|
||||
]);
|
||||
return new URL(`/login/cli/${callbackUrl}`, dashboardUrl).href;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Log in using a token, but only if the token is valid
|
||||
* @function
|
||||
* @protected
|
||||
* Log in using a token, but only if the token is valid.
|
||||
*
|
||||
* @description
|
||||
* This function checks that the token is not only well-structured
|
||||
* but that it also authenticates with the server successfully.
|
||||
*
|
||||
* If authenticated, the token is persisted, if not then the previous
|
||||
* login state is restored.
|
||||
*
|
||||
* @param {String} token - session token or api key
|
||||
* @fulfil {Boolean} - whether the login was successful or not
|
||||
* @returns {Promise}
|
||||
*
|
||||
* utils.loginIfTokenValid('...').then (loggedIn) ->
|
||||
* if loggedIn
|
||||
* console.log('Token is valid!')
|
||||
* @param token - session token or api key
|
||||
* @returns whether the login was successful or not
|
||||
*/
|
||||
export const loginIfTokenValid = async (token: string): Promise<boolean> => {
|
||||
if (_.isEmpty(token?.trim())) {
|
||||
export async function loginIfTokenValid(token?: string): Promise<boolean> {
|
||||
token = (token || '').trim();
|
||||
if (!token) {
|
||||
return false;
|
||||
}
|
||||
const balena = getBalenaSdk();
|
||||
@ -86,4 +73,4 @@ export const loginIfTokenValid = async (token: string): Promise<boolean> => {
|
||||
}
|
||||
}
|
||||
return isLoggedIn;
|
||||
};
|
||||
}
|
||||
|
@ -171,4 +171,5 @@ export default abstract class BalenaCommand extends Command {
|
||||
|
||||
protected outputMessage = output.outputMessage;
|
||||
protected outputData = output.outputData;
|
||||
protected printTitle = output.printTitle;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2016-2020 Balena Ltd.
|
||||
* Copyright 2016 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -22,8 +22,10 @@ import * as cf from '../../utils/common-flags';
|
||||
import { expandForAppName } from '../../utils/helpers';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
||||
import { tryAsInteger } from '../../utils/validation';
|
||||
|
||||
import type { Application, Release } from 'balena-sdk';
|
||||
import type { DataOutputOptions } from '../../framework';
|
||||
|
||||
import { isV14 } from '../../utils/version';
|
||||
|
||||
interface ExtendedDevice extends DeviceWithDeviceType {
|
||||
dashboard_url?: string;
|
||||
@ -42,7 +44,7 @@ interface ExtendedDevice extends DeviceWithDeviceType {
|
||||
undervoltage_detected?: boolean;
|
||||
}
|
||||
|
||||
interface FlagsDef {
|
||||
interface FlagsDef extends DataOutputOptions {
|
||||
help: void;
|
||||
}
|
||||
|
||||
@ -71,13 +73,16 @@ export default class DeviceCmd extends Command {
|
||||
|
||||
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 } = this.parse<FlagsDef, ArgsDef>(DeviceCmd);
|
||||
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
|
||||
DeviceCmd,
|
||||
);
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
@ -163,37 +168,52 @@ export default class DeviceCmd extends Command {
|
||||
);
|
||||
}
|
||||
|
||||
console.log(
|
||||
getVisuals().table.vertical(device, [
|
||||
`$${device.device_name}$`,
|
||||
'id',
|
||||
'device_type',
|
||||
'status',
|
||||
'is_online',
|
||||
'ip_address',
|
||||
'public_address',
|
||||
'mac_address',
|
||||
'fleet',
|
||||
'last_seen',
|
||||
'uuid',
|
||||
'commit',
|
||||
'supervisor_version',
|
||||
'is_web_accessible',
|
||||
'note',
|
||||
'os_version',
|
||||
'dashboard_url',
|
||||
'cpu_usage_percent',
|
||||
'cpu_temp_c',
|
||||
'cpu_id',
|
||||
'memory_usage_mb',
|
||||
'memory_total_mb',
|
||||
'memory_usage_percent',
|
||||
'storage_block_device',
|
||||
'storage_usage_mb',
|
||||
'storage_total_mb',
|
||||
'storage_usage_percent',
|
||||
'undervoltage_detected',
|
||||
]),
|
||||
);
|
||||
const outputFields = [
|
||||
'device_name',
|
||||
'id',
|
||||
'device_type',
|
||||
'status',
|
||||
'is_online',
|
||||
'ip_address',
|
||||
'public_address',
|
||||
'mac_address',
|
||||
'fleet',
|
||||
'last_seen',
|
||||
'uuid',
|
||||
'commit',
|
||||
'supervisor_version',
|
||||
'is_web_accessible',
|
||||
'note',
|
||||
'os_version',
|
||||
'dashboard_url',
|
||||
'cpu_usage_percent',
|
||||
'cpu_temp_c',
|
||||
'cpu_id',
|
||||
'memory_usage_mb',
|
||||
'memory_total_mb',
|
||||
'memory_usage_percent',
|
||||
'storage_block_device',
|
||||
'storage_usage_mb',
|
||||
'storage_total_mb',
|
||||
'storage_usage_percent',
|
||||
'undervoltage_detected',
|
||||
];
|
||||
|
||||
if (isV14()) {
|
||||
await this.outputData(device, outputFields, {
|
||||
...options,
|
||||
hideNullOrUndefinedValues: true,
|
||||
titleField: 'device_name',
|
||||
});
|
||||
} else {
|
||||
// Old output implementation
|
||||
outputFields.unshift(`$${device.device_name}$`);
|
||||
console.log(
|
||||
getVisuals().table.vertical(
|
||||
device,
|
||||
outputFields.filter((f) => f !== 'device_name'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2016-2020 Balena Ltd.
|
||||
* Copyright 2016 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -21,8 +21,10 @@ import * as cf from '../../utils/common-flags';
|
||||
import { expandForAppName } from '../../utils/helpers';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
||||
import { applicationIdInfo, jsonInfo } from '../../utils/messages';
|
||||
|
||||
import type { Application } from 'balena-sdk';
|
||||
import type { DataSetOutputOptions } from '../../framework';
|
||||
|
||||
import { isV14 } from '../../utils/version';
|
||||
|
||||
interface ExtendedDevice extends DeviceWithDeviceType {
|
||||
dashboard_url?: string;
|
||||
@ -30,10 +32,10 @@ interface ExtendedDevice extends DeviceWithDeviceType {
|
||||
device_type?: string | null;
|
||||
}
|
||||
|
||||
interface FlagsDef {
|
||||
interface FlagsDef extends DataSetOutputOptions {
|
||||
fleet?: string;
|
||||
help: void;
|
||||
json: boolean;
|
||||
json?: boolean;
|
||||
}
|
||||
|
||||
export default class DevicesCmd extends Command {
|
||||
@ -58,12 +60,11 @@ export default class DevicesCmd extends Command {
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
fleet: cf.fleet,
|
||||
json: cf.json,
|
||||
...(isV14() ? cf.dataSetOutputFlags : { json: cf.json }),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static primary = true;
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
@ -99,31 +100,52 @@ export default class DevicesCmd extends Command {
|
||||
return device;
|
||||
});
|
||||
|
||||
const fields = [
|
||||
'id',
|
||||
'uuid',
|
||||
'device_name',
|
||||
'device_type',
|
||||
'fleet',
|
||||
'status',
|
||||
'is_online',
|
||||
'supervisor_version',
|
||||
'os_version',
|
||||
'dashboard_url',
|
||||
];
|
||||
if (isV14()) {
|
||||
const outputFields = [
|
||||
'id',
|
||||
'uuid',
|
||||
'device_name',
|
||||
'device_type',
|
||||
'fleet',
|
||||
'status',
|
||||
'is_online',
|
||||
'supervisor_version',
|
||||
'os_version',
|
||||
'dashboard_url',
|
||||
];
|
||||
|
||||
if (options.json) {
|
||||
const { pickAndRename } = await import('../../utils/helpers');
|
||||
const mapped = devices.map((device) => pickAndRename(device, fields));
|
||||
console.log(JSON.stringify(mapped, null, 4));
|
||||
await this.outputData(devices, outputFields, {
|
||||
...options,
|
||||
displayNullValuesAs: 'N/a',
|
||||
});
|
||||
} else {
|
||||
const _ = await import('lodash');
|
||||
console.log(
|
||||
getVisuals().table.horizontal(
|
||||
devices.map((dev) => _.mapValues(dev, (val) => val ?? 'N/a')),
|
||||
fields,
|
||||
),
|
||||
);
|
||||
// Old output implementation
|
||||
const fields = [
|
||||
'id',
|
||||
'uuid',
|
||||
'device_name',
|
||||
'device_type',
|
||||
'fleet',
|
||||
'status',
|
||||
'is_online',
|
||||
'supervisor_version',
|
||||
'os_version',
|
||||
'dashboard_url',
|
||||
];
|
||||
|
||||
if (options.json) {
|
||||
const { pickAndRename } = await import('../../utils/helpers');
|
||||
const mapped = devices.map((device) => pickAndRename(device, fields));
|
||||
console.log(JSON.stringify(mapped, null, 4));
|
||||
} else {
|
||||
const _ = await import('lodash');
|
||||
console.log(
|
||||
getVisuals().table.horizontal(
|
||||
devices.map((dev) => _.mapValues(dev, (val) => val ?? 'N/a')),
|
||||
fields,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2016-2021 Balena Ltd.
|
||||
* Copyright 2016 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -17,12 +17,14 @@
|
||||
import { flags } from '@oclif/command';
|
||||
import * as _ from 'lodash';
|
||||
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 type { DataSetOutputOptions } from '../../framework';
|
||||
|
||||
interface FlagsDef {
|
||||
import { isV14 } from '../../utils/version';
|
||||
|
||||
interface FlagsDef extends DataSetOutputOptions {
|
||||
help: void;
|
||||
json?: boolean;
|
||||
}
|
||||
@ -51,10 +53,7 @@ export default class DevicesSupportedCmd extends Command {
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
help: cf.help,
|
||||
json: flags.boolean({
|
||||
char: 'j',
|
||||
description: 'produce JSON output instead of tabular output',
|
||||
}),
|
||||
...(isV14() ? cf.dataSetOutputFlags : { json: cf.json }),
|
||||
};
|
||||
|
||||
public async run() {
|
||||
@ -70,7 +69,7 @@ export default class DevicesSupportedCmd extends Command {
|
||||
const configDTsBySlug = _.keyBy(configDTs, (dt) => dt.slug);
|
||||
interface DT {
|
||||
slug: string;
|
||||
aliases: string[];
|
||||
aliases: string[] | string;
|
||||
arch: string;
|
||||
name: string;
|
||||
}
|
||||
@ -84,19 +83,25 @@ export default class DevicesSupportedCmd extends Command {
|
||||
const dt: Partial<typeof dts[0]> = dtsBySlug[slug] || {};
|
||||
deviceTypes.push({
|
||||
slug,
|
||||
aliases: options.json ? aliases : [aliases.join(', ')],
|
||||
aliases: options.json ? aliases : aliases.join(', '),
|
||||
arch: (dt.is_of__cpu_architecture as any)?.[0]?.slug || 'n/a',
|
||||
name: dt.name || 'N/A',
|
||||
});
|
||||
}
|
||||
const fields = ['slug', 'aliases', 'arch', 'name'];
|
||||
deviceTypes = _.sortBy(deviceTypes, fields);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(deviceTypes, null, 4));
|
||||
|
||||
if (isV14()) {
|
||||
await this.outputData(deviceTypes, fields, options);
|
||||
} else {
|
||||
const visuals = getVisuals();
|
||||
const output = await visuals.table.horizontal(deviceTypes, fields);
|
||||
console.log(output);
|
||||
// Old output implementation
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(deviceTypes, null, 4));
|
||||
} else {
|
||||
const visuals = getVisuals();
|
||||
const output = await visuals.table.horizontal(deviceTypes, fields);
|
||||
console.log(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2016-2021 Balena Ltd.
|
||||
* Copyright 2016 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -22,12 +22,15 @@ import { ExpectedError } from '../errors';
|
||||
import * as cf from '../utils/common-flags';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy';
|
||||
import { applicationIdInfo } from '../utils/messages';
|
||||
import type { DataSetOutputOptions } from '../framework';
|
||||
|
||||
interface FlagsDef {
|
||||
import { isV14 } from '../utils/version';
|
||||
|
||||
interface FlagsDef extends DataSetOutputOptions {
|
||||
fleet?: string;
|
||||
config: boolean;
|
||||
device?: string; // device UUID
|
||||
json: boolean;
|
||||
json?: boolean;
|
||||
help: void;
|
||||
service?: string; // service name
|
||||
}
|
||||
@ -113,7 +116,7 @@ export default class EnvsCmd extends Command {
|
||||
}),
|
||||
device: { ...cf.device, exclusive: ['fleet'] },
|
||||
help: cf.help,
|
||||
json: cf.json,
|
||||
...(isV14() ? cf.dataSetOutputFlags : { json: cf.json }),
|
||||
service: { ...cf.service, exclusive: ['config'] },
|
||||
};
|
||||
|
||||
@ -181,24 +184,59 @@ export default class EnvsCmd extends Command {
|
||||
return i;
|
||||
});
|
||||
|
||||
if (options.device) {
|
||||
fields.push(options.json ? 'deviceUUID' : 'deviceUUID => DEVICE');
|
||||
}
|
||||
if (!options.config) {
|
||||
fields.push(options.json ? 'serviceName' : 'serviceName => SERVICE');
|
||||
}
|
||||
if (isV14()) {
|
||||
const results = [...varArray] as any;
|
||||
|
||||
if (options.json) {
|
||||
const { pickAndRename } = await import('../utils/helpers');
|
||||
const mapped = varArray.map((o) => pickAndRename(o, fields));
|
||||
this.log(JSON.stringify(mapped, null, 4));
|
||||
// Rename fields
|
||||
if (options.device) {
|
||||
if (options.json) {
|
||||
fields.push('deviceUUID');
|
||||
} else {
|
||||
results.forEach((r: any) => {
|
||||
r.device = r.deviceUUID;
|
||||
delete r.deviceUUID;
|
||||
});
|
||||
|
||||
fields.push('device');
|
||||
}
|
||||
}
|
||||
if (!options.config) {
|
||||
if (options.json) {
|
||||
fields.push('serviceName');
|
||||
} else {
|
||||
results.forEach((r: any) => {
|
||||
r.service = r.serviceName;
|
||||
delete r.serviceName;
|
||||
});
|
||||
fields.push('service');
|
||||
}
|
||||
}
|
||||
|
||||
await this.outputData(results, fields, {
|
||||
...options,
|
||||
sort: options.sort || 'name',
|
||||
});
|
||||
} else {
|
||||
this.log(
|
||||
getVisuals().table.horizontal(
|
||||
_.sortBy(varArray, (v: SDK.EnvironmentVariableBase) => v.name),
|
||||
fields,
|
||||
),
|
||||
);
|
||||
// Old output implementation
|
||||
if (options.device) {
|
||||
fields.push(options.json ? 'deviceUUID' : 'deviceUUID => DEVICE');
|
||||
}
|
||||
if (!options.config) {
|
||||
fields.push(options.json ? 'serviceName' : 'serviceName => SERVICE');
|
||||
}
|
||||
|
||||
if (options.json) {
|
||||
const { pickAndRename } = await import('../utils/helpers');
|
||||
const mapped = varArray.map((o) => pickAndRename(o, fields));
|
||||
this.log(JSON.stringify(mapped, null, 4));
|
||||
} else {
|
||||
this.log(
|
||||
getVisuals().table.horizontal(
|
||||
_.sortBy(varArray, (v: SDK.EnvironmentVariableBase) => v.name),
|
||||
fields,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2016-2020 Balena Ltd.
|
||||
* Copyright 2016 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -20,10 +20,13 @@ import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
||||
import { parseAsInteger } from '../../utils/validation';
|
||||
import type { DataOutputOptions } from '../../framework';
|
||||
|
||||
import { isV14 } from '../../utils/version';
|
||||
|
||||
type IArg<T> = import('@oclif/parser').args.IArg<T>;
|
||||
|
||||
interface FlagsDef {
|
||||
interface FlagsDef extends DataOutputOptions {
|
||||
help: void;
|
||||
}
|
||||
|
||||
@ -52,27 +55,52 @@ export default class KeyCmd extends Command {
|
||||
public static usage = 'key <id>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
...(isV14() ? cf.dataOutputFlags : {}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
const { args: params } = this.parse<{}, ArgsDef>(KeyCmd);
|
||||
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
|
||||
KeyCmd,
|
||||
);
|
||||
|
||||
const key = await getBalenaSdk().models.key.get(params.id);
|
||||
|
||||
// Use 'name' instead of 'title' to match dashboard.
|
||||
const displayKey = {
|
||||
id: key.id,
|
||||
name: key.title,
|
||||
};
|
||||
if (isV14()) {
|
||||
// Use 'name' instead of 'title' to match dashboard.
|
||||
const displayKey = {
|
||||
id: key.id,
|
||||
name: key.title,
|
||||
public_key: key.public_key,
|
||||
};
|
||||
|
||||
console.log(getVisuals().table.vertical(displayKey, ['id', 'name']));
|
||||
if (!options.json) {
|
||||
// Id is redundant, since user must have provided it in command call
|
||||
this.printTitle(displayKey.name);
|
||||
this.outputMessage(displayKey.public_key);
|
||||
} else {
|
||||
await this.outputData(
|
||||
displayKey,
|
||||
['id', 'name', 'public_key'],
|
||||
options,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Old output implementation
|
||||
// Use 'name' instead of 'title' to match dashboard.
|
||||
const displayKey = {
|
||||
id: key.id,
|
||||
name: key.title,
|
||||
};
|
||||
|
||||
// Since the public key string is long, it might
|
||||
// wrap to lines below, causing the table layout to break.
|
||||
// See https://github.com/balena-io/balena-cli/issues/151
|
||||
console.log('\n' + key.public_key);
|
||||
console.log(getVisuals().table.vertical(displayKey, ['id', 'name']));
|
||||
|
||||
// Since the public key string is long, it might
|
||||
// wrap to lines below, causing the table layout to break.
|
||||
// See https://github.com/balena-io/balena-cli/issues/151
|
||||
console.log('\n' + key.public_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2016-2020 Balena Ltd.
|
||||
* Copyright 2016-2022 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -19,8 +19,11 @@ import { flags } from '@oclif/command';
|
||||
import Command from '../command';
|
||||
import * as cf from '../utils/common-flags';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy';
|
||||
import type { DataSetOutputOptions } from '../framework';
|
||||
|
||||
interface FlagsDef {
|
||||
import { isV14 } from '../utils/version';
|
||||
|
||||
interface FlagsDef extends DataSetOutputOptions {
|
||||
help: void;
|
||||
}
|
||||
|
||||
@ -35,13 +38,14 @@ export default class KeysCmd extends Command {
|
||||
public static usage = 'keys';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
...(isV14() ? cf.dataSetOutputFlags : {}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
this.parse<FlagsDef, {}>(KeysCmd);
|
||||
const { flags: options } = this.parse<FlagsDef, {}>(KeysCmd);
|
||||
|
||||
const keys = await getBalenaSdk().models.key.getAll();
|
||||
|
||||
@ -50,6 +54,12 @@ export default class KeysCmd extends Command {
|
||||
return { id: k.id, name: k.title };
|
||||
});
|
||||
|
||||
console.log(getVisuals().table.horizontal(displayKeys, ['id', 'name']));
|
||||
// Display
|
||||
if (isV14()) {
|
||||
await this.outputData(displayKeys, ['id', 'name'], options);
|
||||
} else {
|
||||
// Old output implementation
|
||||
console.log(getVisuals().table.horizontal(displayKeys, ['id', 'name']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2016-2020 Balena Ltd.
|
||||
* Copyright 2016-2022 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -19,8 +19,11 @@ import { flags } from '@oclif/command';
|
||||
import Command from '../command';
|
||||
import * as cf from '../utils/common-flags';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy';
|
||||
import type { DataSetOutputOptions } from '../framework';
|
||||
|
||||
interface FlagsDef {
|
||||
import { isV14 } from '../utils/version';
|
||||
|
||||
interface FlagsDef extends DataSetOutputOptions {
|
||||
help: void;
|
||||
}
|
||||
|
||||
@ -36,12 +39,13 @@ export default class OrgsCmd extends Command {
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
help: cf.help,
|
||||
...(isV14() ? cf.dataSetOutputFlags : {}),
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
this.parse<FlagsDef, {}>(OrgsCmd);
|
||||
const { flags: options } = this.parse<FlagsDef, {}>(OrgsCmd);
|
||||
|
||||
const { getOwnOrganizations } = await import('../utils/sdk');
|
||||
|
||||
@ -49,8 +53,13 @@ export default class OrgsCmd extends Command {
|
||||
const organizations = await getOwnOrganizations(getBalenaSdk());
|
||||
|
||||
// Display
|
||||
console.log(
|
||||
getVisuals().table.horizontal(organizations, ['name', 'handle']),
|
||||
);
|
||||
if (isV14()) {
|
||||
await this.outputData(organizations, ['name', 'handle'], options);
|
||||
} else {
|
||||
// Old output implementation
|
||||
console.log(
|
||||
getVisuals().table.horizontal(organizations, ['name', 'handle']),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2016-2020 Balena Ltd.
|
||||
* Copyright 2016 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -22,8 +22,11 @@ import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
||||
import type * as BalenaSdk from 'balena-sdk';
|
||||
import jsyaml = require('js-yaml');
|
||||
import { tryAsInteger } from '../../utils/validation';
|
||||
import type { DataOutputOptions } from '../../framework';
|
||||
|
||||
interface FlagsDef {
|
||||
import { isV14 } from '../../utils/version';
|
||||
|
||||
interface FlagsDef extends DataOutputOptions {
|
||||
help: void;
|
||||
composition?: boolean;
|
||||
}
|
||||
@ -49,7 +52,9 @@ export default class ReleaseCmd extends Command {
|
||||
default: false,
|
||||
char: 'c',
|
||||
description: 'Return the release composition',
|
||||
exclusive: ['json', 'fields'],
|
||||
}),
|
||||
...(isV14() ? cf.dataOutputFlags : {}),
|
||||
};
|
||||
|
||||
public static args = [
|
||||
@ -68,29 +73,27 @@ export default class ReleaseCmd extends Command {
|
||||
ReleaseCmd,
|
||||
);
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
if (options.composition) {
|
||||
await this.showComposition(params.commitOrId, balena);
|
||||
await this.showComposition(params.commitOrId);
|
||||
} else {
|
||||
await this.showReleaseInfo(params.commitOrId, balena);
|
||||
await this.showReleaseInfo(params.commitOrId, options);
|
||||
}
|
||||
}
|
||||
|
||||
async showComposition(
|
||||
commitOrId: string | number,
|
||||
balena: BalenaSdk.BalenaSDK,
|
||||
) {
|
||||
const release = await balena.models.release.get(commitOrId, {
|
||||
async showComposition(commitOrId: string | number) {
|
||||
const release = await getBalenaSdk().models.release.get(commitOrId, {
|
||||
$select: 'composition',
|
||||
});
|
||||
|
||||
console.log(jsyaml.dump(release.composition));
|
||||
if (isV14()) {
|
||||
this.outputMessage(jsyaml.dump(release.composition));
|
||||
} else {
|
||||
// Old output implementation
|
||||
console.log(jsyaml.dump(release.composition));
|
||||
}
|
||||
}
|
||||
|
||||
async showReleaseInfo(
|
||||
commitOrId: string | number,
|
||||
balena: BalenaSdk.BalenaSDK,
|
||||
) {
|
||||
async showReleaseInfo(commitOrId: string | number, options: FlagsDef) {
|
||||
const fields: Array<keyof BalenaSdk.Release> = [
|
||||
'id',
|
||||
'commit',
|
||||
@ -103,7 +106,7 @@ export default class ReleaseCmd extends Command {
|
||||
'end_timestamp',
|
||||
];
|
||||
|
||||
const release = await balena.models.release.get(commitOrId, {
|
||||
const release = await getBalenaSdk().models.release.get(commitOrId, {
|
||||
$select: fields,
|
||||
$expand: {
|
||||
release_tag: {
|
||||
@ -116,13 +119,28 @@ export default class ReleaseCmd extends Command {
|
||||
.release_tag!.map((t) => `${t.tag_key}=${t.value}`)
|
||||
.join('\n');
|
||||
|
||||
const _ = await import('lodash');
|
||||
const values = _.mapValues(
|
||||
release,
|
||||
(val) => val ?? 'N/a',
|
||||
) as Dictionary<string>;
|
||||
values['tags'] = tagStr;
|
||||
if (isV14()) {
|
||||
await this.outputData(
|
||||
{
|
||||
tags: tagStr,
|
||||
...release,
|
||||
},
|
||||
fields,
|
||||
{
|
||||
displayNullValuesAs: 'N/a',
|
||||
...options,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
// Old output implementation
|
||||
const _ = await import('lodash');
|
||||
const values = _.mapValues(
|
||||
release,
|
||||
(val) => val ?? 'N/a',
|
||||
) as Dictionary<string>;
|
||||
values['tags'] = tagStr;
|
||||
|
||||
console.log(getVisuals().table.vertical(values, [...fields, 'tags']));
|
||||
console.log(getVisuals().table.vertical(values, [...fields, 'tags']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2016-2020 Balena Ltd.
|
||||
* Copyright 2016 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -21,8 +21,11 @@ import * as cf from '../utils/common-flags';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy';
|
||||
import { applicationNameNote } from '../utils/messages';
|
||||
import type * as BalenaSdk from 'balena-sdk';
|
||||
import type { DataSetOutputOptions } from '../framework';
|
||||
|
||||
interface FlagsDef {
|
||||
import { isV14 } from '../utils/version';
|
||||
|
||||
interface FlagsDef extends DataSetOutputOptions {
|
||||
help: void;
|
||||
}
|
||||
|
||||
@ -43,6 +46,7 @@ export default class ReleasesCmd extends Command {
|
||||
public static usage = 'releases <fleet>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
...(isV14() ? cf.dataOutputFlags : {}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
@ -57,7 +61,9 @@ export default class ReleasesCmd extends Command {
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
const { args: params } = this.parse<FlagsDef, ArgsDef>(ReleasesCmd);
|
||||
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
|
||||
ReleasesCmd,
|
||||
);
|
||||
|
||||
const fields: Array<keyof BalenaSdk.Release> = [
|
||||
'id',
|
||||
@ -76,12 +82,20 @@ export default class ReleasesCmd extends Command {
|
||||
{ $select: fields },
|
||||
);
|
||||
|
||||
const _ = await import('lodash');
|
||||
console.log(
|
||||
getVisuals().table.horizontal(
|
||||
releases.map((rel) => _.mapValues(rel, (val) => val ?? 'N/a')),
|
||||
fields,
|
||||
),
|
||||
);
|
||||
if (isV14()) {
|
||||
await this.outputData(releases, fields, {
|
||||
displayNullValuesAs: 'N/a',
|
||||
...options,
|
||||
});
|
||||
} else {
|
||||
// Old output implementation
|
||||
const _ = await import('lodash');
|
||||
console.log(
|
||||
getVisuals().table.horizontal(
|
||||
releases.map((rel) => _.mapValues(rel, (val) => val ?? 'N/a')),
|
||||
fields,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2016-2020 Balena Ltd.
|
||||
* Copyright 2016 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -19,8 +19,11 @@ import { flags } from '@oclif/command';
|
||||
import Command from '../command';
|
||||
import * as cf from '../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../utils/lazy';
|
||||
import type { DataOutputOptions } from '../framework';
|
||||
|
||||
interface FlagsDef {
|
||||
import { isV14 } from '../utils/version';
|
||||
|
||||
interface FlagsDef extends DataOutputOptions {
|
||||
help: void;
|
||||
}
|
||||
|
||||
@ -35,15 +38,27 @@ export default class SettingsCmd extends Command {
|
||||
public static usage = 'settings';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
...(isV14() ? cf.dataOutputFlags : {}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public async run() {
|
||||
this.parse<FlagsDef, {}>(SettingsCmd);
|
||||
const { flags: options } = this.parse<FlagsDef, {}>(SettingsCmd);
|
||||
|
||||
const settings = await getBalenaSdk().settings.getAll();
|
||||
|
||||
const prettyjson = await import('prettyjson');
|
||||
console.log(prettyjson.render(settings));
|
||||
if (isV14()) {
|
||||
// Select all available fields for display
|
||||
const fields = Object.keys(settings);
|
||||
|
||||
await this.outputData(settings, fields, {
|
||||
noCapitalizeKeys: true,
|
||||
...options,
|
||||
});
|
||||
} else {
|
||||
// Old output implementation
|
||||
const prettyjson = await import('prettyjson');
|
||||
console.log(prettyjson.render(settings));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2016-2020 Balena Ltd.
|
||||
* Copyright 2016 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -21,8 +21,12 @@ import { ExpectedError } from '../errors';
|
||||
import * as cf from '../utils/common-flags';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy';
|
||||
import { applicationIdInfo } from '../utils/messages';
|
||||
import type { ApplicationTag, DeviceTag, ReleaseTag } from 'balena-sdk';
|
||||
import type { DataSetOutputOptions } from '../framework';
|
||||
|
||||
interface FlagsDef {
|
||||
import { isV14 } from '../utils/version';
|
||||
|
||||
interface FlagsDef extends DataSetOutputOptions {
|
||||
fleet?: string;
|
||||
device?: string;
|
||||
release?: string;
|
||||
@ -61,6 +65,7 @@ export default class TagsCmd extends Command {
|
||||
...cf.release,
|
||||
exclusive: ['fleet', 'device'],
|
||||
},
|
||||
...(isV14() ? cf.dataSetOutputFlags : {}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
@ -78,7 +83,7 @@ export default class TagsCmd extends Command {
|
||||
|
||||
const { tryAsInteger } = await import('../utils/validation');
|
||||
|
||||
let tags;
|
||||
let tags: ApplicationTag[] | DeviceTag[] | ReleaseTag[] = [];
|
||||
|
||||
if (options.fleet) {
|
||||
const { getFleetSlug } = await import('../utils/sdk');
|
||||
@ -103,11 +108,17 @@ export default class TagsCmd extends Command {
|
||||
tags = await balena.models.release.tags.getAllByRelease(releaseParam);
|
||||
}
|
||||
|
||||
if (!tags || tags.length === 0) {
|
||||
if (tags.length === 0 && !options.json) {
|
||||
// TODO: Later change to output message
|
||||
throw new ExpectedError('No tags found');
|
||||
}
|
||||
|
||||
console.log(getVisuals().table.horizontal(tags, ['tag_key', 'value']));
|
||||
if (isV14()) {
|
||||
await this.outputData(tags, ['tag_key', 'value'], options);
|
||||
} else {
|
||||
// Old output implementation
|
||||
console.log(getVisuals().table.horizontal(tags, ['tag_key', 'value']));
|
||||
}
|
||||
}
|
||||
|
||||
protected missingResourceMessage = stripIndent`
|
||||
|
@ -27,14 +27,7 @@ import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
|
||||
// `@types/node` does not know about `options: { bigint?: boolean }`
|
||||
type statT = (
|
||||
fPath: string,
|
||||
options: { bigint?: boolean },
|
||||
) => fs.Stats | Promise<fs.Stats>;
|
||||
|
||||
// async stat does not work with pkg's internal `/snapshot` filesystem
|
||||
const stat: statT = process.pkg ? fs.statSync : fs.promises.stat;
|
||||
const stat = process.pkg ? fs.statSync : fs.promises.stat;
|
||||
|
||||
let fastBootStarted = false;
|
||||
|
||||
|
@ -1,26 +1,35 @@
|
||||
/*
|
||||
Copyright 2020 Balena
|
||||
|
||||
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.
|
||||
*/
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2020 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { getCliUx, getChalk } from '../utils/lazy';
|
||||
|
||||
/**
|
||||
* Used to extend FlagsDef for commands that output single-record data.
|
||||
* Exposed to user in command options.
|
||||
*/
|
||||
export interface DataOutputOptions {
|
||||
fields?: string;
|
||||
json?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to extend FlagsDef for commands that output multi-record data.
|
||||
* Exposed to user in command options.
|
||||
*/
|
||||
export interface DataSetOutputOptions extends DataOutputOptions {
|
||||
filter?: string;
|
||||
'no-header'?: boolean;
|
||||
@ -28,6 +37,14 @@ export interface DataSetOutputOptions extends DataOutputOptions {
|
||||
sort?: string;
|
||||
}
|
||||
|
||||
// Not exposed to user
|
||||
export interface InternalOutputOptions {
|
||||
displayNullValuesAs?: string;
|
||||
hideNullOrUndefinedValues?: boolean;
|
||||
titleField?: string;
|
||||
noCapitalizeKeys?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output message to STDERR
|
||||
*/
|
||||
@ -49,7 +66,7 @@ export function outputMessage(msg: string) {
|
||||
export async function outputData(
|
||||
data: any[] | {},
|
||||
fields: string[],
|
||||
options: DataOutputOptions | DataSetOutputOptions,
|
||||
options: (DataOutputOptions | DataSetOutputOptions) & InternalOutputOptions,
|
||||
) {
|
||||
if (Array.isArray(data)) {
|
||||
await outputDataSet(data, fields, options as DataSetOutputOptions);
|
||||
@ -68,7 +85,7 @@ export async function outputData(
|
||||
async function outputDataSet(
|
||||
data: any[],
|
||||
fields: string[],
|
||||
options: DataSetOutputOptions,
|
||||
options: DataSetOutputOptions & InternalOutputOptions,
|
||||
) {
|
||||
// Oclif expects fields to be specified in the format used in table headers (though lowercase)
|
||||
// By replacing underscores with spaces here, we can support both header format and actual field name
|
||||
@ -77,6 +94,12 @@ async function outputDataSet(
|
||||
options.filter = options.filter?.replace(/_/g, ' ');
|
||||
options.sort = options.sort?.replace(/_/g, ' ');
|
||||
|
||||
if (!options.json) {
|
||||
data = data.map((d) => {
|
||||
return processNullValues(d, options);
|
||||
});
|
||||
}
|
||||
|
||||
getCliUx().table(
|
||||
data,
|
||||
// Convert fields array to column object keys
|
||||
@ -97,7 +120,7 @@ async function outputDataSet(
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs a single data object (like `resin-cli-visuals table.vertical`),
|
||||
* Outputs a single data object (similar to `resin-cli-visuals table.vertical`),
|
||||
* but supporting a subset of options from `cli-ux table` (--json and --fields)
|
||||
*
|
||||
* @param data Array of data objects to output
|
||||
@ -107,9 +130,9 @@ async function outputDataSet(
|
||||
async function outputDataItem(
|
||||
data: any,
|
||||
fields: string[],
|
||||
options: DataOutputOptions,
|
||||
options: DataOutputOptions & InternalOutputOptions,
|
||||
) {
|
||||
const outData: typeof data = {};
|
||||
let outData: typeof data = {};
|
||||
|
||||
// Convert comma separated list of fields in `options.fields` to array of correct format.
|
||||
// Note, user may have specified the true field name (e.g. `some_field`),
|
||||
@ -125,30 +148,83 @@ async function outputDataItem(
|
||||
}
|
||||
});
|
||||
|
||||
if (
|
||||
(options.displayNullValuesAs || options.hideNullOrUndefinedValues) &&
|
||||
!options.json
|
||||
) {
|
||||
outData = processNullValues(outData, options);
|
||||
}
|
||||
|
||||
if (options.json) {
|
||||
printLine(JSON.stringify(outData, undefined, 2));
|
||||
} else {
|
||||
const chalk = getChalk();
|
||||
const { capitalize } = await import('lodash');
|
||||
|
||||
// Find longest key, so we can align results
|
||||
const longestKeyLength = getLongestObjectKeyLength(outData);
|
||||
|
||||
if (options.titleField) {
|
||||
printTitle(data[options.titleField as keyof any[]], options);
|
||||
}
|
||||
|
||||
// Output one field per line
|
||||
for (const [k, v] of Object.entries(outData)) {
|
||||
for (let [k, v] of Object.entries(outData)) {
|
||||
const shim = ' '.repeat(longestKeyLength - k.length);
|
||||
const kDisplay = capitalize(k.replace(/_/g, ' '));
|
||||
printLine(`${chalk.bold(kDisplay) + shim} : ${v}`);
|
||||
let kDisplay = k.replace(/_/g, ' ');
|
||||
|
||||
// Start multiline values on the line below the field name
|
||||
if (typeof v === 'string' && v.includes('\n')) {
|
||||
v = `\n${v}`;
|
||||
}
|
||||
|
||||
if (!options.noCapitalizeKeys) {
|
||||
kDisplay = capitalize(kDisplay);
|
||||
}
|
||||
if (k !== options.titleField) {
|
||||
printLine(` ${bold(kDisplay) + shim} : ${v}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getLongestObjectKeyLength(o: any): number {
|
||||
return Object.keys(o).length >= 1
|
||||
? Object.keys(o).reduce((a, b) => {
|
||||
return a.length > b.length ? a : b;
|
||||
}).length
|
||||
: 0;
|
||||
/**
|
||||
* Amend null/undefined values in data as per options:
|
||||
* - options.displayNullValuesAs will replace the value with the specified string
|
||||
* - options.hideNullOrUndefinedValues will remove the property from the data
|
||||
*
|
||||
* @param data The data object to process
|
||||
* @param options Output options
|
||||
*
|
||||
* @returns a copy of the data with amended values.
|
||||
*/
|
||||
function processNullValues(data: any, options: InternalOutputOptions) {
|
||||
const dataCopy = { ...data };
|
||||
|
||||
Object.entries(dataCopy).forEach(([k, v]) => {
|
||||
if (v == null) {
|
||||
if (options.displayNullValuesAs) {
|
||||
dataCopy[k] = options.displayNullValuesAs;
|
||||
} else if (options.hideNullOrUndefinedValues) {
|
||||
delete dataCopy[k];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return dataCopy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a title with underscore
|
||||
*
|
||||
* @param title The title string to print
|
||||
* @param options Output options
|
||||
*/
|
||||
export function printTitle(
|
||||
title: string,
|
||||
options?: InternalOutputOptions & DataSetOutputOptions,
|
||||
) {
|
||||
if (!options?.['no-header']) {
|
||||
printLine(` ${capitalize(bold(title))}`);
|
||||
printLine(` ${bold('─'.repeat(title.length))}`);
|
||||
}
|
||||
}
|
||||
|
||||
function printLine(s: any) {
|
||||
@ -156,3 +232,15 @@ function printLine(s: any) {
|
||||
// but using this one explicitly for ease of testing
|
||||
process.stdout.write(s + '\n');
|
||||
}
|
||||
|
||||
function capitalize(s: string) {
|
||||
return `${s[0].toUpperCase()}${s.slice(1)}`;
|
||||
}
|
||||
|
||||
function bold(s: string) {
|
||||
return getChalk().bold(s);
|
||||
}
|
||||
|
||||
function getLongestObjectKeyLength(o: any): number {
|
||||
return Math.max(0, ...Object.keys(o).map((k) => k.length));
|
||||
}
|
||||
|
@ -53,6 +53,12 @@ export async function preparseArgs(argv: string[]): Promise<string[]> {
|
||||
if (extractBooleanFlag(cmdSlice, '--debug')) {
|
||||
process.env.DEBUG = '1';
|
||||
}
|
||||
// support global --v-next flag
|
||||
if (extractBooleanFlag(cmdSlice, '--v-next')) {
|
||||
const { version } = await import('../package.json');
|
||||
const { inc } = await import('semver');
|
||||
process.env.BALENA_CLI_VERSION_OVERRIDE = inc(version, 'major') || '';
|
||||
}
|
||||
unsupportedFlag = extractBooleanFlag(cmdSlice, '--unsupported');
|
||||
}
|
||||
|
||||
|
@ -207,35 +207,6 @@ export async function selectOrganization(organizations?: Organization[]) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function awaitDevice(uuid: string) {
|
||||
const balena = getBalenaSdk();
|
||||
const deviceName = await balena.models.device.getName(uuid);
|
||||
const visuals = getVisuals();
|
||||
const spinner = new visuals.Spinner(
|
||||
`Waiting for ${deviceName} to come online`,
|
||||
);
|
||||
|
||||
const poll = async (): Promise<void> => {
|
||||
const isOnline = await balena.models.device.isOnline(uuid);
|
||||
if (isOnline) {
|
||||
spinner.stop();
|
||||
console.info(`The device **${deviceName}** is online!`);
|
||||
return;
|
||||
} else {
|
||||
// Spinner implementation is smart enough to
|
||||
// not start again if it was already started
|
||||
spinner.start();
|
||||
|
||||
await delay(3000);
|
||||
await poll();
|
||||
}
|
||||
};
|
||||
|
||||
console.info(`Waiting for ${deviceName} to connect to balena...`);
|
||||
await poll();
|
||||
return uuid;
|
||||
}
|
||||
|
||||
export async function awaitDeviceOsUpdate(
|
||||
uuid: string,
|
||||
targetOsVersion: string,
|
||||
|
@ -212,7 +212,7 @@ function handleBuilderMetadata(obj: BuilderMessage, build: RemoteBuild) {
|
||||
}
|
||||
|
||||
const value = match[1];
|
||||
const amount = match[2] || 1;
|
||||
const amount = Number(match[2]) || 1;
|
||||
|
||||
switch (value) {
|
||||
case 'erase':
|
||||
|
@ -77,7 +77,7 @@ export async function exec(
|
||||
.on('error', reject)
|
||||
.on('close', resolve);
|
||||
|
||||
if (stdout) {
|
||||
if (stdout && ps.stdout) {
|
||||
ps.stdout.pipe(stdout);
|
||||
}
|
||||
});
|
||||
|
@ -98,7 +98,7 @@ async function spawnAndPipe(
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
if (stderr) {
|
||||
if (stderr && ps.stderr) {
|
||||
ps.stderr.pipe(stderr);
|
||||
}
|
||||
});
|
||||
|
365
npm-shrinkwrap.json
generated
365
npm-shrinkwrap.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "balena-cli",
|
||||
"version": "13.1.8",
|
||||
"version": "13.1.13",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -1321,14 +1321,6 @@
|
||||
"tslint-no-unused-expression-chai": "^0.1.4",
|
||||
"typescript": "^4.2.4",
|
||||
"yargs": "^16.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "12.20.37",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.37.tgz",
|
||||
"integrity": "sha512-i1KGxqcvJaLQali+WuypQnXwcplhtNtjs66eNsZpp2P2FL/trJJxx/VWsM0YCL2iMoIJrbXje48lvIQAQ4p2ZA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@balena/node-beaglebone-usbboot": {
|
||||
@ -1567,6 +1559,202 @@
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@oclif/core": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@oclif/core/-/core-1.0.10.tgz",
|
||||
"integrity": "sha512-L+IcNU3NoYxwz5hmHfcUlOJ3dpgHRsIj1kAmI9CKEJHq5gBVKlP44Ot179Jke1jKRKX2g9N42izbmlh0SNpkkw==",
|
||||
"requires": {
|
||||
"@oclif/linewrap": "^1.0.0",
|
||||
"chalk": "^4.1.2",
|
||||
"clean-stack": "^3.0.1",
|
||||
"cli-ux": "6.0.5",
|
||||
"debug": "^4.3.3",
|
||||
"fs-extra": "^9.1.0",
|
||||
"get-package-type": "^0.1.0",
|
||||
"globby": "^11.0.4",
|
||||
"indent-string": "^4.0.0",
|
||||
"is-wsl": "^2.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"semver": "^7.3.5",
|
||||
"string-width": "^4.2.3",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"tslib": "^2.3.1",
|
||||
"widest-line": "^3.1.0",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-escapes": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
|
||||
"integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
|
||||
"requires": {
|
||||
"type-fest": "^0.21.3"
|
||||
}
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"clean-stack": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz",
|
||||
"integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==",
|
||||
"requires": {
|
||||
"escape-string-regexp": "4.0.0"
|
||||
}
|
||||
},
|
||||
"cli-progress": {
|
||||
"version": "3.9.1",
|
||||
"resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.9.1.tgz",
|
||||
"integrity": "sha512-AXxiCe2a0Lm0VN+9L0jzmfQSkcZm5EYspfqXKaSIQKqIk+0hnkZ3/v1E9B39mkD6vYhKih3c/RPsJBSwq9O99Q==",
|
||||
"requires": {
|
||||
"colors": "^1.1.2",
|
||||
"string-width": "^4.2.0"
|
||||
}
|
||||
},
|
||||
"cli-ux": {
|
||||
"version": "6.0.5",
|
||||
"resolved": "https://registry.npmjs.org/cli-ux/-/cli-ux-6.0.5.tgz",
|
||||
"integrity": "sha512-q2pvzDiXMNISMqCBh0P2dkofQ/8OiWlEAjl6MDNk5oUZ6p54Fnk1rOaXxohYm+YkLX5YNUonGOrwkvuiwVreIg==",
|
||||
"requires": {
|
||||
"@oclif/core": "^1.0.8",
|
||||
"@oclif/linewrap": "^1.0.0",
|
||||
"@oclif/screen": "^1.0.4 ",
|
||||
"ansi-escapes": "^4.3.0",
|
||||
"ansi-styles": "^4.2.0",
|
||||
"cardinal": "^2.1.1",
|
||||
"chalk": "^4.1.0",
|
||||
"clean-stack": "^3.0.0",
|
||||
"cli-progress": "^3.9.1",
|
||||
"extract-stack": "^2.0.0",
|
||||
"fs-extra": "^8.1",
|
||||
"hyperlinker": "^1.0.0",
|
||||
"indent-string": "^4.0.0",
|
||||
"is-wsl": "^2.2.0",
|
||||
"js-yaml": "^3.13.1",
|
||||
"lodash": "^4.17.21",
|
||||
"natural-orderby": "^2.0.1",
|
||||
"object-treeify": "^1.1.4",
|
||||
"password-prompt": "^1.1.2",
|
||||
"semver": "^7.3.2",
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"supports-color": "^8.1.0",
|
||||
"supports-hyperlinks": "^2.1.0",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"fs-extra": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
||||
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
|
||||
"requires": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^4.0.0",
|
||||
"universalify": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
|
||||
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
|
||||
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
|
||||
},
|
||||
"globby": {
|
||||
"version": "11.0.4",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz",
|
||||
"integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==",
|
||||
"requires": {
|
||||
"array-union": "^2.1.0",
|
||||
"dir-glob": "^3.0.1",
|
||||
"fast-glob": "^3.1.1",
|
||||
"ignore": "^5.1.4",
|
||||
"merge2": "^1.3.0",
|
||||
"slash": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||
},
|
||||
"type-fest": {
|
||||
"version": "0.21.3",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
|
||||
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="
|
||||
},
|
||||
"universalify": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@oclif/errors": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@oclif/errors/-/errors-1.3.3.tgz",
|
||||
@ -2840,9 +3028,9 @@
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "10.17.60",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
|
||||
"integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="
|
||||
"version": "12.20.42",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.42.tgz",
|
||||
"integrity": "sha512-aI3/oo5DzyiI5R/xAhxxRzfZlWlsbbqdgxfTPkqu/Zt+23GXiJvMCyPJT4+xKSXOnLqoL8jJYMLTwvK2M3a5hw=="
|
||||
},
|
||||
"@types/node-cleanup": {
|
||||
"version": "2.1.2",
|
||||
@ -3773,6 +3961,11 @@
|
||||
"rimraf": "^3.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "10.17.60",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
|
||||
"integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="
|
||||
},
|
||||
"balena-sdk": {
|
||||
"version": "15.59.2",
|
||||
"resolved": "https://registry.npmjs.org/balena-sdk/-/balena-sdk-15.59.2.tgz",
|
||||
@ -3818,12 +4011,12 @@
|
||||
}
|
||||
},
|
||||
"balena-preload": {
|
||||
"version": "11.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balena-preload/-/balena-preload-11.0.0.tgz",
|
||||
"integrity": "sha512-2LuPTw6LoVxuasGhn+cLyA5n7kjvwBtQmBsg08KNhcbEpWLkzrV5jhpNxlMPUuoC2xcMv5Rcg6FWbY87QV5zYQ==",
|
||||
"version": "12.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balena-preload/-/balena-preload-12.0.0.tgz",
|
||||
"integrity": "sha512-BD4ayIqqopJB0KFFjjlz0rIpcbbHojG8El8qOBLJHvidatgtgVs5xFWBoF5B7fgdJdjRsclA/AbUMZwovN7t3w==",
|
||||
"requires": {
|
||||
"archiver": "^3.1.1",
|
||||
"balena-sdk": "^15.44.0",
|
||||
"balena-sdk": "^16.0.0",
|
||||
"bluebird": "^3.7.2",
|
||||
"compare-versions": "^3.6.0",
|
||||
"docker-progress": "^5.0.0",
|
||||
@ -3854,32 +4047,6 @@
|
||||
"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",
|
||||
@ -3951,11 +4118,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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",
|
||||
@ -4047,6 +4209,11 @@
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "10.17.60",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
|
||||
"integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="
|
||||
},
|
||||
"balena-hup-action-utils": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/balena-hup-action-utils/-/balena-hup-action-utils-4.1.0.tgz",
|
||||
@ -4113,6 +4280,13 @@
|
||||
"@types/node": "^10.17.26",
|
||||
"balena-errors": "^4.7.1",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "10.17.60",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
|
||||
"integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"balena-sync": {
|
||||
@ -4141,6 +4315,11 @@
|
||||
"typed-error": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "10.17.60",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
|
||||
"integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="
|
||||
},
|
||||
"balena-sdk": {
|
||||
"version": "15.59.2",
|
||||
"resolved": "https://registry.npmjs.org/balena-sdk/-/balena-sdk-15.59.2.tgz",
|
||||
@ -4290,9 +4469,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"big-integer": {
|
||||
"version": "1.6.50",
|
||||
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.50.tgz",
|
||||
"integrity": "sha512-+O2uoQWFRo8ysZNo/rjtri2jIwjr3XfeAgRjAUADRqGG+ZITvyn8J1kvXLTaKVr3hhGXk+f23tKfdzmklVM9vQ=="
|
||||
"version": "1.6.51",
|
||||
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
|
||||
"integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg=="
|
||||
},
|
||||
"binary": {
|
||||
"version": "0.3.0",
|
||||
@ -4934,27 +5113,26 @@
|
||||
}
|
||||
},
|
||||
"cli-ux": {
|
||||
"version": "5.6.3",
|
||||
"resolved": "https://registry.npmjs.org/cli-ux/-/cli-ux-5.6.3.tgz",
|
||||
"integrity": "sha512-/oDU4v8BiDjX2OKcSunGH0iGDiEtj2rZaGyqNuv9IT4CgcSMyVWAMfn0+rEHaOc4n9ka78B0wo1+N1QX89f7mw==",
|
||||
"version": "6.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cli-ux/-/cli-ux-6.0.6.tgz",
|
||||
"integrity": "sha512-CvL4qmV78VhnbyHTswGjpDSQtU+oj3hT9DP9L6yMOwiTiNv0nMjMEV/8zou4CSqO6PtZ2A8qnlZDgAc07Js+aw==",
|
||||
"requires": {
|
||||
"@oclif/command": "^1.6.0",
|
||||
"@oclif/errors": "^1.2.1",
|
||||
"@oclif/core": "1.0.10",
|
||||
"@oclif/linewrap": "^1.0.0",
|
||||
"@oclif/screen": "^1.0.3",
|
||||
"@oclif/screen": "^1.0.4 ",
|
||||
"ansi-escapes": "^4.3.0",
|
||||
"ansi-styles": "^4.2.0",
|
||||
"cardinal": "^2.1.1",
|
||||
"chalk": "^4.1.0",
|
||||
"clean-stack": "^3.0.0",
|
||||
"cli-progress": "^3.4.0",
|
||||
"cli-progress": "^3.9.1",
|
||||
"extract-stack": "^2.0.0",
|
||||
"fs-extra": "^8.1",
|
||||
"hyperlinker": "^1.0.0",
|
||||
"indent-string": "^4.0.0",
|
||||
"is-wsl": "^2.2.0",
|
||||
"js-yaml": "^3.13.1",
|
||||
"lodash": "^4.17.11",
|
||||
"lodash": "^4.17.21",
|
||||
"natural-orderby": "^2.0.1",
|
||||
"object-treeify": "^1.1.4",
|
||||
"password-prompt": "^1.1.2",
|
||||
@ -4975,9 +5153,9 @@
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
|
||||
"integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
@ -4993,6 +5171,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli-progress": {
|
||||
"version": "3.9.1",
|
||||
"resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.9.1.tgz",
|
||||
"integrity": "sha512-AXxiCe2a0Lm0VN+9L0jzmfQSkcZm5EYspfqXKaSIQKqIk+0hnkZ3/v1E9B39mkD6vYhKih3c/RPsJBSwq9O99Q==",
|
||||
"requires": {
|
||||
"colors": "^1.1.2",
|
||||
"string-width": "^4.2.0"
|
||||
}
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
||||
@ -8134,6 +8321,11 @@
|
||||
"has-symbols": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"get-package-type": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
|
||||
"integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q=="
|
||||
},
|
||||
"get-port": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz",
|
||||
@ -13522,11 +13714,11 @@
|
||||
"integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg=="
|
||||
},
|
||||
"prettyjson": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prettyjson/-/prettyjson-1.2.1.tgz",
|
||||
"integrity": "sha1-/P+rQdGcq0365eV15kJGYZsS0ok=",
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/prettyjson/-/prettyjson-1.2.5.tgz",
|
||||
"integrity": "sha512-rksPWtoZb2ZpT5OVgtmy0KHVM+Dca3iVwWY9ifwhcexfjebtgjg3wmrUt9PvJ59XIYBcknQeYHD8IAnVlh9lAw==",
|
||||
"requires": {
|
||||
"colors": "^1.1.2",
|
||||
"colors": "1.4.0",
|
||||
"minimist": "^1.2.0"
|
||||
}
|
||||
},
|
||||
@ -14736,6 +14928,11 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "10.17.60",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
|
||||
"integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="
|
||||
},
|
||||
"bl": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz",
|
||||
@ -14931,11 +15128,35 @@
|
||||
}
|
||||
},
|
||||
"resin-doodles": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/resin-doodles/-/resin-doodles-0.1.1.tgz",
|
||||
"integrity": "sha1-0AmnndrHEhtFDHkUw2Jip4akBe4=",
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/resin-doodles/-/resin-doodles-0.2.0.tgz",
|
||||
"integrity": "sha512-k6qfrsXoQC9Xj/iRfceMYeFJKvY94H0AzTmrHvneUgAuWQD6nBUDSRLPEqWFOQlEW/mGUYMFSsRYtYDAsUR3ow==",
|
||||
"requires": {
|
||||
"colors": "^1.1.2"
|
||||
"chalk": "^4.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"resin-multibuild": {
|
||||
@ -16858,9 +17079,9 @@
|
||||
}
|
||||
},
|
||||
"tmp-promise": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.2.tgz",
|
||||
"integrity": "sha512-OyCLAKU1HzBjL6Ev3gxUeraJNlbNingmi8IrHHEsYH8LTmEuhvYfqvhn2F/je+mjf4N58UmZ96OMEy1JanSCpA==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz",
|
||||
"integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==",
|
||||
"requires": {
|
||||
"tmp": "^0.2.0"
|
||||
}
|
||||
|
16
package.json
16
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "balena-cli",
|
||||
"version": "13.1.8",
|
||||
"version": "13.1.13",
|
||||
"description": "The official balena Command Line Interface",
|
||||
"main": "./build/app.js",
|
||||
"homepage": "https://github.com/balena-io/balena-cli",
|
||||
@ -90,7 +90,7 @@
|
||||
"author": "Balena Inc. (https://balena.io/)",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=12.0.0 <13.0.0",
|
||||
"node": ">=12.8.0 <13.0.0",
|
||||
"npm": "<7.0.0"
|
||||
},
|
||||
"husky": {
|
||||
@ -147,7 +147,7 @@
|
||||
"@types/ndjson": "^2.0.1",
|
||||
"@types/net-keepalive": "^0.4.1",
|
||||
"@types/nock": "^11.1.0",
|
||||
"@types/node": "^10.17.60",
|
||||
"@types/node": "^12.20.42",
|
||||
"@types/node-cleanup": "^2.1.2",
|
||||
"@types/parse-link-header": "^1.0.1",
|
||||
"@types/prettyjson": "^0.0.30",
|
||||
@ -206,7 +206,7 @@
|
||||
"balena-errors": "^4.7.1",
|
||||
"balena-image-fs": "^7.0.6",
|
||||
"balena-image-manager": "^7.1.1",
|
||||
"balena-preload": "^11.0.0",
|
||||
"balena-preload": "^12.0.0",
|
||||
"balena-release": "^3.2.0",
|
||||
"balena-sdk": "^16.9.0",
|
||||
"balena-semver": "^2.3.0",
|
||||
@ -218,8 +218,8 @@
|
||||
"chalk": "^3.0.0",
|
||||
"chokidar": "^3.5.2",
|
||||
"cli-truncate": "^2.1.0",
|
||||
"cli-ux": "^5.5.1",
|
||||
"color-hash": "^1.1.1",
|
||||
"cli-ux": "^6.0.5",
|
||||
"columnify": "^1.5.2",
|
||||
"common-tags": "^1.7.2",
|
||||
"denymount": "^2.3.0",
|
||||
@ -257,14 +257,14 @@
|
||||
"oclif": "^1.18.4",
|
||||
"open": "^7.1.0",
|
||||
"patch-package": "^6.4.7",
|
||||
"prettyjson": "^1.1.3",
|
||||
"prettyjson": "^1.2.5",
|
||||
"progress-stream": "^2.0.0",
|
||||
"reconfix": "^1.0.0-v0-1-0-fork-46760acff4d165f5238bfac5e464256ef1944476",
|
||||
"request": "^2.88.2",
|
||||
"resin-cli-form": "^2.0.2",
|
||||
"resin-cli-visuals": "^1.8.0",
|
||||
"resin-compose-parse": "^2.1.3",
|
||||
"resin-doodles": "^0.1.1",
|
||||
"resin-doodles": "^0.2.0",
|
||||
"resin-multibuild": "^4.12.2",
|
||||
"resin-stream-logger": "^0.1.2",
|
||||
"rimraf": "^3.0.2",
|
||||
@ -287,6 +287,6 @@
|
||||
"windosu": "^0.3.0"
|
||||
},
|
||||
"versionist": {
|
||||
"publishedAt": "2022-01-11T00:59:23.183Z"
|
||||
"publishedAt": "2022-02-10T11:50:34.458Z"
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ import * as path from 'path';
|
||||
import { apiResponsePath, BalenaAPIMock } from '../../nock/balena-api-mock';
|
||||
import { cleanOutput, runCommand } from '../../helpers';
|
||||
|
||||
import { isV14 } from '../../../lib/utils/version';
|
||||
|
||||
describe('balena device', function () {
|
||||
let api: BalenaAPIMock;
|
||||
|
||||
@ -57,9 +59,16 @@ describe('balena device', function () {
|
||||
|
||||
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('org/test app');
|
||||
if (isV14()) {
|
||||
expect(lines).to.have.lengthOf(26);
|
||||
expect(lines[0]).to.equal('sparkling-wood');
|
||||
expect(lines[2].split(':')[0].trim()).to.equal('Id');
|
||||
expect(lines[2].split(':')[1].trim()).to.equal('1747415');
|
||||
} else {
|
||||
expect(lines).to.have.lengthOf(25);
|
||||
expect(lines[0]).to.equal('== SPARKLING WOOD');
|
||||
expect(lines[6].split(':')[1].trim()).to.equal('org/test app');
|
||||
}
|
||||
});
|
||||
|
||||
it.skip('correctly handles devices with missing fields', async () => {
|
||||
@ -79,14 +88,20 @@ describe('balena device', function () {
|
||||
|
||||
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('org/test app');
|
||||
if (isV14()) {
|
||||
expect(lines).to.have.lengthOf(15);
|
||||
expect(lines[0]).to.equal('sparkling-wood');
|
||||
expect(lines[7].split(':')[1].trim()).to.equal('org/test app');
|
||||
} else {
|
||||
expect(lines).to.have.lengthOf(14);
|
||||
expect(lines[0]).to.equal('== SPARKLING WOOD');
|
||||
expect(lines[6].split(':')[1].trim()).to.equal('org/test app');
|
||||
}
|
||||
});
|
||||
|
||||
it('correctly handles devices with missing application', async () => {
|
||||
// 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.
|
||||
it.skip('correctly handles devices with missing fleet', async () => {
|
||||
// Devices with missing fleets will have fleet name set to `N/a`.
|
||||
// e.g. When user has a device associated with fleet that user is no longer a collaborator of.
|
||||
api.scope
|
||||
.get(
|
||||
/^\/v6\/device\?.+&\$expand=belongs_to__application\(\$select=app_name,slug\)/,
|
||||
@ -103,8 +118,15 @@ describe('balena device', function () {
|
||||
|
||||
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');
|
||||
if (isV14()) {
|
||||
expect(lines).to.have.lengthOf(26);
|
||||
expect(lines[0]).to.equal('sparkling-wood');
|
||||
expect(lines[9].split(':')[0].trim()).to.equal('Fleet');
|
||||
expect(lines[9].split(':')[1].trim()).to.equal('N/a');
|
||||
} else {
|
||||
expect(lines).to.have.lengthOf(25);
|
||||
expect(lines[0]).to.equal('== SPARKLING WOOD');
|
||||
expect(lines[6].split(':')[1].trim()).to.equal('N/a');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -21,6 +21,8 @@ import * as path from 'path';
|
||||
import { apiResponsePath, BalenaAPIMock } from '../../nock/balena-api-mock';
|
||||
import { cleanOutput, runCommand } from '../../helpers';
|
||||
|
||||
import { isV14 } from '../../../lib/utils/version';
|
||||
|
||||
describe('balena devices', function () {
|
||||
let api: BalenaAPIMock;
|
||||
|
||||
@ -48,15 +50,24 @@ describe('balena devices', function () {
|
||||
|
||||
const lines = cleanOutput(out);
|
||||
|
||||
expect(lines[0].replace(/ +/g, ' ')).to.equal(
|
||||
'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('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;
|
||||
if (isV14()) {
|
||||
expect(lines[0].replace(/ +/g, ' ')).to.equal(
|
||||
' Id Uuid Device name Device type Fleet Status Is online Supervisor version Os version Dashboard url ',
|
||||
);
|
||||
expect(lines).to.have.lengthOf.at.least(3);
|
||||
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;
|
||||
} else {
|
||||
expect(lines[0].replace(/ +/g, ' ')).to.equal(
|
||||
'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('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;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019-2021 Balena Ltd.
|
||||
* Copyright 2019 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -19,6 +19,7 @@ import { expect } from 'chai';
|
||||
|
||||
import { BalenaAPIMock } from '../../nock/balena-api-mock';
|
||||
import { cleanOutput, runCommand } from '../../helpers';
|
||||
import { isV14 } from '../../../lib/utils/version';
|
||||
|
||||
describe('balena devices supported', function () {
|
||||
let api: BalenaAPIMock;
|
||||
@ -50,7 +51,10 @@ describe('balena devices supported', function () {
|
||||
|
||||
const lines = cleanOutput(out, true);
|
||||
|
||||
expect(lines[0]).to.equal('SLUG ALIASES ARCH NAME');
|
||||
expect(lines[0]).to.equal(
|
||||
isV14() ? ' Slug Aliases Arch Name ' : '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(
|
||||
|
261
tests/commands/env/envs.spec.ts
vendored
261
tests/commands/env/envs.spec.ts
vendored
@ -19,7 +19,9 @@ import { expect } from 'chai';
|
||||
import { stripIndent } from '../../../build/utils/lazy';
|
||||
|
||||
import { BalenaAPIMock } from '../../nock/balena-api-mock';
|
||||
import { runCommand } from '../../helpers';
|
||||
import { runCommand, removeFirstNLines, trimLines } from '../../helpers';
|
||||
|
||||
import { isV14 } from '../../../lib/utils/version';
|
||||
|
||||
describe('balena envs', function () {
|
||||
const appName = 'test';
|
||||
@ -48,15 +50,30 @@ describe('balena envs', function () {
|
||||
|
||||
const { out, err } = await runCommand(`envs -f ${appName}`);
|
||||
|
||||
expect(out.join('')).to.equal(
|
||||
stripIndent`
|
||||
if (isV14()) {
|
||||
let output = out.join('');
|
||||
output = trimLines(removeFirstNLines(output, 2));
|
||||
|
||||
const expected =
|
||||
stripIndent`
|
||||
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(output).to.equal(expected);
|
||||
} else {
|
||||
expect(out.join('')).to.equal(
|
||||
stripIndent`
|
||||
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('');
|
||||
});
|
||||
|
||||
@ -66,12 +83,24 @@ describe('balena envs', function () {
|
||||
|
||||
const { out, err } = await runCommand(`envs -f ${appName} --config`);
|
||||
|
||||
expect(out.join('')).to.equal(
|
||||
stripIndent`
|
||||
if (isV14()) {
|
||||
let output = out.join('');
|
||||
output = trimLines(removeFirstNLines(output, 2));
|
||||
|
||||
const expected =
|
||||
stripIndent`
|
||||
120300 RESIN_SUPERVISOR_NATIVE_LOGGER false gh_user/testApp
|
||||
` + '\n';
|
||||
|
||||
expect(output).to.equal(expected);
|
||||
} else {
|
||||
expect(out.join('')).to.equal(
|
||||
stripIndent`
|
||||
ID NAME VALUE FLEET
|
||||
120300 RESIN_SUPERVISOR_NATIVE_LOGGER false gh_user/testApp
|
||||
` + '\n',
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
expect(err.join('')).to.equal('');
|
||||
});
|
||||
@ -82,15 +111,19 @@ describe('balena envs', function () {
|
||||
|
||||
const { out, err } = await runCommand(`envs -cjf ${appName}`);
|
||||
|
||||
expect(JSON.parse(out.join(''))).to.deep.equal([
|
||||
{
|
||||
fleet: 'gh_user/testApp',
|
||||
id: 120300,
|
||||
name: 'RESIN_SUPERVISOR_NATIVE_LOGGER',
|
||||
value: 'false',
|
||||
},
|
||||
]);
|
||||
expect(err.join('')).to.equal('');
|
||||
if (isV14()) {
|
||||
// TODO: Add tests once oclif json issue resolved.
|
||||
} else {
|
||||
expect(JSON.parse(out.join(''))).to.deep.equal([
|
||||
{
|
||||
fleet: 'gh_user/testApp',
|
||||
id: 120300,
|
||||
name: 'RESIN_SUPERVISOR_NATIVE_LOGGER',
|
||||
value: 'false',
|
||||
},
|
||||
]);
|
||||
expect(err.join('')).to.equal('');
|
||||
}
|
||||
});
|
||||
|
||||
it('should successfully list service variables for a test fleet (-s flag)', async () => {
|
||||
@ -104,14 +137,28 @@ describe('balena envs', function () {
|
||||
`envs -f ${appName} -s ${serviceName}`,
|
||||
);
|
||||
|
||||
expect(out.join('')).to.equal(
|
||||
stripIndent`
|
||||
if (isV14()) {
|
||||
let output = out.join('');
|
||||
output = trimLines(removeFirstNLines(output, 2));
|
||||
|
||||
const expected =
|
||||
stripIndent`
|
||||
120111 svar2 svar2-value gh_user/testApp service2
|
||||
120101 var1 var1-val gh_user/testApp *
|
||||
120102 var2 22 gh_user/testApp *
|
||||
` + '\n';
|
||||
|
||||
expect(output).to.equal(expected);
|
||||
} else {
|
||||
expect(out.join('')).to.equal(
|
||||
stripIndent`
|
||||
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('');
|
||||
});
|
||||
|
||||
@ -126,14 +173,28 @@ describe('balena envs', function () {
|
||||
`envs -f ${appName} -s ${serviceName}`,
|
||||
);
|
||||
|
||||
expect(out.join('')).to.equal(
|
||||
stripIndent`
|
||||
if (isV14()) {
|
||||
let output = out.join('');
|
||||
output = trimLines(removeFirstNLines(output, 2));
|
||||
|
||||
const expected =
|
||||
stripIndent`
|
||||
120110 svar1 svar1-value gh_user/testApp ${serviceName}
|
||||
120101 var1 var1-val gh_user/testApp *
|
||||
120102 var2 22 gh_user/testApp *
|
||||
` + '\n';
|
||||
|
||||
expect(output).to.equal(expected);
|
||||
} else {
|
||||
expect(out.join('')).to.equal(
|
||||
stripIndent`
|
||||
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('');
|
||||
});
|
||||
|
||||
@ -148,8 +209,27 @@ describe('balena envs', function () {
|
||||
const uuid = shortUUID;
|
||||
const result = await runCommand(`envs -d ${uuid}`);
|
||||
let { out } = result;
|
||||
let expected =
|
||||
stripIndent`
|
||||
|
||||
if (isV14()) {
|
||||
let output = out.join('');
|
||||
output = trimLines(removeFirstNLines(output, 2));
|
||||
|
||||
const expected =
|
||||
stripIndent`
|
||||
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';
|
||||
|
||||
expect(output).to.equal(expected);
|
||||
} else {
|
||||
let expected =
|
||||
stripIndent`
|
||||
ID NAME VALUE FLEET DEVICE SERVICE
|
||||
120110 svar1 svar1-value org/test * service1
|
||||
120111 svar2 svar2-value org/test * service2
|
||||
@ -161,10 +241,10 @@ describe('balena envs', function () {
|
||||
120204 var4 44 org/test ${uuid} *
|
||||
` + '\n';
|
||||
|
||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||
expected = expected.replace(/ +/g, ' ');
|
||||
|
||||
expect(out.join('')).to.equal(expected);
|
||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||
expected = expected.replace(/ +/g, ' ');
|
||||
expect(out.join('')).to.equal(expected);
|
||||
}
|
||||
});
|
||||
|
||||
it('should successfully list env variables for a test device (JSON output)', async () => {
|
||||
@ -176,7 +256,11 @@ describe('balena envs', function () {
|
||||
api.expectGetDeviceServiceVars();
|
||||
|
||||
const { out, err } = await runCommand(`envs -jd ${shortUUID}`);
|
||||
const expected = `[
|
||||
|
||||
if (isV14()) {
|
||||
// TODO: Add tests once oclif json issue resolved.
|
||||
} else {
|
||||
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" },
|
||||
@ -187,7 +271,9 @@ describe('balena envs', function () {
|
||||
{ "id": 120204, "fleet": "org/test", "deviceUUID": "${fullUUID}", "name": "var4", "value": "44", "serviceName": "*" }
|
||||
]`;
|
||||
|
||||
expect(JSON.parse(out.join(''))).to.deep.equal(JSON.parse(expected));
|
||||
expect(JSON.parse(out.join(''))).to.deep.equal(JSON.parse(expected));
|
||||
}
|
||||
|
||||
expect(err.join('')).to.equal('');
|
||||
});
|
||||
|
||||
@ -199,17 +285,30 @@ describe('balena envs', function () {
|
||||
|
||||
const result = await runCommand(`envs -d ${shortUUID} --config`);
|
||||
let { out } = result;
|
||||
let expected =
|
||||
stripIndent`
|
||||
if (isV14()) {
|
||||
let output = out.join('');
|
||||
output = trimLines(removeFirstNLines(output, 2));
|
||||
|
||||
const expected =
|
||||
stripIndent`
|
||||
120300 RESIN_SUPERVISOR_NATIVE_LOGGER false org/test *
|
||||
120400 RESIN_SUPERVISOR_POLL_INTERVAL 900900 org/test ${shortUUID}
|
||||
` + '\n';
|
||||
|
||||
expect(output).to.equal(expected);
|
||||
} else {
|
||||
let expected =
|
||||
stripIndent`
|
||||
ID NAME VALUE FLEET DEVICE
|
||||
120300 RESIN_SUPERVISOR_NATIVE_LOGGER false org/test *
|
||||
120400 RESIN_SUPERVISOR_POLL_INTERVAL 900900 org/test ${shortUUID}
|
||||
` + '\n';
|
||||
|
||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||
expected = expected.replace(/ +/g, ' ');
|
||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||
expected = expected.replace(/ +/g, ' ');
|
||||
|
||||
expect(out.join('')).to.equal(expected);
|
||||
expect(out.join('')).to.equal(expected);
|
||||
}
|
||||
});
|
||||
|
||||
it('should successfully list service variables for a test device (-s flag)', async () => {
|
||||
@ -225,8 +324,25 @@ describe('balena envs', function () {
|
||||
const uuid = shortUUID;
|
||||
const result = await runCommand(`envs -d ${uuid} -s ${serviceName}`);
|
||||
let { out } = result;
|
||||
let expected =
|
||||
stripIndent`
|
||||
|
||||
if (isV14()) {
|
||||
let output = out.join('');
|
||||
output = trimLines(removeFirstNLines(output, 2));
|
||||
|
||||
const expected =
|
||||
stripIndent`
|
||||
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';
|
||||
|
||||
expect(output).to.equal(expected);
|
||||
} else {
|
||||
let expected =
|
||||
stripIndent`
|
||||
ID NAME VALUE FLEET DEVICE SERVICE
|
||||
120111 svar2 svar2-value org/test * service2
|
||||
120121 svar4 svar4-value org/test ${uuid} service2
|
||||
@ -236,10 +352,11 @@ describe('balena envs', function () {
|
||||
120204 var4 44 org/test ${uuid} *
|
||||
` + '\n';
|
||||
|
||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||
expected = expected.replace(/ +/g, ' ');
|
||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||
expected = expected.replace(/ +/g, ' ');
|
||||
|
||||
expect(out.join('')).to.equal(expected);
|
||||
expect(out.join('')).to.equal(expected);
|
||||
}
|
||||
});
|
||||
|
||||
it('should successfully list env and service variables for a test device (unknown fleet)', async () => {
|
||||
@ -250,8 +367,23 @@ describe('balena envs', function () {
|
||||
const uuid = shortUUID;
|
||||
const result = await runCommand(`envs -d ${uuid}`);
|
||||
let { out } = result;
|
||||
let expected =
|
||||
stripIndent`
|
||||
|
||||
if (isV14()) {
|
||||
let output = out.join('');
|
||||
output = trimLines(removeFirstNLines(output, 2));
|
||||
|
||||
const expected =
|
||||
stripIndent`
|
||||
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';
|
||||
|
||||
expect(output).to.equal(expected);
|
||||
} else {
|
||||
let expected =
|
||||
stripIndent`
|
||||
ID NAME VALUE FLEET DEVICE SERVICE
|
||||
120120 svar3 svar3-value N/A ${uuid} service1
|
||||
120121 svar4 svar4-value N/A ${uuid} service2
|
||||
@ -259,10 +391,11 @@ describe('balena envs', function () {
|
||||
120204 var4 44 N/A ${uuid} *
|
||||
` + '\n';
|
||||
|
||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||
expected = expected.replace(/ +/g, ' ');
|
||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||
expected = expected.replace(/ +/g, ' ');
|
||||
|
||||
expect(out.join('')).to.equal(expected);
|
||||
expect(out.join('')).to.equal(expected);
|
||||
}
|
||||
});
|
||||
|
||||
it('should successfully list env and service vars for a test device (-s flags)', async () => {
|
||||
@ -278,8 +411,24 @@ describe('balena envs', function () {
|
||||
const uuid = shortUUID;
|
||||
const result = await runCommand(`envs -d ${uuid} -s ${serviceName}`);
|
||||
let { out } = result;
|
||||
let expected =
|
||||
stripIndent`
|
||||
if (isV14()) {
|
||||
let output = out.join('');
|
||||
output = trimLines(removeFirstNLines(output, 2));
|
||||
|
||||
const expected =
|
||||
stripIndent`
|
||||
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';
|
||||
|
||||
expect(output).to.equal(expected);
|
||||
} else {
|
||||
let expected =
|
||||
stripIndent`
|
||||
ID NAME VALUE FLEET DEVICE SERVICE
|
||||
120110 svar1 svar1-value org/test * ${serviceName}
|
||||
120120 svar3 svar3-value org/test ${uuid} ${serviceName}
|
||||
@ -289,10 +438,11 @@ describe('balena envs', function () {
|
||||
120204 var4 44 org/test ${uuid} *
|
||||
` + '\n';
|
||||
|
||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||
expected = expected.replace(/ +/g, ' ');
|
||||
out = out.map((l) => l.replace(/ +/g, ' '));
|
||||
expected = expected.replace(/ +/g, ' ');
|
||||
|
||||
expect(out.join('')).to.equal(expected);
|
||||
expect(out.join('')).to.equal(expected);
|
||||
}
|
||||
});
|
||||
|
||||
it('should successfully list env and service vars for a test device (-js flags)', async () => {
|
||||
@ -308,7 +458,11 @@ describe('balena envs', function () {
|
||||
const { out, err } = await runCommand(
|
||||
`envs -d ${shortUUID} -js ${serviceName}`,
|
||||
);
|
||||
const expected = `[
|
||||
|
||||
if (isV14()) {
|
||||
// TODO: Add tests once oclif json issue resolved.
|
||||
} else {
|
||||
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}" },
|
||||
@ -317,7 +471,8 @@ describe('balena envs', function () {
|
||||
{ "id": 120204, "fleet": "org/test", "deviceUUID": "${fullUUID}", "name": "var4", "value": "44", "serviceName": "*" }
|
||||
]`;
|
||||
|
||||
expect(JSON.parse(out.join(''))).to.deep.equal(JSON.parse(expected));
|
||||
expect(err.join('')).to.equal('');
|
||||
expect(JSON.parse(out.join(''))).to.deep.equal(JSON.parse(expected));
|
||||
expect(err.join('')).to.equal('');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -20,6 +20,8 @@ import { expect } from 'chai';
|
||||
import { BalenaAPIMock } from '../nock/balena-api-mock';
|
||||
import { cleanOutput, runCommand } from '../helpers';
|
||||
|
||||
import { isV14 } from '../../lib/utils/version';
|
||||
|
||||
describe('balena release', function () {
|
||||
let api: BalenaAPIMock;
|
||||
|
||||
@ -34,7 +36,7 @@ describe('balena release', function () {
|
||||
api.done();
|
||||
});
|
||||
|
||||
it('should show release details', async () => {
|
||||
it.skip('should show release details', async () => {
|
||||
api.expectGetRelease();
|
||||
const { out } = await runCommand('release 27fda508c');
|
||||
const lines = cleanOutput(out);
|
||||
@ -44,7 +46,7 @@ describe('balena release', function () {
|
||||
expect(lines[1]).to.contain(' 90247b54de4fa7a0a3cbc85e73c68039');
|
||||
});
|
||||
|
||||
it('should return release composition', async () => {
|
||||
it.skip('should return release composition', async () => {
|
||||
api.expectGetRelease();
|
||||
const { out } = await runCommand('release 27fda508c --composition');
|
||||
const lines = cleanOutput(out);
|
||||
@ -61,8 +63,14 @@ describe('balena release', function () {
|
||||
api.expectGetApplication();
|
||||
const { out } = await runCommand('releases someapp');
|
||||
const lines = cleanOutput(out);
|
||||
expect(lines.length).to.be.equal(2);
|
||||
expect(lines[1]).to.contain('142334');
|
||||
expect(lines[1]).to.contain('90247b54de4fa7a0a3cbc85e73c68039');
|
||||
if (isV14()) {
|
||||
expect(lines.length).to.be.equal(3);
|
||||
expect(lines[2]).to.contain('142334');
|
||||
expect(lines[2]).to.contain('90247b54de4fa7a0a3cbc85e73c68039');
|
||||
} else {
|
||||
expect(lines.length).to.be.equal(2);
|
||||
expect(lines[1]).to.contain('142334');
|
||||
expect(lines[1]).to.contain('90247b54de4fa7a0a3cbc85e73c68039');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -160,8 +160,9 @@ async function startMockSshServer(): Promise<[Server, number]> {
|
||||
});
|
||||
|
||||
return new Promise<[Server, number]>((resolve, reject) => {
|
||||
// port 0: let the OS allocation any available TCP port number
|
||||
const listener = server.listen(0, '127.0.0.1', (err: Error) => {
|
||||
// TODO: remove 'as any' below. According to @types/node v12.20.42, the
|
||||
// callback type is `() => void`, but our code assumes `(err: Error) => void`
|
||||
const listener = (server.listen as any)(0, '127.0.0.1', (err: Error) => {
|
||||
// this callback is called for the 'listening' event
|
||||
if (err) {
|
||||
console.error(`Error starting mock ssh server:\n${err}`);
|
||||
|
@ -141,10 +141,10 @@ describe('DeprecationChecker', function () {
|
||||
|
||||
getStub.resolves(mockCache);
|
||||
|
||||
// Force isTTY to be false (undefined). It happens to be true when
|
||||
// Force isTTY to be false. It happens to be true when
|
||||
// the tests run on balenaCI on macOS and Linux.
|
||||
const originalIsTTY = process.stderr.isTTY;
|
||||
process.stderr.isTTY = undefined;
|
||||
process.stderr.isTTY = false;
|
||||
let result: TestOutput;
|
||||
try {
|
||||
result = await runCommand('version');
|
||||
|
@ -106,21 +106,6 @@ describe('outputDataSet', function () {
|
||||
expect(splitHeader[1]).to.include('thing');
|
||||
});
|
||||
|
||||
/*
|
||||
it('should output fields in the order specified in `fields` param', async () => {
|
||||
const fields = ['thing_color', 'id', 'name'];
|
||||
const options = {};
|
||||
|
||||
await outputDataSet(dataSet, fields, options);
|
||||
|
||||
const headerLine = printLineSpy.firstCall.firstArg.toLowerCase();
|
||||
// split header using the `it` column as delimiter
|
||||
const splitHeader = headerLine.split('id');
|
||||
expect(splitHeader[0]).to.include('thing');
|
||||
expect(splitHeader[1]).to.include('name');
|
||||
});
|
||||
*/
|
||||
|
||||
it('should only output fields specified in `options.fields` if present', async () => {
|
||||
const fields = ['name', 'id', 'thing_color', 'thing_shape'];
|
||||
const options = {
|
||||
@ -167,13 +152,41 @@ describe('outputDataSet', function () {
|
||||
expect(printLineSpy.getCall(0).firstArg).to.include('red');
|
||||
});
|
||||
|
||||
it(
|
||||
'should output `null` values using the provided value, ' +
|
||||
'if `options.displayNullValuesAs` is present',
|
||||
async () => {
|
||||
const fields = ['name', 'id', 'thing_color', 'thing_shape'];
|
||||
const nullValue = 'N/a';
|
||||
const options = {
|
||||
'no-header': true,
|
||||
displayNullValuesAs: nullValue,
|
||||
};
|
||||
|
||||
const extendedDataSet = [
|
||||
...dataSet,
|
||||
{
|
||||
name: 'item3',
|
||||
id: 3,
|
||||
thing_color: null,
|
||||
thing_shape: 'round',
|
||||
},
|
||||
];
|
||||
|
||||
await outputDataSet(extendedDataSet, fields, options);
|
||||
|
||||
expect(printLineSpy.callCount).to.equal(3);
|
||||
expect(printLineSpy.getCall(2).firstArg).to.include(nullValue);
|
||||
},
|
||||
);
|
||||
|
||||
it('should output data in json format, if `options.json` true', async () => {
|
||||
const fields = ['name', 'thing_color', 'thing_shape'];
|
||||
const options = {
|
||||
json: true,
|
||||
};
|
||||
|
||||
// TODO: I've run into an oclif cli-ux bug, where numbers are output as strings in json
|
||||
// TODO: I've run into an oclif cli-ux bug, where all types (number. bool etc.) are output as strings in json
|
||||
// (this can be seen by including 'id' in the fields list above).
|
||||
// Issue opened: https://github.com/oclif/cli-ux/issues/309
|
||||
// For now removing id for this test.
|
||||
|
@ -17,8 +17,8 @@
|
||||
|
||||
import * as _ from 'lodash';
|
||||
import * as path from 'path';
|
||||
|
||||
import * as packageJSON from '../package.json';
|
||||
import { getChalk } from '../lib/utils/lazy';
|
||||
|
||||
const balenaExe = process.platform === 'win32' ? 'balena.exe' : 'balena';
|
||||
const standalonePath = path.resolve(__dirname, '..', 'build-bin', balenaExe);
|
||||
@ -353,3 +353,65 @@ export async function switchSentry(
|
||||
return sentryStatus;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to an array of character codes
|
||||
* @param text the text to convert.
|
||||
* @returns an array of character codes representing the text.
|
||||
*/
|
||||
export function stringToCharCodes(text: string) {
|
||||
return text.split('').map((c) => {
|
||||
return c.charCodeAt(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove leaving and trailing whitespace from each lime of a string.
|
||||
* @param text the text to process
|
||||
* @returns a copy of the text with the lines trimmed.
|
||||
*/
|
||||
export function trimLines(text: string) {
|
||||
let lines = text.split('\n');
|
||||
lines = lines.map((l) => l.trim());
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Pad each line with characters at beginning and end.
|
||||
* @param text the text to pad.
|
||||
* @param startPad the string to prepend each line with.
|
||||
* @param endPad the string to append each line with.
|
||||
* @returns a copy of the text with the specified padding.
|
||||
*/
|
||||
export function padLines(text: string, startPad: string, endPad: string = '') {
|
||||
let lines = text.split('\n');
|
||||
lines = lines.map((l) => {
|
||||
return l === '' ? '' : `${startPad}${l}${endPad}`;
|
||||
});
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Format first nLines bold.
|
||||
* @param text the text to format
|
||||
* @param nLines number of liens to format (from top)
|
||||
* @returns a copy of the text with the specified number of top lines formatted bold.
|
||||
*/
|
||||
export function boldFirstNLines(text: string, nLines: number) {
|
||||
const chalk = getChalk();
|
||||
let lines = text.split('\n');
|
||||
lines = lines.map((l, i) => {
|
||||
return i < nLines ? chalk.bold(l) : l;
|
||||
});
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns first nLines bold.
|
||||
* @param text the text to format
|
||||
* @param nLines number of liens to format (from top)
|
||||
* @returns a copy of the text with the first nLines removed.
|
||||
*/
|
||||
export function removeFirstNLines(text: string, nLines: number) {
|
||||
return text.split('\n').slice(nLines).join(`\n`);
|
||||
}
|
||||
|
@ -113,7 +113,9 @@ async function createProxyServer(): Promise<[number, number]> {
|
||||
let proxyPort = 0; // TCP port number, 0 means automatic allocation
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const listener = server.listen(0, '127.0.0.1', (err: Error) => {
|
||||
// TODO: remove 'as any' below. According to @types/node v12.20.42, the
|
||||
// callback type is `() => void`, but our code assumes `(err: Error) => void`
|
||||
const listener = (server.listen as any)(0, '127.0.0.1', (err: Error) => {
|
||||
if (err) {
|
||||
console.error(`Error starting proxy server:\n${err}`);
|
||||
reject(err);
|
||||
@ -195,7 +197,9 @@ async function createInterceptorServer(): Promise<number> {
|
||||
let interceptorPort = 0;
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const listener = server.listen(0, '127.0.0.1', (err: Error) => {
|
||||
// TODO: remove 'as any' below. According to @types/node v12.20.42, the
|
||||
// callback type is `() => void`, but our code assumes `(err: Error) => void`
|
||||
const listener = (server.listen as any)(0, '127.0.0.1', (err: Error) => {
|
||||
if (err) {
|
||||
console.error(`Error starting interceptor server:\n${err}`);
|
||||
reject(err);
|
||||
|
Reference in New Issue
Block a user