Compare commits

...

47 Commits

Author SHA1 Message Date
e8bc43dc64 v15.2.0 2023-04-05 13:09:24 +00:00
1213689de2 Merge pull request #2606 from balena-io/update-balena-sdk-16.40.0
Add support for device restarts in open-balena
2023-04-05 13:08:14 +00:00
c1017e8e27 Add support for device restarts in open-balena
Update balena-sdk from 16.28.2 to 16.40.0

Change-type: minor
2023-04-05 12:57:33 +03:00
7ad9e685f6 v15.1.3 2023-04-05 08:06:56 +00:00
c778aaffaf Merge pull request #2607 from balena-io/update-balena-sdk-16.28.2
devices supported: Fix showing types without a valid & finalized release
2023-04-05 08:06:05 +00:00
b98047cacf devices supported: Fix showing types without a valid & finalized release
Update balena-sdk from 16.28.0 to 16.28.2

Resolves: #2524
Change-type: patch
2023-04-05 10:19:39 +03:00
03ace6e4b2 v15.1.2 2023-03-27 15:14:47 +00:00
9b4701bcb7 Merge pull request #2601 from balena-io/use-satisfies
Improve type checking by using the satisfies operator
2023-03-27 18:13:56 +03:00
174312977a Improve type checking by using the satisfies operator
Change-type: patch
2023-03-27 16:39:09 +03:00
963d9af817 v15.1.1 2023-03-17 10:20:03 +00:00
af5ec51232 Merge pull request #2600 from balena-io/bump-ts
Update TypeScript to 5.0.2
2023-03-17 12:19:13 +02:00
1cd9fbf6a0 Update TypeScript to 5.0.2
Change-type: patch
2023-03-16 20:53:08 +02:00
72639e9e59 v15.1.0 2023-03-14 20:19:08 +00:00
447dcc1480 Merge pull request #2599 from balena-io/kyle/balena-compose-v2.2.x
Update balena-compose to v2.2.1
2023-03-14 16:18:19 -04:00
564716faa7 Update balena-compose to v2.2.1
Update balena-compose from 2.1.1 to 2.2.1

Change-type: minor
Signed-off-by: Kyle Harding <kyle@balena.io>
2023-03-14 14:59:52 -04:00
3e5b4457c2 v15.0.6 2023-03-13 14:03:48 +00:00
793e70d909 Merge pull request #2597 from balena-io/explicitly-select-devices-fields
Devices: explicitly fetches only used fields
2023-03-13 11:02:51 -03:00
5761a306be Devices: explicitly fetches only used fields
Change-type: patch
2023-03-13 09:35:43 -03:00
adff0f2a0a v15.0.5 2023-03-10 16:25:40 +00:00
4ec45a0c43 Merge pull request #2596 from balena-io/fix-is-legacy-check
Fix isLegacy check which should always relay on the slug
2023-03-10 18:24:48 +02:00
ecf4b046b5 Fix application isLegacy check for rename and deploy
Change-type: patch
2023-03-10 16:33:00 +01:00
b0cae93ac9 v15.0.4 2023-02-21 07:24:19 +00:00
53b66678d4 Merge pull request #2583 from balena-io/hraftery-patch-1-1
Clarify update rate of update-notifier info
2023-02-21 09:23:30 +02:00
0b9b65ef88 patch: Clarify update rate of update notifier info
If the cli has not been run in a while, it will show old update information. It's not obvious why, and this might lead to confusion. So this commit just adds a comment to clarify that out-of-date update notifier info is expected behaviour, and why.
2023-01-26 14:15:43 +11:00
8a84d9d792 v15.0.3 2023-01-18 16:16:39 +00:00
c535b8e1ea Merge pull request #2582 from balena-io/https-npm
Use https for the npm deprecation check, avoiding a redirect
2023-01-18 16:15:01 +00:00
234fb6cd39 Use https for the npm deprecation check, avoiding a redirect
Change-type: patch
2023-01-18 13:11:31 +00:00
8714830b48 v15.0.2 2023-01-14 07:35:13 +00:00
0e07b36691 Merge pull request #2580 from balena-io/joshbwlng/fix-typo
Fix push --nolive doc typo
2023-01-14 09:33:56 +02:00
ba80d3c38c Fix push --nolive doc typo
Change-type: patch
2023-01-13 13:36:44 +09:00
e65dc82cfe v15.0.1 2023-01-10 13:43:24 +00:00
bc727521c6 Merge pull request #2571 from balena-io/nodejs-14
Update to Node 14
2023-01-10 08:41:54 -05:00
a8c0c884d3 Extra linting 2023-01-03 16:08:10 -03:00
b11c7157d3 Update to node 14 2023-01-03 16:04:24 -03:00
578de7bcd4 Process livepush build logs inline
When using livepush, the CLI parses the build logs to obtain the stage
image ids, which are necessary for properly running livepush.

This process used to store the full log output in memory before parsing
the logs for obtaining the stage ids. We have seen this cause issues
before because of the excessive memory usage and it is one the suspects
of #2165, which is blocking the update to Node 14

Change-type: patch
2023-01-03 12:29:54 -03:00
cfc6b3ce9e v15.0.0 2023-01-02 15:21:59 +00:00
1c7a354fe7 Merge pull request #2573 from balena-io/balena-preload-13
Upgrade balena-preload to 13.0.0
2023-01-02 10:20:01 -05:00
40a0941ca3 preload: Drops ability to preload Intel Edison (EOL 2017)
Upgrade balena-preload from 12.2.0 to 13.0.0

Change-type: major
Signed-off-by: Edwin Joassart <edwin.joassart@balena.io>
2023-01-02 15:34:32 +01:00
0ab4760272 v14.5.18 2022-12-29 07:20:50 +00:00
42b2269e81 Merge pull request #2576 from balena-io/flowzone-npm-ci
Update flowzone tests to use npm ci
2022-12-29 02:19:24 -05:00
c818d846b3 Update flowzone tests to use npm ci
Will also make sure that the shrinkwrap is
matching the committed package.json.

Change-type: patch
2022-12-29 08:24:24 +02:00
3328f40416 v14.5.17 2022-12-28 23:56:12 +00:00
58d10c1908 Merge pull request #2575 from balena-io/drop-balena-sync
Stop using the deprecated balena-sync module
2022-12-29 01:54:48 +02:00
2fd0ca6a02 Stop using the deprecated balena-sync module
Change-type: patch
2022-12-29 01:05:51 +02:00
173028fd0d v14.5.16 2022-12-28 23:01:25 +00:00
62d5bf4436 Merge pull request #2574 from balena-io/align-package-json-shrinkwrap
Update the npm-shrinkwrap.json dependencies to match the package.json
2022-12-29 01:00:11 +02:00
63a0d19770 Update the npm-shrinkwrap.json dependencies to match the package.json
Change-type: patch
2022-12-28 21:22:48 +02:00
41 changed files with 2404 additions and 967 deletions

View File

@ -15,8 +15,7 @@ inputs:
default: "accounts+apple@balena.io"
NODE_VERSION:
type: string
# FIXME: (please) https://github.com/balena-io/balena-cli/issues/2165
default: "12.x"
default: "14.x"
VERBOSE:
type: string
default: "true"

View File

