mirror of
https://github.com/balena-io/balena-cli.git
synced 2025-04-24 13:05:55 +00:00
Compare commits
65 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a5c865b7f9 | ||
|
3fb3dd5819 | ||
|
daf5c518fb | ||
|
4fcedd0607 | ||
|
42d9cbb48d | ||
|
408efa91c1 | ||
|
a2209ffe56 | ||
|
3f27db811b | ||
|
839a3050fb | ||
|
c8ea9cfcdb | ||
|
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 | ||
|
727033ae14 | ||
|
c19ce6a905 | ||
|
1a33029738 | ||
|
043bc48a1c | ||
|
a10156a441 | ||
|
4f665f43d2 | ||
|
9f097a96f5 | ||
|
64d1943804 | ||
|
666ce876e6 | ||
|
e01184080f | ||
|
93039b010d | ||
|
795259bf30 | ||
|
fa134d2d39 | ||
|
bef5221ed8 | ||
|
72d6db796c | ||
|
e848eb63ee | ||
|
6f0f7350cf | ||
|
07a88c700e | ||
|
9cae66bd92 | ||
|
cddea24cef |
8
.github/actions/publish/action.yml
vendored
8
.github/actions/publish/action.yml
vendored
@ -28,7 +28,7 @@ runs:
|
|||||||
using: 'composite'
|
using: 'composite'
|
||||||
steps:
|
steps:
|
||||||
- name: Download custom source artifact
|
- name: Download custom source artifact
|
||||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9
|
||||||
with:
|
with:
|
||||||
name: custom-${{ github.event.pull_request.head.sha || github.event.head_commit.id }}-${{ runner.os }}-${{ runner.arch }}
|
name: custom-${{ github.event.pull_request.head.sha || github.event.head_commit.id }}-${{ runner.os }}-${{ runner.arch }}
|
||||||
path: ${{ runner.temp }}
|
path: ${{ runner.temp }}
|
||||||
@ -39,7 +39,7 @@ runs:
|
|||||||
run: tar -xf ${{ runner.temp }}/custom.tgz
|
run: tar -xf ${{ runner.temp }}/custom.tgz
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ inputs.NODE_VERSION }}
|
node-version: ${{ inputs.NODE_VERSION }}
|
||||||
cache: npm
|
cache: npm
|
||||||
@ -94,7 +94,7 @@ runs:
|
|||||||
runner_arch="$(echo "${RUNNER_ARCH}" | tr '[:upper:]' '[:lower:]')"
|
runner_arch="$(echo "${RUNNER_ARCH}" | tr '[:upper:]' '[:lower:]')"
|
||||||
|
|
||||||
if [[ $runner_os =~ darwin|macos|osx ]]; then
|
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_KEYCHAIN=signing_temp
|
||||||
CSC_LINK=${{ fromJSON(inputs.secrets).APPLE_SIGNING }}
|
CSC_LINK=${{ fromJSON(inputs.secrets).APPLE_SIGNING }}
|
||||||
|
|
||||||
@ -135,7 +135,7 @@ runs:
|
|||||||
XCODE_APP_LOADER_TEAM_ID: ${{ inputs.XCODE_APP_LOADER_TEAM_ID }}
|
XCODE_APP_LOADER_TEAM_ID: ${{ inputs.XCODE_APP_LOADER_TEAM_ID }}
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||||
with:
|
with:
|
||||||
name: gh-release-${{ github.event.pull_request.head.sha || github.event.head_commit.id }}-${{ strategy.job-index }}
|
name: gh-release-${{ github.event.pull_request.head.sha || github.event.head_commit.id }}-${{ strategy.job-index }}
|
||||||
path: dist
|
path: dist
|
||||||
|
4
.github/actions/test/action.yml
vendored
4
.github/actions/test/action.yml
vendored
@ -26,7 +26,7 @@ runs:
|
|||||||
steps:
|
steps:
|
||||||
# https://github.com/actions/setup-node#caching-global-packages-data
|
# https://github.com/actions/setup-node#caching-global-packages-data
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4
|
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ inputs.NODE_VERSION }}
|
node-version: ${{ inputs.NODE_VERSION }}
|
||||||
cache: npm
|
cache: npm
|
||||||
@ -58,7 +58,7 @@ runs:
|
|||||||
run: tar --exclude-vcs -acf ${{ runner.temp }}/custom.tgz .
|
run: tar --exclude-vcs -acf ${{ runner.temp }}/custom.tgz .
|
||||||
|
|
||||||
- name: Upload custom artifact
|
- name: Upload custom artifact
|
||||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||||
with:
|
with:
|
||||||
name: custom-${{ github.event.pull_request.head.sha || github.event.head_commit.id }}-${{ runner.os }}-${{ runner.arch }}
|
name: custom-${{ github.event.pull_request.head.sha || github.event.head_commit.id }}-${{ runner.os }}-${{ runner.arch }}
|
||||||
path: ${{ runner.temp }}/custom.tgz
|
path: ${{ runner.temp }}/custom.tgz
|
||||||
|
@ -1,3 +1,446 @@
|
|||||||
|
- commits:
|
||||||
|
- subject: Update balena-config-json to rely on the balena-image-fs helpers
|
||||||
|
hash: 4fcedd0607624ddbd26917e3be5fcbd39d96d2f6
|
||||||
|
body: |
|
||||||
|
Update balena-config-json from 4.2.2 to 4.2.7
|
||||||
|
Update balena-image-fs from 7.5.0 to 7.5.2
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: Thodoris Greasidis
|
||||||
|
nested:
|
||||||
|
- commits:
|
||||||
|
- subject: Fix getBootPartition always warning that the boot partitions were not
|
||||||
|
found
|
||||||
|
hash: d91290d9c4b502652c50a34482ff68448eb0a4c2
|
||||||
|
body: ""
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: Thodoris Greasidis
|
||||||
|
nested: []
|
||||||
|
version: balena-config-json-4.2.7
|
||||||
|
title: ""
|
||||||
|
date: 2025-04-02T14:14:31.495Z
|
||||||
|
- commits:
|
||||||
|
- subject: "write: Allow undefined as a value for the deprecated type parameter"
|
||||||
|
hash: 9d5a44175e7f46f2f3963d794d48d3de8f49300f
|
||||||
|
body: ""
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: Thodoris Greasidis
|
||||||
|
nested: []
|
||||||
|
- subject: Use the balena-image-fs findPartition() helper to find the boot
|
||||||
|
partition
|
||||||
|
hash: 49282ed9bd121d89c8d6fee5095917876cd6f501
|
||||||
|
body: |
|
||||||
|
Update balena-image-fs from 7.4.0 to 7.5.0
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: Thodoris Greasidis
|
||||||
|
nested:
|
||||||
|
- commits:
|
||||||
|
- subject: Add function to find a partition by name/label
|
||||||
|
hash: 4e9b1cfb2739b738dd12bda7d1623e08ad208b46
|
||||||
|
body: ""
|
||||||
|
footer:
|
||||||
|
Change-type: minor
|
||||||
|
change-type: minor
|
||||||
|
Signed-off-by: Ken Bannister <kb2ma@runbox.com>
|
||||||
|
signed-off-by: Ken Bannister <kb2ma@runbox.com>
|
||||||
|
author: Ken Bannister
|
||||||
|
nested: []
|
||||||
|
version: balena-image-fs-7.5.0
|
||||||
|
title: ""
|
||||||
|
date: 2025-03-26T12:35:30.365Z
|
||||||
|
- commits:
|
||||||
|
- subject: bump ext2fs to 4.2.4
|
||||||
|
hash: 300cc6d5cdd12ce0c47986efe98702511a03f4a5
|
||||||
|
body: ""
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
Signed-off-by: Ryan Cooke<ryan@balena.io>
|
||||||
|
signed-off-by: Ryan Cooke<ryan@balena.io>
|
||||||
|
author: Ryan Cooke
|
||||||
|
nested: []
|
||||||
|
version: balena-image-fs-7.4.1
|
||||||
|
title: ""
|
||||||
|
date: 2025-02-21T10:21:04.380Z
|
||||||
|
version: balena-config-json-4.2.6
|
||||||
|
title: ""
|
||||||
|
date: 2025-04-01T01:09:16.169Z
|
||||||
|
- commits:
|
||||||
|
- subject: Update @balena/lint to 9.1.4
|
||||||
|
hash: 0ff1aa1ed8803b622948214493e1f9ec88cfe910
|
||||||
|
body: |
|
||||||
|
Update @balena/lint from 6.2.2 to 9.1.4
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: Thodoris Greasidis
|
||||||
|
nested: []
|
||||||
|
version: balena-config-json-4.2.5
|
||||||
|
title: ""
|
||||||
|
date: 2025-03-26T11:41:52.400Z
|
||||||
|
- commits:
|
||||||
|
- subject: Switch use ts-mocha instead of manually compiling the tests
|
||||||
|
hash: c6e46d393d8670781cf9f94512e8ef05a4236111
|
||||||
|
body: ""
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: Thodoris Greasidis
|
||||||
|
nested: []
|
||||||
|
- subject: Update TypeScript to 5.8.2
|
||||||
|
hash: b8a8183bc14c24a63f5bc5dd60d8906f6d97a00d
|
||||||
|
body: ""
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: Thodoris Greasidis
|
||||||
|
nested: []
|
||||||
|
version: balena-config-json-4.2.4
|
||||||
|
title: ""
|
||||||
|
date: 2025-03-26T09:09:13.516Z
|
||||||
|
- commits:
|
||||||
|
- subject: Update chai to v5
|
||||||
|
hash: 7fe83ffc781eb66ac3535749f7fdf56d458eb959
|
||||||
|
body: |
|
||||||
|
Update @types/chai from 4.3.20 to 5.2.0
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: balena-renovate[bot]
|
||||||
|
nested: []
|
||||||
|
version: balena-config-json-4.2.3
|
||||||
|
title: ""
|
||||||
|
date: 2025-03-26T08:40:58.555Z
|
||||||
|
- commits:
|
||||||
|
- subject: Update dependency jsdoc-to-markdown to v9
|
||||||
|
hash: f22a0a1dae022035e5357c147f681f8b7c703fe3
|
||||||
|
body: |
|
||||||
|
Update jsdoc-to-markdown from 8.0.3 to 9.1.1
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: balena-renovate[bot]
|
||||||
|
nested: []
|
||||||
|
version: balena-image-fs-7.5.2
|
||||||
|
title: ""
|
||||||
|
date: 2025-04-02T10:40:31.219Z
|
||||||
|
- commits:
|
||||||
|
- subject: Update @balena/lint to v9
|
||||||
|
hash: 4da5fa44a43047cdb4f96c37a73a0f0674c9327b
|
||||||
|
body: ""
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: Thodoris Greasidis
|
||||||
|
nested: []
|
||||||
|
- subject: Remove the no longer needed gpt detection helper
|
||||||
|
hash: 0973da43aa321e4e2dad4b83c5beb965b8da7044
|
||||||
|
body: ""
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: Thodoris Greasidis
|
||||||
|
nested: []
|
||||||
|
- subject: Update TypeScript to 5.8.2
|
||||||
|
hash: 1580e7d577e0183c2b5d4f9ce3d0b8f519e4dff3
|
||||||
|
body: ""
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: Thodoris Greasidis
|
||||||
|
nested: []
|
||||||
|
- subject: Drop the package-lock.json
|
||||||
|
hash: 400ba55caa4c4af8098e2b164cd184651c283230
|
||||||
|
body: ""
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: Thodoris Greasidis
|
||||||
|
nested: []
|
||||||
|
version: balena-image-fs-7.5.1
|
||||||
|
title: ""
|
||||||
|
date: 2025-03-26T17:51:35.381Z
|
||||||
|
version: 21.1.9
|
||||||
|
title: ""
|
||||||
|
date: 2025-04-07T12:53:16.860Z
|
||||||
|
- commits:
|
||||||
|
- subject: Update actions/download-artifact action to v4.1.9
|
||||||
|
hash: a2209ffe5677388faf7b9bbccace5343265df51f
|
||||||
|
body: |
|
||||||
|
Update actions/download-artifact from 4.1.8 to 4.1.9
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: balena-renovate[bot]
|
||||||
|
nested: []
|
||||||
|
version: 21.1.8
|
||||||
|
title: ""
|
||||||
|
date: 2025-04-03T16:10:02.341Z
|
||||||
|
- commits:
|
||||||
|
- subject: Update actions/upload-artifact digest to ea165f8
|
||||||
|
hash: c8ea9cfcdbaa9a8abf221132a7d1ff29a966e515
|
||||||
|
body: |
|
||||||
|
Update actions/upload-artifact
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: balena-renovate[bot]
|
||||||
|
nested: []
|
||||||
|
version: 21.1.7
|
||||||
|
title: ""
|
||||||
|
date: 2025-04-03T15:12:14.222Z
|
||||||
|
- 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
|
||||||
|
body: ""
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: Thodoris Greasidis
|
||||||
|
nested: []
|
||||||
|
version: 20.2.10
|
||||||
|
title: ""
|
||||||
|
date: 2025-03-10T17:33:09.548Z
|
||||||
|
- commits:
|
||||||
|
- subject: Fix CORS issue with X-Balena-Client header
|
||||||
|
hash: 64d19438042921e89c522f022327ead85b286e9f
|
||||||
|
body: ""
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
See: https://balena.fibery.io/Work/Project/Extend-the-X-Balena-Client-header-to-include-the-UI-CLI-version-as-well-1174
|
||||||
|
see: https://balena.fibery.io/Work/Project/Extend-the-X-Balena-Client-header-to-include-the-UI-CLI-version-as-well-1174
|
||||||
|
author: Thodoris Greasidis
|
||||||
|
nested: []
|
||||||
|
version: 20.2.9
|
||||||
|
title: ""
|
||||||
|
date: 2025-02-26T12:52:06.672Z
|
||||||
|
- commits:
|
||||||
|
- subject: Update balena-config-json dependency and fix test
|
||||||
|
hash: 93039b010db15fbf1c0d17d4ed8f0db554064de4
|
||||||
|
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: 20.2.8
|
||||||
|
title: ""
|
||||||
|
date: 2025-02-26T00:22:14.010Z
|
||||||
|
- commits:
|
||||||
|
- subject: Use the CLI version in the X-Balena-Client header
|
||||||
|
hash: bef5221ed891db12a0b760f12fc9654e2f4e241b
|
||||||
|
body: ""
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
See: https://balena.fibery.io/Work/Project/Extend-the-X-Balena-Client-header-to-include-the-UI-CLI-version-as-well-1174
|
||||||
|
see: https://balena.fibery.io/Work/Project/Extend-the-X-Balena-Client-header-to-include-the-UI-CLI-version-as-well-1174
|
||||||
|
author: Thodoris Greasidis
|
||||||
|
nested: []
|
||||||
|
version: 20.2.7
|
||||||
|
title: ""
|
||||||
|
date: 2025-02-25T20:21:00.603Z
|
||||||
|
- commits:
|
||||||
|
- subject: Update actions/upload-artifact digest to 4cec3d8
|
||||||
|
hash: 6f0f7350cf65c35abd099a901266821c218478eb
|
||||||
|
body: |
|
||||||
|
Update actions/upload-artifact
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: balena-renovate[bot]
|
||||||
|
nested: []
|
||||||
|
version: 20.2.6
|
||||||
|
title: ""
|
||||||
|
date: 2025-02-25T19:13:48.297Z
|
||||||
|
- commits:
|
||||||
|
- subject: Update actions/setup-node digest to 1d0ff46
|
||||||
|
hash: cddea24cefdfef475731e0a7d2bdec4992959a6b
|
||||||
|
body: |
|
||||||
|
Update actions/setup-node
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: balena-renovate[bot]
|
||||||
|
nested: []
|
||||||
|
version: 20.2.5
|
||||||
|
title: ""
|
||||||
|
date: 2025-02-25T18:10:47.617Z
|
||||||
- commits:
|
- commits:
|
||||||
- subject: Pin docker-modem and dockerode to avoid regression
|
- subject: Pin docker-modem and dockerode to avoid regression
|
||||||
hash: 2cba82e914c720e75b68bd4370a2a92b4d4a7ba0
|
hash: 2cba82e914c720e75b68bd4370a2a92b4d4a7ba0
|
||||||
|
137
CHANGELOG.md
137
CHANGELOG.md
@ -4,6 +4,143 @@ All notable changes to this project will be documented in this file
|
|||||||
automatically by Versionist. DO NOT EDIT THIS FILE MANUALLY!
|
automatically by Versionist. DO NOT EDIT THIS FILE MANUALLY!
|
||||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
## 21.1.9 - 2025-04-07
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary> Update balena-config-json to rely on the balena-image-fs helpers [Thodoris Greasidis] </summary>
|
||||||
|
|
||||||
|
> ### balena-config-json-4.2.7 - 2025-04-02
|
||||||
|
>
|
||||||
|
> * Fix getBootPartition always warning that the boot partitions were not found [Thodoris Greasidis]
|
||||||
|
>
|
||||||
|
> ### balena-config-json-4.2.6 - 2025-04-01
|
||||||
|
>
|
||||||
|
> * write: Allow undefined as a value for the deprecated type parameter [Thodoris Greasidis]
|
||||||
|
>
|
||||||
|
> <details>
|
||||||
|
> <summary> Use the balena-image-fs findPartition() helper to find the boot partition [Thodoris Greasidis] </summary>
|
||||||
|
>
|
||||||
|
>> #### balena-image-fs-7.5.0 - 2025-03-26
|
||||||
|
>>
|
||||||
|
>> * Add function to find a partition by name/label [Ken Bannister]
|
||||||
|
>>
|
||||||
|
>> #### balena-image-fs-7.4.1 - 2025-02-21
|
||||||
|
>>
|
||||||
|
>> * bump ext2fs to 4.2.4 [Ryan Cooke]
|
||||||
|
>>
|
||||||
|
>
|
||||||
|
> </details>
|
||||||
|
>
|
||||||
|
>
|
||||||
|
> ### balena-config-json-4.2.5 - 2025-03-26
|
||||||
|
>
|
||||||
|
> * Update @balena/lint to 9.1.4 [Thodoris Greasidis]
|
||||||
|
>
|
||||||
|
> ### balena-config-json-4.2.4 - 2025-03-26
|
||||||
|
>
|
||||||
|
> * Switch use ts-mocha instead of manually compiling the tests [Thodoris Greasidis]
|
||||||
|
> * Update TypeScript to 5.8.2 [Thodoris Greasidis]
|
||||||
|
>
|
||||||
|
> ### balena-config-json-4.2.3 - 2025-03-26
|
||||||
|
>
|
||||||
|
> * Update chai to v5 [balena-renovate[bot]]
|
||||||
|
>
|
||||||
|
> ### balena-image-fs-7.5.2 - 2025-04-02
|
||||||
|
>
|
||||||
|
> * Update dependency jsdoc-to-markdown to v9 [balena-renovate[bot]]
|
||||||
|
>
|
||||||
|
> ### balena-image-fs-7.5.1 - 2025-03-26
|
||||||
|
>
|
||||||
|
> * Update @balena/lint to v9 [Thodoris Greasidis]
|
||||||
|
> * Remove the no longer needed gpt detection helper [Thodoris Greasidis]
|
||||||
|
> * Update TypeScript to 5.8.2 [Thodoris Greasidis]
|
||||||
|
> * Drop the package-lock.json [Thodoris Greasidis]
|
||||||
|
>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## 21.1.8 - 2025-04-03
|
||||||
|
|
||||||
|
* Update actions/download-artifact action to v4.1.9 [balena-renovate[bot]]
|
||||||
|
|
||||||
|
## 21.1.7 - 2025-04-03
|
||||||
|
|
||||||
|
* Update actions/upload-artifact digest to ea165f8 [balena-renovate[bot]]
|
||||||
|
|
||||||
|
## 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]
|
||||||
|
|
||||||
|
## 20.2.9 - 2025-02-26
|
||||||
|
|
||||||
|
* Fix CORS issue with X-Balena-Client header [Thodoris Greasidis]
|
||||||
|
|
||||||
|
## 20.2.8 - 2025-02-26
|
||||||
|
|
||||||
|
* Update balena-config-json dependency and fix test [Ken Bannister]
|
||||||
|
|
||||||
|
## 20.2.7 - 2025-02-25
|
||||||
|
|
||||||
|
* Use the CLI version in the X-Balena-Client header [Thodoris Greasidis]
|
||||||
|
|
||||||
|
## 20.2.6 - 2025-02-25
|
||||||
|
|
||||||
|
* Update actions/upload-artifact digest to 4cec3d8 [balena-renovate[bot]]
|
||||||
|
|
||||||
|
## 20.2.5 - 2025-02-25
|
||||||
|
|
||||||
|
* Update actions/setup-node digest to 1d0ff46 [balena-renovate[bot]]
|
||||||
|
|
||||||
## 20.2.4 - 2025-02-25
|
## 20.2.4 - 2025-02-25
|
||||||
|
|
||||||
* Pin docker-modem and dockerode to avoid regression [Ken Bannister]
|
* Pin docker-modem and dockerode to avoid regression [Ken Bannister]
|
||||||
|
@ -326,6 +326,8 @@ or to authenticate requests to the API with an 'Authorization: Bearer <key>' hea
|
|||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
$ balena api-key generate "Jenkins Key"
|
$ balena api-key generate "Jenkins Key"
|
||||||
|
$ balena api-key generate "Jenkins Key" 2025-10-30
|
||||||
|
$ balena api-key generate "Jenkins Key" never
|
||||||
|
|
||||||
### Arguments
|
### Arguments
|
||||||
|
|
||||||
@ -333,6 +335,10 @@ Examples:
|
|||||||
|
|
||||||
the API key name
|
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
|
## api-key list
|
||||||
|
|
||||||
### Aliases
|
### Aliases
|
||||||
|
561
npm-shrinkwrap.json
generated
561
npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
22
package.json
22
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "balena-cli",
|
"name": "balena-cli",
|
||||||
"version": "20.2.4",
|
"version": "21.1.9",
|
||||||
"description": "The official balena Command Line Interface",
|
"description": "The official balena Command Line Interface",
|
||||||
"main": "./build/app.js",
|
"main": "./build/app.js",
|
||||||
"homepage": "https://github.com/balena-io/balena-cli",
|
"homepage": "https://github.com/balena-io/balena-cli",
|
||||||
@ -58,6 +58,7 @@
|
|||||||
"build:completion": "node completion/generate-completion.js",
|
"build:completion": "node completion/generate-completion.js",
|
||||||
"build:standalone": "ts-node --transpile-only automation/run.ts build:standalone",
|
"build:standalone": "ts-node --transpile-only automation/run.ts build:standalone",
|
||||||
"build:installer": "ts-node --transpile-only automation/run.ts build:installer",
|
"build:installer": "ts-node --transpile-only automation/run.ts build:installer",
|
||||||
|
"deduplicate-dependencies": "npm dd && git add npm-shrinkwrap.json && git commit --message \"Deduplicate dependencies\"",
|
||||||
"package": "npm run build:fast && npm run build:standalone && npm run build:installer",
|
"package": "npm run build:fast && npm run build:standalone && npm run build:installer",
|
||||||
"pretest": "npm run build",
|
"pretest": "npm run build",
|
||||||
"test": "npm run test:shrinkwrap && npm run test:core",
|
"test": "npm run test:shrinkwrap && npm run test:core",
|
||||||
@ -186,21 +187,21 @@
|
|||||||
"sinon": "^19.0.0",
|
"sinon": "^19.0.0",
|
||||||
"string-to-stream": "^3.0.1",
|
"string-to-stream": "^3.0.1",
|
||||||
"ts-node": "^10.4.0",
|
"ts-node": "^10.4.0",
|
||||||
"typescript": "^5.7.2"
|
"typescript": "^5.8.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@balena/compose": "^6.0.0",
|
"@balena/compose": "^7.0.1",
|
||||||
"@balena/dockerignore": "^1.0.2",
|
"@balena/dockerignore": "^1.0.2",
|
||||||
"@balena/env-parsing": "^1.1.8",
|
"@balena/env-parsing": "^1.1.8",
|
||||||
"@balena/es-version": "^1.0.1",
|
"@balena/es-version": "^1.0.1",
|
||||||
"@oclif/core": "^4.1.0",
|
"@oclif/core": "^4.1.0",
|
||||||
"@sentry/node": "^6.16.1",
|
"@sentry/node": "^6.16.1",
|
||||||
"balena-config-json": "^4.2.0",
|
"balena-config-json": "^4.2.7",
|
||||||
"balena-device-init": "^8.1.3",
|
"balena-device-init": "^8.1.3",
|
||||||
"balena-errors": "^4.7.3",
|
"balena-errors": "^4.7.3",
|
||||||
"balena-image-fs": "^7.3.0",
|
"balena-image-fs": "^7.5.2",
|
||||||
"balena-preload": "^17.0.0",
|
"balena-preload": "^18.0.1",
|
||||||
"balena-sdk": "^20.8.0",
|
"balena-sdk": "^21.3.0",
|
||||||
"balena-semver": "^2.3.0",
|
"balena-semver": "^2.3.0",
|
||||||
"balena-settings-client": "^5.0.2",
|
"balena-settings-client": "^5.0.2",
|
||||||
"balena-settings-storage": "^8.1.0",
|
"balena-settings-storage": "^8.1.0",
|
||||||
@ -211,10 +212,11 @@
|
|||||||
"cli-truncate": "^2.1.0",
|
"cli-truncate": "^2.1.0",
|
||||||
"color-hash": "^1.1.1",
|
"color-hash": "^1.1.1",
|
||||||
"common-tags": "^1.7.2",
|
"common-tags": "^1.7.2",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
"denymount": "^2.3.0",
|
"denymount": "^2.3.0",
|
||||||
"docker-modem": "5.0.5",
|
"docker-modem": "^5.0.6",
|
||||||
"docker-progress": "^5.1.3",
|
"docker-progress": "^5.1.3",
|
||||||
"dockerode": "4.0.3",
|
"dockerode": "^4.0.5",
|
||||||
"ejs": "^3.1.6",
|
"ejs": "^3.1.6",
|
||||||
"etcher-sdk": "9.1.0",
|
"etcher-sdk": "9.1.0",
|
||||||
"express": "^4.17.2",
|
"express": "^4.17.2",
|
||||||
@ -274,6 +276,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"versionist": {
|
"versionist": {
|
||||||
"publishedAt": "2025-02-25T17:17:01.584Z"
|
"publishedAt": "2025-04-07T12:53:18.732Z"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,16 @@
|
|||||||
|
|
||||||
import { Args, Command } from '@oclif/core';
|
import { Args, Command } from '@oclif/core';
|
||||||
import { ExpectedError } from '../../errors';
|
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() {
|
async function isLoggedInWithJwt() {
|
||||||
const balena = getBalenaSdk();
|
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>',
|
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.
|
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 = {
|
public static args = {
|
||||||
name: Args.string({
|
name: Args.string({
|
||||||
description: 'the API key name',
|
description: 'the API key name',
|
||||||
required: true,
|
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;
|
public static authenticated = true;
|
||||||
@ -55,9 +72,61 @@ export default class GenerateCmd extends Command {
|
|||||||
public async run() {
|
public async run() {
|
||||||
const { args: params } = await this.parse(GenerateCmd);
|
const { args: params } = await this.parse(GenerateCmd);
|
||||||
|
|
||||||
|
let expiryDateResponse: string | number | undefined = params.expiryDate;
|
||||||
let key;
|
let key;
|
||||||
try {
|
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) {
|
} catch (e) {
|
||||||
if (e.name === 'BalenaNotLoggedIn') {
|
if (e.name === 'BalenaNotLoggedIn') {
|
||||||
if (await isLoggedInWithJwt()) {
|
if (await isLoggedInWithJwt()) {
|
||||||
|
@ -64,7 +64,12 @@ export default class ConfigInjectCmd extends Command {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const config = await import('balena-config-json');
|
const config = await import('balena-config-json');
|
||||||
await config.write(drive, '', configJSON);
|
await config.write(
|
||||||
|
drive,
|
||||||
|
// Will be removed in the next major of balena-config-json
|
||||||
|
undefined,
|
||||||
|
configJSON,
|
||||||
|
);
|
||||||
|
|
||||||
console.info('Done');
|
console.info('Done');
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ export default class ConfigReadCmd extends Command {
|
|||||||
await safeUmount(drive);
|
await safeUmount(drive);
|
||||||
|
|
||||||
const config = await import('balena-config-json');
|
const config = await import('balena-config-json');
|
||||||
const configJSON = await config.read(drive, '');
|
const configJSON = await config.read(drive);
|
||||||
|
|
||||||
if (options.json) {
|
if (options.json) {
|
||||||
console.log(JSON.stringify(configJSON, null, 4));
|
console.log(JSON.stringify(configJSON, null, 4));
|
||||||
|
@ -62,7 +62,7 @@ export default class ConfigReconfigureCmd extends Command {
|
|||||||
await safeUmount(drive);
|
await safeUmount(drive);
|
||||||
|
|
||||||
const config = await import('balena-config-json');
|
const config = await import('balena-config-json');
|
||||||
const { uuid } = await config.read(drive, '');
|
const { uuid } = await config.read(drive);
|
||||||
await safeUmount(drive);
|
await safeUmount(drive);
|
||||||
|
|
||||||
if (!uuid) {
|
if (!uuid) {
|
||||||
|
@ -64,14 +64,19 @@ export default class ConfigWriteCmd extends Command {
|
|||||||
await safeUmount(drive);
|
await safeUmount(drive);
|
||||||
|
|
||||||
const config = await import('balena-config-json');
|
const config = await import('balena-config-json');
|
||||||
const configJSON = await config.read(drive, '');
|
const configJSON = await config.read(drive);
|
||||||
|
|
||||||
console.info(`Setting ${params.key} to ${params.value}`);
|
console.info(`Setting ${params.key} to ${params.value}`);
|
||||||
ConfigWriteCmd.updateConfigJson(configJSON, params.key, params.value);
|
ConfigWriteCmd.updateConfigJson(configJSON, params.key, params.value);
|
||||||
|
|
||||||
await denyMount(drive, async () => {
|
await denyMount(drive, async () => {
|
||||||
await safeUmount(drive);
|
await safeUmount(drive);
|
||||||
await config.write(drive, '', configJSON);
|
await config.write(
|
||||||
|
drive,
|
||||||
|
// Will be removed in the next major of balena-config-json
|
||||||
|
undefined,
|
||||||
|
configJSON,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
console.info('Done');
|
console.info('Done');
|
||||||
|
@ -368,6 +368,7 @@ ${dockerignoreHelp}
|
|||||||
!opts.shouldUploadLogs,
|
!opts.shouldUploadLogs,
|
||||||
composeOpts.projectPath,
|
composeOpts.projectPath,
|
||||||
opts.createAsDraft,
|
opts.createAsDraft,
|
||||||
|
project.descriptors,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,45 +77,59 @@ export default class DeviceCmd extends Command {
|
|||||||
|
|
||||||
const balena = getBalenaSdk();
|
const balena = getBalenaSdk();
|
||||||
|
|
||||||
const device = (await balena.models.device.get(
|
let device: ExtendedDevice;
|
||||||
params.uuid,
|
if (options.json) {
|
||||||
options.json
|
const [deviceBase, deviceComputed] = await Promise.all([
|
||||||
? {
|
balena.models.device.get(params.uuid, {
|
||||||
$expand: {
|
$expand: {
|
||||||
device_tag: {
|
device_tag: {
|
||||||
$select: ['tag_key', 'value'],
|
$select: ['tag_key', 'value'],
|
||||||
},
|
|
||||||
...expandForAppName.$expand,
|
|
||||||
},
|
},
|
||||||
}
|
...expandForAppName.$expand,
|
||||||
: {
|
|
||||||
$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;
|
}),
|
||||||
|
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) {
|
if (options.view) {
|
||||||
const open = await import('open');
|
const open = await import('open');
|
||||||
|
@ -37,6 +37,7 @@ import type {
|
|||||||
Release,
|
Release,
|
||||||
} from 'balena-sdk';
|
} from 'balena-sdk';
|
||||||
import type { Preloader } from 'balena-preload';
|
import type { Preloader } from 'balena-preload';
|
||||||
|
import type * as Fs from 'fs';
|
||||||
|
|
||||||
export default class PreloadCmd extends Command {
|
export default class PreloadCmd extends Command {
|
||||||
public static description = stripIndent`
|
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
|
// balena-preload currently does not work with numerical app IDs
|
||||||
// Load app here, and use app slug from hereon
|
// Load app here, and use app slug from hereon
|
||||||
const fleetSlug: string | undefined = options.fleet
|
const fleetSlug: string | undefined = options.fleet
|
||||||
@ -295,7 +332,7 @@ Can be repeated to add multiple certificates.\
|
|||||||
owns__release: {
|
owns__release: {
|
||||||
$select: ['id', 'commit', 'end_timestamp', 'composition'],
|
$select: ['id', 'commit', 'end_timestamp', 'composition'],
|
||||||
$expand: {
|
$expand: {
|
||||||
contains__image: {
|
release_image: {
|
||||||
$select: ['image'],
|
$select: ['image'],
|
||||||
$expand: {
|
$expand: {
|
||||||
image: {
|
image: {
|
||||||
|
@ -128,6 +128,7 @@ export const createRelease = async function (
|
|||||||
draft: boolean,
|
draft: boolean,
|
||||||
semver: string | undefined,
|
semver: string | undefined,
|
||||||
contract: import('@balena/compose/dist/release/models').ReleaseModel['contract'],
|
contract: import('@balena/compose/dist/release/models').ReleaseModel['contract'],
|
||||||
|
imgDescriptors: ImageDescriptor[],
|
||||||
): Promise<Release> {
|
): Promise<Release> {
|
||||||
const _ = require('lodash') as typeof import('lodash');
|
const _ = require('lodash') as typeof import('lodash');
|
||||||
const crypto = require('crypto') as typeof import('crypto');
|
const crypto = require('crypto') as typeof import('crypto');
|
||||||
@ -167,6 +168,7 @@ export const createRelease = async function (
|
|||||||
semver,
|
semver,
|
||||||
is_final: !draft,
|
is_final: !draft,
|
||||||
contract,
|
contract,
|
||||||
|
imgDescriptors,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -240,7 +242,7 @@ export const getPreviousRepos = (
|
|||||||
status: 'success',
|
status: 'success',
|
||||||
},
|
},
|
||||||
$expand: {
|
$expand: {
|
||||||
contains__image: {
|
release_image: {
|
||||||
$select: 'image',
|
$select: 'image',
|
||||||
$expand: { image: { $select: 'is_stored_at__image_location' } },
|
$expand: { image: { $select: 'is_stored_at__image_location' } },
|
||||||
},
|
},
|
||||||
@ -252,7 +254,7 @@ export const getPreviousRepos = (
|
|||||||
.then(function (release) {
|
.then(function (release) {
|
||||||
// grab all images from the latest release, return all image locations in the registry
|
// grab all images from the latest release, return all image locations in the registry
|
||||||
if (release.length > 0) {
|
if (release.length > 0) {
|
||||||
const images = release[0].contains__image as Array<{
|
const images = release[0].release_image as Array<{
|
||||||
image: [SDK.Image];
|
image: [SDK.Image];
|
||||||
}>;
|
}>;
|
||||||
const { getRegistryAndName } =
|
const { getRegistryAndName } =
|
||||||
|
@ -1375,6 +1375,7 @@ export async function deployProject(
|
|||||||
skipLogUpload: boolean,
|
skipLogUpload: boolean,
|
||||||
projectPath: string,
|
projectPath: string,
|
||||||
isDraft: boolean,
|
isDraft: boolean,
|
||||||
|
imgDescriptors: ImageDescriptor[],
|
||||||
): Promise<import('@balena/compose/dist/release/models').ReleaseModel> {
|
): Promise<import('@balena/compose/dist/release/models').ReleaseModel> {
|
||||||
const releaseMod = await import('@balena/compose/dist/release');
|
const releaseMod = await import('@balena/compose/dist/release');
|
||||||
const { createRelease, tagServiceImages } = await import('./compose');
|
const { createRelease, tagServiceImages } = await import('./compose');
|
||||||
@ -1405,6 +1406,7 @@ export async function deployProject(
|
|||||||
isDraft,
|
isDraft,
|
||||||
contract?.version,
|
contract?.version,
|
||||||
contract,
|
contract,
|
||||||
|
imgDescriptors,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
const { client: pineClient, release, serviceImages } = $release;
|
const { client: pineClient, release, serviceImages } = $release;
|
||||||
|
@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
import type * as BalenaSdk from 'balena-sdk';
|
import type * as BalenaSdk from 'balena-sdk';
|
||||||
import * as semver from 'balena-semver';
|
|
||||||
import { getBalenaSdk, stripIndent } from './lazy';
|
import { getBalenaSdk, stripIndent } from './lazy';
|
||||||
|
|
||||||
export interface ImgConfig {
|
export interface ImgConfig {
|
||||||
@ -122,16 +121,10 @@ export function generateDeviceConfig(
|
|||||||
// os.getConfig always returns a config for an app
|
// os.getConfig always returns a config for an app
|
||||||
delete config.apiKey;
|
delete config.apiKey;
|
||||||
|
|
||||||
if (deviceApiKey == null && semver.satisfies(options.version, '<2.0.3')) {
|
config.deviceApiKey =
|
||||||
config.apiKey = await sdk.models.application.generateApiKey(
|
typeof deviceApiKey === 'string' && deviceApiKey
|
||||||
application.id,
|
? deviceApiKey
|
||||||
);
|
: await sdk.models.device.generateDeviceKey(device.uuid);
|
||||||
} else {
|
|
||||||
config.deviceApiKey =
|
|
||||||
typeof deviceApiKey === 'string' && deviceApiKey
|
|
||||||
? deviceApiKey
|
|
||||||
: await sdk.models.device.generateDeviceKey(device.uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
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
|
* @description
|
||||||
* If the device image does not exist, return false.
|
* If the device image does not exist, return false.
|
||||||
*
|
*
|
||||||
* @param {String} deviceType - device type slug or alias
|
* @param {String} deviceType - device type slug or alias
|
||||||
* @param {String} version - the exact balenaOS version number
|
* @param {String} version - the exact balenaOS version number
|
||||||
* @returns {Promise<Boolean>} is image fresh
|
* @returns {Promise<Boolean>} is image cached
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* isImageFresh('raspberry-pi', '1.2.3').then (isFresh) ->
|
* isImageCached ('raspberry-pi', '1.2.3').then (isCached) ->
|
||||||
* if isFresh
|
* if isCached
|
||||||
* console.log('The Raspberry Pi image v1.2.3 is fresh!')
|
* 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);
|
const imagePath = await getImagePath(deviceType, version);
|
||||||
let createdDate;
|
|
||||||
try {
|
try {
|
||||||
createdDate = await getFileCreatedDate(imagePath);
|
const createdDate = await getFileCreatedDate(imagePath);
|
||||||
|
return createdDate != null;
|
||||||
} catch {
|
} catch {
|
||||||
// Swallow errors from getFileCreatedTime.
|
|
||||||
}
|
|
||||||
if (createdDate == null) {
|
|
||||||
return false;
|
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';
|
versionOrRange = 'latest';
|
||||||
}
|
}
|
||||||
const version = await resolveVersion(deviceType, versionOrRange);
|
const version = await resolveVersion(deviceType, versionOrRange);
|
||||||
const isFresh = await isImageFresh(deviceType, version);
|
const isFresh = await isImageCached(deviceType, version);
|
||||||
const $stream = isFresh
|
const $stream = isFresh
|
||||||
? await getImage(deviceType, version)
|
? await getImage(deviceType, version)
|
||||||
: await doDownload({ ...options, deviceType, version });
|
: await doDownload({ ...options, deviceType, version });
|
||||||
|
@ -21,6 +21,7 @@ import type { Chalk } from 'chalk';
|
|||||||
import type * as visuals from 'resin-cli-visuals';
|
import type * as visuals from 'resin-cli-visuals';
|
||||||
import type * as CliForm from 'resin-cli-form';
|
import type * as CliForm from 'resin-cli-form';
|
||||||
import type { ux } from '@oclif/core';
|
import type { ux } from '@oclif/core';
|
||||||
|
import { version } from '../../package.json';
|
||||||
|
|
||||||
// Equivalent of _.once but avoiding the need to import lodash for lazy deps
|
// Equivalent of _.once but avoiding the need to import lodash for lazy deps
|
||||||
const once = <T>(fn: () => T) => {
|
const once = <T>(fn: () => T) => {
|
||||||
@ -43,9 +44,26 @@ export const onceAsync = <T>(fn: () => Promise<T>) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getBalenaSdk = once(() =>
|
const cliXBalenaClientHeaderInterceptor: BalenaSdk.Interceptor = {
|
||||||
(require('balena-sdk') as typeof BalenaSdk).fromSharedOptions(),
|
request($request) {
|
||||||
);
|
if ($request.headers['X-Balena-Client']) {
|
||||||
|
// We intentionally overwrite the sdk version string from the header
|
||||||
|
// to conserve bandwidth. We only do that when the SDK already has specified
|
||||||
|
// the X-Balena-Client header, since that signals that this is a safe url to
|
||||||
|
// include the extra header and will not cause CORS errors.
|
||||||
|
$request.headers['X-Balena-Client'] = `balena-cli/${version}`;
|
||||||
|
}
|
||||||
|
return $request;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getBalenaSdk = once(() => {
|
||||||
|
const sdk = (require('balena-sdk') as typeof BalenaSdk).fromSharedOptions();
|
||||||
|
if (!sdk.interceptors.includes(cliXBalenaClientHeaderInterceptor)) {
|
||||||
|
sdk.interceptors.push(cliXBalenaClientHeaderInterceptor);
|
||||||
|
}
|
||||||
|
return sdk;
|
||||||
|
});
|
||||||
|
|
||||||
export const getVisuals = once(
|
export const getVisuals = once(
|
||||||
() => require('resin-cli-visuals') as typeof visuals,
|
() => require('resin-cli-visuals') as typeof visuals,
|
||||||
|
@ -114,6 +114,14 @@ describe('balena device', function () {
|
|||||||
'Content-Type': 'application/json',
|
'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');
|
const { out, err } = await runCommand('device 27fda508c --json');
|
||||||
expect(err).to.be.empty;
|
expect(err).to.be.empty;
|
||||||
const json = JSON.parse(out.join(''));
|
const json = JSON.parse(out.join(''));
|
||||||
|
@ -231,6 +231,10 @@ if (process.platform !== 'win32') {
|
|||||||
err.flatMap((line) => line.split('\n')).filter((line) => line !== ''),
|
err.flatMap((line) => line.split('\n')).filter((line) => line !== ''),
|
||||||
).to.deep.equal(
|
).to.deep.equal(
|
||||||
stripIndent`
|
stripIndent`
|
||||||
|
[warn] "${tmpDummyPath}":
|
||||||
|
[warn] Found partition table with 1 partitions,
|
||||||
|
[warn] but none with a name/label in ['resin-boot', 'flash-boot', 'balena-boot'].
|
||||||
|
[warn] Will scan all partitions for contents.
|
||||||
[warn] "${tmpDummyPath}":
|
[warn] "${tmpDummyPath}":
|
||||||
[warn] 1 partition(s) found, but none containing file "/device-type.json".
|
[warn] 1 partition(s) found, but none containing file "/device-type.json".
|
||||||
[warn] Assuming default boot partition number '1'.
|
[warn] Assuming default boot partition number '1'.
|
||||||
|
@ -82,7 +82,7 @@ describe('balena release', function () {
|
|||||||
expect(err).to.be.empty;
|
expect(err).to.be.empty;
|
||||||
const json = JSON.parse(out.join(''));
|
const json = JSON.parse(out.join(''));
|
||||||
expect(json[0].commit).to.equal('90247b54de4fa7a0a3cbc85e73c68039');
|
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',
|
'2020-01-04T01:13:08.583Z',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
"build_log": null,
|
"build_log": null,
|
||||||
"start_timestamp": "2021-08-25T22:18:33.624Z",
|
"start_timestamp": "2021-08-25T22:18:33.624Z",
|
||||||
"end_timestamp": "2021-08-25T22:18:48.820Z",
|
"end_timestamp": "2021-08-25T22:18:48.820Z",
|
||||||
"contains__image": [
|
"release_image": [
|
||||||
{
|
{
|
||||||
"image": [
|
"image": [
|
||||||
{
|
{
|
||||||
|
@ -42,7 +42,7 @@ describe('image-manager', function () {
|
|||||||
|
|
||||||
describe('given the image is fresh', function () {
|
describe('given the image is fresh', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
this.cacheIsImageFresh = stub(imageManager, 'isImageFresh');
|
this.cacheIsImageFresh = stub(imageManager, 'isImageCached');
|
||||||
return this.cacheIsImageFresh.resolves(true);
|
return this.cacheIsImageFresh.resolves(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ describe('image-manager', function () {
|
|||||||
|
|
||||||
describe('given the image is not fresh', function () {
|
describe('given the image is not fresh', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
this.cacheIsImageFresh = stub(imageManager, 'isImageFresh');
|
this.cacheIsImageFresh = stub(imageManager, 'isImageCached');
|
||||||
return this.cacheIsImageFresh.resolves(false);
|
return this.cacheIsImageFresh.resolves(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -280,7 +280,7 @@ describe('image-manager', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.isImageFresh()', () => {
|
describe('.isImageCached()', () => {
|
||||||
describe('given the raspberry-pi manifest', function () {
|
describe('given the raspberry-pi manifest', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
this.getDeviceTypeManifestBySlugStub = stub(
|
this.getDeviceTypeManifestBySlugStub = stub(
|
||||||
@ -314,78 +314,8 @@ describe('image-manager', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return false', async function () {
|
it('should return false', async function () {
|
||||||
expect(await imageManager.isImageFresh('raspberry-pi', '1.2.3')).to.be
|
expect(await imageManager.isImageCached('raspberry-pi', '1.2.3')).to
|
||||||
.false;
|
.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;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user