mirror of
https://github.com/balena-io/balena-cli.git
synced 2025-06-24 18:45:07 +00:00
Compare commits
22 Commits
os-configu
...
dual-stack
Author | SHA1 | Date | |
---|---|---|---|
237a0a621e | |||
a1952bf347 | |||
017c767f61 | |||
7d79c4e24b | |||
60bc5092e0 | |||
a33a794931 | |||
f0ede6fca2 | |||
dbe177e766 | |||
09f80730a8 | |||
327d28c103 | |||
56ab785a82 | |||
305d65d5ed | |||
c4d3686a34 | |||
ce06854b55 | |||
8db05cc8a7 | |||
7a22c987d2 | |||
45efbcdfe3 | |||
d6a9b78b3e | |||
e8ac3ea960 | |||
0ffa0f85a2 | |||
5e7479f60e | |||
07365c45f2 |
@ -1,3 +1,208 @@
|
||||
- commits:
|
||||
- subject: Update balena-preload to 17.0.0
|
||||
hash: 60bc5092e0d6014aecf57d157cac536b3c645f0f
|
||||
body: |
|
||||
Update balena-preload from 16.0.0 to 17.0.0
|
||||
footer:
|
||||
Change-type: patch
|
||||
change-type: patch
|
||||
author: Thodoris Greasidis
|
||||
nested:
|
||||
- commits:
|
||||
- subject: Improve typings
|
||||
hash: 67f0f3e9e3b4b37fbaeef2843dc4171a7d661c41
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: patch
|
||||
change-type: patch
|
||||
author: Thodoris Greasidis
|
||||
nested: []
|
||||
- subject: Stop returning Bluebird promises & drop it from the dependencies
|
||||
hash: 85375cbc1fdb0ce1ad08756f178ed55596e606d9
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: major
|
||||
change-type: major
|
||||
author: Thodoris Greasidis
|
||||
nested: []
|
||||
version: balena-preload-17.0.0
|
||||
title: ""
|
||||
date: 2024-10-21T16:20:33.486Z
|
||||
version: 20.2.1
|
||||
title: ""
|
||||
date: 2025-01-01T18:04:56.723Z
|
||||
- commits:
|
||||
- subject: "os configure: Give precedence to the boot partition located in the
|
||||
image over the device-type.json contents"
|
||||
hash: dbe177e76639040c5fac49746fbbcee2e3360d0a
|
||||
body: |
|
||||
Update balena-device-init from 8.0.0 to 8.1.0
|
||||
footer:
|
||||
Change-type: minor
|
||||
change-type: minor
|
||||
author: Thodoris Greasidis
|
||||
nested:
|
||||
- commits:
|
||||
- subject: Try to find the boot partition by inspecting the image
|
||||
hash: de53f11c56133bf2757c5361f8dbc6afa9e826fc
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: minor
|
||||
change-type: minor
|
||||
author: Thodoris Greasidis
|
||||
nested: []
|
||||
version: balena-device-init-8.1.0
|
||||
title: ""
|
||||
date: 2024-12-30T12:41:49.474Z
|
||||
- commits:
|
||||
- subject: Drop the unnecessary eslint.config.js
|
||||
hash: 91d587f34bf985bf65e6c21278e7cd70f9281656
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: patch
|
||||
change-type: patch
|
||||
author: Thodoris Greasidis
|
||||
nested: []
|
||||
- subject: "packacke.json: Explicitly set type commonjs"
|
||||
hash: f6a0be1d12f09d7d910e5ae35f48f1268542a74e
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: patch
|
||||
change-type: patch
|
||||
author: Thodoris Greasidis
|
||||
nested: []
|
||||
version: balena-device-init-8.0.1
|
||||
title: ""
|
||||
date: 2024-12-19T13:41:14.170Z
|
||||
version: 20.2.0
|
||||
title: ""
|
||||
date: 2024-12-31T18:41:45.907Z
|
||||
- commits:
|
||||
- subject: Add more realistic os configure tests
|
||||
hash: 305d65d5ed07cce767599ac4e5dd131e2a87d10d
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: patch
|
||||
change-type: patch
|
||||
author: Thodoris Greasidis
|
||||
nested: []
|
||||
version: 20.1.6
|
||||
title: ""
|
||||
date: 2024-12-30T17:16:09.432Z
|
||||
- commits:
|
||||
- subject: Update shrinkwrapped express to v4.21.2
|
||||
hash: 7a22c987d24aca0754bde5618c232424ede72656
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: patch
|
||||
change-type: patch
|
||||
author: Oskar Williams
|
||||
nested: []
|
||||
version: 20.1.5
|
||||
title: ""
|
||||
date: 2024-12-20T17:55:20.670Z
|
||||
- commits:
|
||||
- subject: Update balena-device-init to 8.0.0
|
||||
hash: e8ac3ea960b1765470b496dbd714036d9b2ecd93
|
||||
body: |
|
||||
Update balena-device-init from 7.0.1 to 8.0.0
|
||||
footer:
|
||||
Change-type: patch
|
||||
change-type: patch
|
||||
See: https://github.com/balena-io-modules/balena-device-init/pull/46
|
||||
see: https://github.com/balena-io-modules/balena-device-init/pull/46
|
||||
author: Thodoris Greasidis
|
||||
nested:
|
||||
- commits:
|
||||
- subject: Avoid running linting in the custom tests
|
||||
hash: 420f93d5b2c60b67157897d8bbe0fab315c98b54
|
||||
body: |
|
||||
Balena-lint for some reason was failing in the
|
||||
windows custom test runner.
|
||||
footer:
|
||||
Change-type: patch
|
||||
change-type: patch
|
||||
author: Thodoris Greasidis
|
||||
nested: []
|
||||
- subject: Stop returning Bluebird promises
|
||||
hash: b76200a113fee69e11bf182945c382081f8d25e1
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: major
|
||||
change-type: major
|
||||
author: Thodoris Greasidis
|
||||
nested: []
|
||||
- subject: "package: Publish only the build & typings folders"
|
||||
hash: d1754650272913577b3ce0786941ad54d4acddc8
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: major
|
||||
change-type: major
|
||||
author: Thodoris Greasidis
|
||||
nested: []
|
||||
- subject: Convert to TypeScript with es6 module notation
|
||||
hash: 3576d2c423b294a8703f5474f72a9b906cef927a
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: major
|
||||
change-type: major
|
||||
author: Thodoris Greasidis
|
||||
nested: []
|
||||
- subject: Replace gulp, coffeelint & mochainon with tsc, @balena/lint, mocha,
|
||||
chai & sinon
|
||||
hash: 4c0cf8af370962e8eefda3a2855113629c4ffbb5
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: patch
|
||||
change-type: patch
|
||||
author: Thodoris Greasidis
|
||||
nested: []
|
||||
- subject: Drop support for node <20.6.0
|
||||
hash: b7c76be409a7a691db66466bc6d6eb746c0ca51e
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: major
|
||||
change-type: major
|
||||
author: Thodoris Greasidis
|
||||
nested: []
|
||||
version: balena-device-init-8.0.0
|
||||
title: ""
|
||||
date: 2024-12-18T17:01:33.082Z
|
||||
- commits:
|
||||
- subject: "flowzone: Update runner versions"
|
||||
hash: ba1222ac43ca13b720b64d18129193be8b99af5b
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: patch
|
||||
change-type: patch
|
||||
author: Thodoris Greasidis
|
||||
nested: []
|
||||
- subject: Pin etcher-sdk to 9.0.8 to match resin-device-operations and fix tests
|
||||
hash: 65cde1b940b7bdcc64d34534afa4d9b7b1d14303
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: patch
|
||||
change-type: patch
|
||||
author: Thodoris Greasidis
|
||||
nested: []
|
||||
version: balena-device-init-7.0.2
|
||||
title: ""
|
||||
date: 2024-12-17T20:16:06.532Z
|
||||
version: 20.1.4
|
||||
title: ""
|
||||
date: 2024-12-20T16:57:31.612Z
|
||||
- commits:
|
||||
- subject: Update oclif to 4.17.0 and @oclif/core 4.1.0
|
||||
hash: 07365c45f2d9a46094faa60d62395c4ba139c9f6
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: patch
|
||||
change-type: patch
|
||||
author: Otavio Jacobi
|
||||
nested: []
|
||||
version: 20.1.3
|
||||
title: ""
|
||||
date: 2024-12-20T14:32:59.953Z
|
||||
- commits:
|
||||
- subject: Remove unnecessary `Promise.resolve` and `Promise.reject`
|
||||
hash: e192767156ef7123be76fb838b8cf1a4d80aeb54
|
||||
|
67
CHANGELOG.md
67
CHANGELOG.md
@ -4,6 +4,73 @@ 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/).
|
||||
|
||||
## 20.2.1 - 2025-01-01
|
||||
|
||||
|
||||
<details>
|
||||
<summary> Update balena-preload to 17.0.0 [Thodoris Greasidis] </summary>
|
||||
|
||||
> ### balena-preload-17.0.0 - 2024-10-21
|
||||
>
|
||||
> * Improve typings [Thodoris Greasidis]
|
||||
> * Stop returning Bluebird promises & drop it from the dependencies [Thodoris Greasidis]
|
||||
>
|
||||
|
||||
</details>
|
||||
|
||||
## 20.2.0 - 2024-12-31
|
||||
|
||||
|
||||
<details>
|
||||
<summary> os configure: Give precedence to the boot partition located in the image over the device-type.json contents [Thodoris Greasidis] </summary>
|
||||
|
||||
> ### balena-device-init-8.1.0 - Invalid date
|
||||
>
|
||||
> * Try to find the boot partition by inspecting the image [Thodoris Greasidis]
|
||||
>
|
||||
> ### balena-device-init-8.0.1 - 2024-12-19
|
||||
>
|
||||
> * Drop the unnecessary eslint.config.js [Thodoris Greasidis]
|
||||
> * packacke.json: Explicitly set type commonjs [Thodoris Greasidis]
|
||||
>
|
||||
|
||||
</details>
|
||||
|
||||
## 20.1.6 - 2024-12-30
|
||||
|
||||
* Add more realistic os configure tests [Thodoris Greasidis]
|
||||
|
||||
## 20.1.5 - 2024-12-20
|
||||
|
||||
* Update shrinkwrapped express to v4.21.2 [Oskar Williams]
|
||||
|
||||
## 20.1.4 - 2024-12-20
|
||||
|
||||
|
||||
<details>
|
||||
<summary> Update balena-device-init to 8.0.0 [Thodoris Greasidis] </summary>
|
||||
|
||||
> ### balena-device-init-8.0.0 - 2024-12-18
|
||||
>
|
||||
> * Avoid running linting in the custom tests [Thodoris Greasidis]
|
||||
> * Stop returning Bluebird promises [Thodoris Greasidis]
|
||||
> * package: Publish only the build & typings folders [Thodoris Greasidis]
|
||||
> * Convert to TypeScript with es6 module notation [Thodoris Greasidis]
|
||||
> * Replace gulp, coffeelint & mochainon with tsc, @balena/lint, mocha, chai & sinon [Thodoris Greasidis]
|
||||
> * Drop support for node <20.6.0 [Thodoris Greasidis]
|
||||
>
|
||||
> ### balena-device-init-7.0.2 - 2024-12-17
|
||||
>
|
||||
> * flowzone: Update runner versions [Thodoris Greasidis]
|
||||
> * Pin etcher-sdk to 9.0.8 to match resin-device-operations and fix tests [Thodoris Greasidis]
|
||||
>
|
||||
|
||||
</details>
|
||||
|
||||
## 20.1.3 - 2024-12-20
|
||||
|
||||
* Update oclif to 4.17.0 and @oclif/core 4.1.0 [Otavio Jacobi]
|
||||
|
||||
## 20.1.2 - 2024-12-17
|
||||
|
||||
* Remove unnecessary `Promise.resolve` and `Promise.reject` [Pagan Gazzard]
|
||||
|
2986
npm-shrinkwrap.json
generated
2986
npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "balena-cli",
|
||||
"version": "20.1.2",
|
||||
"version": "20.2.1",
|
||||
"description": "The official balena Command Line Interface",
|
||||
"main": "./build/app.js",
|
||||
"homepage": "https://github.com/balena-io/balena-cli",
|
||||
@ -180,7 +180,7 @@
|
||||
"mock-fs": "^5.2.0",
|
||||
"mock-require": "^3.0.3",
|
||||
"nock": "^13.2.1",
|
||||
"oclif": "^4.14.0",
|
||||
"oclif": "^4.17.0",
|
||||
"rewire": "^7.0.0",
|
||||
"simple-git": "^3.14.1",
|
||||
"sinon": "^19.0.0",
|
||||
@ -193,13 +193,13 @@
|
||||
"@balena/dockerignore": "^1.0.2",
|
||||
"@balena/env-parsing": "^1.1.8",
|
||||
"@balena/es-version": "^1.0.1",
|
||||
"@oclif/core": "^4.0.31",
|
||||
"@oclif/core": "^4.1.0",
|
||||
"@sentry/node": "^6.16.1",
|
||||
"balena-config-json": "^4.2.0",
|
||||
"balena-device-init": "^7.0.1",
|
||||
"balena-device-init": "^8.1.0",
|
||||
"balena-errors": "^4.7.3",
|
||||
"balena-image-fs": "^7.0.6",
|
||||
"balena-preload": "^16.0.0",
|
||||
"balena-preload": "^17.0.0",
|
||||
"balena-sdk": "^20.8.0",
|
||||
"balena-semver": "^2.3.0",
|
||||
"balena-settings-client": "^5.0.2",
|
||||
@ -274,6 +274,6 @@
|
||||
}
|
||||
},
|
||||
"versionist": {
|
||||
"publishedAt": "2024-12-17T11:34:56.167Z"
|
||||
"publishedAt": "2025-01-01T18:04:59.323Z"
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
diff --git a/node_modules/oclif/lib/commands/pack/win.js b/node_modules/oclif/lib/commands/pack/win.js
|
||||
index ef7f90e..8264b7c 100644
|
||||
index bfe9205..482519e 100644
|
||||
--- a/node_modules/oclif/lib/commands/pack/win.js
|
||||
+++ b/node_modules/oclif/lib/commands/pack/win.js
|
||||
@@ -76,6 +76,12 @@ InstallDir "\$PROGRAMFILES${arch === 'x64' ? '64' : ''}\\${config.dirname}"
|
||||
@@ -86,6 +86,12 @@ InstallDir "\$PROGRAMFILES${arch === 'x64' ? '64' : ''}\\${config.dirname}"
|
||||
${customization}
|
||||
|
||||
Section "${config.name} CLI \${VERSION}"
|
||||
@ -16,20 +16,18 @@ index ef7f90e..8264b7c 100644
|
||||
File /r bin
|
||||
File /r client
|
||||
diff --git a/node_modules/oclif/lib/tarballs/build.js b/node_modules/oclif/lib/tarballs/build.js
|
||||
index 14d5a6e..7b42a6f 100644
|
||||
index f0c8d95..a72400e 100644
|
||||
--- a/node_modules/oclif/lib/tarballs/build.js
|
||||
+++ b/node_modules/oclif/lib/tarballs/build.js
|
||||
@@ -200,6 +200,13 @@ const extractCLI = async (tarball, c) => {
|
||||
@@ -218,6 +218,11 @@ const extractCLI = async (tarball, c) => {
|
||||
(0, promises_1.rm)(path.join(workspace, path.basename(tarball)), { recursive: true }),
|
||||
(0, fs_extra_1.remove)(path.join(workspace, 'bin', 'run.cmd')),
|
||||
]);
|
||||
+
|
||||
+ // The oclif installers are a production installation, while the source
|
||||
+ // `bin` folder may contain a `.fast-boot.json` file of a dev installation.
|
||||
+ // This has previously led to issues preventing the CLI from starting, so
|
||||
+ // delete `.fast-boot.json` (if any) from the destination folder.
|
||||
+ await (0, fs_extra_1.remove)(path.join(workspace, 'bin', '.fast-boot.json'));
|
||||
+
|
||||
};
|
||||
const buildTarget = async (target, c, options) => {
|
||||
const workspace = c.workspace(target);
|
||||
if (target.platform === 'win32' && target.arch === 'arm64' && (0, semver_1.lt)(c.nodeVersion, '20.0.0')) {
|
2
repo.yml
2
repo.yml
@ -6,6 +6,8 @@ upstream:
|
||||
url: 'https://github.com/balena-io/balena-sdk'
|
||||
- repo: 'balena-config-json'
|
||||
url: 'https://github.com/balena-io-modules/balena-config-json'
|
||||
- repo: 'balena-device-init'
|
||||
url: 'https://github.com/balena-io-modules/balena-device-init'
|
||||
- repo: 'balena-image-manager'
|
||||
url: 'https://github.com/balena-io-modules/balena-image-manager'
|
||||
- repo: 'balena-preload'
|
||||
|
@ -203,7 +203,7 @@ export default class DeviceTunnelCmd extends Command {
|
||||
parsePortMapping(portMapping: string) {
|
||||
const mappingElements = portMapping.split(':');
|
||||
|
||||
let localAddress = 'localhost';
|
||||
let localAddress = '::';
|
||||
|
||||
// First element is always remotePort
|
||||
// TODO: figure out why we have explicitly passed `undefined` for the radix parameter
|
||||
|
@ -110,6 +110,27 @@ export async function getManifest(
|
||||
const init = await import('balena-device-init');
|
||||
const sdk = getBalenaSdk();
|
||||
const manifest = await init.getImageManifest(image);
|
||||
if (manifest != null) {
|
||||
const config = manifest.configuration?.config;
|
||||
if (config?.partition != null) {
|
||||
const { getBootPartition } = await import('balena-config-json');
|
||||
// Find the device-type.json property that holds the boot partition number for
|
||||
// this device type (config.partition or config.partition.primary) and overwrite it
|
||||
// with the boot partition number that was found by inspecting the image.
|
||||
// since it's deprecated & no longer updated for newer releases.
|
||||
if (typeof config.partition === 'number') {
|
||||
config.partition = await getBootPartition(image);
|
||||
} else if (config.partition.primary != null) {
|
||||
config.partition.primary = await getBootPartition(image);
|
||||
}
|
||||
// TODO: Add handling for when we no longer include a `config.partition` at all.
|
||||
}
|
||||
} else {
|
||||
// TODO: Change this in the next major to throw, after confirming that this works for all supported OS versions.
|
||||
console.error(
|
||||
`[warn] Error while finding a device-type.json on the provided image path. Attempting to fetch from the API.`,
|
||||
);
|
||||
}
|
||||
if (
|
||||
manifest != null &&
|
||||
manifest.slug !== deviceType &&
|
||||
|
@ -21,6 +21,8 @@ import * as process from 'process';
|
||||
import { runCommand } from '../../helpers';
|
||||
import { promisify } from 'util';
|
||||
import * as tmp from 'tmp';
|
||||
import type * as $imagefs from 'balena-image-fs';
|
||||
import * as stripIndent from 'common-tags/lib/stripIndent';
|
||||
|
||||
tmp.setGracefulCleanup();
|
||||
const tmpNameAsync = promisify(tmp.tmpName);
|
||||
@ -29,30 +31,92 @@ import { BalenaAPIMock } from '../../nock/balena-api-mock';
|
||||
|
||||
if (process.platform !== 'win32') {
|
||||
describe('balena os configure', function () {
|
||||
let imagefs: typeof $imagefs;
|
||||
let api: BalenaAPIMock;
|
||||
let tmpPath: string;
|
||||
let tmpDummyPath: string;
|
||||
let tmpMatchingDtJsonPartitionPath: string;
|
||||
let tmpNonMatchingDtJsonPartitionPath: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
before(async function () {
|
||||
// We conditionally import balena-image-fs, since when imported on top level then unrelated tests on win32 failed with:
|
||||
// EPERM: operation not permitted, rename 'C:\Users\RUNNER~1\AppData\Local\Temp\tmp-<...>.inprogress' -> 'C:\Users\RUNNER~1\AppData\Local\Temp\tmp-<...>'
|
||||
// at async Object.rename (node:internal/fs/promises:782:10) {
|
||||
imagefs = await import('balena-image-fs');
|
||||
tmpDummyPath = (await tmpNameAsync()) as string;
|
||||
await fs.copyFile('./tests/test-data/dummy.img', tmpDummyPath);
|
||||
tmpMatchingDtJsonPartitionPath = (await tmpNameAsync()) as string;
|
||||
await fs.copyFile(
|
||||
'./tests/test-data/mock-jetson-nano-6.0.13.with-boot-partition-12.img',
|
||||
tmpMatchingDtJsonPartitionPath,
|
||||
);
|
||||
|
||||
tmpNonMatchingDtJsonPartitionPath = (await tmpNameAsync()) as string;
|
||||
// Create an image with a device-type.json that mentions a non matching boot partition.
|
||||
// We copy the pre-existing image and modify it, since including a separate one
|
||||
// would add 18MB more to the repository.
|
||||
await fs.copyFile(
|
||||
'./tests/test-data/mock-jetson-nano-6.0.13.with-boot-partition-12.img',
|
||||
tmpNonMatchingDtJsonPartitionPath,
|
||||
);
|
||||
await imagefs.interact(
|
||||
tmpNonMatchingDtJsonPartitionPath,
|
||||
12,
|
||||
async (_fs) => {
|
||||
const readFileAsync = promisify(_fs.readFile);
|
||||
const writeFileAsync = promisify(_fs.writeFile);
|
||||
|
||||
const dtJson = JSON.parse(
|
||||
await readFileAsync('/device-type.json', { encoding: 'utf8' }),
|
||||
);
|
||||
expect(dtJson).to.have.nested.property(
|
||||
'configuration.config.partition',
|
||||
12,
|
||||
);
|
||||
dtJson.configuration.config.partition = 999;
|
||||
await writeFileAsync('/device-type.json', JSON.stringify(dtJson));
|
||||
|
||||
await writeFileAsync(
|
||||
'/os-release',
|
||||
stripIndent`
|
||||
ID="balena-os"
|
||||
NAME="balenaOS"
|
||||
VERSION="6.1.25"
|
||||
VERSION_ID="6.1.25"
|
||||
PRETTY_NAME="balenaOS 6.1.25"
|
||||
DISTRO_CODENAME="kirkstone"
|
||||
MACHINE="jetson-nano"
|
||||
META_BALENA_VERSION="6.1.25"`,
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
api = new BalenaAPIMock();
|
||||
api.expectGetWhoAmI({ optional: true, persist: true });
|
||||
tmpPath = (await tmpNameAsync()) as string;
|
||||
await fs.copyFile('./tests/test-data/dummy.img', tmpPath);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
afterEach(() => {
|
||||
api.done();
|
||||
await fs.unlink(tmpPath);
|
||||
});
|
||||
|
||||
it('should inject a valid config.json file', async () => {
|
||||
after(async () => {
|
||||
await fs.unlink(tmpDummyPath);
|
||||
await fs.unlink(tmpMatchingDtJsonPartitionPath);
|
||||
await fs.unlink(tmpNonMatchingDtJsonPartitionPath);
|
||||
});
|
||||
|
||||
it('should detect the OS version and inject a valid config.json file to a 6.0.13 image with partition 12 as boot & matching device-type.json', async () => {
|
||||
api.expectGetApplication();
|
||||
api.expectGetConfigDeviceTypes();
|
||||
api.expectGetDeviceTypes();
|
||||
// It should not reach to /config or /device-types/v1 but instead find
|
||||
// everything required from the device-type.json in the image.
|
||||
// api.expectGetConfigDeviceTypes();
|
||||
api.expectDownloadConfig();
|
||||
|
||||
const command: string[] = [
|
||||
`os configure ${tmpPath}`,
|
||||
'--device-type raspberrypi3',
|
||||
'--version 2.47.0+rev1',
|
||||
`os configure ${tmpMatchingDtJsonPartitionPath}`,
|
||||
'--device-type jetson-nano',
|
||||
'--fleet testApp',
|
||||
'--config-app-update-poll-interval 10',
|
||||
'--config-network ethernet',
|
||||
@ -65,8 +129,117 @@ if (process.platform !== 'win32') {
|
||||
expect(err.join('')).to.equal('');
|
||||
|
||||
// confirm the image contains a config.json...
|
||||
const imagefs = await import('balena-image-fs');
|
||||
const config = await imagefs.interact(tmpPath, 1, async (_fs) => {
|
||||
const config = await imagefs.interact(
|
||||
tmpMatchingDtJsonPartitionPath,
|
||||
12,
|
||||
async (_fs) => {
|
||||
const readFileAsync = promisify(_fs.readFile);
|
||||
const dtJson = JSON.parse(
|
||||
await readFileAsync('/device-type.json', { encoding: 'utf8' }),
|
||||
);
|
||||
// confirm that the device-type.json mentions the expected partition
|
||||
expect(dtJson).to.have.nested.property(
|
||||
'configuration.config.partition',
|
||||
12,
|
||||
);
|
||||
return await readFileAsync('/config.json');
|
||||
},
|
||||
);
|
||||
expect(config).to.not.be.empty;
|
||||
|
||||
// confirm the image has the correct config.json values...
|
||||
const configObj = JSON.parse(config.toString('utf8'));
|
||||
expect(configObj).to.have.property('deviceType', 'jetson-nano');
|
||||
expect(configObj).to.have.property('initialDeviceName', 'testDeviceName');
|
||||
});
|
||||
|
||||
it('should detect the OS version and inject a valid config.json file to a 6.1.25 image with partition 12 as boot & a non-matching device-type.json', async () => {
|
||||
api.expectGetApplication();
|
||||
api.expectGetDeviceTypes();
|
||||
// It should not reach to /config or /device-types/v1 but instead find
|
||||
// everything required from the device-type.json in the image.
|
||||
// api.expectGetConfigDeviceTypes();
|
||||
api.expectDownloadConfig();
|
||||
|
||||
const command: string[] = [
|
||||
`os configure ${tmpNonMatchingDtJsonPartitionPath}`,
|
||||
'--device-type jetson-nano',
|
||||
'--fleet testApp',
|
||||
'--config-app-update-poll-interval 10',
|
||||
'--config-network ethernet',
|
||||
'--initial-device-name testDeviceName',
|
||||
'--provisioning-key-name testKey',
|
||||
'--provisioning-key-expiry-date 2050-12-12',
|
||||
];
|
||||
|
||||
const { err } = await runCommand(command.join(' '));
|
||||
expect(err.join('')).to.equal('');
|
||||
|
||||
// confirm the image contains a config.json...
|
||||
const config = await imagefs.interact(
|
||||
tmpNonMatchingDtJsonPartitionPath,
|
||||
12,
|
||||
async (_fs) => {
|
||||
const readFileAsync = promisify(_fs.readFile);
|
||||
const dtJson = JSON.parse(
|
||||
await readFileAsync('/device-type.json', { encoding: 'utf8' }),
|
||||
);
|
||||
// confirm that the device-type.json mentions the expected partition
|
||||
expect(dtJson).to.have.nested.property(
|
||||
'configuration.config.partition',
|
||||
999,
|
||||
);
|
||||
return await readFileAsync('/config.json');
|
||||
},
|
||||
);
|
||||
expect(config).to.not.be.empty;
|
||||
|
||||
// confirm the image has the correct config.json values...
|
||||
const configObj = JSON.parse(config.toString('utf8'));
|
||||
expect(configObj).to.have.property('deviceType', 'jetson-nano');
|
||||
expect(configObj).to.have.property('initialDeviceName', 'testDeviceName');
|
||||
});
|
||||
|
||||
// TODO: In the next major consider just failing when we can't find a device-types.json in the image.
|
||||
it('should inject a valid config.json file to a dummy image', async () => {
|
||||
api.expectGetApplication();
|
||||
// Since the dummy image doesn't include a device-type.json
|
||||
// we have to reach to the API to fetch the manifest of the device type.
|
||||
api.expectGetConfigDeviceTypes();
|
||||
api.expectDownloadConfig();
|
||||
|
||||
const command: string[] = [
|
||||
`os configure ${tmpDummyPath}`,
|
||||
'--device-type raspberrypi3',
|
||||
'--version 2.47.0+rev1',
|
||||
'--fleet testApp',
|
||||
'--config-app-update-poll-interval 10',
|
||||
'--config-network ethernet',
|
||||
'--initial-device-name testDeviceName',
|
||||
'--provisioning-key-name testKey',
|
||||
'--provisioning-key-expiry-date 2050-12-12',
|
||||
];
|
||||
|
||||
const { err } = await runCommand(command.join(' '));
|
||||
// Once we replace the dummy.img with one that includes a os-release & device-type.json
|
||||
// then we should be able to change this to expect no errors.
|
||||
expect(
|
||||
err.flatMap((line) => line.split('\n')).filter((line) => line !== ''),
|
||||
).to.deep.equal(
|
||||
stripIndent`
|
||||
[warn] "${tmpDummyPath}":
|
||||
[warn] 1 partition(s) found, but none containing file "/device-type.json".
|
||||
[warn] Assuming default boot partition number '1'.
|
||||
[warn] "${tmpDummyPath}":
|
||||
[warn] Could not find a previous "/config.json" file in partition '1'.
|
||||
[warn] Proceeding anyway, but this is unexpected.
|
||||
[warn] Error while finding a device-type.json on the provided image path. Attempting to fetch from the API.`.split(
|
||||
'\n',
|
||||
),
|
||||
);
|
||||
|
||||
// confirm the image contains a config.json...
|
||||
const config = await imagefs.interact(tmpDummyPath, 1, async (_fs) => {
|
||||
return await promisify(_fs.readFile)('/config.json');
|
||||
});
|
||||
expect(config).to.not.be.empty;
|
||||
|
@ -61,22 +61,25 @@ export class BalenaAPIMock extends NockMock {
|
||||
}
|
||||
|
||||
public expectDownloadConfig(opts: ScopeOpts = {}) {
|
||||
this.optPost('/download-config', opts).reply(
|
||||
200,
|
||||
JSON.parse(`{
|
||||
"applicationId":1301645,
|
||||
"deviceType":"raspberrypi3",
|
||||
"userId":43699,
|
||||
"appUpdatePollInterval":600000,
|
||||
"listenPort":48484,
|
||||
"vpnPort":443,
|
||||
"apiEndpoint":"https://api.balena-cloud.com",
|
||||
"vpnEndpoint":"vpn.balena-cloud.com",
|
||||
"registryEndpoint":"registry2.balena-cloud.com",
|
||||
"deltaEndpoint":"https://delta.balena-cloud.com",
|
||||
"apiKey":"nothingtoseehere"
|
||||
}`),
|
||||
);
|
||||
this.optPost('/download-config', opts).reply(200, (_uri, body) => {
|
||||
let deviceType = 'raspberrypi3';
|
||||
if (typeof body === 'object' && 'deviceType' in body) {
|
||||
deviceType = body.deviceType;
|
||||
}
|
||||
return JSON.parse(`{
|
||||
"applicationId":1301645,
|
||||
"deviceType":"${deviceType}",
|
||||
"userId":43699,
|
||||
"appUpdatePollInterval":600000,
|
||||
"listenPort":48484,
|
||||
"vpnPort":443,
|
||||
"apiEndpoint":"https://api.balena-cloud.com",
|
||||
"vpnEndpoint":"vpn.balena-cloud.com",
|
||||
"registryEndpoint":"registry2.balena-cloud.com",
|
||||
"deltaEndpoint":"https://delta.balena-cloud.com",
|
||||
"apiKey":"nothingtoseehere"
|
||||
}`);
|
||||
});
|
||||
}
|
||||
|
||||
public expectApplicationProvisioning(opts: ScopeOpts = {}) {
|
||||
|
Binary file not shown.
105
typings/balena-device-init/index.d.ts
vendored
105
typings/balena-device-init/index.d.ts
vendored
@ -1,105 +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-device-init' {
|
||||
import type { DeviceTypeJson } from 'balena-sdk';
|
||||
|
||||
interface OperationState {
|
||||
operation:
|
||||
| CopyOperation
|
||||
| ReplaceOperation
|
||||
| RunScriptOperation
|
||||
| BurnOperation;
|
||||
percentage: number;
|
||||
}
|
||||
|
||||
interface Operation {
|
||||
command: string;
|
||||
}
|
||||
|
||||
interface CopyOperation extends Operation {
|
||||
command: 'copy';
|
||||
from: { path: string };
|
||||
to: { path: string };
|
||||
}
|
||||
|
||||
interface ReplaceOperation extends Operation {
|
||||
command: 'replace';
|
||||
copy: string;
|
||||
replace: string;
|
||||
file: {
|
||||
path: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface RunScriptOperation extends Operation {
|
||||
command: 'run-script';
|
||||
script: string;
|
||||
arguments?: string[];
|
||||
}
|
||||
|
||||
interface BurnOperation extends Operation {
|
||||
command: 'burn';
|
||||
image?: string;
|
||||
}
|
||||
|
||||
interface BurnProgress {
|
||||
type: 'write' | 'check';
|
||||
percentage: number;
|
||||
transferred: number;
|
||||
length: number;
|
||||
remaining: number;
|
||||
eta: number;
|
||||
runtime: number;
|
||||
delta: number;
|
||||
speed: number;
|
||||
}
|
||||
|
||||
interface InitializeEmitter {
|
||||
on(event: 'stdout' | 'stderr', callback: (msg: string) => void): void;
|
||||
on(event: 'state', callback: (state: OperationState) => void): void;
|
||||
on(event: 'burn', callback: (state: BurnProgress) => void): void;
|
||||
on(event: 'end', callback: () => void): void;
|
||||
on(event: 'error', callback: (error: Error) => void): void;
|
||||
}
|
||||
|
||||
// As of writing this, these are Bluebird promises, but we are typing then
|
||||
// as normal Promises so that we do not rely on Bluebird specific methods,
|
||||
// so that the CLI will not require any change once the package drops Bluebird.
|
||||
|
||||
export function configure(
|
||||
image: string,
|
||||
manifest: DeviceTypeJson.DeviceType,
|
||||
config: object,
|
||||
options?: object,
|
||||
): Promise<InitializeEmitter>;
|
||||
|
||||
export function initialize(
|
||||
image: string,
|
||||
manifest: DeviceTypeJson.DeviceType,
|
||||
config: object,
|
||||
): Promise<InitializeEmitter>;
|
||||
|
||||
export function getImageOsVersion(
|
||||
image: string,
|
||||
manifest: DeviceTypeJson.DeviceType,
|
||||
): Promise<string | null>;
|
||||
|
||||
export function getImageManifest(
|
||||
image: string,
|
||||
): Promise<DeviceTypeJson.DeviceType | null>;
|
||||
}
|
Reference in New Issue
Block a user