@ -12,8 +12,7 @@ inputs:
# --- custom environment
NODE_VERSION:
type: string
# FIXME: (please) https://github.com/balena-io/balena-cli/issues/2165
default: "12.x"
default: "14.x"
VERBOSE:
type: string
default: "true"
@ -36,7 +35,7 @@ runs:
[[ '${{ inputs.VERBOSE }}' =~ on|On|Yes|yes|true|True ]] && set -x
if [[ -e package-lock.json ]]; then
if [[ -e package-lock.json ]] || [[ -e npm-shrinkwrap.json ]]; then
npm ci
else
npm i

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,306 @@ 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/).
## 15.2.0 - 2023-04-05
<details>
<summary> Add support for device restarts in open-balena [Thodoris Greasidis] </summary>
> ### balena-sdk-16.40.0 - 2023-04-05
>
> * device.reboot: Fix the typings requiring a second argument [Thodoris Greasidis]
> * device.restartApplication: Use the supervisor endpoint to issue restarts [Thodoris Greasidis]
>
> ### balena-sdk-16.39.1 - 2023-04-04
>
> * patch: Split instruction strings on linebreak [Vipul Gupta (@vipulgupta2048)]
>
> ### balena-sdk-16.39.0 - Invalid date
>
> * Add `device history` model [fisehara]
>
> ### balena-sdk-16.38.2 - 2023-03-28
>
> * Fix credit-bundle jsdocs [Josh Bowling]
>
> ### balena-sdk-16.38.1 - 2023-03-27
>
> * Deprecate the device-type.json's instructions field [Thodoris Greasidis]
>
> ### balena-sdk-16.38.0 - 2023-03-21
>
> * Add aliases for the DT contrast slugs used in getInstructions [Thodoris Greasidis]
>
> ### balena-sdk-16.37.0 - 2023-03-21
>
> * device-type/getInstructions: Overload to accept the device type contract [Thodoris Greasidis]
>
> ### balena-sdk-16.36.6 - 2023-03-20
>
> * Update TypeScript to 5.0.2 [Thodoris Greasidis]
>
> ### balena-sdk-16.36.5 - 2023-03-16
>
> * patch: Improve jetsonFlash provisioning partial [Vipul Gupta (@vipulgupta2048)]
>
> ### balena-sdk-16.36.4 - 2023-03-15
>
> * Avoid running write operation tests in parallel to support retries [Thodoris Greasidis]
> * Retry failing tests twice [Thodoris Greasidis]
> * Fix tests per removal of `microservices-starter` application type [myarmolinsky]
>
> ### balena-sdk-16.36.3 - 2023-02-28
>
> * models/device-type: Add test for Radxa Zero instructions [Alexandru Costache]
> * lib/models: Add radxaFlash protocol for Radxa boards [Alexandru Costache]
>
> ### balena-sdk-16.36.2 - 2023-02-24
>
> * tests: Stop using flowzone internal env vars to for skipping npm test [Thodoris Greasidis]
>
> ### balena-sdk-16.36.1 - 2023-02-20
>
> * Add plan validity date fields [Josh Bowling]
>
> ### balena-sdk-16.36.0 - 2023-02-16
>
> * Add contract partial based instruction generation [Micah Halter]
>
> ### balena-sdk-16.35.0 - 2023-02-10
>
> * Add `CreditBundle` model [myarmolinsky]
>
> ### balena-sdk-16.34.0 - 2023-02-09
>
> * Add configVarInvalidRegex to Config Var typing [Felipe Lalanne]
>
> ### balena-sdk-16.33.0 - 2023-02-09
>
> * CurrentServiceWithCommit: Add release `raw_version` to type [myarmolinsky]
>
> ### balena-sdk-16.32.3 - 2023-02-07
>
> * Optimize the device.get method [Thodoris Greasidis]
>
> ### balena-sdk-16.32.2 - 2023-02-02
>
> * Improve pine typings for public resources without id fields [Thodoris Greasidis]
>
> ### balena-sdk-16.32.1 - 2023-01-16
>
> * Drop no longer used .travis.yml & .hound.yml [Thodoris Greasidis]
> * Rerun prettier [Thodoris Greasidis]
>
> ### balena-sdk-16.32.0 - 2023-01-05
>
> * typings: Add the device.is_frozen field [Thodoris Greasidis]
>
> ### balena-sdk-16.31.2 - 2022-12-20
>
> * application.create: Deprecate the `parent` option [Thodoris Greasidis]
> * Deprecate the device.getAllByParentDevice() method [Thodoris Greasidis]
> * Simplify the device.move() checks [Thodoris Greasidis]
>
> ### balena-sdk-16.31.1 - 2022-12-17
>
> * Replace appveyor with flowzone [Thodoris Greasidis]
>
> ### balena-sdk-16.31.0 - 2022-12-16
>
> * Add `updateAccountInfo` method to billing model for updating billing account info [myarmolinsky]
>
> ### balena-sdk-16.30.2 - 2022-12-13
>
> * Flowzone: Allow external contributions [Thodoris Greasidis]
>
> ### balena-sdk-16.30.1 - 2022-12-07
>
> * patch: bump catch-uncommitted from 1.6.2 to 2.0.0 [dependabot[bot]]
>
> ### balena-sdk-16.30.0 - 2022-11-24
>
> * Add utils and export mergePineOptions `balena.utils.mergePineOptions()` [JSReds]
>
> ### balena-sdk-16.29.3 - 2022-11-24
>
> * device.getWithServiceDetails: Stop auto-expanding the gateway_downloads [Thodoris Greasidis]
>
> ### balena-sdk-16.29.2 - 2022-11-16
>
> * Update TypeScript to 4.9.3 [Thodoris Greasidis]
>
> ### balena-sdk-16.29.1 - 2022-11-12
>
> * Fix release end_timestamp type [Thodoris Greasidis]
>
> ### balena-sdk-16.29.0 - 2022-11-12
>
>
> <details>
> <summary> Support filtered $count operations inside $filter & $orderby [Thodoris Greasidis] </summary>
>
>> #### pinejs-client-js-6.12.0 - 2022-11-10
>>
>> * Deprecate the 'a/count' notation in $orderby [Thodoris Greasidis]
>> * Deprecate the $count: { $op: number } notation [Thodoris Greasidis]
>> * Add support for `$filter: { $op: [{ $count: {} }, number] }` notation [Thodoris Greasidis]
>>
>> #### pinejs-client-js-6.11.0 - 2022-11-09
>>
>> * Deprecate non-$filter props inside `$expand: { a: { $count: {...}}}` [Thodoris Greasidis]
>> * Add support for `$orderby: { a: { $count: ... }, $dir: 'asc' }` notation [Thodoris Greasidis]
>>
>> #### pinejs-client-js-6.10.7 - 2022-11-07
>>
>> * Refactor the deprecation message definitions [Thodoris Greasidis]
>>
>> #### pinejs-client-js-6.10.6 - 2022-11-01
>>
>> * tests: Support `.only` & `.skip` in the higher level test functions [Thodoris Greasidis]
>>
>> #### pinejs-client-js-6.10.5 - 2022-10-14
>>
>> * Flowzone: Use inherited secrets [Pagan Gazzard]
>>
>> #### pinejs-client-js-6.10.4 - 2022-09-26
>>
>> * Specify node 10 as the minimum supported node engine in the package.json [Thodoris Greasidis]
>> * Replace balenaCI with flowzone [Thodoris Greasidis]
>>
>> #### pinejs-client-js-6.10.3 - 2022-09-15
>>
>> * Fix $count typings to only allow $filter under it [Thodoris Greasidis]
>>
>> #### pinejs-client-js-6.10.2 - 2022-04-08
>>
>> * Update dependencies [Pagan Gazzard]
>> * Remove circleci [Pagan Gazzard]
>>
>> #### pinejs-client-js-6.10.1 - 2022-02-08
>>
>> * Do not await the _request() result to allow enhanced promises downstream [Thodoris Greasidis]
>>
>> #### pinejs-client-js-6.10.0 - 2022-01-24
>>
>> * Add optional retry logic to client [Paul Jonathan Zoulin]
>>
>
> </details>
>
>
> ### balena-sdk-16.28.4 - 2022-11-04
>
> * Use deep imports for date-fns to improve tree-shaking [Thodoris Greasidis]
> * Enable esModuleInterop build option [Thodoris Greasidis]
>
> ### balena-sdk-16.28.3 - 2022-11-03
>
> * Update balena-errors to v4.7.3 [JSReds]
>
</details>
## 15.1.3 - 2023-04-05
<details>
<summary> devices supported: Fix showing types without a valid & finalized release [Thodoris Greasidis] </summary>
> ### balena-sdk-16.28.2 - 2022-10-27
>
> * Update tests to run on node 18 [Thodoris Greasidis]
> * deviceType.getAllSupported: Require a valid & final release to exist [Thodoris Greasidis]
>
> ### balena-sdk-16.28.1 - 2022-10-14
>
> * flowzone: Run the node tests using the latest LTS version [Thodoris Greasidis]
>
</details>
## 15.1.2 - 2023-03-27
* Improve type checking by using the satisfies operator [Thodoris Greasidis]
## 15.1.1 - 2023-03-17
* Update TypeScript to 5.0.2 [Thodoris Greasidis]
## 15.1.0 - 2023-03-14
<details>
<summary> Update balena-compose to v2.2.1 [Kyle Harding] </summary>
> ### balena-compose-2.2.1 - 2023-03-14
>
> * Ignore references to build stages when evaluating manifests [Kyle Harding]
>
> ### balena-compose-2.2.0 - 2023-03-13
>
> * OCI Image Index should allow platform opts [Kyle Harding]
>
> ### balena-compose-2.1.4 - 2023-03-13
>
> * Write to debug log when using platform option [Kyle Harding]
>
> ### balena-compose-2.1.3 - 2023-03-01
>
> * Fixup tests to use recent debian:bullseye-slim images [Kyle Harding]
>
> ### balena-compose-2.1.2 - 2022-10-17
>
> * test/multibuild: Use 127.0.0.1 for the extra_hosts test [Ken Bannister]
> * Output error text to aid test debugging [Ken Bannister]
> * Replace balenaCI & circleCI with flowzone [Thodoris Greasidis]
> * Pin dockerode to v3.3.3 to avoid regression [Ken Bannister]
> * Prettify fixup [Ken Bannister]
> * Fix underspecified generics in release/models [Ken Bannister]
>
</details>
## 15.0.6 - 2023-03-13
* Devices: explicitly fetches only used fields [Otávio Jacobi]
## 15.0.5 - 2023-03-10
* Fix application isLegacy check for rename and deploy [JSReds]
## 15.0.4 - 2023-02-21
* patch: Clarify update rate of update notifier info [Heath Raftery]
## 15.0.3 - 2023-01-18
* Use https for the npm deprecation check, avoiding a redirect [Pagan Gazzard]
## 15.0.2 - 2023-01-14
* Fix push --nolive doc typo [Josh Bowling]
## 15.0.1 - 2023-01-10
* Process livepush build logs inline [Felipe Lalanne]
## 15.0.0 - 2023-01-02
* preload: Drops ability to preload Intel Edison (EOL 2017) Upgrade balena-preload from 12.2.0 to 13.0.0 [JOASSART Edwin]
## 14.5.18 - 2022-12-29
* Update flowzone tests to use npm ci [Thodoris Greasidis]
## 14.5.17 - 2022-12-28
* Stop using the deprecated balena-sync module [Thodoris Greasidis]
## 14.5.16 - 2022-12-28
* Update the npm-shrinkwrap.json dependencies to match the package.json [Thodoris Greasidis]
## 14.5.15 - 2022-12-12
* patch: update balena-preload to 12.2.0 [Edwin Joassart]

