From 8fe6d6c0268f69bcf3bcac3c57470272b959e9b0 Mon Sep 17 00:00:00 2001 From: myarmolinsky Date: Tue, 11 Mar 2025 08:16:03 -0400 Subject: [PATCH 1/7] Update `balena-sdk` to 21.2.1 Change-type: patch --- npm-shrinkwrap.json | 77 +++++++++++++++++++++++++++++++++++---------- package.json | 2 +- 2 files changed, 62 insertions(+), 17 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 33027553..998ae885 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -21,7 +21,7 @@ "balena-errors": "^4.7.3", "balena-image-fs": "^7.3.0", "balena-preload": "^17.0.0", - "balena-sdk": "^20.8.0", + "balena-sdk": "^21.2.1", "balena-semver": "^2.3.0", "balena-settings-client": "^5.0.2", "balena-settings-storage": "^8.1.0", @@ -7463,6 +7463,58 @@ "node": ">=18" } }, + "node_modules/balena-preload/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==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/balena-preload/node_modules/balena-sdk": { + "version": "20.9.1", + "resolved": "https://registry.npmjs.org/balena-sdk/-/balena-sdk-20.9.1.tgz", + "integrity": "sha512-M1Fl8z12JlbLY0LNogSogzwuNKIbMNPggLZoGOh5EpCwhzlyeYe/wCXdSxIG18I6QRRrKhCcbraCGwZR11l6RA==", + "license": "Apache-2.0", + "dependencies": { + "@balena/es-version": "^1.0.0", + "@types/json-schema": "^7.0.9", + "@types/node": "^18.19.50", + "abortcontroller-polyfill": "^1.7.1", + "balena-auth": "^6.0.1", + "balena-errors": "^4.9.0", + "balena-hup-action-utils": "~6.2.0", + "balena-register-device": "^9.0.4", + "balena-request": "^14.0.0", + "balena-semver": "^2.3.0", + "balena-settings-client": "^5.0.0", + "date-fns": "^3.0.5", + "handlebars": "^4.7.7", + "lodash": "^4.17.21", + "memoizee": "^0.4.15", + "mime": "^3.0.0", + "ndjson": "^2.0.0", + "p-throttle": "^4.1.1", + "pinejs-client-core": "^7.0.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0" + } + }, + "node_modules/balena-preload/node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/balena-register-device": { "version": "9.0.4", "resolved": "https://registry.npmjs.org/balena-register-device/-/balena-register-device-9.0.4.tgz", @@ -7505,13 +7557,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.2.1", + "resolved": "https://registry.npmjs.org/balena-sdk/-/balena-sdk-21.2.1.tgz", + "integrity": "sha512-GpOO4LUntLuLI1FcZmI0NZeLwqd+ljFwWcS1kGzX2/GuI72EDAwrEEbojDsuJyyZhjxGw0eHOat0J+AtU6Qvhw==", + "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 +7584,7 @@ "tslib": "^2.1.0" }, "engines": { - "node": ">=18.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==", - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" + "node": "^20.12.0 || >= 22.0.0" } }, "node_modules/balena-sdk/node_modules/mime": { @@ -19579,7 +19623,8 @@ "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==" + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" }, "node_modules/unique-string": { "version": "2.0.0", diff --git a/package.json b/package.json index 22ea7210..5edc02b7 100644 --- a/package.json +++ b/package.json @@ -201,7 +201,7 @@ "balena-errors": "^4.7.3", "balena-image-fs": "^7.3.0", "balena-preload": "^17.0.0", - "balena-sdk": "^20.8.0", + "balena-sdk": "^21.2.1", "balena-semver": "^2.3.0", "balena-settings-client": "^5.0.2", "balena-settings-storage": "^8.1.0", From ed0e03ddb274da294f719dc0e307ec37591e10d7 Mon Sep 17 00:00:00 2001 From: myarmolinsky Date: Tue, 11 Mar 2025 08:16:50 -0400 Subject: [PATCH 2/7] Add dependency `date-fns` Change-type: patch --- npm-shrinkwrap.json | 28 +++++++++++++++++++++++++--- package.json | 1 + 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 998ae885..9d14fe9b 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -32,6 +32,7 @@ "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-progress": "^5.1.3", @@ -7503,6 +7504,16 @@ "node": ">=18.0" } }, + "node_modules/balena-preload/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", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/balena-preload/node_modules/mime": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", @@ -7587,6 +7598,16 @@ "node": "^20.12.0 || >= 22.0.0" } }, + "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", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/balena-sdk/node_modules/mime": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", @@ -9157,9 +9178,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" diff --git a/package.json b/package.json index 5edc02b7..23574103 100644 --- a/package.json +++ b/package.json @@ -212,6 +212,7 @@ "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-progress": "^5.1.3", From 9d3120b144c2c017eda55463b034f1561d264213 Mon Sep 17 00:00:00 2001 From: myarmolinsky Date: Tue, 11 Mar 2025 08:17:27 -0400 Subject: [PATCH 3/7] Update `balena-preload` to 18.0.1 Change-type: patch --- npm-shrinkwrap.json | 80 ++++----------------------------------------- package.json | 2 +- 2 files changed, 7 insertions(+), 75 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 9d14fe9b..157ae3d1 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -20,7 +20,7 @@ "balena-device-init": "^8.1.3", "balena-errors": "^4.7.3", "balena-image-fs": "^7.3.0", - "balena-preload": "^17.0.0", + "balena-preload": "^18.0.1", "balena-sdk": "^21.2.1", "balena-semver": "^2.3.0", "balena-settings-client": "^5.0.2", @@ -7446,12 +7446,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", @@ -7461,69 +7461,7 @@ "tar-fs": "^2.1.1" }, "engines": { - "node": ">=18" - } - }, - "node_modules/balena-preload/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==", - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/balena-preload/node_modules/balena-sdk": { - "version": "20.9.1", - "resolved": "https://registry.npmjs.org/balena-sdk/-/balena-sdk-20.9.1.tgz", - "integrity": "sha512-M1Fl8z12JlbLY0LNogSogzwuNKIbMNPggLZoGOh5EpCwhzlyeYe/wCXdSxIG18I6QRRrKhCcbraCGwZR11l6RA==", - "license": "Apache-2.0", - "dependencies": { - "@balena/es-version": "^1.0.0", - "@types/json-schema": "^7.0.9", - "@types/node": "^18.19.50", - "abortcontroller-polyfill": "^1.7.1", - "balena-auth": "^6.0.1", - "balena-errors": "^4.9.0", - "balena-hup-action-utils": "~6.2.0", - "balena-register-device": "^9.0.4", - "balena-request": "^14.0.0", - "balena-semver": "^2.3.0", - "balena-settings-client": "^5.0.0", - "date-fns": "^3.0.5", - "handlebars": "^4.7.7", - "lodash": "^4.17.21", - "memoizee": "^0.4.15", - "mime": "^3.0.0", - "ndjson": "^2.0.0", - "p-throttle": "^4.1.1", - "pinejs-client-core": "^7.0.0", - "tslib": "^2.1.0" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/balena-preload/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", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/kossnocorp" - } - }, - "node_modules/balena-preload/node_modules/mime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=10.0.0" + "node": "^20.12.0 || >=22.0.0" } }, "node_modules/balena-register-device": { @@ -19642,12 +19580,6 @@ "resolved": "git+ssh://git@github.com/balena-io-modules/unbzip2-stream.git#4a54f56a25b58950f9e4277c56db2912d62242e7", "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==", - "license": "MIT" - }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", diff --git a/package.json b/package.json index 23574103..364b1376 100644 --- a/package.json +++ b/package.json @@ -200,7 +200,7 @@ "balena-device-init": "^8.1.3", "balena-errors": "^4.7.3", "balena-image-fs": "^7.3.0", - "balena-preload": "^17.0.0", + "balena-preload": "^18.0.1", "balena-sdk": "^21.2.1", "balena-semver": "^2.3.0", "balena-settings-client": "^5.0.2", From c0fd1e3886c932dc474340eb41a6e0db11ede412 Mon Sep 17 00:00:00 2001 From: myarmolinsky Date: Tue, 11 Mar 2025 08:18:56 -0400 Subject: [PATCH 4/7] Deduplicate dependencies --- npm-shrinkwrap.json | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 157ae3d1..31bceeba 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -5841,11 +5841,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", @@ -19580,6 +19575,12 @@ "resolved": "git+ssh://git@github.com/balena-io-modules/unbzip2-stream.git#4a54f56a25b58950f9e4277c56db2912d62242e7", "integrity": "sha512-hqSQ+EGPNNs80IGpZszUngM7ttKBCgjRtrw+2PTRDUd1UNvPOoSL4M7V1bDBJXktGv8KaVJFRKk2mWi6RmbiEg==" }, + "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==", + "license": "MIT" + }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", From ecc6f80164fca3c0cde42b140b6d7404abe8c877 Mon Sep 17 00:00:00 2001 From: myarmolinsky Date: Wed, 5 Feb 2025 12:17:38 -0500 Subject: [PATCH 5/7] api-key generate: Add required argument `expiryDate` Change-type: major --- docs/balena-cli.md | 6 +++ src/commands/api-key/generate.ts | 75 ++++++++++++++++++++++++++++++-- 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/docs/balena-cli.md b/docs/balena-cli.md index 3d5c2f49..779c1039 100644 --- a/docs/balena-cli.md +++ b/docs/balena-cli.md @@ -326,6 +326,8 @@ or to authenticate requests to the API with an 'Authorization: Bearer ' 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 diff --git a/src/commands/api-key/generate.ts b/src/commands/api-key/generate.ts index fcdeba0c..2d6a4d17 100644 --- a/src/commands/api-key/generate.ts +++ b/src/commands/api-key/generate.ts @@ -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 ', or to authenticate requests to the API with an 'Authorization: Bearer ' 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()) { From aad62d1ccd11ebb69b1035d5b95aef93d384bfd5 Mon Sep 17 00:00:00 2001 From: myarmolinsky Date: Wed, 5 Feb 2025 12:19:59 -0500 Subject: [PATCH 6/7] Drop support for OS versions <2.14.0 Change-type: major --- src/utils/config.ts | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/utils/config.ts b/src/utils/config.ts index 28f57c52..45c5e548 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -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; }) From bc3558dd8ebbdecdc8bd81963522aaefd6df16ab Mon Sep 17 00:00:00 2001 From: myarmolinsky Date: Wed, 5 Feb 2025 12:20:42 -0500 Subject: [PATCH 7/7] Address SDK major v21 breaking changes --- src/commands/preload/index.ts | 2 +- src/utils/compose.ts | 4 +- src/utils/image-manager.ts | 28 +++---- tests/commands/release.spec.ts | 2 +- .../api-response/release-GET-v7.json | 2 +- .../utils/image-manager/image-manager.spec.ts | 80 ++----------------- 6 files changed, 19 insertions(+), 99 deletions(-) diff --git a/src/commands/preload/index.ts b/src/commands/preload/index.ts index 3e093685..100acd0a 100644 --- a/src/commands/preload/index.ts +++ b/src/commands/preload/index.ts @@ -295,7 +295,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: { diff --git a/src/utils/compose.ts b/src/utils/compose.ts index 1d1f5837..8404cdce 100644 --- a/src/utils/compose.ts +++ b/src/utils/compose.ts @@ -240,7 +240,7 @@ export const getPreviousRepos = ( status: 'success', }, $expand: { - contains__image: { + release_image: { $select: 'image', $expand: { image: { $select: 'is_stored_at__image_location' } }, }, @@ -252,7 +252,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 } = diff --git a/src/utils/image-manager.ts b/src/utils/image-manager.ts index 382c46cc..60cb01ab 100644 --- a/src/utils/image-manager.ts +++ b/src/utils/image-manager.ts @@ -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} is image fresh + * @returns {Promise} 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 }); diff --git a/tests/commands/release.spec.ts b/tests/commands/release.spec.ts index 1ced28ca..8dc75d67 100644 --- a/tests/commands/release.spec.ts +++ b/tests/commands/release.spec.ts @@ -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', ); }); diff --git a/tests/test-data/api-response/release-GET-v7.json b/tests/test-data/api-response/release-GET-v7.json index 3f0b8788..9ac6f9b5 100644 --- a/tests/test-data/api-response/release-GET-v7.json +++ b/tests/test-data/api-response/release-GET-v7.json @@ -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": [ { diff --git a/tests/utils/image-manager/image-manager.spec.ts b/tests/utils/image-manager/image-manager.spec.ts index 962df029..5b1595aa 100644 --- a/tests/utils/image-manager/image-manager.spec.ts +++ b/tests/utils/image-manager/image-manager.spec.ts @@ -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; }); }); });