mirror of
https://github.com/balena-io/balena-cli.git
synced 2025-06-24 10:35:39 +00:00
Compare commits
35 Commits
Author | SHA1 | Date | |
---|---|---|---|
776115ef5d | |||
f031ec1dea | |||
fe42438090 | |||
b616fbdd79 | |||
81edfbbae1 | |||
663e83c3b8 | |||
b650f8ff6d | |||
58234f17e1 | |||
77905f4a74 | |||
30076fabe6 | |||
28703bb5ae | |||
37b3c6abe9 | |||
b4e473e4d4 | |||
0d4e411777 | |||
7e6f2189e8 | |||
3903daf8a8 | |||
18bc0d61e7 | |||
7f2daeebb0 | |||
813e9cb82e | |||
3bcb3c1b2e | |||
20d76556c2 | |||
e829068725 | |||
650e896f70 | |||
a9042124ea | |||
d24d78dac7 | |||
42c50ef8ae | |||
ba4b9bd447 | |||
02c0ea5b59 | |||
bc3558dd8e | |||
aad62d1ccd | |||
ecc6f80164 | |||
c0fd1e3886 | |||
9d3120b144 | |||
ed0e03ddb2 | |||
8fe6d6c026 |
4
.github/actions/publish/action.yml
vendored
4
.github/actions/publish/action.yml
vendored
@ -39,7 +39,7 @@ runs:
|
||||
run: tar -xf ${{ runner.temp }}/custom.tgz
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4
|
||||
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||
with:
|
||||
node-version: ${{ inputs.NODE_VERSION }}
|
||||
cache: npm
|
||||
@ -94,7 +94,7 @@ runs:
|
||||
runner_arch="$(echo "${RUNNER_ARCH}" | tr '[:upper:]' '[:lower:]')"
|
||||
|
||||
if [[ $runner_os =~ darwin|macos|osx ]]; then
|
||||
CSC_KEY_PASSWORD=${{ fromJSON(inputs.secrets).APPLE_SIGNING_PASSWORD }}
|
||||
CSC_KEY_PASSWORD='${{ fromJSON(inputs.secrets).APPLE_SIGNING_PASSWORD }}'
|
||||
CSC_KEYCHAIN=signing_temp
|
||||
CSC_LINK=${{ fromJSON(inputs.secrets).APPLE_SIGNING }}
|
||||
|
||||
|
2
.github/actions/test/action.yml
vendored
2
.github/actions/test/action.yml
vendored
@ -26,7 +26,7 @@ runs:
|
||||
steps:
|
||||
# https://github.com/actions/setup-node#caching-global-packages-data
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4
|
||||
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||
with:
|
||||
node-version: ${{ inputs.NODE_VERSION }}
|
||||
cache: npm
|
||||
|
@ -1,3 +1,168 @@
|
||||
- commits:
|
||||
- subject: Update actions/setup-node digest to cdca736
|
||||
hash: fe4243809033735a6439f39a1a33dfd00039d656
|
||||
body: |
|
||||
Update actions/setup-node
|
||||
footer:
|
||||
Change-type: patch
|
||||
change-type: patch
|
||||
author: balena-renovate[bot]
|
||||
nested: []
|
||||
version: 21.1.6
|
||||
title: ""
|
||||
date: 2025-04-03T14:12:06.042Z
|
||||
- commits:
|
||||
- subject: Update dockerode/docker-modem dependencies for fixes
|
||||
hash: b650f8ff6d01d2144886253f93aa1d1867f51980
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: patch
|
||||
change-type: patch
|
||||
Signed-off-by: Ken Bannister <kb2ma@runbox.com>
|
||||
signed-off-by: Ken Bannister <kb2ma@runbox.com>
|
||||
author: Ken Bannister
|
||||
nested: []
|
||||
version: 21.1.5
|
||||
title: ""
|
||||
date: 2025-04-03T13:28:08.855Z
|
||||
- commits:
|
||||
- subject: Add comment with secure boot signature file example for preload
|
||||
hash: 28703bb5ae13539ab4c1c597e6a53a5292a7edde
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: patch
|
||||
change-type: patch
|
||||
Signed-off-by: Ken Bannister <kb2ma@runbox.com>
|
||||
signed-off-by: Ken Bannister <kb2ma@runbox.com>
|
||||
author: Ken Bannister
|
||||
nested: []
|
||||
version: 21.1.4
|
||||
title: ""
|
||||
date: 2025-04-02T09:16:27.791Z
|
||||
- commits:
|
||||
- subject: Fix device detail for open balena
|
||||
hash: 0d4e411777dd53d83c475da3653ab94176e07d7d
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: patch
|
||||
change-type: patch
|
||||
author: Otavio Jacobi
|
||||
nested: []
|
||||
version: 21.1.3
|
||||
title: ""
|
||||
date: 2025-03-28T16:57:00.250Z
|
||||
- commits:
|
||||
- subject: Deny preload for an image with secure boot enabled
|
||||
hash: 7f2daeebb0973a59682ba4300e1b00bce6f6aead
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: patch
|
||||
change-type: patch
|
||||
Signed-off-by: Ken Bannister <kb2ma@runbox.com>
|
||||
signed-off-by: Ken Bannister <kb2ma@runbox.com>
|
||||
author: Ken Bannister
|
||||
nested: []
|
||||
version: 21.1.2
|
||||
title: ""
|
||||
date: 2025-03-27T12:20:22.883Z
|
||||
- commits:
|
||||
- subject: Bump balena-sdk to 21.3.0
|
||||
hash: e82906872538a7401e31bd52e662e8356a89d413
|
||||
body: |
|
||||
Update balena-sdk from 21.2.1 to 21.3.0
|
||||
footer:
|
||||
Change-type: patch
|
||||
change-type: patch
|
||||
author: Otavio Jacobi
|
||||
nested:
|
||||
- commits:
|
||||
- subject: "device: add `changed_api_heartbeat_state_on__date` to typings"
|
||||
hash: bfa52cf58c7d06859a1b5c6f62ff7c71324d3d20
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: minor
|
||||
change-type: minor
|
||||
author: Otavio Jacobi
|
||||
nested: []
|
||||
version: balena-sdk-21.3.0
|
||||
title: ""
|
||||
date: 2025-03-26T19:54:35.558Z
|
||||
- commits:
|
||||
- subject: fix linting
|
||||
hash: 13320aad81ba3dfc4950b9960f015b058222c4be
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: patch
|
||||
change-type: patch
|
||||
author: Otavio Jacobi
|
||||
nested: []
|
||||
version: balena-sdk-21.2.2
|
||||
title: ""
|
||||
date: 2025-03-26T18:55:53.610Z
|
||||
version: 21.1.1
|
||||
title: ""
|
||||
date: 2025-03-26T20:34:44.546Z
|
||||
- commits:
|
||||
- subject: Add support for new requirement labels feature
|
||||
hash: 42c50ef8aed110b317a0472d928bf75e372b4c0b
|
||||
body: |
|
||||
Updates @balena/compose to v7 to include this new feature.
|
||||
footer:
|
||||
See: https://balena.fibery.io/Work/Project/Refactoring-container-contracts-1205
|
||||
see: https://balena.fibery.io/Work/Project/Refactoring-container-contracts-1205
|
||||
Depends-on: https://github.com/balena-io-modules/balena-compose/pull/64
|
||||
depends-on: https://github.com/balena-io-modules/balena-compose/pull/64
|
||||
Change-type: minor
|
||||
change-type: minor
|
||||
author: Felipe Lalanne
|
||||
nested: []
|
||||
version: 21.1.0
|
||||
title: ""
|
||||
date: 2025-03-12T19:34:17.610Z
|
||||
- commits:
|
||||
- subject: Drop support for OS versions <2.14.0
|
||||
hash: aad62d1ccd11ebb69b1035d5b95aef93d384bfd5
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: major
|
||||
change-type: major
|
||||
author: myarmolinsky
|
||||
nested: []
|
||||
- subject: "api-key generate: Add required argument `expiryDate`"
|
||||
hash: ecc6f80164fca3c0cde42b140b6d7404abe8c877
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: major
|
||||
change-type: major
|
||||
author: myarmolinsky
|
||||
nested: []
|
||||
- subject: Update `balena-preload` to 18.0.1
|
||||
hash: 9d3120b144c2c017eda55463b034f1561d264213
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: patch
|
||||
change-type: patch
|
||||
author: myarmolinsky
|
||||
nested: []
|
||||
- subject: Add dependency `date-fns`
|
||||
hash: ed0e03ddb274da294f719dc0e307ec37591e10d7
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: patch
|
||||
change-type: patch
|
||||
author: myarmolinsky
|
||||
nested: []
|
||||
- subject: Update `balena-sdk` to 21.2.1
|
||||
hash: 8fe6d6c0268f69bcf3bcac3c57470272b959e9b0
|
||||
body: ""
|
||||
footer:
|
||||
Change-type: patch
|
||||
change-type: patch
|
||||
author: myarmolinsky
|
||||
nested: []
|
||||
version: 21.0.0
|
||||
title: ""
|
||||
date: 2025-03-11T14:42:28.479Z
|
||||
- commits:
|
||||
- subject: Update TypeScript to 5.8.2
|
||||
hash: a10156a441b737275cabfb03bd10bfc5aba7bc88
|
||||
|
49
CHANGELOG.md
49
CHANGELOG.md
@ -4,6 +4,55 @@ 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/).
|
||||
|
||||
## 21.1.6 - 2025-04-03
|
||||
|
||||
* Update actions/setup-node digest to cdca736 [balena-renovate[bot]]
|
||||
|
||||
## 21.1.5 - 2025-04-03
|
||||
|
||||
* Update dockerode/docker-modem dependencies for fixes [Ken Bannister]
|
||||
|
||||
## 21.1.4 - 2025-04-02
|
||||
|
||||
* Add comment with secure boot signature file example for preload [Ken Bannister]
|
||||
|
||||
## 21.1.3 - 2025-03-28
|
||||
|
||||
* Fix device detail for open balena [Otavio Jacobi]
|
||||
|
||||
## 21.1.2 - 2025-03-27
|
||||
|
||||
* Deny preload for an image with secure boot enabled [Ken Bannister]
|
||||
|
||||
## 21.1.1 - 2025-03-26
|
||||
|
||||
|
||||
<details>
|
||||
<summary> Bump balena-sdk to 21.3.0 [Otavio Jacobi] </summary>
|
||||
|
||||
> ### balena-sdk-21.3.0 - 2025-03-26
|
||||
>
|
||||
> * device: add `changed_api_heartbeat_state_on__date` to typings [Otavio Jacobi]
|
||||
>
|
||||
> ### balena-sdk-21.2.2 - 2025-03-26
|
||||
>
|
||||
> * fix linting [Otavio Jacobi]
|
||||
>
|
||||
|
||||
</details>
|
||||
|
||||
## 21.1.0 - 2025-03-12
|
||||
|
||||
* Add support for new requirement labels feature [Felipe Lalanne]
|
||||
|
||||
## 21.0.0 - 2025-03-11
|
||||
|
||||
* Drop support for OS versions <2.14.0 [myarmolinsky]
|
||||
* api-key generate: Add required argument `expiryDate` [myarmolinsky]
|
||||
* Update `balena-preload` to 18.0.1 [myarmolinsky]
|
||||
* Add dependency `date-fns` [myarmolinsky]
|
||||
* Update `balena-sdk` to 21.2.1 [myarmolinsky]
|
||||
|
||||
## 20.2.10 - 2025-03-10
|
||||
|
||||
* Update TypeScript to 5.8.2 [Thodoris Greasidis]
|
||||
|
@ -326,6 +326,8 @@ or to authenticate requests to the API with an 'Authorization: Bearer <key>' hea
|
||||
Examples:
|
||||
|
||||
$ balena api-key generate "Jenkins Key"
|
||||
$ balena api-key generate "Jenkins Key" 2025-10-30
|
||||
$ balena api-key generate "Jenkins Key" never
|
||||
|
||||
### Arguments
|
||||
|
||||
@ -333,6 +335,10 @@ Examples:
|
||||
|
||||
the API key name
|
||||
|
||||
#### EXPIRYDATE
|
||||
|
||||
the expiry date of the API key as an ISO date string, or "never" for no expiry
|
||||
|
||||
## api-key list
|
||||
|
||||
### Aliases
|
||||
|
290
npm-shrinkwrap.json
generated
290
npm-shrinkwrap.json
generated
@ -1,16 +1,16 @@
|
||||
{
|
||||
"name": "balena-cli",
|
||||
"version": "20.2.10",
|
||||
"version": "21.1.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "balena-cli",
|
||||
"version": "20.2.10",
|
||||
"version": "21.1.6",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@balena/compose": "^6.0.0",
|
||||
"@balena/compose": "^7.0.1",
|
||||
"@balena/dockerignore": "^1.0.2",
|
||||
"@balena/env-parsing": "^1.1.8",
|
||||
"@balena/es-version": "^1.0.1",
|
||||
@ -19,9 +19,9 @@
|
||||
"balena-config-json": "^4.2.2",
|
||||
"balena-device-init": "^8.1.3",
|
||||
"balena-errors": "^4.7.3",
|
||||
"balena-image-fs": "^7.3.0",
|
||||
"balena-preload": "^17.0.0",
|
||||
"balena-sdk": "^20.8.0",
|
||||
"balena-image-fs": "^7.5.0",
|
||||
"balena-preload": "^18.0.1",
|
||||
"balena-sdk": "^21.3.0",
|
||||
"balena-semver": "^2.3.0",
|
||||
"balena-settings-client": "^5.0.2",
|
||||
"balena-settings-storage": "^8.1.0",
|
||||
@ -32,10 +32,11 @@
|
||||
"cli-truncate": "^2.1.0",
|
||||
"color-hash": "^1.1.1",
|
||||
"common-tags": "^1.7.2",
|
||||
"date-fns": "^4.1.0",
|
||||
"denymount": "^2.3.0",
|
||||
"docker-modem": "5.0.5",
|
||||
"docker-modem": "^5.0.6",
|
||||
"docker-progress": "^5.1.3",
|
||||
"dockerode": "4.0.3",
|
||||
"dockerode": "^4.0.5",
|
||||
"ejs": "^3.1.6",
|
||||
"etcher-sdk": "9.1.0",
|
||||
"express": "^4.17.2",
|
||||
@ -1281,13 +1282,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz",
|
||||
"integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz",
|
||||
"integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.26.9"
|
||||
"@babel/types": "^7.27.0"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
@ -1297,9 +1298,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser/node_modules/@babel/types": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz",
|
||||
"integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
|
||||
"integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -1325,9 +1326,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template/node_modules/@babel/types": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz",
|
||||
"integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
|
||||
"integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -1360,14 +1361,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse/node_modules/@babel/generator": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz",
|
||||
"integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz",
|
||||
"integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.26.9",
|
||||
"@babel/types": "^7.26.9",
|
||||
"@babel/parser": "^7.27.0",
|
||||
"@babel/types": "^7.27.0",
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.25",
|
||||
"jsesc": "^3.0.2"
|
||||
@ -1377,9 +1378,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse/node_modules/@babel/types": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz",
|
||||
"integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
|
||||
"integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -1473,9 +1474,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@balena/compose": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@balena/compose/-/compose-6.0.0.tgz",
|
||||
"integrity": "sha512-tqGJQJpVTdWxaZpSlm/PP23EU3myqmoMIJgJ6ryseYf3A50AcaJ1raRyIxbZ9wQGWu81TB8g47cY9mossIc90g==",
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@balena/compose/-/compose-7.0.1.tgz",
|
||||
"integrity": "sha512-Xic5WJg7i4D9y9TN+BnY8PYbQLLMewN8CR8i+fZ76GHWyBRfk9+tP3JUpmymvSaK9QTKC2UE2nFiadzbfyIFAg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"ajv": "^6.12.3",
|
||||
"docker-file-parser": "^1.0.7",
|
||||
@ -1616,9 +1618,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@balena/lint/node_modules/@eslint/eslintrc": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz",
|
||||
"integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==",
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
|
||||
"integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -1653,9 +1655,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@balena/lint/node_modules/@eslint/js": {
|
||||
"version": "9.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.22.0.tgz",
|
||||
"integrity": "sha512-vLFajx9o8d1/oL2ZkpMYbkLv8nDB6yaIwFNt7nI4+I80U/z03SxmfOMsLbvWr3p7C+Wnoh//aOu2pQW8cS0HCQ==",
|
||||
"version": "9.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz",
|
||||
"integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -1726,19 +1728,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@balena/lint/node_modules/eslint": {
|
||||
"version": "9.22.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.22.0.tgz",
|
||||
"integrity": "sha512-9V/QURhsRN40xuHXWjV64yvrzMjcz7ZyNoF2jJFmy9j/SLk0u1OLSZgXi28MrXjymnjEGSR80WCdab3RGMDveQ==",
|
||||
"version": "9.23.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.23.0.tgz",
|
||||
"integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
"@eslint/config-array": "^0.19.2",
|
||||
"@eslint/config-helpers": "^0.1.0",
|
||||
"@eslint/config-helpers": "^0.2.0",
|
||||
"@eslint/core": "^0.12.0",
|
||||
"@eslint/eslintrc": "^3.3.0",
|
||||
"@eslint/js": "9.22.0",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "9.23.0",
|
||||
"@eslint/plugin-kit": "^0.2.7",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
@ -1940,10 +1942,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@balena/lint/node_modules/lru-cache": {
|
||||
"version": "11.0.2",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz",
|
||||
"integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==",
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz",
|
||||
"integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
@ -2095,9 +2098,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@balena/sbvr-types": {
|
||||
"version": "9.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@balena/sbvr-types/-/sbvr-types-9.2.0.tgz",
|
||||
"integrity": "sha512-dDZkOuaJ2ldQdVYczTBX1rFhqZ/896myi77D+sXtJmPHiupvBs4cTxHBduDT7l/BmIwc8g2jp09sm3u+/zuDBA==",
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@balena/sbvr-types/-/sbvr-types-9.2.2.tgz",
|
||||
"integrity": "sha512-vxVnVaETQ+uTPxEcdY5zz5IUC4dWhw8NEof6birMo5EJ/E6m6cmsRvx04DikI3LYPqtAFS8M1aDaT4gKjH5F+g==",
|
||||
"license": "BSD",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
@ -2234,9 +2237,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/config-helpers": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.1.0.tgz",
|
||||
"integrity": "sha512-kLrdPDJE1ckPo94kmPPf9Hfd0DU0Jw6oKYrhe+pwSC0iTUInmTa+w6fw8sGgcfkFJGNdWOUeOaDM4quW4a7OkA==",
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.0.tgz",
|
||||
"integrity": "sha512-yJLLmLexii32mGrhW29qvU3QBVTu0GUmEf/J4XsBtVhp4JkIUFN/BjWqTF63yRvGApIDpZm5fa97LtYtINmfeQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
@ -2514,9 +2517,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/checkbox/node_modules/@inquirer/core": {
|
||||
"version": "10.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.8.tgz",
|
||||
"integrity": "sha512-HpAqR8y715zPpM9e/9Q+N88bnGwqqL8ePgZ0SMv/s3673JLMv3bIkoivGmjPqXlEgisUksSXibweQccUwEx4qQ==",
|
||||
"version": "10.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.9.tgz",
|
||||
"integrity": "sha512-sXhVB8n20NYkUBfDYgizGHlpRVaCRjtuzNZA6xpALIUbkgfd2Hjz+DfEN6+h1BRnuxw0/P4jCIMjMsEOAMwAJw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -2679,13 +2682,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/core/node_modules/@types/node": {
|
||||
"version": "22.13.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz",
|
||||
"integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==",
|
||||
"version": "22.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz",
|
||||
"integrity": "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.20.0"
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/core/node_modules/ansi-escapes": {
|
||||
@ -2746,10 +2749,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/core/node_modules/undici-types": {
|
||||
"version": "6.20.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
||||
"dev": true
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@inquirer/core/node_modules/wrap-ansi": {
|
||||
"version": "6.2.0",
|
||||
@ -2783,9 +2787,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/editor/node_modules/@inquirer/core": {
|
||||
"version": "10.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.8.tgz",
|
||||
"integrity": "sha512-HpAqR8y715zPpM9e/9Q+N88bnGwqqL8ePgZ0SMv/s3673JLMv3bIkoivGmjPqXlEgisUksSXibweQccUwEx4qQ==",
|
||||
"version": "10.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.9.tgz",
|
||||
"integrity": "sha512-sXhVB8n20NYkUBfDYgizGHlpRVaCRjtuzNZA6xpALIUbkgfd2Hjz+DfEN6+h1BRnuxw0/P4jCIMjMsEOAMwAJw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -2943,9 +2947,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/expand/node_modules/@inquirer/core": {
|
||||
"version": "10.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.8.tgz",
|
||||
"integrity": "sha512-HpAqR8y715zPpM9e/9Q+N88bnGwqqL8ePgZ0SMv/s3673JLMv3bIkoivGmjPqXlEgisUksSXibweQccUwEx4qQ==",
|
||||
"version": "10.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.9.tgz",
|
||||
"integrity": "sha512-sXhVB8n20NYkUBfDYgizGHlpRVaCRjtuzNZA6xpALIUbkgfd2Hjz+DfEN6+h1BRnuxw0/P4jCIMjMsEOAMwAJw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -3099,9 +3103,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/number/node_modules/@inquirer/core": {
|
||||
"version": "10.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.8.tgz",
|
||||
"integrity": "sha512-HpAqR8y715zPpM9e/9Q+N88bnGwqqL8ePgZ0SMv/s3673JLMv3bIkoivGmjPqXlEgisUksSXibweQccUwEx4qQ==",
|
||||
"version": "10.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.9.tgz",
|
||||
"integrity": "sha512-sXhVB8n20NYkUBfDYgizGHlpRVaCRjtuzNZA6xpALIUbkgfd2Hjz+DfEN6+h1BRnuxw0/P4jCIMjMsEOAMwAJw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -3233,9 +3237,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/password/node_modules/@inquirer/core": {
|
||||
"version": "10.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.8.tgz",
|
||||
"integrity": "sha512-HpAqR8y715zPpM9e/9Q+N88bnGwqqL8ePgZ0SMv/s3673JLMv3bIkoivGmjPqXlEgisUksSXibweQccUwEx4qQ==",
|
||||
"version": "10.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.9.tgz",
|
||||
"integrity": "sha512-sXhVB8n20NYkUBfDYgizGHlpRVaCRjtuzNZA6xpALIUbkgfd2Hjz+DfEN6+h1BRnuxw0/P4jCIMjMsEOAMwAJw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -3374,13 +3378,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/prompts/node_modules/@inquirer/confirm": {
|
||||
"version": "5.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.7.tgz",
|
||||
"integrity": "sha512-Xrfbrw9eSiHb+GsesO8TQIeHSMTP0xyvTCeeYevgZ4sKW+iz9w/47bgfG9b0niQm+xaLY2EWPBINUPldLwvYiw==",
|
||||
"version": "5.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.8.tgz",
|
||||
"integrity": "sha512-dNLWCYZvXDjO3rnQfk2iuJNL4Ivwz/T2+C3+WnNfJKsNGSuOs3wAo2F6e0p946gtSAk31nZMfW+MRmYaplPKsg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@inquirer/core": "^10.1.8",
|
||||
"@inquirer/core": "^10.1.9",
|
||||
"@inquirer/type": "^3.0.5"
|
||||
},
|
||||
"engines": {
|
||||
@ -3396,9 +3400,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/prompts/node_modules/@inquirer/core": {
|
||||
"version": "10.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.8.tgz",
|
||||
"integrity": "sha512-HpAqR8y715zPpM9e/9Q+N88bnGwqqL8ePgZ0SMv/s3673JLMv3bIkoivGmjPqXlEgisUksSXibweQccUwEx4qQ==",
|
||||
"version": "10.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.9.tgz",
|
||||
"integrity": "sha512-sXhVB8n20NYkUBfDYgizGHlpRVaCRjtuzNZA6xpALIUbkgfd2Hjz+DfEN6+h1BRnuxw0/P4jCIMjMsEOAMwAJw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -3424,13 +3428,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/prompts/node_modules/@inquirer/input": {
|
||||
"version": "4.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.7.tgz",
|
||||
"integrity": "sha512-rCQAipJNA14UTH84df/z4jDJ9LZ54H6zzuCAi7WZ0qVqx3CSqLjfXAMd5cpISIxbiHVJCPRB81gZksq6CZsqDg==",
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.8.tgz",
|
||||
"integrity": "sha512-WXJI16oOZ3/LiENCAxe8joniNp8MQxF6Wi5V+EBbVA0ZIOpFcL4I9e7f7cXse0HJeIPCWO8Lcgnk98juItCi7Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@inquirer/core": "^10.1.8",
|
||||
"@inquirer/core": "^10.1.9",
|
||||
"@inquirer/type": "^3.0.5"
|
||||
},
|
||||
"engines": {
|
||||
@ -3446,13 +3450,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/prompts/node_modules/@inquirer/select": {
|
||||
"version": "4.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.0.10.tgz",
|
||||
"integrity": "sha512-Tg8S9nESnCfISu5tCZSuXpXq0wHuDVimj7xyHstABgR34zcJnLdq/VbjB2mdZvNAMAehYBnNzSjxB06UE8LLAA==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.1.0.tgz",
|
||||
"integrity": "sha512-z0a2fmgTSRN+YBuiK1ROfJ2Nvrpij5lVN3gPDkQGhavdvIVGHGW29LwYZfM/j42Ai2hUghTI/uoBuTbrJk42bA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@inquirer/core": "^10.1.8",
|
||||
"@inquirer/core": "^10.1.9",
|
||||
"@inquirer/figures": "^1.0.11",
|
||||
"@inquirer/type": "^3.0.5",
|
||||
"ansi-escapes": "^4.3.2",
|
||||
@ -3583,9 +3587,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/rawlist/node_modules/@inquirer/core": {
|
||||
"version": "10.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.8.tgz",
|
||||
"integrity": "sha512-HpAqR8y715zPpM9e/9Q+N88bnGwqqL8ePgZ0SMv/s3673JLMv3bIkoivGmjPqXlEgisUksSXibweQccUwEx4qQ==",
|
||||
"version": "10.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.9.tgz",
|
||||
"integrity": "sha512-sXhVB8n20NYkUBfDYgizGHlpRVaCRjtuzNZA6xpALIUbkgfd2Hjz+DfEN6+h1BRnuxw0/P4jCIMjMsEOAMwAJw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -3718,9 +3722,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/search/node_modules/@inquirer/core": {
|
||||
"version": "10.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.8.tgz",
|
||||
"integrity": "sha512-HpAqR8y715zPpM9e/9Q+N88bnGwqqL8ePgZ0SMv/s3673JLMv3bIkoivGmjPqXlEgisUksSXibweQccUwEx4qQ==",
|
||||
"version": "10.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.9.tgz",
|
||||
"integrity": "sha512-sXhVB8n20NYkUBfDYgizGHlpRVaCRjtuzNZA6xpALIUbkgfd2Hjz+DfEN6+h1BRnuxw0/P4jCIMjMsEOAMwAJw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -5826,9 +5830,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.17.24",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.24.tgz",
|
||||
"integrity": "sha512-d7fGCyB96w9BnWQrOsJtpyiSaBcAYYr75bnK6ZRjDbql2cGLj/3GsL5OYmLPNq76l7Gf2q4Rv9J2o6h5CrD9sA==",
|
||||
"version": "20.17.30",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.30.tgz",
|
||||
"integrity": "sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.19.2"
|
||||
@ -5840,11 +5844,6 @@
|
||||
"integrity": "sha512-HTksao/sZs9nqxKD/vWOR3WxSrQsyJlBPEFFCgq9lMmhRsuQF+2p6hy+7FaCYn6lOeiDc3ywI8jDQ2bz5y6m8w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node/node_modules/undici-types": {
|
||||
"version": "6.19.8",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
||||
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
|
||||
},
|
||||
"node_modules/@types/parse-json": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
||||
@ -7429,9 +7428,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/balena-image-fs": {
|
||||
"version": "7.4.1",
|
||||
"resolved": "https://registry.npmjs.org/balena-image-fs/-/balena-image-fs-7.4.1.tgz",
|
||||
"integrity": "sha512-md41m3Jo54EOj7ASrqCD2GJ+LtF9AmMT46dABIbaPvVerY1zUEW8rGfIfrgq4Tz3tL82y73spoVhcabtkaRj5A==",
|
||||
"version": "7.5.0",
|
||||
"resolved": "https://registry.npmjs.org/balena-image-fs/-/balena-image-fs-7.5.0.tgz",
|
||||
"integrity": "sha512-JVydPTG7hiaX6uAGxzc2HWlVASqXAnb1Ypg1zOU8M6H1US6cbcv3L2ZixHla9yzQnZOy4+SLgV4wc9A4T0CPcQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"ext2fs": "^4.2.4",
|
||||
@ -7445,12 +7444,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/balena-preload": {
|
||||
"version": "17.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balena-preload/-/balena-preload-17.0.0.tgz",
|
||||
"integrity": "sha512-J0pduyqFFsPC70jgLdw7nANILW5nUGuShKayZauVuGs18NuYKlu7UT7w/b6QOMYtDK+YGjMWEf0uHTdiSzKZQQ==",
|
||||
"version": "18.0.1",
|
||||
"resolved": "https://registry.npmjs.org/balena-preload/-/balena-preload-18.0.1.tgz",
|
||||
"integrity": "sha512-KP5u9T3J6CNAD/w+iaPv+BLnhX/2pgjybFuYQcei5laPIEmy1eWiP/IAJENB5FJ8i6ME4H6sU3RA5bSeCr/0ZA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"balena-sdk": "^20.1.3",
|
||||
"balena-sdk": "^21.2.1",
|
||||
"compare-versions": "^3.6.0",
|
||||
"docker-progress": "^5.0.0",
|
||||
"dockerode": "^4.0.2",
|
||||
@ -7460,7 +7459,7 @@
|
||||
"tar-fs": "^2.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
"node": "^20.12.0 || >=22.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/balena-register-device": {
|
||||
@ -7505,13 +7504,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/balena-sdk": {
|
||||
"version": "20.8.0",
|
||||
"resolved": "https://registry.npmjs.org/balena-sdk/-/balena-sdk-20.8.0.tgz",
|
||||
"integrity": "sha512-t0uaxEJIp1P/ZiQ9Yf4P1VUtrWEbUjjsd7p0Xz93x9JtvtqqCnCmFV+N96atqPGY+QEYZveGOZ2qwBee5H2pig==",
|
||||
"version": "21.3.0",
|
||||
"resolved": "https://registry.npmjs.org/balena-sdk/-/balena-sdk-21.3.0.tgz",
|
||||
"integrity": "sha512-3Ub2doNsXwBPoRNr/feoMY18tXCUOTkclq6/Qe3lEiQA40/Q/pBfglveVXuOV6P2ljyC6jayJ8aR/3r5Kov/2w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@balena/es-version": "^1.0.0",
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"@types/node": "^18.19.50",
|
||||
"@types/node": "^20.17.8",
|
||||
"abortcontroller-polyfill": "^1.7.1",
|
||||
"balena-auth": "^6.0.1",
|
||||
"balena-errors": "^4.9.0",
|
||||
@ -7531,16 +7531,17 @@
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0"
|
||||
"node": "^20.12.0 || >= 22.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/balena-sdk/node_modules/@types/node": {
|
||||
"version": "18.19.80",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.80.tgz",
|
||||
"integrity": "sha512-kEWeMwMeIvxYkeg1gTc01awpwLbfMRZXdIhwRcakd/KlK53jmRC26LqcbIt7fnAQTu5GzlnWmzA3H6+l1u6xxQ==",
|
||||
"node_modules/balena-sdk/node_modules/date-fns": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
|
||||
"integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
}
|
||||
},
|
||||
"node_modules/balena-sdk/node_modules/mime": {
|
||||
@ -9113,9 +9114,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
|
||||
"integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
@ -9465,9 +9467,9 @@
|
||||
"integrity": "sha512-6+VnnhZpxwWvvKwjkRnuqlTtlBRJuM+3cCSXmZoYhyXcdgxx6l/3lwYpqmJ9qmhzgWVeATkpVsTua92BsObJjw=="
|
||||
},
|
||||
"node_modules/docker-modem": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.5.tgz",
|
||||
"integrity": "sha512-Cxw8uEcvNTRmsQuGqzzfiCnfGgf96tVJItLh8taOX0miTcIBALKH5TckCSuZbpbjP7uhAl81dOL9sxfa6HgCIg==",
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.6.tgz",
|
||||
"integrity": "sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"debug": "^4.1.1",
|
||||
@ -9514,34 +9516,23 @@
|
||||
}
|
||||
},
|
||||
"node_modules/dockerode": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.3.tgz",
|
||||
"integrity": "sha512-QSXJFcBQNaGZO6U3qWW4B7p8yRIJn/dWmvL2AQWfO/bjptBBO6QYdVkYSYFz9qoivP2jsOHZfmXMAfrK0BMKyg==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.5.tgz",
|
||||
"integrity": "sha512-ZPmKSr1k1571Mrh7oIBS/j0AqAccoecY2yH420ni5j1KyNMgnoTh4Nu4FWunh0HZIJmRSmSysJjBIpa/zyWUEA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@balena/dockerignore": "^1.0.2",
|
||||
"@grpc/grpc-js": "^1.11.1",
|
||||
"@grpc/proto-loader": "^0.7.13",
|
||||
"docker-modem": "^5.0.5",
|
||||
"docker-modem": "^5.0.6",
|
||||
"protobufjs": "^7.3.2",
|
||||
"tar-fs": "~2.0.1",
|
||||
"tar-fs": "~2.1.2",
|
||||
"uuid": "^10.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dockerode/node_modules/tar-fs": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz",
|
||||
"integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==",
|
||||
"dependencies": {
|
||||
"chownr": "^1.1.1",
|
||||
"mkdirp-classic": "^0.5.2",
|
||||
"pump": "^3.0.0",
|
||||
"tar-stream": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dockerode/node_modules/uuid": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
|
||||
@ -16337,9 +16328,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/patch-package/node_modules/yaml": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz",
|
||||
"integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==",
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz",
|
||||
"integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"yaml": "bin.mjs"
|
||||
@ -19577,9 +19568,10 @@
|
||||
"integrity": "sha512-hqSQ+EGPNNs80IGpZszUngM7ttKBCgjRtrw+2PTRDUd1UNvPOoSL4M7V1bDBJXktGv8KaVJFRKk2mWi6RmbiEg=="
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
|
||||
"version": "6.19.8",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
||||
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unique-string": {
|
||||
"version": "2.0.0",
|
||||
|
17
package.json
17
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "balena-cli",
|
||||
"version": "20.2.10",
|
||||
"version": "21.1.6",
|
||||
"description": "The official balena Command Line Interface",
|
||||
"main": "./build/app.js",
|
||||
"homepage": "https://github.com/balena-io/balena-cli",
|
||||
@ -190,7 +190,7 @@
|
||||
"typescript": "^5.8.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@balena/compose": "^6.0.0",
|
||||
"@balena/compose": "^7.0.1",
|
||||
"@balena/dockerignore": "^1.0.2",
|
||||
"@balena/env-parsing": "^1.1.8",
|
||||
"@balena/es-version": "^1.0.1",
|
||||
@ -199,9 +199,9 @@
|
||||
"balena-config-json": "^4.2.2",
|
||||
"balena-device-init": "^8.1.3",
|
||||
"balena-errors": "^4.7.3",
|
||||
"balena-image-fs": "^7.3.0",
|
||||
"balena-preload": "^17.0.0",
|
||||
"balena-sdk": "^20.8.0",
|
||||
"balena-image-fs": "^7.5.0",
|
||||
"balena-preload": "^18.0.1",
|
||||
"balena-sdk": "^21.3.0",
|
||||
"balena-semver": "^2.3.0",
|
||||
"balena-settings-client": "^5.0.2",
|
||||
"balena-settings-storage": "^8.1.0",
|
||||
@ -212,10 +212,11 @@
|
||||
"cli-truncate": "^2.1.0",
|
||||
"color-hash": "^1.1.1",
|
||||
"common-tags": "^1.7.2",
|
||||
"date-fns": "^4.1.0",
|
||||
"denymount": "^2.3.0",
|
||||
"docker-modem": "5.0.5",
|
||||
"docker-modem": "^5.0.6",
|
||||
"docker-progress": "^5.1.3",
|
||||
"dockerode": "4.0.3",
|
||||
"dockerode": "^4.0.5",
|
||||
"ejs": "^3.1.6",
|
||||
"etcher-sdk": "9.1.0",
|
||||
"express": "^4.17.2",
|
||||
@ -275,6 +276,6 @@
|
||||
}
|
||||
},
|
||||
"versionist": {
|
||||
"publishedAt": "2025-03-10T17:33:10.482Z"
|
||||
"publishedAt": "2025-04-03T14:12:06.976Z"
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,16 @@
|
||||
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import { ExpectedError } from '../../errors';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import { getBalenaSdk, getCliForm, stripIndent } from '../../utils/lazy';
|
||||
import {
|
||||
formatDuration,
|
||||
intervalToDuration,
|
||||
isValid,
|
||||
parseISO,
|
||||
} from 'date-fns';
|
||||
|
||||
// In days
|
||||
const durations = [1, 7, 30, 90];
|
||||
|
||||
async function isLoggedInWithJwt() {
|
||||
const balena = getBalenaSdk();
|
||||
@ -41,13 +50,21 @@ export default class GenerateCmd extends Command {
|
||||
This key can be used to log into the CLI using 'balena login --token <key>',
|
||||
or to authenticate requests to the API with an 'Authorization: Bearer <key>' header.
|
||||
`;
|
||||
public static examples = ['$ balena api-key generate "Jenkins Key"'];
|
||||
public static examples = [
|
||||
'$ balena api-key generate "Jenkins Key"',
|
||||
'$ balena api-key generate "Jenkins Key" 2025-10-30',
|
||||
'$ balena api-key generate "Jenkins Key" never',
|
||||
];
|
||||
|
||||
public static args = {
|
||||
name: Args.string({
|
||||
description: 'the API key name',
|
||||
required: true,
|
||||
}),
|
||||
expiryDate: Args.string({
|
||||
description:
|
||||
'the expiry date of the API key as an ISO date string, or "never" for no expiry',
|
||||
}),
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
@ -55,9 +72,61 @@ export default class GenerateCmd extends Command {
|
||||
public async run() {
|
||||
const { args: params } = await this.parse(GenerateCmd);
|
||||
|
||||
let expiryDateResponse: string | number | undefined = params.expiryDate;
|
||||
let key;
|
||||
try {
|
||||
key = await getBalenaSdk().models.apiKey.create(params.name);
|
||||
if (!expiryDateResponse) {
|
||||
expiryDateResponse = await getCliForm().ask({
|
||||
message: 'Please pick an expiry date for the API key',
|
||||
type: 'list',
|
||||
choices: [...durations, 'custom', 'never'].map((duration) => ({
|
||||
name:
|
||||
duration === 'never'
|
||||
? 'No expiration'
|
||||
: typeof duration === 'number'
|
||||
? formatDuration(
|
||||
intervalToDuration({
|
||||
start: 0,
|
||||
end: duration * 24 * 60 * 60 * 1000,
|
||||
}),
|
||||
)
|
||||
: 'Custom expiration',
|
||||
value: duration,
|
||||
})),
|
||||
});
|
||||
}
|
||||
let expiryDate: Date | null;
|
||||
if (expiryDateResponse === 'never') {
|
||||
expiryDate = null;
|
||||
} else if (expiryDateResponse === 'custom') {
|
||||
do {
|
||||
expiryDate = parseISO(
|
||||
await getCliForm().ask({
|
||||
message:
|
||||
'Please enter an expiry date for the API key as an ISO date string',
|
||||
type: 'input',
|
||||
}),
|
||||
);
|
||||
if (!isValid(expiryDate)) {
|
||||
console.error('Invalid date format');
|
||||
}
|
||||
} while (!isValid(expiryDate));
|
||||
} else if (typeof expiryDateResponse === 'string') {
|
||||
expiryDate = parseISO(expiryDateResponse);
|
||||
if (!isValid(expiryDate)) {
|
||||
throw new Error(
|
||||
'Invalid date format, please use a valid ISO date string',
|
||||
);
|
||||
}
|
||||
} else {
|
||||
expiryDate = new Date(
|
||||
Date.now() + expiryDateResponse * 24 * 60 * 60 * 1000,
|
||||
);
|
||||
}
|
||||
key = await getBalenaSdk().models.apiKey.create({
|
||||
name: params.name,
|
||||
expiryDate: expiryDate === null ? null : expiryDate.toISOString(),
|
||||
});
|
||||
} catch (e) {
|
||||
if (e.name === 'BalenaNotLoggedIn') {
|
||||
if (await isLoggedInWithJwt()) {
|
||||
|
@ -368,6 +368,7 @@ ${dockerignoreHelp}
|
||||
!opts.shouldUploadLogs,
|
||||
composeOpts.projectPath,
|
||||
opts.createAsDraft,
|
||||
project.descriptors,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -77,45 +77,59 @@ export default class DeviceCmd extends Command {
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
const device = (await balena.models.device.get(
|
||||
params.uuid,
|
||||
options.json
|
||||
? {
|
||||
$expand: {
|
||||
device_tag: {
|
||||
$select: ['tag_key', 'value'],
|
||||
},
|
||||
...expandForAppName.$expand,
|
||||
let device: ExtendedDevice;
|
||||
if (options.json) {
|
||||
const [deviceBase, deviceComputed] = await Promise.all([
|
||||
balena.models.device.get(params.uuid, {
|
||||
$expand: {
|
||||
device_tag: {
|
||||
$select: ['tag_key', 'value'],
|
||||
},
|
||||
}
|
||||
: {
|
||||
$select: [
|
||||
'device_name',
|
||||
'id',
|
||||
'overall_status',
|
||||
'is_online',
|
||||
'ip_address',
|
||||
'mac_address',
|
||||
'last_connectivity_event',
|
||||
'uuid',
|
||||
'supervisor_version',
|
||||
'is_web_accessible',
|
||||
'note',
|
||||
'os_version',
|
||||
'memory_usage',
|
||||
'memory_total',
|
||||
'public_address',
|
||||
'storage_block_device',
|
||||
'storage_usage',
|
||||
'storage_total',
|
||||
'cpu_usage',
|
||||
'cpu_temp',
|
||||
'cpu_id',
|
||||
'is_undervolted',
|
||||
],
|
||||
...expandForAppName,
|
||||
...expandForAppName.$expand,
|
||||
},
|
||||
)) as ExtendedDevice;
|
||||
}),
|
||||
balena.models.device.get(params.uuid, {
|
||||
$select: [
|
||||
'overall_status',
|
||||
'overall_progress',
|
||||
'should_be_running__release',
|
||||
],
|
||||
}),
|
||||
]);
|
||||
|
||||
device = {
|
||||
...deviceBase,
|
||||
...deviceComputed,
|
||||
} as ExtendedDevice;
|
||||
} else {
|
||||
device = (await balena.models.device.get(params.uuid, {
|
||||
$select: [
|
||||
'device_name',
|
||||
'id',
|
||||
'overall_status',
|
||||
'is_online',
|
||||
'ip_address',
|
||||
'mac_address',
|
||||
'last_connectivity_event',
|
||||
'uuid',
|
||||
'supervisor_version',
|
||||
'is_web_accessible',
|
||||
'note',
|
||||
'os_version',
|
||||
'memory_usage',
|
||||
'memory_total',
|
||||
'public_address',
|
||||
'storage_block_device',
|
||||
'storage_usage',
|
||||
'storage_total',
|
||||
'cpu_usage',
|
||||
'cpu_temp',
|
||||
'cpu_id',
|
||||
'is_undervolted',
|
||||
],
|
||||
...expandForAppName,
|
||||
})) as ExtendedDevice;
|
||||
}
|
||||
|
||||
if (options.view) {
|
||||
const open = await import('open');
|
||||
|
@ -37,6 +37,7 @@ import type {
|
||||
Release,
|
||||
} from 'balena-sdk';
|
||||
import type { Preloader } from 'balena-preload';
|
||||
import type * as Fs from 'fs';
|
||||
|
||||
export default class PreloadCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
@ -161,6 +162,42 @@ Can be repeated to add multiple certificates.\
|
||||
);
|
||||
}
|
||||
|
||||
// Verify that image is not enabled for secure boot. First, confirm it is
|
||||
// a secure boot image with a .sig file in the /opt directory of the rootA
|
||||
// partition. For example, below are contents for generic-amd64 device type:
|
||||
// $ ls -l opt
|
||||
// total 864696
|
||||
// -rw-r--r-- 1 root root 2378170368 Mar 26 09:14 balena-image-generic-amd64.balenaos-img
|
||||
// -rw-r--r-- 1 root root 512 Mar 9 2018 balena-image-generic-amd64.balenaos-img.sig
|
||||
const { explorePartition, BalenaPartition } = await import(
|
||||
'../../utils/image-contents'
|
||||
);
|
||||
const isSecureBoot = await explorePartition<boolean>(
|
||||
params.image,
|
||||
BalenaPartition.ROOTA,
|
||||
async (fs: typeof Fs): Promise<boolean> => {
|
||||
try {
|
||||
const files = await fs.promises.readdir('/opt');
|
||||
return files.some((el) => el.endsWith('balenaos-img.sig'));
|
||||
} catch {
|
||||
// Typically one of:
|
||||
// - Error: No such file or directory
|
||||
// - Error: Unsupported filesystem.
|
||||
// - ErrnoException: node_ext2fs_open ENOENT (44) args: [5261576,5268064,"r",0]
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
);
|
||||
// Next verify that config.json enables secureboot.
|
||||
if (isSecureBoot) {
|
||||
const { read } = await import('balena-config-json');
|
||||
const config = await read(params.image, '');
|
||||
if (config.installer?.secureboot === true) {
|
||||
throw new ExpectedError("Can't preload image with secure boot enabled");
|
||||
}
|
||||
}
|
||||
|
||||
// balena-preload currently does not work with numerical app IDs
|
||||
// Load app here, and use app slug from hereon
|
||||
const fleetSlug: string | undefined = options.fleet
|
||||
@ -295,7 +332,7 @@ Can be repeated to add multiple certificates.\
|
||||
owns__release: {
|
||||
$select: ['id', 'commit', 'end_timestamp', 'composition'],
|
||||
$expand: {
|
||||
contains__image: {
|
||||
release_image: {
|
||||
$select: ['image'],
|
||||
$expand: {
|
||||
image: {
|
||||
|
@ -128,6 +128,7 @@ export const createRelease = async function (
|
||||
draft: boolean,
|
||||
semver: string | undefined,
|
||||
contract: import('@balena/compose/dist/release/models').ReleaseModel['contract'],
|
||||
imgDescriptors: ImageDescriptor[],
|
||||
): Promise<Release> {
|
||||
const _ = require('lodash') as typeof import('lodash');
|
||||
const crypto = require('crypto') as typeof import('crypto');
|
||||
@ -167,6 +168,7 @@ export const createRelease = async function (
|
||||
semver,
|
||||
is_final: !draft,
|
||||
contract,
|
||||
imgDescriptors,
|
||||
});
|
||||
|
||||
return {
|
||||
@ -240,7 +242,7 @@ export const getPreviousRepos = (
|
||||
status: 'success',
|
||||
},
|
||||
$expand: {
|
||||
contains__image: {
|
||||
release_image: {
|
||||
$select: 'image',
|
||||
$expand: { image: { $select: 'is_stored_at__image_location' } },
|
||||
},
|
||||
@ -252,7 +254,7 @@ export const getPreviousRepos = (
|
||||
.then(function (release) {
|
||||
// grab all images from the latest release, return all image locations in the registry
|
||||
if (release.length > 0) {
|
||||
const images = release[0].contains__image as Array<{
|
||||
const images = release[0].release_image as Array<{
|
||||
image: [SDK.Image];
|
||||
}>;
|
||||
const { getRegistryAndName } =
|
||||
|
@ -1375,6 +1375,7 @@ export async function deployProject(
|
||||
skipLogUpload: boolean,
|
||||
projectPath: string,
|
||||
isDraft: boolean,
|
||||
imgDescriptors: ImageDescriptor[],
|
||||
): Promise<import('@balena/compose/dist/release/models').ReleaseModel> {
|
||||
const releaseMod = await import('@balena/compose/dist/release');
|
||||
const { createRelease, tagServiceImages } = await import('./compose');
|
||||
@ -1405,6 +1406,7 @@ export async function deployProject(
|
||||
isDraft,
|
||||
contract?.version,
|
||||
contract,
|
||||
imgDescriptors,
|
||||
),
|
||||
);
|
||||
const { client: pineClient, release, serviceImages } = $release;
|
||||
|
@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
import type * as BalenaSdk from 'balena-sdk';
|
||||
import * as semver from 'balena-semver';
|
||||
import { getBalenaSdk, stripIndent } from './lazy';
|
||||
|
||||
export interface ImgConfig {
|
||||
@ -122,16 +121,10 @@ export function generateDeviceConfig(
|
||||
// os.getConfig always returns a config for an app
|
||||
delete config.apiKey;
|
||||
|
||||
if (deviceApiKey == null && semver.satisfies(options.version, '<2.0.3')) {
|
||||
config.apiKey = await sdk.models.application.generateApiKey(
|
||||
application.id,
|
||||
);
|
||||
} else {
|
||||
config.deviceApiKey =
|
||||
typeof deviceApiKey === 'string' && deviceApiKey
|
||||
? deviceApiKey
|
||||
: await sdk.models.device.generateDeviceKey(device.uuid);
|
||||
}
|
||||
config.deviceApiKey =
|
||||
typeof deviceApiKey === 'string' && deviceApiKey
|
||||
? deviceApiKey
|
||||
: await sdk.models.device.generateDeviceKey(device.uuid);
|
||||
|
||||
return config;
|
||||
})
|
||||
|
69
src/utils/image-contents.ts
Normal file
69
src/utils/image-contents.ts
Normal file
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 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.
|
||||
*/
|
||||
|
||||
// Utilities to explore the contents in a balenaOS image.
|
||||
|
||||
import * as imagefs from 'balena-image-fs';
|
||||
import * as filedisk from 'file-disk';
|
||||
import { getPartitions } from 'partitioninfo';
|
||||
import type * as Fs from 'fs';
|
||||
|
||||
/**
|
||||
* @summary IDs for the standard balenaOS partitions
|
||||
* @description Values are the base name for a partition on disk
|
||||
*/
|
||||
export enum BalenaPartition {
|
||||
BOOT = 'boot',
|
||||
ROOTA = 'rootA',
|
||||
ROOTB = 'rootB',
|
||||
STATE = 'state',
|
||||
DATA = 'data',
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Allow a provided function to explore the contents of one of the well-known
|
||||
* partitions of a balenaOS image
|
||||
*
|
||||
* @param {string} imagePath - pathname of image for search
|
||||
* @param {BalenaPartition} partitionId - partition to find
|
||||
* @param {(fs) => Promise<T>} - function for exploration
|
||||
* @returns {T}
|
||||
*/
|
||||
export async function explorePartition<T>(
|
||||
imagePath: string,
|
||||
partitionId: BalenaPartition,
|
||||
exploreFn: (fs: typeof Fs) => Promise<T>,
|
||||
): Promise<T> {
|
||||
return await filedisk.withOpenFile(imagePath, 'r', async (handle) => {
|
||||
const disk = new filedisk.FileDisk(handle, true, false, false);
|
||||
const partitionInfo = await getPartitions(disk, {
|
||||
includeExtended: false,
|
||||
getLogical: true,
|
||||
});
|
||||
|
||||
const findResult = await imagefs.findPartition(disk, partitionInfo, [
|
||||
`resin-${partitionId}`,
|
||||
`flash-${partitionId}`,
|
||||
`balena-${partitionId}`,
|
||||
]);
|
||||
if (findResult == null) {
|
||||
throw new Error(`Can't find partition for ${partitionId}`);
|
||||
}
|
||||
|
||||
return await imagefs.interact<T>(disk, findResult.index, exploreFn);
|
||||
});
|
||||
}
|
@ -76,38 +76,28 @@ export const getImagePath = async (deviceType: string, version?: string) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Determine if a device image is fresh
|
||||
* @summary Determine if a device image is cached
|
||||
*
|
||||
* @description
|
||||
* If the device image does not exist, return false.
|
||||
*
|
||||
* @param {String} deviceType - device type slug or alias
|
||||
* @param {String} version - the exact balenaOS version number
|
||||
* @returns {Promise<Boolean>} is image fresh
|
||||
* @returns {Promise<Boolean>} is image cached
|
||||
*
|
||||
* @example
|
||||
* isImageFresh('raspberry-pi', '1.2.3').then (isFresh) ->
|
||||
* if isFresh
|
||||
* console.log('The Raspberry Pi image v1.2.3 is fresh!')
|
||||
* isImageCached ('raspberry-pi', '1.2.3').then (isCached) ->
|
||||
* if isCached
|
||||
* console.log('The Raspberry Pi image v1.2.3 is cached!')
|
||||
*/
|
||||
export const isImageFresh = async (deviceType: string, version: string) => {
|
||||
export const isImageCached = async (deviceType: string, version: string) => {
|
||||
const imagePath = await getImagePath(deviceType, version);
|
||||
let createdDate;
|
||||
try {
|
||||
createdDate = await getFileCreatedDate(imagePath);
|
||||
const createdDate = await getFileCreatedDate(imagePath);
|
||||
return createdDate != null;
|
||||
} catch {
|
||||
// Swallow errors from getFileCreatedTime.
|
||||
}
|
||||
if (createdDate == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
const lastModifiedDate = await balena.models.os.getLastModified(
|
||||
deviceType,
|
||||
version,
|
||||
);
|
||||
return lastModifiedDate < createdDate;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -286,7 +276,7 @@ export const getStream = async (
|
||||
versionOrRange = 'latest';
|
||||
}
|
||||
const version = await resolveVersion(deviceType, versionOrRange);
|
||||
const isFresh = await isImageFresh(deviceType, version);
|
||||
const isFresh = await isImageCached(deviceType, version);
|
||||
const $stream = isFresh
|
||||
? await getImage(deviceType, version)
|
||||
: await doDownload({ ...options, deviceType, version });
|
||||
|
@ -114,6 +114,14 @@ describe('balena device', function () {
|
||||
'Content-Type': 'application/json',
|
||||
});
|
||||
|
||||
api.scope
|
||||
.get(
|
||||
/^\/v\d+\/device\?.+&\$select=overall_status,overall_progress,should_be_running__release$/,
|
||||
)
|
||||
.replyWithFile(200, path.join(apiResponsePath, 'device.json'), {
|
||||
'Content-Type': 'application/json',
|
||||
});
|
||||
|
||||
const { out, err } = await runCommand('device 27fda508c --json');
|
||||
expect(err).to.be.empty;
|
||||
const json = JSON.parse(out.join(''));
|
||||
|
@ -82,7 +82,7 @@ describe('balena release', function () {
|
||||
expect(err).to.be.empty;
|
||||
const json = JSON.parse(out.join(''));
|
||||
expect(json[0].commit).to.equal('90247b54de4fa7a0a3cbc85e73c68039');
|
||||
expect(json[0].contains__image[0].image[0].start_timestamp).to.equal(
|
||||
expect(json[0].release_image[0].image[0].start_timestamp).to.equal(
|
||||
'2020-01-04T01:13:08.583Z',
|
||||
);
|
||||
});
|
||||
|
@ -10,7 +10,7 @@
|
||||
"build_log": null,
|
||||
"start_timestamp": "2021-08-25T22:18:33.624Z",
|
||||
"end_timestamp": "2021-08-25T22:18:48.820Z",
|
||||
"contains__image": [
|
||||
"release_image": [
|
||||
{
|
||||
"image": [
|
||||
{
|
||||
|
@ -42,7 +42,7 @@ describe('image-manager', function () {
|
||||
|
||||
describe('given the image is fresh', function () {
|
||||
beforeEach(function () {
|
||||
this.cacheIsImageFresh = stub(imageManager, 'isImageFresh');
|
||||
this.cacheIsImageFresh = stub(imageManager, 'isImageCached');
|
||||
return this.cacheIsImageFresh.resolves(true);
|
||||
});
|
||||
|
||||
@ -68,7 +68,7 @@ describe('image-manager', function () {
|
||||
|
||||
describe('given the image is not fresh', function () {
|
||||
beforeEach(function () {
|
||||
this.cacheIsImageFresh = stub(imageManager, 'isImageFresh');
|
||||
this.cacheIsImageFresh = stub(imageManager, 'isImageCached');
|
||||
return this.cacheIsImageFresh.resolves(false);
|
||||
});
|
||||
|
||||
@ -280,7 +280,7 @@ describe('image-manager', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('.isImageFresh()', () => {
|
||||
describe('.isImageCached()', () => {
|
||||
describe('given the raspberry-pi manifest', function () {
|
||||
beforeEach(function () {
|
||||
this.getDeviceTypeManifestBySlugStub = stub(
|
||||
@ -314,78 +314,8 @@ describe('image-manager', function () {
|
||||
});
|
||||
|
||||
it('should return false', async function () {
|
||||
expect(await imageManager.isImageFresh('raspberry-pi', '1.2.3')).to.be
|
||||
.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('given a fixed created time', function () {
|
||||
beforeEach(function () {
|
||||
this.utilsGetFileCreatedDate = stub(
|
||||
imageManager,
|
||||
'getFileCreatedDate',
|
||||
);
|
||||
this.utilsGetFileCreatedDate.resolves(
|
||||
new Date('2014-01-01T00:00:00.000Z'),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
this.utilsGetFileCreatedDate.restore();
|
||||
});
|
||||
|
||||
describe('given the file was created before the os last modified time', function () {
|
||||
beforeEach(function () {
|
||||
this.osGetLastModified = stub(balena.models.os, 'getLastModified');
|
||||
this.osGetLastModified.resolves(
|
||||
new Date('2014-02-01T00:00:00.000Z'),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
this.osGetLastModified.restore();
|
||||
});
|
||||
|
||||
it('should return false', function () {
|
||||
const promise = imageManager.isImageFresh('raspberry-pi', '1.2.3');
|
||||
return expect(promise).to.eventually.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('given the file was created after the os last modified time', function () {
|
||||
beforeEach(function () {
|
||||
this.osGetLastModified = stub(balena.models.os, 'getLastModified');
|
||||
this.osGetLastModified.resolves(
|
||||
new Date('2013-01-01T00:00:00.000Z'),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
this.osGetLastModified.restore();
|
||||
});
|
||||
|
||||
it('should return true', function () {
|
||||
const promise = imageManager.isImageFresh('raspberry-pi', '1.2.3');
|
||||
return expect(promise).to.eventually.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('given the file was created just at the os last modified time', function () {
|
||||
beforeEach(function () {
|
||||
this.osGetLastModified = stub(balena.models.os, 'getLastModified');
|
||||
this.osGetLastModified.resolves(
|
||||
new Date('2014-00-01T00:00:00.000Z'),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
this.osGetLastModified.restore();
|
||||
});
|
||||
|
||||
it('should return false', function () {
|
||||
const promise = imageManager.isImageFresh('raspberry-pi', '1.2.3');
|
||||
return expect(promise).to.eventually.be.false;
|
||||
});
|
||||
expect(await imageManager.isImageCached('raspberry-pi', '1.2.3')).to
|
||||
.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Reference in New Issue
Block a user