View File

@ -78,8 +78,8 @@ 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 12 (min 12.8.0).**
> **Versions 13 and later are not yet fully supported.**
> **The balena CLI currently requires Node.js version 14.**
> **Versions 15 and later are not yet fully supported.**
### Install development tools
@ -89,7 +89,7 @@ some development tools to be installed first, as follows.
$ sudo apt-get update && sudo apt-get -y install curl python3 git make g++
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
$ . ~/.bashrc
$ nvm install 12
$ nvm install 14
```
The `curl` command line above uses
@ -106,14 +106,14 @@ recommended.
```sh
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
$ . ~/.bashrc
$ nvm install 12
$ nvm install 14
```
#### **Windows** (not WSL)
Install:
* Node.js v12 from the [Nodejs.org releases page](https://nodejs.org/en/download/releases/).
* Node.js v14 from the [Nodejs.org releases page](https://nodejs.org/en/download/releases/).
* If you'd like the ability to switch between Node.js versions, install
[nvm-windows](https://github.com/coreybutler/nvm-windows#node-version-manager-nvm-for-windows)
instead.

View File

@ -2788,7 +2788,7 @@ used (usually $HOME/.balena/secrets.yml|.json)
Don't run a live session on this push. The filesystem will not be monitored,
and changes will not be synchronized to any running containers. Note that both
this flag and --detached and required to cause the process to end once the
this flag and --detached are required to cause the process to end once the
initial build has completed.
#### -d, --detached

View File

@ -340,7 +340,7 @@ ${dockerignoreHelp}
);
let release: Release | ComposeReleaseInfo['release'];
if (appType?.is_legacy) {
if (appType.slug === 'legacy-v1' || appType.slug === 'legacy-v2') {
const { deployLegacy } = require('../utils/deploy-legacy');
const msg = getChalk().yellow(

View File

@ -21,6 +21,7 @@ import type {
BalenaSDK,
Device,
DeviceType,
PineOptions,
PineTypedResult,
} from 'balena-sdk';
import Command from '../../command';
@ -153,7 +154,7 @@ export default class DeviceMoveCmd extends Command {
$select: 'slug',
},
},
} as const;
} satisfies PineOptions<DeviceType>;
const deviceTypes = (await balena.models.deviceType.getAllSupported(
deviceTypeOptions,
)) as Array<PineTypedResult<DeviceType, typeof deviceTypeOptions>>;

View File

@ -22,7 +22,7 @@ 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 { Application, Device, PineOptions } from 'balena-sdk';
interface ExtendedDevice extends DeviceWithDeviceType {
dashboard_url?: string;
@ -36,6 +36,18 @@ interface FlagsDef {
json: boolean;
}
const devicesSelectFields = {
$select: [
'id',
'uuid',
'device_name',
'status',
'is_online',
'supervisor_version',
'os_version',
],
} satisfies PineOptions<Device>;
export default class DevicesCmd extends Command {
public static description = stripIndent`
List all devices.
@ -70,6 +82,7 @@ export default class DevicesCmd extends Command {
const { flags: options } = this.parse<FlagsDef, {}>(DevicesCmd);
const balena = getBalenaSdk();
const devicesOptions = { ...devicesSelectFields, ...expandForAppName };
let devices;
@ -78,11 +91,11 @@ export default class DevicesCmd extends Command {
const application = await getApplication(balena, options.fleet);
devices = (await balena.models.device.getAllByApplication(
application.id,
expandForAppName,
devicesOptions,
)) as ExtendedDevice[];
} else {
devices = (await balena.models.device.getAll(
expandForAppName,
devicesOptions,
)) as ExtendedDevice[];
}

View File

@ -61,15 +61,15 @@ export default class DevicesSupportedCmd extends Command {
public async run() {
const { flags: options } = this.parse<FlagsDef, {}>(DevicesSupportedCmd);
const pineOptions = {
$select: (['slug', 'name'] as const).slice(),
$select: ['slug', 'name'],
$expand: {
is_of__cpu_architecture: { $select: 'slug' },
device_type_alias: {
$select: 'is_referenced_by__alias',
$orderby: 'is_referenced_by__alias asc',
$orderby: { is_referenced_by__alias: 'asc' },
},
},
} as const;
} satisfies BalenaSdk.PineOptions<BalenaSdk.DeviceType>;
const dts = (await getBalenaSdk().models.deviceType.getAllSupported(
pineOptions,
)) as Array<

View File

@ -80,7 +80,7 @@ export default class FleetRenameCmd extends Command {
const application = await getApplication(balena, params.fleet, {
$expand: {
application_type: {
$select: ['is_legacy'],
$select: ['slug'],
},
},
});
@ -92,7 +92,7 @@ export default class FleetRenameCmd extends Command {
// Check app supports renaming
const appType = (application.application_type as ApplicationType[])?.[0];
if (appType.is_legacy) {
if (appType.slug === 'legacy-v1' || appType.slug === 'legacy-v2') {
throw new ExpectedError(
`Fleet ${params.fleet} is of 'legacy' type, and cannot be renamed.`,
);

View File

@ -187,7 +187,7 @@ Can be repeated to add multiple certificates.\
: undefined;
const progressBars: {
[key: string]: ReturnType<typeof getVisuals>['Progress'];
[key: string]: InstanceType<ReturnType<typeof getVisuals>['Progress']>;
} = {};
const progressHandler = function (event: {
@ -201,7 +201,7 @@ Can be repeated to add multiple certificates.\
};
const spinners: {
[key: string]: ReturnType<typeof getVisuals>['Spinner'];
[key: string]: InstanceType<ReturnType<typeof getVisuals>['Spinner']>;
} = {};
const spinnerHandler = function (event: { name: string; action: string }) {

View File

@ -178,7 +178,7 @@ export default class PushCmd extends Command {
description: stripIndent`
Don't run a live session on this push. The filesystem will not be monitored,
and changes will not be synchronized to any running containers. Note that both
this flag and --detached and required to cause the process to end once the
this flag and --detached are required to cause the process to end once the
initial build has completed.`,
default: false,
}),

View File

@ -16,7 +16,6 @@
*/
import { flags } from '@oclif/command';
import type { LocalBalenaOsDevice } from 'balena-sync';
import Command from '../command';
import * as cf from '../utils/common-flags';
import { getCliUx, stripIndent } from '../utils/lazy';
@ -72,7 +71,7 @@ export default class ScanCmd extends Command {
public async run() {
const _ = await import('lodash');
const { discover } = await import('balena-sync');
const { discoverLocalBalenaOsDevices } = await import('../utils/discover');
const prettyjson = await import('prettyjson');
const dockerUtils = await import('../utils/docker');
@ -88,8 +87,7 @@ export default class ScanCmd extends Command {
const ux = getCliUx();
ux.action.start('Scanning for local balenaOS devices');
const localDevices: LocalBalenaOsDevice[] =
await discover.discoverLocalBalenaOsDevices(discoverTimeout);
const localDevices = await discoverLocalBalenaOsDevices(discoverTimeout);
const engineReachableDevices: boolean[] = await Promise.all(
localDevices.map(async ({ address }: { address: string }) => {
const docker = await dockerUtils.createClient({
@ -106,7 +104,7 @@ export default class ScanCmd extends Command {
}),
);
const developmentDevices: LocalBalenaOsDevice[] = localDevices.filter(
const developmentDevices = localDevices.filter(
(_localDevice, index) => engineReachableDevices[index],
);
@ -116,18 +114,15 @@ export default class ScanCmd extends Command {
_.isEqual,
);
const productionDevicesInfo = _.map(
productionDevices,
(device: LocalBalenaOsDevice) => {
return {
host: device.host,
address: device.address,
osVariant: 'production',
dockerInfo: undefined,
dockerVersion: undefined,
};
},
);
const productionDevicesInfo = productionDevices.map((device) => {
return {
host: device.host,
address: device.address,
osVariant: 'production',
dockerInfo: undefined,
dockerVersion: undefined,
};
});
// Query devices for info
const devicesInfo = await Promise.all(

View File

@ -86,7 +86,7 @@ export class DeprecationChecker {
* @param version Semver without 'v' prefix, e.g. '12.0.0.'
*/
protected getNpmUrl(version: string) {
return `http://registry.npmjs.org/balena-cli/${version}`;
return `https://registry.npmjs.org/balena-cli/${version}`;
}
/**

View File

@ -91,7 +91,7 @@ export default class BalenaHelp extends Help {
.map((pc) => {
return commands.find((c) => c.id === pc.replace(' ', ':'));
})
.filter((c): c is typeof commands[0] => !!c);
.filter((c): c is (typeof commands)[0] => !!c);
let usageLength = 0;
for (const cmd of primaryCommands) {

View File

@ -209,9 +209,9 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
globalLogger.logDebug('Fetching device information...');
const deviceInfo = await api.getDeviceInformation();
let buildLogs: Dictionary<string> | undefined;
let imageIds: Dictionary<string[]> | undefined;
if (!opts.nolive) {
buildLogs = {};
imageIds = {};
}
const { awaitInterruptibleTask } = await import('../helpers');
@ -223,7 +223,7 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
deviceInfo,
globalLogger,
opts,
buildLogs,
imageIds,
);
globalLogger.outputDeferredMessages();
@ -265,7 +265,7 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
docker,
logger: globalLogger,
composition: project.composition,
buildLogs: buildLogs!,
imageIds: imageIds!,
deployOpts: opts,
});
promises.push(livepush.init());
@ -312,6 +312,14 @@ function connectToDocker(host: string, port: number): Docker {
});
}
function extractDockerArrowMessage(outputLine: string): string | undefined {
const arrowTest = /^.*\s*-+>\s*(.+)/i;
const match = arrowTest.exec(outputLine);
if (match != null) {
return match[1];
}
}
async function performBuilds(
composition: Composition,
tarStream: Readable,
@ -319,7 +327,7 @@ async function performBuilds(
deviceInfo: DeviceInfo,
logger: Logger,
opts: DeviceDeployOptions,
buildLogs?: Dictionary<string>,
imageIds?: Dictionary<string[]>,
): Promise<BuildTask[]> {
const multibuild = await import('@balena/compose/dist/multibuild');
@ -345,14 +353,29 @@ async function performBuilds(
// If we're passed a build logs object make sure to set it
// up properly
let logHandlers: ((serviceName: string, line: string) => void) | undefined;
if (buildLogs != null) {
const lastArrowMessage: Dictionary<string> = {};
if (imageIds != null) {
for (const task of buildTasks) {
if (!task.external) {
buildLogs[task.serviceName] = '';
imageIds[task.serviceName] = [];
}
}
logHandlers = (serviceName: string, line: string) => {
buildLogs[serviceName] += `${line}\n`;
// If this was a from line, take the last found
// image id and save it
if (
/step \d+(?:\/\d+)?\s*:\s*FROM/i.test(line) &&
lastArrowMessage[serviceName] != null
) {
imageIds[serviceName].push(lastArrowMessage[serviceName]);
} else {
const msg = extractDockerArrowMessage(line);
if (msg != null) {
lastArrowMessage[serviceName] = msg;
}
}
};
}
@ -413,12 +436,26 @@ export async function rebuildSingleTask(
// the logs, so any calller who wants to keep track of
// this should provide the following callback
containerIdCb?: (id: string) => void,
): Promise<string> {
): Promise<string[]> {
const multibuild = await import('@balena/compose/dist/multibuild');
// First we run the build task, to get the new image id
let buildLogs = '';
const stageIds = [] as string[];
let lastArrowMessage: string | undefined;
const logHandler = (_s: string, line: string) => {
buildLogs += `${line}\n`;
// If this was a FROM line, take the last found
// image id and save it as a stage id
if (
/step \d+(?:\/\d+)?\s*:\s*FROM/i.test(line) &&
lastArrowMessage != null
) {
stageIds.push(lastArrowMessage);
} else {
const msg = extractDockerArrowMessage(line);
if (msg != null) {
lastArrowMessage = msg;
}
}
if (containerIdCb != null) {
const match = line.match(/^\s*--->\s*Running\s*in\s*([a-f0-9]*)\s*$/i);
@ -477,7 +514,7 @@ export async function rebuildSingleTask(
]);
}
return buildLogs;
return stageIds;
}
function assignOutputHandlers(

View File

@ -52,7 +52,6 @@ interface MonitoredContainer {
containerId: string;
}
type BuildLogs = Dictionary<string>;
type StageImageIDs = Dictionary<string[]>;
export interface LivepushOpts {
@ -62,7 +61,7 @@ export interface LivepushOpts {
docker: Dockerode;
api: DeviceAPI;
logger: Logger;
buildLogs: BuildLogs;
imageIds: StageImageIDs;
deployOpts: DeviceDeployOptions;
}
@ -97,7 +96,7 @@ export class LivepushManager {
this.api = opts.api;
this.logger = opts.logger;
this.deployOpts = opts.deployOpts;
this.imageIds = LivepushManager.getMultistageImageIDs(opts.buildLogs);
this.imageIds = opts.imageIds;
}
public async init(): Promise<void> {
@ -297,33 +296,6 @@ export class LivepushManager {
return new Dockerfile(content).generateLiveDockerfile();
}
private static getMultistageImageIDs(buildLogs: BuildLogs): StageImageIDs {
const stageIds: StageImageIDs = {};
_.each(buildLogs, (log, serviceName) => {
stageIds[serviceName] = [];
const lines = log.split(/\r?\n/);
let lastArrowMessage: string | undefined;
for (const line of lines) {
// If this was a from line, take the last found
// image id and save it
if (
/step \d+(?:\/\d+)?\s*:\s*FROM/i.test(line) &&
lastArrowMessage != null
) {
stageIds[serviceName].push(lastArrowMessage);
} else {
const msg = LivepushManager.extractDockerArrowMessage(line);
if (msg != null) {
lastArrowMessage = msg;
}
}
}
});
return stageIds;
}
private async awaitDeviceStateSettle(): Promise<void> {
// Cache the state to avoid unnecessary calls
this.lastDeviceStatus = await this.api.getStatus();
@ -405,9 +377,9 @@ export class LivepushManager {
);
}
let buildLog: string;
let stageImages: string[];
try {
buildLog = await rebuildSingleTask(
stageImages = await rebuildSingleTask(
serviceName,
this.docker,
this.logger,
@ -466,17 +438,13 @@ export class LivepushManager {
);
}
const buildLogs: Dictionary<string> = {};
buildLogs[serviceName] = buildLog;
const stageImages = LivepushManager.getMultistageImageIDs(buildLogs);
const dockerfile = new Dockerfile(buildTask.dockerfile!);
instance.livepush = await Livepush.init({
dockerfile,
context: buildTask.context!,
containerId: container.containerId,
stageImages: stageImages[serviceName],
stageImages,
docker: this.docker,
});
this.assignLivepushOutputHandlers(serviceName, instance.livepush);
@ -536,16 +504,6 @@ export class LivepushManager {
});
}
private static extractDockerArrowMessage(
outputLine: string,
): string | undefined {
const arrowTest = /^.*\s*-+>\s*(.+)/i;
const match = arrowTest.exec(outputLine);
if (match != null) {
return match[1];
}
}
private getDockerfilePathFromTask(task: BuildTask): string[] {
switch (task.projectType) {
case 'Standard Dockerfile':

40
lib/utils/discover.ts Normal file
View File

@ -0,0 +1,40 @@
import { enumerateServices, findServices } from 'resin-discoverable-services';
interface LocalBalenaOsDevice {
address: string;
host: string;
osVariant?: string;
port: number;
}
// Although we only check for 'balena-ssh', we know, implicitly, that balenaOS
// devices come with 'rsync' installed that can be used over SSH.
const avahiBalenaSshTag = 'resin-ssh';
export async function discoverLocalBalenaOsDevices(
timeout = 4000,
): Promise<LocalBalenaOsDevice[]> {
const availableServices = await enumerateServices();
const serviceDefinitions = Array.from(availableServices)
.filter((s) => Array.from(s.tags).includes(avahiBalenaSshTag))
.map((s) => s.service);
if (serviceDefinitions.length === 0) {
throw new Error(
`Could not find any available '${avahiBalenaSshTag}' services`,
);
}
const services = await findServices(serviceDefinitions, timeout);
return services.map(function (service) {
// User referer address to get device IP. This will work fine assuming that
// a device only advertises own services.
const {
referer: { address },
host,
port,
} = service;
return { address, host, port };
});
}

View File

@ -137,7 +137,7 @@ export const areDeviceTypesCompatible = async (
$select: 'slug',
},
},
} as const;
} satisfies BalenaSdk.PineOptions<BalenaSdk.DeviceType>;
const [appDeviceType, osDeviceType] = await Promise.all(
[appDeviceTypeSlug, osDeviceTypeSlug].map(
(dtSlug) =>
@ -184,7 +184,7 @@ export async function getAppWithArch(
const options: BalenaSdk.PineOptions<BalenaSdk.Application> = {
$expand: {
application_type: {
$select: ['name', 'slug', 'supports_multicontainer', 'is_legacy'],
$select: ['name', 'slug', 'supports_multicontainer'],
},
is_for__device_type: {
$select: 'slug',
@ -439,11 +439,11 @@ export function getProxyConfig(): ProxyConfig | undefined {
export const expandForAppName = {
$expand: {
belongs_to__application: { $select: ['app_name', 'slug'] as any },
belongs_to__application: { $select: ['app_name', 'slug'] },
is_of__device_type: { $select: 'slug' },
is_running__release: { $select: 'commit' },
},
} as const;
} satisfies BalenaSdk.PineOptions<BalenaSdk.Device>;
export const expandForAppNameAndCpuArch = {
$expand: {
@ -457,7 +457,7 @@ export const expandForAppNameAndCpuArch = {
},
},
},
} as const;
} satisfies BalenaSdk.PineOptions<BalenaSdk.Device>;
/**
* Use the `readline` library on Windows to install SIGINT handlers.

View File

@ -163,12 +163,61 @@ async function getOsVersion(deviceIp: string): Promise<string> {
return match[1];
}
const dockerPort = 2375;
const dockerTimeout = 2000;
async function selectLocalBalenaOsDevice(timeout = 4000): Promise<string> {
const { discoverLocalBalenaOsDevices } = await import('../utils/discover');
const { SpinnerPromise } = getVisuals();
const devices = await new SpinnerPromise({
promise: discoverLocalBalenaOsDevices(timeout),
startMessage: 'Discovering local balenaOS devices..',
stopMessage: 'Reporting discovered devices',
});
const responsiveDevices: typeof devices = [];
const Docker = await import('docker-toolbelt');
await Promise.all(
devices.map(async function (device) {
const address = device?.address;
if (!address) {
return;
}
try {
const docker = new Docker({
host: address,
port: dockerPort,
timeout: dockerTimeout,
});
await docker.ping();
responsiveDevices.push(device);
} catch {
return;
}
}),
);
if (!responsiveDevices.length) {
throw new Error('Could not find any local balenaOS devices');
}
return getCliForm().ask({
message: 'select a device',
type: 'list',
default: devices[0].address,
choices: responsiveDevices.map((device) => ({
name: `${device.host || 'untitled'} (${device.address})`,
value: device.address,
})),
});
}
async function selectLocalDevice(): Promise<string> {
const { forms } = await import('balena-sync');
let hostnameOrIp;
try {
hostnameOrIp = await forms.selectLocalBalenaOsDevice();
const hostnameOrIp = await selectLocalBalenaOsDevice();
console.error(`==> Selected device: ${hostnameOrIp}`);
return hostnameOrIp;
} catch (e) {
if (e.message.toLowerCase().includes('could not find any')) {
throw new ExpectedError(e);
@ -176,8 +225,6 @@ async function selectLocalDevice(): Promise<string> {
throw e;
}
}
return hostnameOrIp;
}
async function selectAppFromList(
@ -208,7 +255,7 @@ async function getOrSelectApplication(
$select: 'slug',
},
},
} as const;
} satisfies BalenaSdk.PineOptions<BalenaSdk.DeviceType>;
const [deviceType, allDeviceTypes] = await Promise.all([
sdk.models.deviceType.get(deviceTypeSlug, pineOptions) as Promise<
BalenaSdk.PineTypedResult<BalenaSdk.DeviceType, typeof pineOptions>

View File

@ -151,23 +151,23 @@ export async function runRemoteCommand({
let exitCode: number | undefined;
let exitSignal: NodeJS.Signals | undefined;
try {
[exitCode, exitSignal] = await new Promise<[number, NodeJS.Signals]>(
(resolve, reject) => {
const ps = spawn(program, args, { stdio })
.on('error', reject)
.on('close', (code, signal) => resolve([code, signal]));
[exitCode, exitSignal] = await new Promise((resolve, reject) => {
const ps = spawn(program, args, { stdio })
.on('error', reject)
.on('close', (code, signal) =>
resolve([code ?? undefined, signal ?? undefined]),
);
if (ps.stdin && stdin && typeof stdin !== 'string') {
stdin.pipe(ps.stdin);
}
if (ps.stdout && stdout && typeof stdout !== 'string') {
ps.stdout.pipe(stdout);
}
if (ps.stderr && stderr && typeof stderr !== 'string') {
ps.stderr.pipe(stderr);
}
},
);
if (ps.stdin && stdin && typeof stdin !== 'string') {
stdin.pipe(ps.stdin);
}
if (ps.stdout && stdout && typeof stdout !== 'string') {
ps.stdout.pipe(stdout);
}
if (ps.stderr && stderr && typeof stderr !== 'string') {
ps.stderr.pipe(stderr);
}
});
} catch (error) {
const msg = [
`ssh failed with exit code=${exitCode} signal=${exitSignal}:`,

View File

@ -19,8 +19,10 @@ import * as UpdateNotifier from 'update-notifier';
import packageJSON = require('../../package.json');
// Check for an update once a day. 1 day granularity should be
// enough, rather than every run.
// Check for an update at most once a day. 1 day granularity should be
// enough, rather than every run. Note because we show the information
// from the *last* time we ran, if the cli has not been run for a while
// the update info can be out of date.
const balenaUpdateInterval = 1000 * 60 * 60 * 24 * 1;
let notifier: UpdateNotifier.UpdateNotifier;

1294
npm-shrinkwrap.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "balena-cli",
"version": "14.5.15",
"version": "15.2.0",
"description": "The official balena Command Line Interface",
"main": "./build/app.js",
"homepage": "https://github.com/balena-io/balena-cli",
@ -27,7 +27,6 @@
"scripts": [
"build/**/*.js",
"node_modules/balena-sdk/es2018/index.js",
"node_modules/balena-sync/build/**/*.js",
"node_modules/pinejs-client-request/node_modules/pinejs-client-core/es2018/index.js",
"node_modules/@balena/compose/dist/parse/schemas/*.json"
],
@ -90,7 +89,7 @@
"author": "Balena Inc. (https://balena.io/)",
"license": "Apache-2.0",
"engines": {
"node": ">=12 <16"
"node": ">=14 <16"
},
"husky": {
"hooks": {
@ -114,7 +113,7 @@
]
},
"devDependencies": {
"@balena/lint": "^6.2.0",
"@balena/lint": "^6.2.2",
"@oclif/config": "^1.18.2",
"@oclif/parser": "^3.8.6",
"@octokit/plugin-throttling": "^3.5.1",
@ -146,7 +145,7 @@
"@types/ndjson": "^2.0.1",
"@types/net-keepalive": "^0.4.1",
"@types/nock": "^11.1.0",
"@types/node": "^12.20.42",
"@types/node": "^14.18.36",
"@types/node-cleanup": "^2.1.2",
"@types/parse-link-header": "^1.0.1",
"@types/prettyjson": "^0.0.30",
@ -190,10 +189,10 @@
"simple-git": "^3.14.1",
"sinon": "^11.1.2",
"ts-node": "^10.4.0",
"typescript": "^4.6.4"
"typescript": "^5.0.2"
},
"dependencies": {
"@balena/compose": "^2.1.1",
"@balena/compose": "^2.2.1",
"@balena/dockerignore": "^1.0.2",
"@balena/es-version": "^1.0.1",
"@oclif/command": "^1.8.16",
@ -204,15 +203,14 @@
"JSONStream": "^1.0.3",
"balena-config-json": "^4.2.0",
"balena-device-init": "^6.0.0",
"balena-errors": "^4.7.1",
"balena-errors": "^4.7.3",
"balena-image-fs": "^7.0.6",
"balena-image-manager": "^8.0.0",
"balena-preload": "^12.2.0",
"balena-sdk": "^16.28.0",
"balena-preload": "^13.0.0",
"balena-sdk": "^16.40.0",
"balena-semver": "^2.3.0",
"balena-settings-client": "^4.0.7",
"balena-settings-storage": "^7.0.0",
"balena-sync": "^11.0.2",
"bluebird": "^3.7.2",
"body-parser": "^1.19.1",
"chalk": "^3.0.0",
@ -225,6 +223,7 @@
"denymount": "^2.3.0",
"docker-modem": "3.0.0",
"docker-progress": "^5.1.3",
"docker-toolbelt": "^3.3.10",
"dockerode": "^3.3.1",
"ejs": "^3.1.6",
"etcher-sdk": "^6.2.1",
@ -262,6 +261,7 @@
"request": "^2.88.2",
"resin-cli-form": "^2.0.2",
"resin-cli-visuals": "^1.8.0",
"resin-discoverable-services": "^2.0.3",
"resin-doodles": "^0.2.0",
"resin-stream-logger": "^0.1.2",
"rimraf": "^3.0.2",
@ -284,6 +284,6 @@
"windosu": "^0.3.0"
},
"versionist": {
"publishedAt": "2022-12-12T13:41:12.779Z"
"publishedAt": "2023-04-05T13:09:21.964Z"
}
}

View File

@ -10,8 +10,6 @@ upstream:
url: 'https://github.com/balena-io-modules/balena-image-manager'
- repo: 'balena-preload'
url: 'https://github.com/balena-io-modules/balena-preload'
- repo: 'balena-sync'
url: 'https://github.com/balena-io-modules/balena-sync'
- repo: 'etcher-sdk'
url: 'https://github.com/balena-io-modules/etcher-sdk/'
- repo: 'balena-compose'

View File

@ -38,7 +38,7 @@ describe('balena devices', function () {
it('should list devices from own and collaborator apps', async () => {
api.scope
.get(
'/v6/device?$orderby=device_name%20asc&$expand=belongs_to__application($select=app_name,slug),is_of__device_type($select=slug),is_running__release($select=commit)',
'/v6/device?$orderby=device_name%20asc&$select=id,uuid,device_name,status,is_online,supervisor_version,os_version&$expand=belongs_to__application($select=app_name,slug),is_of__device_type($select=slug),is_running__release($select=commit)',
)
.replyWithFile(200, path.join(apiResponsePath, 'devices.json'), {
'Content-Type': 'application/json',

View File

@ -138,6 +138,7 @@ describe('balena envs', function () {
});
it('should successfully list env variables for a test device', async () => {
api.expectGetDevice({ shortUUID, fullUUID });
api.expectGetDevice({ fullUUID });
api.expectGetDeviceEnvVars();
api.expectGetApplication();
@ -145,20 +146,19 @@ describe('balena envs', function () {
api.expectGetAppServiceVars();
api.expectGetDeviceServiceVars();
const uuid = shortUUID;
const result = await runCommand(`envs -d ${uuid}`);
const result = await runCommand(`envs -d ${shortUUID}`);
let { out } = result;
let expected =
stripIndent`
ID NAME VALUE FLEET DEVICE SERVICE
120110 svar1 svar1-value org/test * service1
120111 svar2 svar2-value org/test * service2
120120 svar3 svar3-value org/test ${uuid} service1
120121 svar4 svar4-value org/test ${uuid} service2
120120 svar3 svar3-value org/test ${shortUUID} service1
120121 svar4 svar4-value org/test ${shortUUID} 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} *
120203 var3 var3-val org/test ${shortUUID} *
120204 var4 44 org/test ${shortUUID} *
` + '\n';
out = out.map((l) => l.replace(/ +/g, ' '));
@ -168,6 +168,7 @@ describe('balena envs', function () {
});
it('should successfully list env variables for a test device (JSON output)', async () => {
api.expectGetDevice({ shortUUID, fullUUID });
api.expectGetDevice({ fullUUID });
api.expectGetDeviceEnvVars();
api.expectGetApplication();
@ -192,6 +193,7 @@ describe('balena envs', function () {
});
it('should successfully list config variables for a test device', async () => {
api.expectGetDevice({ shortUUID, fullUUID });
api.expectGetDevice({ fullUUID });
api.expectGetDeviceConfigVars();
api.expectGetApplication();
@ -216,24 +218,24 @@ describe('balena envs', function () {
const serviceName = 'service2';
api.expectGetService({ serviceName });
api.expectGetApplication();
api.expectGetDevice({ shortUUID, fullUUID });
api.expectGetDevice({ fullUUID });
api.expectGetDeviceServiceVars();
api.expectGetAppEnvVars();
api.expectGetAppServiceVars();
api.expectGetDeviceEnvVars();
const uuid = shortUUID;
const result = await runCommand(`envs -d ${uuid} -s ${serviceName}`);
const result = await runCommand(`envs -d ${shortUUID} -s ${serviceName}`);
let { out } = result;
let expected =
stripIndent`
ID NAME VALUE FLEET DEVICE SERVICE
120111 svar2 svar2-value org/test * service2
120121 svar4 svar4-value org/test ${uuid} service2
120121 svar4 svar4-value org/test ${shortUUID} 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} *
120203 var3 var3-val org/test ${shortUUID} *
120204 var4 44 org/test ${shortUUID} *
` + '\n';
out = out.map((l) => l.replace(/ +/g, ' '));
@ -243,20 +245,20 @@ describe('balena envs', function () {
});
it('should successfully list env and service variables for a test device (unknown fleet)', async () => {
api.expectGetDevice({ shortUUID, fullUUID, inaccessibleApp: true });
api.expectGetDevice({ fullUUID, inaccessibleApp: true });
api.expectGetDeviceEnvVars();
api.expectGetDeviceServiceVars();
const uuid = shortUUID;
const result = await runCommand(`envs -d ${uuid}`);
const result = await runCommand(`envs -d ${shortUUID}`);
let { out } = result;
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
120203 var3 var3-val N/A ${uuid} *
120204 var4 44 N/A ${uuid} *
120120 svar3 svar3-value N/A ${shortUUID} service1
120121 svar4 svar4-value N/A ${shortUUID} service2
120203 var3 var3-val N/A ${shortUUID} *
120204 var4 44 N/A ${shortUUID} *
` + '\n';
out = out.map((l) => l.replace(/ +/g, ' '));
@ -271,22 +273,22 @@ describe('balena envs', function () {
api.expectGetApplication();
api.expectGetAppEnvVars();
api.expectGetAppServiceVars();
api.expectGetDevice({ shortUUID, fullUUID });
api.expectGetDevice({ fullUUID });
api.expectGetDeviceEnvVars();
api.expectGetDeviceServiceVars();
const uuid = shortUUID;
const result = await runCommand(`envs -d ${uuid} -s ${serviceName}`);
const result = await runCommand(`envs -d ${shortUUID} -s ${serviceName}`);
let { out } = result;
let expected =
stripIndent`
ID NAME VALUE FLEET DEVICE SERVICE
120110 svar1 svar1-value org/test * ${serviceName}
120120 svar3 svar3-value org/test ${uuid} ${serviceName}
120120 svar3 svar3-value org/test ${shortUUID} ${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} *
120203 var3 var3-val org/test ${shortUUID} *
120204 var4 44 org/test ${shortUUID} *
` + '\n';
out = out.map((l) => l.replace(/ +/g, ' '));
@ -301,6 +303,7 @@ describe('balena envs', function () {
api.expectGetApplication();
api.expectGetAppEnvVars();
api.expectGetAppServiceVars();
api.expectGetDevice({ shortUUID, fullUUID });
api.expectGetDevice({ fullUUID });
api.expectGetDeviceEnvVars();
api.expectGetDeviceServiceVars();

View File

@ -178,7 +178,7 @@ async function startMockSshServer(): Promise<[Server, number]> {
});
return await new Promise<[Server, number]>((resolve, reject) => {
// TODO: remove 'as any' below. According to @types/node v12.20.42, the
// TODO: remove 'as any' below. According to @types/node v14.18.36, 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

View File

@ -212,13 +212,20 @@ export class BalenaAPIMock extends NockMock {
public expectGetDevice(opts: {
fullUUID: string;
shortUUID?: string;
inaccessibleApp?: boolean;
isOnline?: boolean;
optional?: boolean;
persist?: boolean;
}) {
const id = 7654321;
this.optGet(/^\/v\d+\/device($|\?)/, opts).reply(200, {
const providedUuid = opts.shortUUID ?? opts.fullUUID;
this.optGet(
providedUuid.length !== 32
? /^\/v\d+\/device($|\?)/
: /^\/v\d+\/device\(uuid=%27[0-9a-f]{32}%27\)/,
opts,
).reply(200, {
d: [
{
id,

View File

@ -113,7 +113,7 @@ async function createProxyServer(): Promise<[number, number]> {
let proxyPort = 0; // TCP port number, 0 means automatic allocation
await new Promise<void>((resolve, reject) => {
// TODO: remove 'as any' below. According to @types/node v12.20.42, the
// TODO: remove 'as any' below. According to @types/node v14.18.36, 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) {
@ -197,7 +197,7 @@ async function createInterceptorServer(): Promise<number> {
let interceptorPort = 0;
await new Promise<void>((resolve, reject) => {
// TODO: remove 'as any' below. According to @types/node v12.20.42, the
// TODO: remove 'as any' below. According to @types/node v14.18.36, 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) {

View File

@ -6,7 +6,7 @@
"name": "Starter",
"slug": "microservices-starter",
"supports_multicontainer": true,
"is_legacy": false,
"is_legacy": true,
"__metadata": {}
}
],

View File

@ -6,7 +6,7 @@
"name": "Starter",
"slug": "microservices-starter",
"supports_multicontainer": true,
"is_legacy": false,
"is_legacy": true,
"__metadata": {}
}
],

View File

@ -3,20 +3,11 @@
{
"belongs_to__application": [
{
"app_name": "test app",
"slug": "org/test app",
"__metadata": {}
}
],
"id": 1747415,
"belongs_to__user": {
"__deferred": {
"uri": "/resin/user(46272)"
},
"__id": 46272
},
"is_managed_by__device": null,
"actor": 4180757,
"device_name": "sparkling-wood",
"is_of__device_type": [{ "slug": "raspberrypi4-64" }],
"uuid": "fda508c8583011b8466c26abdd5159f2",
@ -25,50 +16,10 @@
"commit": "18756d3386c25a044db66b89e0409804"
}
],
"note": null,
"local_id": null,
"status": "Idle",
"is_online": false,
"last_connectivity_event": "2019-11-23T00:26:35.074Z",
"is_connected_to_vpn": false,
"last_vpn_event": "2019-11-23T00:26:35.074Z",
"ip_address": "192.168.0.112",
"vpn_address": null,
"public_address": "89.186.29.129",
"os_version": "balenaOS 2.44.0+rev3",
"os_variant": "dev",
"supervisor_version": "10.3.7",
"should_be_managed_by__supervisor_release": null,
"is_managed_by__service_instance": {
"__deferred": {
"uri": "/resin/service_instance(124111)"
},
"__id": 124111
},
"provisioning_progress": null,
"provisioning_state": "",
"download_progress": null,
"is_web_accessible": false,
"longitude": "22.5853",
"latitude": "51.2712",
"location": "Lublin, Lublin, Poland",
"custom_longitude": "",
"custom_latitude": "",
"logs_channel": null,
"is_locked_until__date": null,
"is_accessible_by_support_until__date": null,
"created_at": "2019-11-18T12:27:37.423Z",
"is_active": true,
"api_heartbeat_state": "offline",
"cpu_usage" : 34,
"cpu_temp" : 56.2,
"cpu_id" : "some cpu id",
"memory_usage" : 1000,
"memory_total" : 4000,
"storage_block_device" : "/dev/mmcblk0",
"storage_usage" : 1000,
"storage_total" : 64000,
"is_undervolted" : true,
"__metadata": {
"uri": "/resin/device(@id)?@id=1747415"
}
@ -76,14 +27,6 @@
{
"belongs_to__application": [],
"id": 1747416,
"belongs_to__user": {
"__deferred": {
"uri": "/resin/user(46272)"
},
"__id": 46272
},
"is_managed_by__device": null,
"actor": 4180757,
"device_name": "dashing-spruce",
"is_of__device_type": [{ "slug": "raspberrypi4-64" }],
"uuid": "fda508c8583011b8466c26abdd5159f3",
@ -92,50 +35,10 @@
"commit": "18756d3386c25a044db66b89e0409804"
}
],
"note": null,
"local_id": null,
"status": "Idle",
"is_online": false,
"last_connectivity_event": "2019-11-23T00:26:35.074Z",
"is_connected_to_vpn": false,
"last_vpn_event": "2019-11-23T00:26:35.074Z",
"ip_address": "192.168.0.112",
"vpn_address": null,
"public_address": "89.186.29.129",
"os_version": "balenaOS 2.44.0+rev3",
"os_variant": "dev",
"supervisor_version": "10.3.7",
"should_be_managed_by__supervisor_release": null,
"is_managed_by__service_instance": {
"__deferred": {
"uri": "/resin/service_instance(124111)"
},
"__id": 124111
},
"provisioning_progress": null,
"provisioning_state": "",
"download_progress": null,
"is_web_accessible": false,
"longitude": "22.5853",
"latitude": "51.2712",
"location": "Lublin, Lublin, Poland",
"custom_longitude": "",
"custom_latitude": "",
"logs_channel": null,
"is_locked_until__date": null,
"is_accessible_by_support_until__date": null,
"created_at": "2019-11-18T12:27:37.423Z",
"is_active": true,
"api_heartbeat_state": "offline",
"cpu_usage" : 34,
"cpu_temp" : 56.2,
"cpu_id" : "some cpu id",
"memory_usage" : 1000,
"memory_total" : 4000,
"storage_block_device" : "/dev/mmcblk0",
"storage_usage" : 1000,
"storage_total" : 64000,
"is_undervolted" : true,
"__metadata": {
"uri": "/resin/device(@id)?@id=1747415"
}

View File

@ -1,21 +1,3 @@
> Warning Cannot resolve 'module'
node_modules/balena-sync/build/index.js
Dynamic require may fail at run time, because the requested file
is unknown at compilation time and not included into executable.
Use a string literal as an argument for 'require', or leave it
as is and specify the resolved file name in 'scripts' option.
> Warning Cannot resolve ''./' + command'
node_modules/balena-sync/build/capitano/index.js
Dynamic require may fail at run time, because the requested file
is unknown at compilation time and not included into executable.
Use a string literal as an argument for 'require', or leave it
as is and specify the resolved file name in 'scripts' option.
> Warning Cannot resolve ''./' + target'
node_modules/balena-sync/build/sync/index.js
Dynamic require may fail at run time, because the requested file
is unknown at compilation time and not included into executable.
Use a string literal as an argument for 'require', or leave it
as is and specify the resolved file name in 'scripts' option.
> Warning Cannot include file %1 into executable.
The file must be distributed with executable as %2.
%1: node_modules/open/xdg-open

View File

@ -1,21 +1,3 @@
> Warning Cannot resolve 'module'
node_modules/balena-sync/build/index.js
Dynamic require may fail at run time, because the requested file
is unknown at compilation time and not included into executable.
Use a string literal as an argument for 'require', or leave it
as is and specify the resolved file name in 'scripts' option.
> Warning Cannot resolve ''./' + command'
node_modules/balena-sync/build/capitano/index.js
Dynamic require may fail at run time, because the requested file
is unknown at compilation time and not included into executable.
Use a string literal as an argument for 'require', or leave it
as is and specify the resolved file name in 'scripts' option.
> Warning Cannot resolve ''./' + target'
node_modules/balena-sync/build/sync/index.js
Dynamic require may fail at run time, because the requested file
is unknown at compilation time and not included into executable.
Use a string literal as an argument for 'require', or leave it
as is and specify the resolved file name in 'scripts' option.
> Warning Cannot include file %1 into executable.
The file must be distributed with executable as %2.
%1: node_modules/open/xdg-open

View File

@ -1,21 +1,3 @@
> Warning Cannot resolve 'module'
node_modules\balena-sync\build\index.js
Dynamic require may fail at run time, because the requested file
is unknown at compilation time and not included into executable.
Use a string literal as an argument for 'require', or leave it
as is and specify the resolved file name in 'scripts' option.
> Warning Cannot resolve ''./' + command'
node_modules\balena-sync\build\capitano\index.js
Dynamic require may fail at run time, because the requested file
is unknown at compilation time and not included into executable.
Use a string literal as an argument for 'require', or leave it
as is and specify the resolved file name in 'scripts' option.
> Warning Cannot resolve ''./' + target'
node_modules\balena-sync\build\sync\index.js
Dynamic require may fail at run time, because the requested file
is unknown at compilation time and not included into executable.
Use a string literal as an argument for 'require', or leave it
as is and specify the resolved file name in 'scripts' option.
> Warning Cannot include file %1 into executable.
The file must be distributed with executable as %2.
%1: node_modules\open\xdg-open

View File

@ -45,7 +45,7 @@ class MockLivepushManager extends LivepushManager {
docker: {} as import('dockerode'),
api: {} as import('../../../lib/utils/device/api').DeviceAPI,
logger: {} as import('../../../lib/utils/logger'),
buildLogs: {},
imageIds: {},
deployOpts:
{} as import('../../../lib/utils/device/deploy').DeviceDeployOptions,
});

View File

@ -1,41 +0,0 @@
/**
* @license
* 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.
* 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.
*/
declare module 'balena-sync' {
import { CommandDefinition } from 'capitano';
export function capitano(tool: 'balena-cli'): CommandDefinition;
export interface LocalBalenaOsDevice {
address: string;
host: string;
osVariant: string;
port: number;
}
declare namespace forms {
export function selectLocalBalenaOsDevice(
timeout?: number,
): Promise<string>;
}
declare namespace discover {
export function discoverLocalBalenaOsDevices(
timeout?: number,
): Promise<LocalBalenaOsDevice[]>;
}
}

23
typings/docker-toolbelt/index.d.ts vendored Normal file
View File

@ -0,0 +1,23 @@
declare module 'docker-toolbelt' {
import * as Docker from 'dockerode';
interface ImageSpec {
registry?: string;
imageName: string;
tagName: string;
digest?: string;
}
type ProgressCallback = (event: any) => void;
class DockerToolbelt extends Docker {
public getRegistryAndName(image: string): Promise<ImageSpec>;
public createDeltaAsync(
src: string,
dest: string,
onProgress?: ProgressCallback,
): Promise<string>;
}
export = DockerToolbelt;
}

View File

@ -15,4 +15,25 @@
* limitations under the License.
*/
declare module 'resin-cli-visuals';
declare module 'resin-cli-visuals' {
export const Progress: new (...options: any[]) => any;
export class Spinner {
constructor(message?: string);
spinner: any;
start(): void;
stop(): void;
}
export const SpinnerPromise: new <T>(options: {
promise: T;
startMessage: string;
stopMessage: string;
}) => T;
export const table: {
horizontal: (...options: any[]) => any;
vertical: (...options: any[]) => any;
};
export const drive: (...options: any[]) => any;
}