mirror of
https://github.com/balena-io/balena-cli.git
synced 2025-06-24 18:45:07 +00:00
Compare commits
283 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 | |||
727033ae14 | |||
c19ce6a905 | |||
1a33029738 | |||
043bc48a1c | |||
a10156a441 | |||
4f665f43d2 | |||
9f097a96f5 | |||
64d1943804 | |||
666ce876e6 | |||
e01184080f | |||
93039b010d | |||
795259bf30 | |||
fa134d2d39 | |||
bef5221ed8 | |||
72d6db796c | |||
e848eb63ee | |||
6f0f7350cf | |||
07a88c700e | |||
9cae66bd92 | |||
cddea24cef | |||
b1c246c0b4 | |||
00b4d57a03 | |||
2cba82e914 | |||
1352c5c823 | |||
c86eb97010 | |||
53be743b9d | |||
d9f21b4c3f | |||
261ab398dd | |||
f28a9992e4 | |||
29e7827eb1 | |||
1d77cf3665 | |||
017c767f61 | |||
7d79c4e24b | |||
60bc5092e0 | |||
a33a794931 | |||
f0ede6fca2 | |||
dbe177e766 | |||
09f80730a8 | |||
327d28c103 | |||
56ab785a82 | |||
305d65d5ed | |||
c4d3686a34 | |||
ce06854b55 | |||
8db05cc8a7 | |||
7a22c987d2 | |||
45efbcdfe3 | |||
d6a9b78b3e | |||
e8ac3ea960 | |||
0ffa0f85a2 | |||
5e7479f60e | |||
07365c45f2 | |||
e5076434c6 | |||
5d687f5a55 | |||
e192767156 | |||
5a8d2fad5f | |||
45f482fad1 | |||
c0e7ae9c91 | |||
36077cacda | |||
82b9983450 | |||
703dbd01c9 | |||
602e63c8a9 | |||
2ab635f49a | |||
322736a145 | |||
c347b67b25 | |||
4022beeb56 | |||
ccf97cfc9f | |||
9c5fe14f2e | |||
38e29251e7 | |||
bfc7a14646 | |||
610db81fcb | |||
d1f7d6d07f | |||
694eb78aaa | |||
1caccafbcd | |||
61d4d1f1e7 | |||
a01c85bc15 | |||
5d7b7cfc6f | |||
92fd9e0883 | |||
24273b5ac0 | |||
6155509f4c | |||
735af9f6a9 | |||
d7c60e6dea | |||
bcb42c8a21 | |||
04f5e0fa2b | |||
8cb5804848 | |||
91c3fced49 | |||
99a94eafbb | |||
b4cff78588 | |||
8577bb6281 | |||
e0f081623b | |||
2887ab8200 | |||
aaf4625abb | |||
6f30dc0550 | |||
6565ef4392 | |||
60b46192a2 | |||
bb101de96e | |||
ca6344bf4c | |||
48596fa318 | |||
7fb28ddb21 | |||
6dd6f43d64 | |||
07a7bd76fe | |||
4b3fdcf99c | |||
d023d0af91 | |||
de1821d7ac | |||
12923c9b84 | |||
8be069dbdb | |||
9d3f9128a8 | |||
61ebf9e4fd | |||
84985022e5 | |||
c5d8f73263 | |||
5db0c71bb3 | |||
c7a06f7259 | |||
bc66febc50 | |||
bb80311700 | |||
3dee7bd6f6 | |||
3251f04287 | |||
35dce4579a | |||
71ef00534d | |||
b6f8be27ec | |||
13110cca45 | |||
d4b554da1b | |||
1275c11573 | |||
593233a99f | |||
ec92f21b70 | |||
5adc43bcbd | |||
1ee9a68288 | |||
56e5dafb20 | |||
f0e0c0d728 | |||
afd14794f5 | |||
37e08e4667 | |||
61af57acc9 | |||
19be0fec1a | |||
2b656c23b3 | |||
2bf7b81645 | |||
746b6fa439 | |||
f52e218290 | |||
8655b89313 | |||
09d52c504d | |||
e5cee648f2 | |||
98a6b431d9 | |||
f305d5d9a0 | |||
5cb5ac80a6 | |||
3eb3b3b584 | |||
8ee5ede34d | |||
d6d08cc7c9 | |||
76c63d4b20 | |||
bff5897047 | |||
22c9fd399e | |||
65222b5fc9 | |||
d554658eee | |||
8ded517dd9 | |||
b0d8b021d5 | |||
2eb5d7f6b3 | |||
f1924bba6b | |||
44082e22e4 | |||
2d15530f61 | |||
a7c612f7de | |||
147ce8067a | |||
5e7ac09dd0 | |||
4c9fc89a6b | |||
9b1eb57973 | |||
76c08b6c13 | |||
3f82f42652 | |||
69d820878a | |||
aaaee47e0c | |||
e7761a616b | |||
9559d5cba3 | |||
c1649dd828 | |||
8e9b992a59 | |||
b77f266bd7 | |||
3c9ac76982 | |||
e483d06d2b | |||
24d2d19d33 | |||
ba5bb7b12c | |||
9082e7b3f7 | |||
0716544042 | |||
e1858aa69d | |||
082cce332a | |||
218e0a1b6b | |||
4065c5775c | |||
fd966df1f0 | |||
5eba175bf1 | |||
bd1b71bf2f | |||
bd7ea3d21a | |||
31662d9175 | |||
417c75484b | |||
fcd77e97d9 | |||
1dd819ae61 | |||
388e02ce85 | |||
30446605e1 | |||
e853b15f12 | |||
96cf380f66 | |||
5d0d02a24d | |||
f5ca07a422 | |||
6b5c6e072b | |||
3615f8e525 | |||
ec6cbd120e | |||
65b278e40b | |||
8d1394a77d | |||
ca0a3ee147 | |||
019af9e703 | |||
f213940c84 | |||
5243803342 | |||
811e009ba9 | |||
45e0f21685 | |||
60f5f47930 | |||
cad5543863 | |||
a1e936bb3f | |||
49984c2366 | |||
2a0ab6abbb | |||
349bab7702 | |||
af2c04540f | |||
cdfabb8f92 | |||
5e058d5158 | |||
6a81ed2d70 | |||
d323c0742c | |||
9cdde4f6c2 | |||
ebe10360b3 | |||
440c5ad15b | |||
0699278220 | |||
0eb5c78e33 | |||
067232b5c4 | |||
5716ba29ad | |||
fc2234b0dd | |||
12cdb14638 | |||
b936c51941 | |||
6c23b06b4c | |||
87c52c55ed | |||
f792343180 | |||
680d592af2 | |||
f52e6bd8b4 | |||
0847daba1b | |||
057b37ae38 | |||
deb7de8951 | |||
55dbe42e84 | |||
3e8bc57fdb | |||
d206e7cd66 | |||
7092db8ee8 | |||
276d61cf6c | |||
77ccd9c39c | |||
9e140eff13 | |||
da95baa70c | |||
a3ec75c2c7 | |||
f6f6be8ee8 | |||
09e653692b | |||
3ac89b236a | |||
bd472f2380 | |||
b5dcf45c40 | |||
7e2b5abe60 |
@ -1,2 +0,0 @@
|
||||
/completion/*
|
||||
/bin/*
|
21
.eslintrc.js
21
.eslintrc.js
@ -1,21 +0,0 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@balena/lint/config/.eslintrc.js'],
|
||||
parserOptions: {
|
||||
project: 'tsconfig.dev.json',
|
||||
},
|
||||
root: true,
|
||||
rules: {
|
||||
ignoreDefinitionFiles: 0,
|
||||
// to avoid the `warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion`
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/no-shadow': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
{
|
||||
paths: ['resin-cli-visuals', 'chalk', 'common-tags', 'resin-cli-form'],
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
|
||||
},
|
||||
};
|
6
.github/actions/publish/action.yml
vendored
6
.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@1e60f620b9541d16bece96c5465dc8ee9832be0b # 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 }}
|
||||
|
||||
@ -135,7 +135,7 @@ runs:
|
||||
XCODE_APP_LOADER_TEAM_ID: ${{ inputs.XCODE_APP_LOADER_TEAM_ID }}
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4
|
||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
|
||||
with:
|
||||
name: gh-release-${{ github.event.pull_request.head.sha || github.event.head_commit.id }}-${{ strategy.job-index }}
|
||||
path: dist
|
||||
|
4
.github/actions/test/action.yml
vendored
4
.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@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4
|
||||
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||
with:
|
||||
node-version: ${{ inputs.NODE_VERSION }}
|
||||
cache: npm
|
||||
@ -58,7 +58,7 @@ runs:
|
||||
run: tar --exclude-vcs -acf ${{ runner.temp }}/custom.tgz .
|
||||
|
||||
- name: Upload custom artifact
|
||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4
|
||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
|
||||
with:
|
||||
name: custom-${{ github.event.pull_request.head.sha || github.event.head_commit.id }}-${{ runner.os }}-${{ runner.arch }}
|
||||
path: ${{ runner.temp }}/custom.tgz
|
||||
|
4
.github/workflows/flowzone.yml
vendored
4
.github/workflows/flowzone.yml
vendored
@ -26,7 +26,7 @@ jobs:
|
||||
"os": [
|
||||
["self-hosted", "X64"],
|
||||
["self-hosted", "ARM64"],
|
||||
["macos-12"],
|
||||
["macos-13"],
|
||||
["windows-2019"],
|
||||
["macos-latest-xlarge"]
|
||||
]
|
||||
@ -36,7 +36,7 @@ jobs:
|
||||
"os": [
|
||||
["self-hosted", "X64"],
|
||||
["self-hosted", "ARM64"],
|
||||
["macos-12"],
|
||||
["macos-13"],
|
||||
["windows-2019"],
|
||||
["macos-latest-xlarge"]
|
||||
]
|
||||
|
File diff suppressed because it is too large
Load Diff
435
CHANGELOG.md
435
CHANGELOG.md
@ -4,6 +4,441 @@ 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]
|
||||
|
||||
## 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
|
||||
|
||||
* Pin docker-modem and dockerode to avoid regression [Ken Bannister]
|
||||
|
||||
## 20.2.3 - 2025-01-15
|
||||
|
||||
* Remove unused old eslint version files [Otavio Jacobi]
|
||||
|
||||
## 20.2.2 - 2025-01-12
|
||||
|
||||
* Use the promises namespace of balena-image-fs [Thodoris Greasidis]
|
||||
|
||||
<details>
|
||||
<summary> Update balena-device-init to 8.1.3 & balena-image-fs to 7.3.0 [Thodoris Greasidis] </summary>
|
||||
|
||||
> ### balena-image-fs-7.3.0 - 2025-01-06
|
||||
>
|
||||
> * Drop Bluebird from devDependencies [Thodoris Greasidis]
|
||||
> * flowzone: Remove empty with clause [Thodoris Greasidis]
|
||||
> * Add the promises namespace as part of the exposed fs [Thodoris Greasidis]
|
||||
>
|
||||
> ### balena-image-fs-7.2.2 - 2024-01-02
|
||||
>
|
||||
> * Update dependency @types/node to v20 [Self-hosted Renovate Bot]
|
||||
>
|
||||
> ### balena-image-fs-7.2.1 - 2023-12-19
|
||||
>
|
||||
> * Remove repo config from flowzone.yml [Kyle Harding]
|
||||
>
|
||||
> ### balena-image-fs-7.2.0 - 2023-01-20
|
||||
>
|
||||
> * Add support for Node 18 [Akis Kesoglou]
|
||||
>
|
||||
> ### balena-image-fs-7.1.2 - 2023-01-05
|
||||
>
|
||||
> * Update dependencies [ab77]
|
||||
>
|
||||
> ### balena-image-fs-7.1.1 - 2022-12-20
|
||||
>
|
||||
> * Update dependency jsdoc-to-markdown to 8.0.0 [Renovate Bot]
|
||||
>
|
||||
> ### balena-image-fs-7.1.0 - 2022-12-13
|
||||
>
|
||||
> * update dependencies [Zane Hitchcox]
|
||||
>
|
||||
> ### balena-device-init-8.1.3 - 2025-01-09
|
||||
>
|
||||
> * README: Remove the travisci badge [Thodoris Greasidis]
|
||||
>
|
||||
> ### balena-device-init-8.1.2 - 2025-01-09
|
||||
>
|
||||
>
|
||||
> <details>
|
||||
> <summary> Use the promises namespace of balena-image-fs [Thodoris Greasidis] </summary>
|
||||
>
|
||||
>> #### balena-image-fs-7.3.0 - 2025-01-06
|
||||
>>
|
||||
>> * Drop Bluebird from devDependencies [Thodoris Greasidis]
|
||||
>> * flowzone: Remove empty with clause [Thodoris Greasidis]
|
||||
>> * Add the promises namespace as part of the exposed fs [Thodoris Greasidis]
|
||||
>>
|
||||
>> #### balena-image-fs-7.2.2 - 2024-01-02
|
||||
>>
|
||||
>> * Update dependency @types/node to v20 [Self-hosted Renovate Bot]
|
||||
>>
|
||||
>> #### balena-image-fs-7.2.1 - 2023-12-19
|
||||
>>
|
||||
>> * Remove repo config from flowzone.yml [Kyle Harding]
|
||||
>>
|
||||
>> #### balena-image-fs-7.2.0 - 2023-01-20
|
||||
>>
|
||||
>> * Add support for Node 18 [Akis Kesoglou]
|
||||
>>
|
||||
>> #### balena-image-fs-7.1.2 - 2023-01-05
|
||||
>>
|
||||
>> * Update dependencies [ab77]
|
||||
>>
|
||||
>> #### balena-image-fs-7.1.1 - 2022-12-20
|
||||
>>
|
||||
>> * Update dependency jsdoc-to-markdown to 8.0.0 [Renovate Bot]
|
||||
>>
|
||||
>> #### balena-image-fs-7.1.0 - 2022-12-13
|
||||
>>
|
||||
>> * update dependencies [Zane Hitchcox]
|
||||
>>
|
||||
>
|
||||
> </details>
|
||||
>
|
||||
>
|
||||
> ### balena-device-init-8.1.1 - 2025-01-06
|
||||
>
|
||||
> * Convert some parts to async await and simplify [Thodoris Greasidis]
|
||||
> * Avoid unnecessary destructuring [Thodoris Greasidis]
|
||||
>
|
||||
|
||||
</details>
|
||||
|
||||
## 20.2.1 - 2025-01-01
|
||||
|
||||
|
||||
<details>
|
||||
<summary> Update balena-preload to 17.0.0 [Thodoris Greasidis] </summary>
|
||||
|
||||
> ### balena-preload-17.0.0 - 2024-10-21
|
||||
>
|
||||
> * Improve typings [Thodoris Greasidis]
|
||||
> * Stop returning Bluebird promises & drop it from the dependencies [Thodoris Greasidis]
|
||||
>
|
||||
|
||||
</details>
|
||||
|
||||
## 20.2.0 - 2024-12-31
|
||||
|
||||
|
||||
<details>
|
||||
<summary> os configure: Give precedence to the boot partition located in the image over the device-type.json contents [Thodoris Greasidis] </summary>
|
||||
|
||||
> ### balena-device-init-8.1.0 - Invalid date
|
||||
>
|
||||
> * Try to find the boot partition by inspecting the image [Thodoris Greasidis]
|
||||
>
|
||||
> ### balena-device-init-8.0.1 - 2024-12-19
|
||||
>
|
||||
> * Drop the unnecessary eslint.config.js [Thodoris Greasidis]
|
||||
> * packacke.json: Explicitly set type commonjs [Thodoris Greasidis]
|
||||
>
|
||||
|
||||
</details>
|
||||
|
||||
## 20.1.6 - 2024-12-30
|
||||
|
||||
* Add more realistic os configure tests [Thodoris Greasidis]
|
||||
|
||||
## 20.1.5 - 2024-12-20
|
||||
|
||||
* Update shrinkwrapped express to v4.21.2 [Oskar Williams]
|
||||
|
||||
## 20.1.4 - 2024-12-20
|
||||
|
||||
|
||||
<details>
|
||||
<summary> Update balena-device-init to 8.0.0 [Thodoris Greasidis] </summary>
|
||||
|
||||
> ### balena-device-init-8.0.0 - 2024-12-18
|
||||
>
|
||||
> * Avoid running linting in the custom tests [Thodoris Greasidis]
|
||||
> * Stop returning Bluebird promises [Thodoris Greasidis]
|
||||
> * package: Publish only the build & typings folders [Thodoris Greasidis]
|
||||
> * Convert to TypeScript with es6 module notation [Thodoris Greasidis]
|
||||
> * Replace gulp, coffeelint & mochainon with tsc, @balena/lint, mocha, chai & sinon [Thodoris Greasidis]
|
||||
> * Drop support for node <20.6.0 [Thodoris Greasidis]
|
||||
>
|
||||
> ### balena-device-init-7.0.2 - 2024-12-17
|
||||
>
|
||||
> * flowzone: Update runner versions [Thodoris Greasidis]
|
||||
> * Pin etcher-sdk to 9.0.8 to match resin-device-operations and fix tests [Thodoris Greasidis]
|
||||
>
|
||||
|
||||
</details>
|
||||
|
||||
## 20.1.3 - 2024-12-20
|
||||
|
||||
* Update oclif to 4.17.0 and @oclif/core 4.1.0 [Otavio Jacobi]
|
||||
|
||||
## 20.1.2 - 2024-12-17
|
||||
|
||||
* Remove unnecessary `Promise.resolve` and `Promise.reject` [Pagan Gazzard]
|
||||
|
||||
## 20.1.1 - 2024-12-16
|
||||
|
||||
* Update @balena/lint to v9.1.3 [Otavio Jacobi]
|
||||
|
||||
## 20.1.0 - 2024-12-12
|
||||
|
||||
* `device os-update`: Add handling for updates that require takeover [myarmolinsky]
|
||||
* Update `balena-sdk` [myarmolinsky]
|
||||
* Update `@balena/compose` [myarmolinsky]
|
||||
|
||||
## 20.0.9 - 2024-12-05
|
||||
|
||||
* Update shrinkwrapped express to v4.21.1 [Oskar Williams]
|
||||
|
||||
## 20.0.8 - 2024-12-04
|
||||
|
||||
* Run test and publish with macos-13 [Otavio Jacobi]
|
||||
|
||||
## 20.0.7 - 2024-11-23
|
||||
|
||||
* Update TypeScript to 5.7.2 [Thodoris Greasidis]
|
||||
|
||||
## 20.0.6 - 2024-11-08
|
||||
|
||||
* Refactor balena build for clarity [Thodoris Greasidis]
|
||||
|
||||
## 20.0.5 - 2024-11-05
|
||||
|
||||
* Update actions/upload-artifact digest to b4b15b8 [balena-renovate[bot]]
|
||||
|
||||
## 20.0.4 - 2024-11-05
|
||||
|
||||
* Update actions/setup-node digest to 39370e3 [balena-renovate[bot]]
|
||||
|
||||
## 20.0.3 - 2024-11-05
|
||||
|
||||
* api-key generate: Display a descriptive error when the generation fails due to a stale JWT [Thodoris Greasidis]
|
||||
|
||||
## 20.0.2 - 2024-10-29
|
||||
|
||||
* Restore ability to cat key into `ssh-key add` [myarmolinsky]
|
||||
|
||||
## 20.0.1 - 2024-10-29
|
||||
|
||||
* Fix sending input to some aliases not working [myarmolinsky]
|
||||
|
||||
## 20.0.0 - 2024-10-25
|
||||
|
||||
* `device update`: Use detached HUP for os updates [jaomaloy]
|
||||
* Drop `-h` flag for help and stop manually adding `help` per command in favor of oclif automatically adding it [myarmolinsky]
|
||||
* Stop checking for very old, long-removed commands [myarmolinsky]
|
||||
* Deprecate `devices supported` command in favor of `device-type list` [myarmolinsky]
|
||||
* Deprecate `notes` command in favor of `device note` [myarmolinsky]
|
||||
* Deprecate `tunnel` command in favor of `device tunnel` [myarmolinsky]
|
||||
* Deprecate `env add` in favor of `env set` [myarmolinsky]
|
||||
* Deprecate `ssh` command in favor of `device ssh` [myarmolinsky]
|
||||
* Deprecate `logs` command in favor of `device logs` [myarmolinsky]
|
||||
* Deprecate `scan` command in favor of `device detect` [myarmolinsky]
|
||||
* Deprecate `orgs` command in favor of `organization list` [myarmolinsky]
|
||||
* Deprecate `tags` command in favor of `tag list` [myarmolinsky]
|
||||
* Deprecate `envs` command in favor of `env list` [myarmolinsky]
|
||||
* Deprecate `key` commands in favor of `ssh-key` [myarmolinsky]
|
||||
* Deprecate `keys` command in favor of `key list` [myarmolinsky]
|
||||
* Deprecate `releases` command in favor of `release list` [myarmolinsky]
|
||||
* Deprecate `fleets` command in favor of `fleet list` [myarmolinsky]
|
||||
* Deprecate `api-keys` command in favor of `api-key list` [myarmolinsky]
|
||||
* Deprecate `devices` command in favor of `device list` [myarmolinsky]
|
||||
* Docs: Show whether an alias is deprecated [myarmolinsky]
|
||||
* Update `balena-preload` to 16.0.0 [myarmolinsky]
|
||||
* Tests: Drop unused `my_application` resource mock [myarmolinsky]
|
||||
* Rename `device.overall_status` values: `IDLE` to `OPERATIONAL`, `OFFLINE` to `DISCONNECTED`; add `REDUCED_FUNCTIONALITY` [myarmolinsky]
|
||||
* Update `@balena/compose` to 5.0.0 [myarmolinsky]
|
||||
* Bump balena-sdk to 20.3.0 [myarmolinsky]
|
||||
|
||||
## 19.16.0 - 2024-10-23
|
||||
|
||||
* device-type list: Add `--all` flag for including no longer supported device types in the list [myarmolinsky]
|
||||
* Add alias `device-type list` for command `devices supported` [myarmolinsky]
|
||||
|
||||
## 19.15.0 - 2024-10-23
|
||||
|
||||
* Add alias `device note` for command `notes` [myarmolinsky]
|
||||
|
||||
## 19.14.0 - 2024-10-22
|
||||
|
||||
* Add alias `device tunnel` for command `tunnel` [myarmolinsky]
|
||||
|
||||
## 19.13.1 - 2024-10-21
|
||||
|
||||
* Update dependency chalk to v4 [Self-hosted Renovate Bot]
|
||||
|
||||
## 19.13.0 - 2024-10-21
|
||||
|
||||
* Add alias `env set` for command `env add` [myarmolinsky]
|
||||
|
||||
## 19.12.1 - 2024-10-21
|
||||
|
||||
* Update `@oclif/core` [myarmolinsky]
|
||||
|
||||
## 19.12.0 - 2024-10-21
|
||||
|
||||
* Add alias `device ssh` for `ssh` command [myarmolinsky]
|
||||
|
||||
## 19.11.1 - 2024-10-21
|
||||
|
||||
* Update dependency sinon to v19 [Self-hosted Renovate Bot]
|
||||
|
||||
## 19.11.0 - 2024-10-21
|
||||
|
||||
* Add alias `device logs` for `logs` [myarmolinsky]
|
||||
* git mv `logs/index` to `device/logs` [myarmolinsky]
|
||||
|
||||
## 19.10.0 - 2024-10-21
|
||||
|
||||
* Add alias `device detect` for `scan` [myarmolinsky]
|
||||
|
||||
## 19.9.0 - 2024-10-18
|
||||
|
||||
* Add alias `organization list` for `orgs` command [myarmolinsky]
|
||||
|
||||
## 19.8.0 - 2024-10-18
|
||||
|
||||
* Add alias `tag list` for `tags` command [myarmolinsky]
|
||||
|
||||
## 19.7.0 - 2024-10-18
|
||||
|
||||
* Add alias `env list` for command `envs` [myarmolinsky]
|
||||
|
||||
## 19.6.0 - 2024-10-18
|
||||
|
||||
* Add `ssh-key` as aliases for all `key` commands [myarmolinsky]
|
||||
|
||||
## 19.5.0 - 2024-10-17
|
||||
|
||||
* Add `key list` alias for `keys` command [myarmolinsky]
|
||||
|
||||
## 19.4.0 - 2024-10-16
|
||||
|
||||
* Add `release list` alias for `releases` command [myarmolinsky]
|
||||
|
||||
## 19.3.0 - 2024-10-16
|
||||
|
||||
* Add alias `fleet list` for `fleets` command [myarmolinsky]
|
||||
|
||||
## 19.2.0 - 2024-10-16
|
||||
|
||||
* Add alias `api-key list` for command `api-keys` [myarmolinsky]
|
||||
|
||||
## 19.1.3 - 2024-10-16
|
||||
|
||||
* Remove custom sorting of OS commands in docs in favor of alphabetizing [myarmolinsky]
|
||||
|
||||
## 19.1.2 - 2024-10-16
|
||||
|
||||
* Remove no longer needed dependency `get-stdin` [myarmolinsky]
|
||||
* Remove custom override of oclif Command class in favor of `prerun` hook [myarmolinsky]
|
||||
|
||||
## 19.1.1 - 2024-10-14
|
||||
|
||||
* Deduplicate dependencies [myarmolinsky]
|
||||
* Fix changelog entry for v19.1.0 [myarmolinsky]
|
||||
|
||||
## 19.1.0 - 2024-10-11
|
||||
|
||||
* Docs: Show aliases for commands [myarmolinsky]
|
||||
* Add alias `device list` for `devices` command [myarmolinsky]
|
||||
* Deduplicate dependencies [myarmolinsky]
|
||||
|
||||
## 19.0.20 - 2024-10-11
|
||||
|
||||
* Docs: Generate CLI command references from file names instead of usage [myarmolinsky]
|
||||
* Use default oclif `USAGE` message for all commands [myarmolinsky]
|
||||
|
||||
## 19.0.19 - 2024-10-11
|
||||
|
||||
* Deduplicate dependencies [myarmolinsky]
|
||||
* Fix update notification release notes link [myarmolinsky]
|
||||
|
||||
## 19.0.18 - 2024-10-08
|
||||
|
||||
* Contributing: No longer request separate folders for plural commands [myarmolinsky]
|
||||
|
||||
## 19.0.17 - 2024-10-08
|
||||
|
||||
* Remove dev dependency `parse-link-header` [myarmolinsky]
|
||||
* Remove dev dependency @octokit/rest [myarmolinsky]
|
||||
* Remove dev dependency @octokit/plugin-throttling [myarmolinsky]
|
||||
* Remove no longer needed references and tests for mixpanel [myarmolinsky]
|
||||
* Remove dev dependency `@types/mixpanel` [myarmolinsky]
|
||||
|
||||
## 19.0.16 - 2024-10-08
|
||||
|
||||
* compose: Reduce the properties updated to only the necessary [Thodoris Greasidis]
|
||||
|
||||
## 19.0.15 - 2024-10-08
|
||||
|
||||
* Remove unused `mockery` dev dependency [myarmolinsky]
|
||||
|
@ -133,7 +133,6 @@ To add a new command to be documented,
|
||||
1. Find the resource which it is part of or create a new one.
|
||||
2. List the location of the build file
|
||||
3. Make sure to add your files in alphabetical order
|
||||
4. Resources with plural names needs to have 2 sections if they have commands like: "fleet, fleets" or "device, devices" or "tag, tags"
|
||||
|
||||
Once added, run the command `npm run build` to generate the documentation
|
||||
|
||||
|
@ -145,7 +145,7 @@ container) in order to allow npm scripts like `postinstall` to be executed.
|
||||
|
||||
## Additional Dependencies
|
||||
|
||||
The `balena ssh`, `scan`, `build`, `deploy` and `preload` commands may require
|
||||
The `balena device ssh`, `device detect`, `build`, `deploy` and `preload` commands may require
|
||||
additional software to be installed. Check the Additional Dependencies sections for each operating
|
||||
system:
|
||||
|
||||
|
@ -33,7 +33,7 @@ as described above.
|
||||
|
||||
## sudo configuration
|
||||
|
||||
A few CLI commands require execution through sudo, e.g. `sudo balena scan`.
|
||||
A few CLI commands require execution through sudo, e.g. `sudo balena device detect`.
|
||||
If your Linux distribution has an `/etc/sudoers` file that defines a `secure_path`
|
||||
setting, run `sudo visudo` to edit it and add the balena CLI's installation folder to
|
||||
the ***pre-existing*** `secure_path` setting, for example:
|
||||
@ -61,19 +61,19 @@ instructions](https://docs.docker.com/install/overview/) to install Docker on th
|
||||
workstation as the balena CLI. The [advanced installation
|
||||
options](./INSTALL-ADVANCED.md#additional-dependencies) document describes other possibilities.
|
||||
|
||||
### balena ssh
|
||||
### balena device ssh
|
||||
|
||||
The `balena ssh` command requires the `ssh` command-line tool to be available. Most Linux
|
||||
The `balena device ssh` command requires the `ssh` command-line tool to be available. Most Linux
|
||||
distributions will already have it installed. Otherwise, `sudo apt-get install openssh-client`
|
||||
should do the trick on Debian or Ubuntu.
|
||||
|
||||
The `balena ssh` command also requires an SSH key to be added to your balena account: see [SSH
|
||||
The `balena device ssh` command also requires an SSH key to be added to your balena account: see [SSH
|
||||
Access documentation](https://www.balena.io/docs/learn/manage/ssh-access/). The `balena key*`
|
||||
command set can also be used to list and manage SSH keys: see `balena help -v`.
|
||||
|
||||
### balena scan
|
||||
### balena device detect
|
||||
|
||||
The `balena scan` command requires a multicast DNS (mDNS) service like
|
||||
The `balena device detect` command requires a multicast DNS (mDNS) service like
|
||||
[Avahi](https://en.wikipedia.org/wiki/Avahi_(software)), which is installed by default on most
|
||||
desktop Linux distributions. Otherwise, on Debian or Ubuntu, the installation command would be
|
||||
`sudo apt-get install avahi-daemon`.
|
||||
|
@ -19,7 +19,7 @@ Selected operating system: **macOS**
|
||||
- On the terminal prompt, type `balena version` and hit Enter. It should display
|
||||
the version of the balena CLI that you have installed.
|
||||
|
||||
No further steps are required to run most CLI commands. The `balena ssh`, `build`, `deploy`
|
||||
No further steps are required to run most CLI commands. The `balena device ssh`, `build`, `deploy`
|
||||
and `preload` commands may require additional software to be installed, as described
|
||||
in the next section.
|
||||
|
||||
@ -41,9 +41,9 @@ instructions](https://docs.docker.com/install/overview/) to install Docker on th
|
||||
workstation as the balena CLI. The [advanced installation
|
||||
options](./INSTALL-ADVANCED.md#additional-dependencies) document describes other possibilities.
|
||||
|
||||
### balena ssh
|
||||
### balena device ssh
|
||||
|
||||
The `balena ssh` command requires the `ssh` command-line tool to be available. To check whether
|
||||
The `balena device ssh` command requires the `ssh` command-line tool to be available. To check whether
|
||||
it is already installed, run `ssh` on a Terminal window. If it is not yet installed, the options
|
||||
include:
|
||||
|
||||
@ -52,7 +52,7 @@ include:
|
||||
Components → Command Line Tools → Install.
|
||||
* Or, install [Homebrew](https://brew.sh/), then `brew install openssh`
|
||||
|
||||
The `balena ssh` command also requires an SSH key to be added to your balena account: see [SSH
|
||||
The `balena device ssh` command also requires an SSH key to be added to your balena account: see [SSH
|
||||
Access documentation](https://www.balena.io/docs/learn/manage/ssh-access/). The `balena key*`
|
||||
command set can also be used to list and manage SSH keys: see `balena help -v`.
|
||||
|
||||
|
@ -19,7 +19,7 @@ Selected operating system: **Windows**
|
||||
- On the command prompt, type `balena version` and hit Enter. It should display
|
||||
the version of the balena CLI that you have installed.
|
||||
|
||||
No further steps are required to run most CLI commands. The `balena ssh`, `scan`, `build`,
|
||||
No further steps are required to run most CLI commands. The `balena device ssh`, `device detect`, `build`,
|
||||
`deploy` and `preload` commands may require additional software to be installed, as
|
||||
described below.
|
||||
|
||||
@ -34,9 +34,9 @@ instructions](https://docs.docker.com/install/overview/) to install Docker on th
|
||||
workstation as the balena CLI. The [advanced installation
|
||||
options](./INSTALL-ADVANCED.md#additional-dependencies) document describes other possibilities.
|
||||
|
||||
### balena ssh
|
||||
### balena device ssh
|
||||
|
||||
The `balena ssh` command requires the `ssh` command-line tool to be available. Microsoft started
|
||||
The `balena device ssh` command requires the `ssh` command-line tool to be available. Microsoft started
|
||||
distributing an SSH client with Windows 10, which is automatically installed through Windows
|
||||
Update. To check whether it is installed, run `ssh` on a Windows Command Prompt or PowerShell. It
|
||||
can also be [manually
|
||||
@ -44,13 +44,13 @@ installed](https://docs.microsoft.com/en-us/windows-server/administration/openss
|
||||
if needed. For older versions of Windows, there are several ssh/OpenSSH clients provided by 3rd
|
||||
parties.
|
||||
|
||||
The `balena ssh` command also requires an SSH key to be added to your balena account: see [SSH
|
||||
The `balena device ssh` command also requires an SSH key to be added to your balena account: see [SSH
|
||||
Access documentation](https://www.balena.io/docs/learn/manage/ssh-access/). The `balena key*`
|
||||
command set can also be used to list and manage SSH keys: see `balena help -v`.
|
||||
|
||||
### balena scan
|
||||
### balena device detect
|
||||
|
||||
The `balena scan` command requires a multicast DNS (mDNS) service like Apple's Bonjour.
|
||||
The `balena device detect` command requires a multicast DNS (mDNS) service like Apple's Bonjour.
|
||||
Many Windows machines will already have this service installed, as it is bundled in popular
|
||||
applications such as Skype (Wikipedia lists [several others](https://en.wikipedia.org/wiki/Bonjour_(software))).
|
||||
Otherwise, Bonjour for Windows can be downloaded and installed from: https://support.apple.com/kb/DL999
|
||||
|
@ -88,9 +88,9 @@ HTTP(S) proxies can be configured through any of the following methods, in prece
|
||||
* The `HTTPS_PROXY` and/or `HTTP_PROXY` environment variables, in the same URL format as
|
||||
`BALENARC_PROXY`.
|
||||
|
||||
### Proxy setup for balena ssh
|
||||
### Proxy setup for balena device ssh
|
||||
|
||||
In order to work behind a proxy server, the `balena ssh` command requires the
|
||||
In order to work behind a proxy server, the `balena device ssh` command requires the
|
||||
[`proxytunnel`](http://proxytunnel.sourceforge.net/) package (command-line tool) to be installed.
|
||||
`proxytunnel` is available for Linux distributions like Ubuntu/Debian (`apt install proxytunnel`),
|
||||
and for macOS through [Homebrew](https://brew.sh/). Windows support is limited to the [Windows
|
||||
@ -110,7 +110,7 @@ The `BALENARC_NO_PROXY` variable may be used to exclude specified destinations f
|
||||
> * This feature requires CLI version 11.30.8 or later. In the case of the npm [installation
|
||||
> option](https://github.com/balena-io/balena-cli/blob/master/INSTALL.md), it also requires
|
||||
> Node.js version 10.16.0 or later.
|
||||
> * To exclude a `balena ssh` target from proxying (IP address or `.local` hostname), the
|
||||
> * To exclude a `balena device ssh` target from proxying (IP address or `.local` hostname), the
|
||||
> `--noproxy` option should be specified in addition to the `BALENARC_NO_PROXY` variable.
|
||||
|
||||
By default (if `BALENARC_NO_PROXY` is not defined), all [private IPv4
|
||||
|
@ -31,7 +31,7 @@ command again.
|
||||
|
||||
Check whether the SD card is locked (a physical switch on the side of the card).
|
||||
|
||||
## I get `connect ETIMEDOUT` with `balena tunnel`
|
||||
## I get `connect ETIMEDOUT` with `balena device tunnel`
|
||||
|
||||
Please update the CLI to the latest version. This issue was fixed in v12.38.5.
|
||||
For more details, see: https://github.com/balena-io/balena-cli/issues/2172
|
||||
@ -79,10 +79,10 @@ Try resetting the ownership by running:
|
||||
$ sudo chown -R <user> $HOME/.balena
|
||||
```
|
||||
|
||||
## Broken line wrapping / cursor behavior with `balena ssh`
|
||||
## Broken line wrapping / cursor behavior with `balena device ssh`
|
||||
|
||||
Users sometimes come across broken line wrapping or cursor behavior in text terminals, for example
|
||||
when long command lines are typed in a `balena ssh` session, or when using text editors like `vim`
|
||||
when long command lines are typed in a `balena device ssh` session, or when using text editors like `vim`
|
||||
or `nano`. This is not something specific to the balena CLI, being also a commonly reported issue
|
||||
with standard remote terminal tools like `ssh` or `telnet`. It is often a remote shell
|
||||
configuration issue (files like `/etc/profile`, `~/.bash_profile`, `~/.bash_login`, `~/.profile`
|
||||
|
@ -305,7 +305,7 @@ async function zipPkg() {
|
||||
);
|
||||
}
|
||||
await fs.mkdirp(path.dirname(outputFile));
|
||||
await new Promise((resolve, reject) => {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
console.log(`Zipping standalone package to "${outputFile}"...`);
|
||||
|
||||
const archive = archiver('zip', {
|
||||
|
@ -36,9 +36,6 @@ import { GlobSync } from 'glob';
|
||||
* This dictionary is the source of truth that creates the docs config which is used
|
||||
* to generate the CLI documentation. By default, the folder name will be used.
|
||||
*
|
||||
* Resources with plural names needs to have 2 sections if they have commands like:
|
||||
* "fleet, fleets" or "device, devices" or "tag, tags"
|
||||
*
|
||||
*/
|
||||
|
||||
interface Category {
|
||||
@ -55,21 +52,15 @@ interface Documentation {
|
||||
// Mapping folders names to custom headings in the docs
|
||||
const commandHeadings: { [key: string]: string } = {
|
||||
'api-key': 'API Keys',
|
||||
'api-keys': 'API Keys',
|
||||
login: 'Authentication',
|
||||
whoami: 'Authentication',
|
||||
logout: 'Authentication',
|
||||
env: 'Environment Variables',
|
||||
envs: 'Environment Variables',
|
||||
help: 'Help and Version',
|
||||
key: 'SSH Keys',
|
||||
keys: 'SSH Keys',
|
||||
orgs: 'Organizations',
|
||||
'ssh-key': 'SSH Keys',
|
||||
organization: 'Organizations',
|
||||
os: 'OS',
|
||||
util: 'Utilities',
|
||||
ssh: 'Network',
|
||||
scan: 'Network',
|
||||
tunnel: 'Network',
|
||||
build: 'Deploy',
|
||||
join: 'Platform',
|
||||
leave: 'Platform',
|
||||
@ -154,7 +145,7 @@ export async function getCapitanoDoc(): Promise<typeof capitanoDoc> {
|
||||
throw new Error(`Error parsing section title`);
|
||||
}
|
||||
// match[1] has the title, match[2] has the rest
|
||||
return match && match[2];
|
||||
return match?.[2];
|
||||
}),
|
||||
mdParser.getSectionOfTitle('Installation'),
|
||||
mdParser.getSectionOfTitle('Choosing a shell (command prompt/terminal)'),
|
||||
|
2
automation/capitanodoc/doc-types.d.ts
vendored
2
automation/capitanodoc/doc-types.d.ts
vendored
@ -26,7 +26,7 @@ export interface Document {
|
||||
|
||||
export interface Category {
|
||||
title: string;
|
||||
commands: OclifCommand[];
|
||||
commands: Array<OclifCommand & { name: string }>;
|
||||
}
|
||||
|
||||
export { OclifCommand };
|
||||
|
@ -18,7 +18,6 @@ import * as path from 'path';
|
||||
import { getCapitanoDoc } from './capitanodoc';
|
||||
import type { Category, Document, OclifCommand } from './doc-types';
|
||||
import * as markdown from './markdown';
|
||||
import { stripIndent } from '../../src/utils/lazy';
|
||||
|
||||
/**
|
||||
* Generates the markdown document (as a string) for the CLI documentation
|
||||
@ -39,7 +38,7 @@ export async function renderMarkdown(): Promise<string> {
|
||||
};
|
||||
|
||||
for (const jsFilename of commandCategory.files) {
|
||||
category.commands.push(...(await importOclifCommands(jsFilename)));
|
||||
category.commands.push(await importOclifCommands(jsFilename));
|
||||
}
|
||||
result.categories.push(category);
|
||||
}
|
||||
@ -47,51 +46,23 @@ export async function renderMarkdown(): Promise<string> {
|
||||
return markdown.render(result);
|
||||
}
|
||||
|
||||
// Help is now managed via a plugin
|
||||
// This fake command allows capitanodoc to include help in docs
|
||||
class FakeHelpCommand {
|
||||
description = stripIndent`
|
||||
List balena commands, or get detailed help for a specific command.
|
||||
async function importOclifCommands(jsFilename: string) {
|
||||
const command = (await import(path.join(process.cwd(), jsFilename)))
|
||||
.default as OclifCommand;
|
||||
|
||||
List balena commands, or get detailed help for a specific command.
|
||||
`;
|
||||
|
||||
examples = [
|
||||
'$ balena help',
|
||||
'$ balena help login',
|
||||
'$ balena help os download',
|
||||
];
|
||||
|
||||
args = {
|
||||
command: {
|
||||
description: 'command to show help for',
|
||||
},
|
||||
};
|
||||
|
||||
usage = 'help [command]';
|
||||
|
||||
flags = {
|
||||
verbose: {
|
||||
description: 'show additional commands',
|
||||
char: '-v',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function importOclifCommands(
|
||||
jsFilename: string,
|
||||
): Promise<OclifCommand[]> {
|
||||
// TODO: Currently oclif commands with no `usage` overridden will cause
|
||||
// an error when parsed. This should be improved so that `usage` does not have
|
||||
// to be overridden if not necessary.
|
||||
|
||||
const command: OclifCommand =
|
||||
jsFilename === 'help'
|
||||
? (new FakeHelpCommand() as unknown as OclifCommand)
|
||||
: ((await import(path.join(process.cwd(), jsFilename)))
|
||||
.default as OclifCommand);
|
||||
|
||||
return [command];
|
||||
return {
|
||||
...command,
|
||||
// build/commands/device/index.js -> device
|
||||
// build/commands/device/list.js -> device list
|
||||
name: jsFilename
|
||||
.split('/')
|
||||
.slice(2)
|
||||
.join(' ')
|
||||
.split('.')
|
||||
.slice(0, 1)
|
||||
.join(' ')
|
||||
.split(' index')[0],
|
||||
} as Category['commands'][0];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -18,12 +18,27 @@ import { Parser } from '@oclif/core';
|
||||
import * as ent from 'ent';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { getManualSortCompareFunction } from '../../src/utils/helpers';
|
||||
import { capitanoizeOclifUsage } from '../../src/utils/oclif-utils';
|
||||
import type { Category, Document, OclifCommand } from './doc-types';
|
||||
import type { Category, Document } from './doc-types';
|
||||
|
||||
function renderOclifCommand(command: OclifCommand): string[] {
|
||||
const result = [`## ${ent.encode(command.usage || '')}`];
|
||||
function renderOclifCommand(command: Category['commands'][0]): string[] {
|
||||
const result = [`## ${ent.encode(command.name || '')}`];
|
||||
if (command.aliases?.length) {
|
||||
result.push('### Aliases');
|
||||
result.push(
|
||||
command.aliases
|
||||
.map(
|
||||
(alias) =>
|
||||
`- \`${alias}\`${command.deprecateAliases ? ' *(deprecated)*' : ''}`,
|
||||
)
|
||||
.join('\n'),
|
||||
);
|
||||
result.push(
|
||||
`\nTo use one of the aliases, replace \`${command.name}\` with the alias.`,
|
||||
);
|
||||
}
|
||||
|
||||
result.push('### Description');
|
||||
const description = (command.description || '')
|
||||
.split('\n')
|
||||
.slice(1) // remove the first line, which oclif uses as help header
|
||||
@ -80,7 +95,7 @@ function renderToc(categories: Category[]): string[] {
|
||||
result.push(
|
||||
category.commands
|
||||
.map((command) => {
|
||||
const signature = capitanoizeOclifUsage(command.usage);
|
||||
const signature = capitanoizeOclifUsage(command.name);
|
||||
return `\t- [${ent.encode(signature)}](${getAnchor(signature)})`;
|
||||
})
|
||||
.join('\n'),
|
||||
@ -89,33 +104,7 @@ function renderToc(categories: Category[]): string[] {
|
||||
return result;
|
||||
}
|
||||
|
||||
const manualCategorySorting: { [category: string]: string[] } = {
|
||||
'Environment Variables': ['envs', 'env rm', 'env add', 'env rename'],
|
||||
OS: [
|
||||
'os versions',
|
||||
'os download',
|
||||
'os build config',
|
||||
'os configure',
|
||||
'os initialize',
|
||||
],
|
||||
};
|
||||
|
||||
function sortCommands(doc: Document): void {
|
||||
for (const category of doc.categories) {
|
||||
if (category.title in manualCategorySorting) {
|
||||
category.commands = category.commands.sort(
|
||||
getManualSortCompareFunction<OclifCommand, string>(
|
||||
manualCategorySorting[category.title],
|
||||
(cmd: OclifCommand, x: string) =>
|
||||
(cmd.usage || '').toString().replace(/\W+/g, ' ').includes(x),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function render(doc: Document) {
|
||||
sortCommands(doc);
|
||||
const result = [
|
||||
`# ${doc.title}`,
|
||||
doc.introduction,
|
||||
|
@ -1,215 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as _ from 'lodash';
|
||||
import * as semver from 'semver';
|
||||
import { Octokit } from '@octokit/rest';
|
||||
import { throttling } from '@octokit/plugin-throttling';
|
||||
|
||||
const { GITHUB_TOKEN } = process.env;
|
||||
|
||||
/** Return a cached Octokit instance, creating a new one as needed. */
|
||||
const getOctokit = _.once(function () {
|
||||
const OctokitConstructor = Octokit.plugin(throttling);
|
||||
return new OctokitConstructor({
|
||||
auth: GITHUB_TOKEN,
|
||||
throttle: {
|
||||
onRateLimit: (retryAfter: number, options: any) => {
|
||||
console.warn(
|
||||
`Request quota exhausted for request ${options.method} ${options.url}`,
|
||||
);
|
||||
// retries 3 times
|
||||
if (options.request.retryCount < 3) {
|
||||
console.log(`Retrying after ${retryAfter} seconds!`);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
onAbuseLimit: (_retryAfter: number, options: any) => {
|
||||
// does not retry, only logs a warning
|
||||
console.warn(
|
||||
`Abuse detected for request ${options.method} ${options.url}`,
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Extract pagination information (current page, total pages, ordinal number)
|
||||
* from the 'link' response header (example below), using the parse-link-header
|
||||
* npm package:
|
||||
* "link": "<https://api.github.com/repositories/187370853/releases?per_page=2&page=2>; rel=\"next\",
|
||||
* <https://api.github.com/repositories/187370853/releases?per_page=2&page=3>; rel=\"last\""
|
||||
*
|
||||
* @param response Octokit response object (including response.headers.link)
|
||||
* @param perPageDefault Default per_page pagination value if missing in URL
|
||||
* @return Object where 'page' is the current page number (1-based),
|
||||
* 'pages' is the total number of pages, and 'ordinal' is the ordinal number
|
||||
* (3rd, 4th, 5th...) of the first item in the current page.
|
||||
*/
|
||||
async function getPageNumbers(
|
||||
response: any,
|
||||
perPageDefault: number,
|
||||
): Promise<{ page: number; pages: number; ordinal: number }> {
|
||||
const res = { page: 1, pages: 1, ordinal: 1 };
|
||||
if (!response.headers.link) {
|
||||
return res;
|
||||
}
|
||||
const parse = await import('parse-link-header');
|
||||
|
||||
const parsed = parse(response.headers.link);
|
||||
if (parsed == null) {
|
||||
throw new Error(`Failed to parse link header: '${response.headers.link}'`);
|
||||
}
|
||||
let perPage = perPageDefault;
|
||||
if (parsed.next) {
|
||||
if (parsed.next.per_page) {
|
||||
perPage = parseInt(parsed.next.per_page, 10);
|
||||
}
|
||||
res.page = parseInt(parsed.next.page!, 10) - 1;
|
||||
res.pages = parseInt(parsed.last!.page!, 10);
|
||||
} else {
|
||||
if (parsed.prev!.per_page) {
|
||||
perPage = parseInt(parsed.prev!.per_page, 10);
|
||||
}
|
||||
res.page = res.pages = parseInt(parsed.prev!.page!, 10) + 1;
|
||||
}
|
||||
res.ordinal = (res.page - 1) * perPage + 1;
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over every GitHub release in the given owner/repo, check whether
|
||||
* its tag_name matches against the affectedVersions semver spec, and if so
|
||||
* replace its release description (body) with the given newDescription value.
|
||||
* @param owner GitHub repo owner, e.g. 'balena-io' or 'pdcastro'
|
||||
* @param repo GitHub repo, e.g. 'balena-cli'
|
||||
* @param affectedVersions Semver spec, e.g. '2.6.1 - 7.10.9 || 8.0.0'
|
||||
* @param newDescription New release description (body)
|
||||
* @param editID Short string present in newDescription, e.g. '[AA101]', that
|
||||
* can be searched to determine whether that release has already been updated.
|
||||
*/
|
||||
async function updateGitHubReleaseDescriptions(
|
||||
owner: string,
|
||||
repo: string,
|
||||
affectedVersions: string,
|
||||
newDescription: string,
|
||||
editID: string,
|
||||
) {
|
||||
const perPage = 30;
|
||||
const octokit = getOctokit();
|
||||
const options = octokit.repos.listReleases.endpoint.merge({
|
||||
owner,
|
||||
repo,
|
||||
per_page: perPage,
|
||||
});
|
||||
let errCount = 0;
|
||||
type Release =
|
||||
import('@octokit/rest').RestEndpointMethodTypes['repos']['listReleases']['response']['data'][0];
|
||||
for await (const response of octokit.paginate.iterator<Release>(options)) {
|
||||
const {
|
||||
page: thisPage,
|
||||
pages: totalPages,
|
||||
ordinal,
|
||||
} = await getPageNumbers(response, perPage);
|
||||
let i = 0;
|
||||
for (const cliRelease of response.data) {
|
||||
const prefix = `[#${ordinal + i++} pg ${thisPage}/${totalPages}]`;
|
||||
if (!cliRelease.id) {
|
||||
console.error(
|
||||
`${prefix} Error: missing release ID (errCount=${++errCount})`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
const skipMsg = `${prefix} skipping release "${cliRelease.tag_name}" (${cliRelease.id})`;
|
||||
if (cliRelease.draft === true) {
|
||||
console.info(`${skipMsg}: draft release`);
|
||||
continue;
|
||||
} else if (cliRelease.body && cliRelease.body.includes(editID)) {
|
||||
console.info(`${skipMsg}: already updated`);
|
||||
continue;
|
||||
} else if (!semver.satisfies(cliRelease.tag_name, affectedVersions)) {
|
||||
console.info(`${skipMsg}: outside version range`);
|
||||
continue;
|
||||
} else {
|
||||
const updatedRelease = {
|
||||
owner,
|
||||
repo,
|
||||
release_id: cliRelease.id,
|
||||
body: newDescription,
|
||||
};
|
||||
let oldBodyPreview = cliRelease.body;
|
||||
if (oldBodyPreview) {
|
||||
oldBodyPreview = oldBodyPreview.replace(/\s+/g, ' ').trim();
|
||||
if (oldBodyPreview.length > 12) {
|
||||
oldBodyPreview = oldBodyPreview.substring(0, 9) + '...';
|
||||
}
|
||||
}
|
||||
console.info(
|
||||
`${prefix} updating release "${cliRelease.tag_name}" (${cliRelease.id}) old body="${oldBodyPreview}"`,
|
||||
);
|
||||
try {
|
||||
await octokit.repos.updateRelease(updatedRelease);
|
||||
} catch (err) {
|
||||
console.error(
|
||||
`${skipMsg}: Error: ${err.message} (count=${++errCount})`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a warning description to CLI releases affected by a mixpanel tracking
|
||||
* security issue (#1359). This function can be executed "manually" with the
|
||||
* following command line:
|
||||
*
|
||||
* npx ts-node --type-check -P automation/tsconfig.json automation/run.ts fix1359
|
||||
*/
|
||||
export async function updateDescriptionOfReleasesAffectedByIssue1359() {
|
||||
// Run only on Linux/Node10, instead of all platform/Node combinations.
|
||||
// (It could have been any other platform, as long as it only runs once.)
|
||||
if (process.platform !== 'linux' || semver.major(process.version) !== 10) {
|
||||
return;
|
||||
}
|
||||
const owner = 'balena-io';
|
||||
const repo = 'balena-cli';
|
||||
const affectedVersions =
|
||||
'2.6.1 - 7.10.9 || 8.0.0 - 8.1.0 || 9.0.0 - 9.15.6 || 10.0.0 - 10.17.5 || 11.0.0 - 11.7.2';
|
||||
const editID = '[AA100]';
|
||||
let newDescription = `
|
||||
Please note: the "login" command in this release is affected by a
|
||||
security issue fixed in versions
|
||||
[7.10.10](https://github.com/balena-io/balena-cli/releases/tag/v7.10.10),
|
||||
[8.1.1](https://github.com/balena-io/balena-cli/releases/tag/v8.1.1),
|
||||
[9.15.7](https://github.com/balena-io/balena-cli/releases/tag/v9.15.7),
|
||||
[10.17.6](https://github.com/balena-io/balena-cli/releases/tag/v10.17.6),
|
||||
[11.7.3](https://github.com/balena-io/balena-cli/releases/tag/v11.7.3)
|
||||
and later. If you need to use this version, avoid passing your password,
|
||||
keys or tokens as command-line arguments. ${editID}`;
|
||||
// remove line breaks and collapse white space
|
||||
newDescription = newDescription.replace(/\s+/g, ' ').trim();
|
||||
await updateGitHubReleaseDescriptions(
|
||||
owner,
|
||||
repo,
|
||||
affectedVersions,
|
||||
newDescription,
|
||||
editID,
|
||||
);
|
||||
}
|
@ -24,7 +24,6 @@ import {
|
||||
signFilesForNotarization,
|
||||
testShrinkwrap,
|
||||
} from './build-bin';
|
||||
import { updateDescriptionOfReleasesAffectedByIssue1359 } from './deploy-bin';
|
||||
|
||||
// DEBUG set to falsy for negative values else is truthy
|
||||
process.env.DEBUG = ['0', 'no', 'false', '', undefined].includes(
|
||||
@ -54,7 +53,6 @@ async function parse(args?: string[]) {
|
||||
'sign:binaries': signFilesForNotarization,
|
||||
'catch-uncommitted': catchUncommitted,
|
||||
'test-shrinkwrap': testShrinkwrap,
|
||||
fix1359: updateDescriptionOfReleasesAffectedByIssue1359,
|
||||
};
|
||||
for (const arg of args) {
|
||||
if (!Object.hasOwn(commands, arg)) {
|
||||
|
@ -3,7 +3,7 @@ import * as semver from 'semver';
|
||||
|
||||
const changeTypes = ['major', 'minor', 'patch'] as const;
|
||||
|
||||
const validateChangeType = (maybeChangeType: string = 'minor') => {
|
||||
const validateChangeType = (maybeChangeType = 'minor') => {
|
||||
maybeChangeType = maybeChangeType.toLowerCase();
|
||||
switch (maybeChangeType) {
|
||||
case 'patch':
|
||||
@ -136,5 +136,4 @@ async function main() {
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
main();
|
||||
void main();
|
||||
|
@ -19,6 +19,7 @@ import { spawn } from 'child_process';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { diffTrimmedLines } from 'diff';
|
||||
import * as whichMod from 'which';
|
||||
|
||||
export const ROOT = path.join(__dirname, '..');
|
||||
|
||||
@ -101,7 +102,6 @@ export function loadPackageJson() {
|
||||
* @returns The program's full path, e.g. 'C:\WINDOWS\System32\OpenSSH\ssh.EXE'
|
||||
*/
|
||||
export async function which(program: string): Promise<string> {
|
||||
const whichMod = await import('which');
|
||||
let programPath: string;
|
||||
try {
|
||||
programPath = await whichMod(program);
|
||||
@ -132,7 +132,7 @@ export async function whichSpawn(
|
||||
.on('error', reject)
|
||||
.on('close', resolve);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
reject(err as Error);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
|
@ -57,7 +57,10 @@ require('ts-node').register({
|
||||
project: path.join(rootDir, 'tsconfig.json'),
|
||||
transpileOnly: true,
|
||||
});
|
||||
require('../src/app').run(undefined, { dir: __dirname, development: true });
|
||||
void require('../src/app').run(undefined, {
|
||||
dir: __dirname,
|
||||
development: true,
|
||||
});
|
||||
|
||||
// Modify package.json oclif paths from build/ -> src/, or vice versa
|
||||
function modifyOclifPaths(revert) {
|
||||
|
@ -5,7 +5,7 @@
|
||||
process.env.UV_THREADPOOL_SIZE = '64';
|
||||
|
||||
// Disable oclif registering ts-node
|
||||
process.env.OCLIF_TS_NODE = 0;
|
||||
process.env.OCLIF_TS_NODE = '0';
|
||||
|
||||
async function run() {
|
||||
// Use fast-boot to cache require lookups, speeding up startup
|
||||
@ -18,4 +18,4 @@ async function run() {
|
||||
await require('../build/app').run(undefined, { dir: __dirname });
|
||||
}
|
||||
|
||||
run();
|
||||
void run();
|
||||
|
@ -8,27 +8,28 @@ _balena() {
|
||||
local context state line curcontext="$curcontext"
|
||||
|
||||
# Valid top-level completions
|
||||
main_commands=( api-key api-keys app block build config deploy device devices env envs fleet fleets internal join key keys leave local login logout logs notes orgs os preload push release releases scan settings ssh support tag tags tunnel util version whoami )
|
||||
main_commands=( api-key app block build config deploy device device-type env fleet internal join leave local login logout organization os preload push release settings ssh-key support tag util version whoami )
|
||||
# Sub-completions
|
||||
api_key_cmds=( generate revoke )
|
||||
api_key_cmds=( generate list revoke )
|
||||
app_cmds=( create )
|
||||
block_cmds=( create )
|
||||
config_cmds=( generate inject read reconfigure write )
|
||||
device_cmds=( deactivate identify init local-mode move os-update pin public-url purge reboot register rename restart rm shutdown start-service stop-service track-fleet )
|
||||
devices_cmds=( supported )
|
||||
env_cmds=( add rename rm )
|
||||
fleet_cmds=( create pin purge rename restart rm track-latest )
|
||||
device_type_cmds=( list )
|
||||
device_cmds=( deactivate detect identify init list local-mode logs move note os-update pin public-url purge reboot register rename restart rm shutdown ssh start-service stop-service track-fleet tunnel )
|
||||
env_cmds=( list rename rm set )
|
||||
fleet_cmds=( create list pin purge rename restart rm track-latest )
|
||||
internal_cmds=( osinit )
|
||||
key_cmds=( add rm )
|
||||
local_cmds=( configure flash )
|
||||
organization_cmds=( list )
|
||||
os_cmds=( build-config configure download initialize versions )
|
||||
release_cmds=( finalize invalidate validate )
|
||||
tag_cmds=( rm set )
|
||||
release_cmds=( finalize invalidate list validate )
|
||||
ssh_key_cmds=( add list rm )
|
||||
tag_cmds=( list rm set )
|
||||
|
||||
|
||||
_arguments -C \
|
||||
'(- 1 *)--version[show version and exit]' \
|
||||
'(- 1 *)'{-h,--help}'[show help options and exit]' \
|
||||
'(- 1 *)--help[show help options and exit]' \
|
||||
'1:first command:_balena_main_cmds' \
|
||||
'2:second command:_balena_sec_cmds' \
|
||||
&& ret=0
|
||||
@ -54,12 +55,12 @@ _balena_sec_cmds() {
|
||||
"config")
|
||||
_describe -t config_cmds 'config_cmd' config_cmds "$@" && ret=0
|
||||
;;
|
||||
"device-type")
|
||||
_describe -t device_type_cmds 'device-type_cmd' device_type_cmds "$@" && ret=0
|
||||
;;
|
||||
"device")
|
||||
_describe -t device_cmds 'device_cmd' device_cmds "$@" && ret=0
|
||||
;;
|
||||
"devices")
|
||||
_describe -t devices_cmds 'devices_cmd' devices_cmds "$@" && ret=0
|
||||
;;
|
||||
"env")
|
||||
_describe -t env_cmds 'env_cmd' env_cmds "$@" && ret=0
|
||||
;;
|
||||
@ -69,18 +70,21 @@ _balena_sec_cmds() {
|
||||
"internal")
|
||||
_describe -t internal_cmds 'internal_cmd' internal_cmds "$@" && ret=0
|
||||
;;
|
||||
"key")
|
||||
_describe -t key_cmds 'key_cmd' key_cmds "$@" && ret=0
|
||||
;;
|
||||
"local")
|
||||
_describe -t local_cmds 'local_cmd' local_cmds "$@" && ret=0
|
||||
;;
|
||||
"organization")
|
||||
_describe -t organization_cmds 'organization_cmd' organization_cmds "$@" && ret=0
|
||||
;;
|
||||
"os")
|
||||
_describe -t os_cmds 'os_cmd' os_cmds "$@" && ret=0
|
||||
;;
|
||||
"release")
|
||||
_describe -t release_cmds 'release_cmd' release_cmds "$@" && ret=0
|
||||
;;
|
||||
"ssh-key")
|
||||
_describe -t ssh_key_cmds 'ssh-key_cmd' ssh_key_cmds "$@" && ret=0
|
||||
;;
|
||||
"tag")
|
||||
_describe -t tag_cmds 'tag_cmd' tag_cmds "$@" && ret=0
|
||||
;;
|
||||
|
@ -7,22 +7,23 @@ _balena_complete()
|
||||
local cur prev
|
||||
|
||||
# Valid top-level completions
|
||||
main_commands="api-key api-keys app block build config deploy device devices env envs fleet fleets internal join key keys leave local login logout logs notes orgs os preload push release releases scan settings ssh support tag tags tunnel util version whoami"
|
||||
main_commands="api-key app block build config deploy device device-type env fleet internal join leave local login logout organization os preload push release settings ssh-key support tag util version whoami"
|
||||
# Sub-completions
|
||||
api_key_cmds="generate revoke"
|
||||
api_key_cmds="generate list revoke"
|
||||
app_cmds="create"
|
||||
block_cmds="create"
|
||||
config_cmds="generate inject read reconfigure write"
|
||||
device_cmds="deactivate identify init local-mode move os-update pin public-url purge reboot register rename restart rm shutdown start-service stop-service track-fleet"
|
||||
devices_cmds="supported"
|
||||
env_cmds="add rename rm"
|
||||
fleet_cmds="create pin purge rename restart rm track-latest"
|
||||
device_type_cmds="list"
|
||||
device_cmds="deactivate detect identify init list local-mode logs move note os-update pin public-url purge reboot register rename restart rm shutdown ssh start-service stop-service track-fleet tunnel"
|
||||
env_cmds="list rename rm set"
|
||||
fleet_cmds="create list pin purge rename restart rm track-latest"
|
||||
internal_cmds="osinit"
|
||||
key_cmds="add rm"
|
||||
local_cmds="configure flash"
|
||||
organization_cmds="list"
|
||||
os_cmds="build-config configure download initialize versions"
|
||||
release_cmds="finalize invalidate validate"
|
||||
tag_cmds="rm set"
|
||||
release_cmds="finalize invalidate list validate"
|
||||
ssh_key_cmds="add list rm"
|
||||
tag_cmds="list rm set"
|
||||
|
||||
|
||||
|
||||
@ -48,12 +49,12 @@ _balena_complete()
|
||||
config)
|
||||
COMPREPLY=( $(compgen -W "$config_cmds" -- $cur) )
|
||||
;;
|
||||
device-type)
|
||||
COMPREPLY=( $(compgen -W "$device_type_cmds" -- $cur) )
|
||||
;;
|
||||
device)
|
||||
COMPREPLY=( $(compgen -W "$device_cmds" -- $cur) )
|
||||
;;
|
||||
devices)
|
||||
COMPREPLY=( $(compgen -W "$devices_cmds" -- $cur) )
|
||||
;;
|
||||
env)
|
||||
COMPREPLY=( $(compgen -W "$env_cmds" -- $cur) )
|
||||
;;
|
||||
@ -63,18 +64,21 @@ _balena_complete()
|
||||
internal)
|
||||
COMPREPLY=( $(compgen -W "$internal_cmds" -- $cur) )
|
||||
;;
|
||||
key)
|
||||
COMPREPLY=( $(compgen -W "$key_cmds" -- $cur) )
|
||||
;;
|
||||
local)
|
||||
COMPREPLY=( $(compgen -W "$local_cmds" -- $cur) )
|
||||
;;
|
||||
organization)
|
||||
COMPREPLY=( $(compgen -W "$organization_cmds" -- $cur) )
|
||||
;;
|
||||
os)
|
||||
COMPREPLY=( $(compgen -W "$os_cmds" -- $cur) )
|
||||
;;
|
||||
release)
|
||||
COMPREPLY=( $(compgen -W "$release_cmds" -- $cur) )
|
||||
;;
|
||||
ssh-key)
|
||||
COMPREPLY=( $(compgen -W "$ssh_key_cmds" -- $cur) )
|
||||
;;
|
||||
tag)
|
||||
COMPREPLY=( $(compgen -W "$tag_cmds" -- $cur) )
|
||||
;;
|
||||
|
@ -14,7 +14,7 @@ $sub_cmds$
|
||||
|
||||
_arguments -C \
|
||||
'(- 1 *)--version[show version and exit]' \
|
||||
'(- 1 *)'{-h,--help}'[show help options and exit]' \
|
||||
'(- 1 *)--help[show help options and exit]' \
|
||||
'1:first command:_balena_main_cmds' \
|
||||
'2:second command:_balena_sec_cmds' \
|
||||
&& ret=0
|
||||
|
1806
docs/balena-cli.md
1806
docs/balena-cli.md
File diff suppressed because it is too large
Load Diff
32
eslint.config.js
Normal file
32
eslint.config.js
Normal file
@ -0,0 +1,32 @@
|
||||
const { FlatCompat } = require('@eslint/eslintrc');
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
});
|
||||
module.exports = [
|
||||
...require('@balena/lint/config/eslint.config'),
|
||||
...compat.config({
|
||||
parserOptions: {
|
||||
project: 'tsconfig.dev.json',
|
||||
},
|
||||
ignorePatterns: ['**/generate-completion.js', '**/bin/**/*'],
|
||||
rules: {
|
||||
ignoreDefinitionFiles: 0,
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/no-shadow': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'@typescript-eslint/no-require-imports': 'off',
|
||||
'@typescript-eslint/no-unnecessary-type-assertion': 'off',
|
||||
'@typescript-eslint/prefer-nullish-coalescing': 'warn',
|
||||
|
||||
'no-restricted-imports': ['error', {
|
||||
paths: ['resin-cli-visuals', 'chalk', 'common-tags', 'resin-cli-form'],
|
||||
}],
|
||||
|
||||
'@typescript-eslint/no-unused-vars': ['error', {
|
||||
argsIgnorePattern: '^_',
|
||||
caughtErrorsIgnorePattern: '^_',
|
||||
}],
|
||||
},
|
||||
}),
|
||||
];
|
6847
npm-shrinkwrap.json
generated
6847
npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
43
package.json
43
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "balena-cli",
|
||||
"version": "19.0.15",
|
||||
"version": "21.1.6",
|
||||
"description": "The official balena Command Line Interface",
|
||||
"main": "./build/app.js",
|
||||
"homepage": "https://github.com/balena-io/balena-cli",
|
||||
@ -58,6 +58,7 @@
|
||||
"build:completion": "node completion/generate-completion.js",
|
||||
"build:standalone": "ts-node --transpile-only automation/run.ts build:standalone",
|
||||
"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",
|
||||
"pretest": "npm run build",
|
||||
"test": "npm run test:shrinkwrap && npm run test:core",
|
||||
@ -99,7 +100,7 @@
|
||||
"helpClass": "./build/help",
|
||||
"topicSeparator": " ",
|
||||
"hooks": {
|
||||
"prerun": "./build/hooks/prerun/track",
|
||||
"prerun": "./build/hooks/prerun",
|
||||
"command_not_found": "./build/hooks/command-not-found/suggest"
|
||||
},
|
||||
"additionalHelpFlags": [
|
||||
@ -111,10 +112,8 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@balena/lint": "^8.0.0",
|
||||
"@balena/lint": "^9.1.3",
|
||||
"@electron/notarize": "^2.0.0",
|
||||
"@octokit/plugin-throttling": "^3.5.1",
|
||||
"@octokit/rest": "^18.6.7",
|
||||
"@types/archiver": "^6.0.2",
|
||||
"@types/bluebird": "^3.5.36",
|
||||
"@types/body-parser": "^1.19.2",
|
||||
@ -139,14 +138,12 @@
|
||||
"@types/klaw": "^3.0.6",
|
||||
"@types/lodash": "^4.14.178",
|
||||
"@types/mime": "^3.0.4",
|
||||
"@types/mixpanel": "^2.14.3",
|
||||
"@types/mocha": "^10.0.7",
|
||||
"@types/mock-fs": "^4.13.4",
|
||||
"@types/mock-require": "^2.0.1",
|
||||
"@types/ndjson": "^2.0.1",
|
||||
"@types/node": "^20.0.0",
|
||||
"@types/node-cleanup": "^2.1.2",
|
||||
"@types/parse-link-header": "^2.0.3",
|
||||
"@types/prettyjson": "^0.0.33",
|
||||
"@types/progress-stream": "^2.0.2",
|
||||
"@types/request": "^2.48.7",
|
||||
@ -184,49 +181,48 @@
|
||||
"mock-fs": "^5.2.0",
|
||||
"mock-require": "^3.0.3",
|
||||
"nock": "^13.2.1",
|
||||
"oclif": "^4.14.0",
|
||||
"parse-link-header": "^2.0.0",
|
||||
"oclif": "^4.17.0",
|
||||
"rewire": "^7.0.0",
|
||||
"simple-git": "^3.14.1",
|
||||
"sinon": "^18.0.0",
|
||||
"sinon": "^19.0.0",
|
||||
"string-to-stream": "^3.0.1",
|
||||
"ts-node": "^10.4.0",
|
||||
"typescript": "^5.6.2"
|
||||
"typescript": "^5.8.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@balena/compose": "^4.0.1",
|
||||
"@balena/compose": "^7.0.1",
|
||||
"@balena/dockerignore": "^1.0.2",
|
||||
"@balena/env-parsing": "^1.1.8",
|
||||
"@balena/es-version": "^1.0.1",
|
||||
"@oclif/core": "^4.0.8",
|
||||
"@oclif/core": "^4.1.0",
|
||||
"@sentry/node": "^6.16.1",
|
||||
"balena-config-json": "^4.2.0",
|
||||
"balena-device-init": "^7.0.1",
|
||||
"balena-config-json": "^4.2.2",
|
||||
"balena-device-init": "^8.1.3",
|
||||
"balena-errors": "^4.7.3",
|
||||
"balena-image-fs": "^7.0.6",
|
||||
"balena-preload": "^15.0.6",
|
||||
"balena-sdk": "^19.7.3",
|
||||
"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",
|
||||
"body-parser": "^1.19.1",
|
||||
"bonjour-service": "^1.2.1",
|
||||
"chalk": "^3.0.0",
|
||||
"chalk": "^4.0.0",
|
||||
"chokidar": "^3.5.2",
|
||||
"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.3",
|
||||
"docker-modem": "^5.0.6",
|
||||
"docker-progress": "^5.1.3",
|
||||
"dockerode": "^4.0.2",
|
||||
"dockerode": "^4.0.5",
|
||||
"ejs": "^3.1.6",
|
||||
"etcher-sdk": "9.1.0",
|
||||
"express": "^4.17.2",
|
||||
"fast-boot2": "^1.1.0",
|
||||
"fast-levenshtein": "^3.0.0",
|
||||
"filenamify": "^4.3.0",
|
||||
"get-stdin": "^8.0.0",
|
||||
"glob": "^7.2.0",
|
||||
"global-agent": "^2.2.0",
|
||||
"global-tunnel-ng": "^2.1.1",
|
||||
@ -237,6 +233,7 @@
|
||||
"is-root": "^2.1.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"JSONStream": "^1.0.3",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"livepush": "^3.5.1",
|
||||
"lodash": "^4.17.21",
|
||||
"mime": "^2.4.6",
|
||||
@ -279,6 +276,6 @@
|
||||
}
|
||||
},
|
||||
"versionist": {
|
||||
"publishedAt": "2024-10-08T13:11:02.162Z"
|
||||
"publishedAt": "2025-04-03T14:12:06.976Z"
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
diff --git a/node_modules/@oclif/core/lib/help/command.js b/node_modules/@oclif/core/lib/help/command.js
|
||||
index 90922c8..6b7f417 100644
|
||||
index 33105a0..0436982 100644
|
||||
--- a/node_modules/@oclif/core/lib/help/command.js
|
||||
+++ b/node_modules/@oclif/core/lib/help/command.js
|
||||
@@ -58,7 +58,8 @@ class CommandHelp extends formatter_1.HelpFormatter {
|
||||
@ -13,10 +13,10 @@ index 90922c8..6b7f417 100644
|
||||
if (a.default)
|
||||
description = `${(0, theme_1.colorize)(this.config?.theme?.flagDefaultValue, `[default: ${a.default}]`)} ${description}`;
|
||||
diff --git a/node_modules/@oclif/core/lib/help/index.js b/node_modules/@oclif/core/lib/help/index.js
|
||||
index 4a34b89..d7eb6ac 100644
|
||||
index 0b48c0e..ff4fed4 100644
|
||||
--- a/node_modules/@oclif/core/lib/help/index.js
|
||||
+++ b/node_modules/@oclif/core/lib/help/index.js
|
||||
@@ -172,11 +172,12 @@ class Help extends HelpBase {
|
||||
@@ -173,11 +173,12 @@ class Help extends HelpBase {
|
||||
}
|
||||
this.log(this.formatCommand(command));
|
||||
this.log('');
|
@ -1,8 +1,8 @@
|
||||
diff --git a/node_modules/oclif/lib/commands/pack/win.js b/node_modules/oclif/lib/commands/pack/win.js
|
||||
index ef7f90e..8264b7c 100644
|
||||
index bfe9205..482519e 100644
|
||||
--- a/node_modules/oclif/lib/commands/pack/win.js
|
||||
+++ b/node_modules/oclif/lib/commands/pack/win.js
|
||||
@@ -76,6 +76,12 @@ InstallDir "\$PROGRAMFILES${arch === 'x64' ? '64' : ''}\\${config.dirname}"
|
||||
@@ -86,6 +86,12 @@ InstallDir "\$PROGRAMFILES${arch === 'x64' ? '64' : ''}\\${config.dirname}"
|
||||
${customization}
|
||||
|
||||
Section "${config.name} CLI \${VERSION}"
|
||||
@ -16,20 +16,18 @@ index ef7f90e..8264b7c 100644
|
||||
File /r bin
|
||||
File /r client
|
||||
diff --git a/node_modules/oclif/lib/tarballs/build.js b/node_modules/oclif/lib/tarballs/build.js
|
||||
index 14d5a6e..7b42a6f 100644
|
||||
index f0c8d95..a72400e 100644
|
||||
--- a/node_modules/oclif/lib/tarballs/build.js
|
||||
+++ b/node_modules/oclif/lib/tarballs/build.js
|
||||
@@ -200,6 +200,13 @@ const extractCLI = async (tarball, c) => {
|
||||
@@ -218,6 +218,11 @@ const extractCLI = async (tarball, c) => {
|
||||
(0, promises_1.rm)(path.join(workspace, path.basename(tarball)), { recursive: true }),
|
||||
(0, fs_extra_1.remove)(path.join(workspace, 'bin', 'run.cmd')),
|
||||
]);
|
||||
+
|
||||
+ // The oclif installers are a production installation, while the source
|
||||
+ // `bin` folder may contain a `.fast-boot.json` file of a dev installation.
|
||||
+ // This has previously led to issues preventing the CLI from starting, so
|
||||
+ // delete `.fast-boot.json` (if any) from the destination folder.
|
||||
+ await (0, fs_extra_1.remove)(path.join(workspace, 'bin', '.fast-boot.json'));
|
||||
+
|
||||
};
|
||||
const buildTarget = async (target, c, options) => {
|
||||
const workspace = c.workspace(target);
|
||||
if (target.platform === 'win32' && target.arch === 'arm64' && (0, semver_1.lt)(c.nodeVersion, '20.0.0')) {
|
4
repo.yml
4
repo.yml
@ -6,6 +6,10 @@ upstream:
|
||||
url: 'https://github.com/balena-io/balena-sdk'
|
||||
- repo: 'balena-config-json'
|
||||
url: 'https://github.com/balena-io-modules/balena-config-json'
|
||||
- repo: 'balena-image-fs'
|
||||
url: 'https://github.com/balena-io-modules/balena-image-fs'
|
||||
- repo: 'balena-device-init'
|
||||
url: 'https://github.com/balena-io-modules/balena-device-init'
|
||||
- repo: 'balena-image-manager'
|
||||
url: 'https://github.com/balena-io-modules/balena-image-manager'
|
||||
- repo: 'balena-preload'
|
||||
|
@ -101,11 +101,9 @@ async function init() {
|
||||
|
||||
/** Execute the oclif parser and the CLI command. */
|
||||
async function oclifRun(command: string[], options: AppOptions) {
|
||||
let deprecationPromise: Promise<void>;
|
||||
let deprecationPromise: Promise<void> | undefined;
|
||||
// check and enforce the CLI's deprecation policy
|
||||
if (unsupportedFlag || process.env.BALENARC_UNSUPPORTED) {
|
||||
deprecationPromise = Promise.resolve();
|
||||
} else {
|
||||
if (!(unsupportedFlag || process.env.BALENARC_UNSUPPORTED)) {
|
||||
const { DeprecationChecker } = await import('./deprecation');
|
||||
const deprecationChecker = new DeprecationChecker(packageJSON.version);
|
||||
// warnAndAbortIfDeprecated uses previously cached data only
|
||||
@ -153,7 +151,7 @@ async function oclifRun(command: string[], options: AppOptions) {
|
||||
}
|
||||
})(!options.noFlush);
|
||||
|
||||
const { trackPromise } = await import('./hooks/prerun/track');
|
||||
const { trackPromise } = await import('./hooks/prerun');
|
||||
|
||||
await Promise.all([trackPromise, deprecationPromise, runPromise]);
|
||||
}
|
||||
|
170
src/command.ts
170
src/command.ts
@ -1,170 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
import { Command } from '@oclif/core';
|
||||
import {
|
||||
InsufficientPrivilegesError,
|
||||
NotAvailableInOfflineModeError,
|
||||
} from './errors';
|
||||
import { stripIndent } from './utils/lazy';
|
||||
|
||||
export default abstract class BalenaCommand extends Command {
|
||||
/**
|
||||
* When set to true, command will be listed in `help`,
|
||||
* otherwise listed in `help --verbose` with secondary commands.
|
||||
*/
|
||||
public static primary = false;
|
||||
|
||||
/**
|
||||
* Require elevated privileges to run.
|
||||
* When set to true, command will exit with an error
|
||||
* if executed without root on Mac/Linux
|
||||
* or if executed by non-Administrator on Windows.
|
||||
*/
|
||||
public static root = false;
|
||||
|
||||
/**
|
||||
* Require authentication to run.
|
||||
* When set to true, command will exit with an error
|
||||
* if user is not already logged in.
|
||||
*/
|
||||
public static authenticated = false;
|
||||
|
||||
/**
|
||||
* Require an internet connection to run.
|
||||
* When set to true, command will exit with an error
|
||||
* if user is running in offline mode (BALENARC_OFFLINE_MODE).
|
||||
*/
|
||||
public static offlineCompatible = false;
|
||||
|
||||
/**
|
||||
* Accept piped input.
|
||||
* When set to true, command will read from stdin during init
|
||||
* and make contents available on member `stdin`.
|
||||
*/
|
||||
public static readStdin = false;
|
||||
|
||||
public stdin: string;
|
||||
|
||||
/**
|
||||
* Throw InsufficientPrivilegesError if not root on Mac/Linux
|
||||
* or non-Administrator on Windows.
|
||||
*
|
||||
* Called automatically if `root=true`.
|
||||
* Can be called explicitly by command implementation, if e.g.:
|
||||
* - check should only be done conditionally
|
||||
* - other code needs to execute before check
|
||||
*/
|
||||
protected static async checkElevatedPrivileges() {
|
||||
const isElevated = await (await import('is-elevated'))();
|
||||
if (!isElevated) {
|
||||
throw new InsufficientPrivilegesError(
|
||||
'You need root/admin privileges to run this command',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw NotLoggedInError if not logged in.
|
||||
*
|
||||
* Called automatically if `authenticated=true`.
|
||||
* Can be called explicitly by command implementation, if e.g.:
|
||||
* - check should only be done conditionally
|
||||
* - other code needs to execute before check
|
||||
*
|
||||
* Note, currently public to allow use outside of derived commands
|
||||
* (as some command implementations require this. Can be made protected
|
||||
* if this changes).
|
||||
*
|
||||
* @throws {NotLoggedInError}
|
||||
*/
|
||||
public static async checkLoggedIn() {
|
||||
await (await import('./utils/patterns')).checkLoggedIn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw NotLoggedInError if not logged in when condition true.
|
||||
*
|
||||
* @param {boolean} doCheck - will check if true.
|
||||
* @throws {NotLoggedInError}
|
||||
*/
|
||||
public static async checkLoggedInIf(doCheck: boolean) {
|
||||
if (doCheck) {
|
||||
await this.checkLoggedIn();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw NotAvailableInOfflineModeError if in offline mode.
|
||||
*
|
||||
* Called automatically if `onlineOnly=true`.
|
||||
* Can be called explicitly by command implementation, if e.g.:
|
||||
* - check should only be done conditionally
|
||||
* - other code needs to execute before check
|
||||
*
|
||||
* Note, currently public to allow use outside of derived commands
|
||||
* (as some command implementations require this. Can be made protected
|
||||
* if this changes).
|
||||
*
|
||||
* @throws {NotAvailableInOfflineModeError}
|
||||
*/
|
||||
public static checkNotUsingOfflineMode() {
|
||||
if (process.env.BALENARC_OFFLINE_MODE) {
|
||||
throw new NotAvailableInOfflineModeError(stripIndent`
|
||||
This command requires an internet connection, and cannot be used in offline mode.
|
||||
To leave offline mode, unset the BALENARC_OFFLINE_MODE environment variable.
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read stdin contents and make available to command.
|
||||
*
|
||||
* This approach could be improved in the future to automatically set argument
|
||||
* values from stdin based in configuration, minimising command implementation.
|
||||
*/
|
||||
protected async getStdin() {
|
||||
this.stdin = await (await import('get-stdin'))();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a logger instance.
|
||||
*/
|
||||
protected static async getLogger() {
|
||||
return (await import('./utils/logger')).getLogger();
|
||||
}
|
||||
|
||||
protected async init() {
|
||||
const ctr = this.constructor as typeof BalenaCommand;
|
||||
|
||||
if (ctr.root) {
|
||||
await BalenaCommand.checkElevatedPrivileges();
|
||||
}
|
||||
|
||||
if (ctr.authenticated) {
|
||||
await BalenaCommand.checkLoggedIn();
|
||||
}
|
||||
|
||||
if (!ctr.offlineCompatible) {
|
||||
BalenaCommand.checkNotUsingOfflineMode();
|
||||
}
|
||||
|
||||
if (ctr.readStdin) {
|
||||
await this.getStdin();
|
||||
}
|
||||
}
|
||||
}
|
@ -15,11 +15,30 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import { ExpectedError } from '../../errors';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
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();
|
||||
try {
|
||||
const token = await balena.auth.getToken();
|
||||
const { default: jwtDecode } = await import('jwt-decode');
|
||||
jwtDecode(token);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export default class GenerateCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
@ -31,19 +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,
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'api-key generate <name>';
|
||||
|
||||
public static flags = {
|
||||
help: cf.help,
|
||||
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;
|
||||
@ -51,11 +72,70 @@ 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()) {
|
||||
throw new ExpectedError(stripIndent`
|
||||
This command requires you to have been recently authenticated.
|
||||
Please login again with 'balena login'.
|
||||
In case you are using the Web authorization method, you need to logout and re-login to the dashboard first.
|
||||
`);
|
||||
}
|
||||
throw new ExpectedError(stripIndent`
|
||||
This command cannot be run when logged in with an API key.
|
||||
Please login again with 'balena login' and select an alternative method.
|
||||
|
@ -15,12 +15,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import { Flags, Command } from '@oclif/core';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
||||
|
||||
export default class ApiKeysCmd extends Command {
|
||||
export default class APIKeyListCmd extends Command {
|
||||
public static aliases = ['api-keys'];
|
||||
public static deprecateAliases = true;
|
||||
|
||||
public static description = stripIndent`
|
||||
Print a list of balenaCloud API keys.
|
||||
|
||||
@ -28,12 +30,9 @@ export default class ApiKeysCmd extends Command {
|
||||
|
||||
Print a list of balenaCloud API keys for the current user or for a specific fleet with the \`--fleet\` option.
|
||||
`;
|
||||
public static examples = ['$ balena api-keys'];
|
||||
|
||||
public static usage = 'api-keys';
|
||||
public static examples = ['$ balena api-key list'];
|
||||
|
||||
public static flags = {
|
||||
help: cf.help,
|
||||
user: Flags.boolean({
|
||||
char: 'u',
|
||||
description: 'show API keys for your user',
|
||||
@ -44,7 +43,7 @@ export default class ApiKeysCmd extends Command {
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
const { flags: options } = await this.parse(ApiKeysCmd);
|
||||
const { flags: options } = await this.parse(APIKeyListCmd);
|
||||
|
||||
const { getApplication } = await import('../../utils/sdk');
|
||||
const actorId = options.fleet
|
||||
@ -52,7 +51,7 @@ export default class ApiKeysCmd extends Command {
|
||||
await getApplication(getBalenaSdk(), options.fleet, {
|
||||
$select: 'actor',
|
||||
})
|
||||
).actor
|
||||
).actor.__id
|
||||
: await getBalenaSdk().auth.getActorId();
|
||||
const keys = await getBalenaSdk().pine.get({
|
||||
resource: 'api_key',
|
@ -15,9 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
|
||||
export default class RevokeCmd extends Command {
|
||||
@ -41,12 +39,6 @@ export default class RevokeCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'api-key revoke <ids>';
|
||||
|
||||
public static flags = {
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
@ -58,9 +50,9 @@ export default class RevokeCmd extends Command {
|
||||
return;
|
||||
}
|
||||
await Promise.all(
|
||||
apiKeyIds.map(
|
||||
async (id) => await getBalenaSdk().models.apiKey.revoke(Number(id)),
|
||||
),
|
||||
apiKeyIds.map(async (id) => {
|
||||
await getBalenaSdk().models.apiKey.revoke(Number(id));
|
||||
}),
|
||||
);
|
||||
console.log('Successfully revoked the given API keys');
|
||||
}
|
||||
|
@ -15,10 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags, Args } from '@oclif/core';
|
||||
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Flags, Args, Command } from '@oclif/core';
|
||||
import { stripIndent } from '../../utils/lazy';
|
||||
|
||||
export default class AppCreateCmd extends Command {
|
||||
@ -30,10 +27,10 @@ export default class AppCreateCmd extends Command {
|
||||
You can specify the organization the app should belong to using
|
||||
the \`--organization\` option. The organization's handle, not its name,
|
||||
should be provided. Organization handles can be listed with the
|
||||
\`balena orgs\` command.
|
||||
\`balena organization list\` command.
|
||||
|
||||
The app's default device type is specified with the \`--type\` option.
|
||||
The \`balena devices supported\` command can be used to list the available
|
||||
The \`balena device-type list\` command can be used to list the available
|
||||
device types.
|
||||
|
||||
Interactive dropdowns will be shown for selection if no device type or
|
||||
@ -56,8 +53,6 @@ export default class AppCreateCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'app create <name>';
|
||||
|
||||
public static flags = {
|
||||
organization: Flags.string({
|
||||
char: 'o',
|
||||
@ -66,9 +61,8 @@ export default class AppCreateCmd extends Command {
|
||||
type: Flags.string({
|
||||
char: 't',
|
||||
description:
|
||||
'app device type (Check available types with `balena devices supported`)',
|
||||
'app device type (Check available types with `balena device-type list`)',
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
@ -15,10 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags, Args } from '@oclif/core';
|
||||
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Flags, Args, Command } from '@oclif/core';
|
||||
import { stripIndent } from '../../utils/lazy';
|
||||
|
||||
export default class BlockCreateCmd extends Command {
|
||||
@ -30,10 +27,10 @@ export default class BlockCreateCmd extends Command {
|
||||
You can specify the organization the block should belong to using
|
||||
the \`--organization\` option. The organization's handle, not its name,
|
||||
should be provided. Organization handles can be listed with the
|
||||
\`balena orgs\` command.
|
||||
\`balena organization list\` command.
|
||||
|
||||
The block's default device type is specified with the \`--type\` option.
|
||||
The \`balena devices supported\` command can be used to list the available
|
||||
The \`balena device-type list\` command can be used to list the available
|
||||
device types.
|
||||
|
||||
Interactive dropdowns will be shown for selection if no device type or
|
||||
@ -56,8 +53,6 @@ export default class BlockCreateCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'block create <name>';
|
||||
|
||||
public static flags = {
|
||||
organization: Flags.string({
|
||||
char: 'o',
|
||||
@ -66,9 +61,8 @@ export default class BlockCreateCmd extends Command {
|
||||
type: Flags.string({
|
||||
char: 't',
|
||||
description:
|
||||
'block device type (Check available types with `balena devices supported`)',
|
||||
'block device type (Check available types with `balena device-type list`)',
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
@ -15,8 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args, Flags } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import { Args, Flags, Command } from '@oclif/core';
|
||||
import { getBalenaSdk } from '../../utils/lazy';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import * as compose from '../../utils/compose';
|
||||
@ -37,15 +36,16 @@ import { buildProject, composeCliFlags } from '../../utils/compose_ts';
|
||||
import type { BuildOpts, DockerCliFlags } from '../../utils/docker';
|
||||
import { dockerCliFlags } from '../../utils/docker';
|
||||
|
||||
// TODO: For this special one we can't use Interfaces.InferredFlags/InferredArgs
|
||||
// because of the 'registry-secrets' type which is defined in the actual code
|
||||
// as a path (string | undefined) but then the cli turns it into an object
|
||||
interface FlagsDef extends ComposeCliFlags, DockerCliFlags {
|
||||
type ComposeGenerateOptsParam = Parameters<typeof compose.generateOpts>[0];
|
||||
|
||||
interface PrepareBuildOpts
|
||||
extends ComposeCliFlags,
|
||||
DockerCliFlags,
|
||||
ComposeGenerateOptsParam {
|
||||
arch?: string;
|
||||
deviceType?: string;
|
||||
fleet?: string;
|
||||
source?: string; // Not part of command profile - source param copied here.
|
||||
help: void;
|
||||
source?: string;
|
||||
}
|
||||
|
||||
export default class BuildCmd extends Command {
|
||||
@ -84,8 +84,6 @@ ${dockerignoreHelp}
|
||||
source: Args.string({ description: 'path of project source directory' }),
|
||||
};
|
||||
|
||||
public static usage = 'build [source]';
|
||||
|
||||
public static flags = {
|
||||
arch: Flags.string({
|
||||
description: 'the architecture to build for',
|
||||
@ -98,9 +96,6 @@ ${dockerignoreHelp}
|
||||
fleet: cf.fleet,
|
||||
...composeCliFlags,
|
||||
...dockerCliFlags,
|
||||
// NOTE: Not supporting -h for help, because of clash with -h in DockerCliFlags
|
||||
// Revisit this in future release.
|
||||
help: Flags.help({}),
|
||||
};
|
||||
|
||||
public static primary = true;
|
||||
@ -108,38 +103,43 @@ ${dockerignoreHelp}
|
||||
public async run() {
|
||||
const { args: params, flags: options } = await this.parse(BuildCmd);
|
||||
|
||||
await Command.checkLoggedInIf(!!options.fleet);
|
||||
const Logger = await import('../../utils/logger');
|
||||
const { checkLoggedInIf } = await import('../../utils/patterns');
|
||||
|
||||
await checkLoggedInIf(!!options.fleet);
|
||||
|
||||
(await import('events')).defaultMaxListeners = 1000;
|
||||
|
||||
const sdk = getBalenaSdk();
|
||||
|
||||
const logger = await Command.getLogger();
|
||||
const logger = Logger.getLogger();
|
||||
logger.logDebug('Parsing input...');
|
||||
|
||||
// `build` accepts `source` as a parameter, but compose expects it as an option
|
||||
options.source = params.source;
|
||||
delete params.source;
|
||||
const prepareBuildOpts = {
|
||||
...options,
|
||||
source: params.source,
|
||||
};
|
||||
|
||||
await this.resolveArchFromDeviceType(sdk, options);
|
||||
await this.resolveArchFromDeviceType(sdk, prepareBuildOpts);
|
||||
|
||||
await this.validateOptions(options, sdk);
|
||||
await this.validateOptions(prepareBuildOpts, sdk);
|
||||
|
||||
// Build args are under consideration for removal - warn user
|
||||
if (options.buildArg) {
|
||||
if (prepareBuildOpts.buildArg) {
|
||||
console.log(buildArgDeprecation);
|
||||
}
|
||||
|
||||
const app = await this.getAppAndResolveArch(options);
|
||||
const app = await this.getAppAndResolveArch(prepareBuildOpts);
|
||||
|
||||
const { docker, buildOpts, composeOpts } = await this.prepareBuild(options);
|
||||
const { docker, buildOpts, composeOpts } =
|
||||
await this.prepareBuild(prepareBuildOpts);
|
||||
|
||||
try {
|
||||
await this.buildProject(docker, logger, composeOpts, {
|
||||
appType: app?.application_type?.[0],
|
||||
arch: options.arch!,
|
||||
deviceType: options.deviceType!,
|
||||
buildEmulated: options.emulated,
|
||||
arch: prepareBuildOpts.arch!,
|
||||
deviceType: prepareBuildOpts.deviceType!,
|
||||
buildEmulated: prepareBuildOpts.emulated,
|
||||
buildOpts,
|
||||
});
|
||||
} catch (err) {
|
||||
@ -151,7 +151,7 @@ ${dockerignoreHelp}
|
||||
logger.logSuccess('Build succeeded!');
|
||||
}
|
||||
|
||||
protected async validateOptions(opts: FlagsDef, sdk: BalenaSDK) {
|
||||
protected async validateOptions(opts: PrepareBuildOpts, sdk: BalenaSDK) {
|
||||
// Validate option combinations
|
||||
if (
|
||||
(opts.fleet == null && (opts.arch == null || opts.deviceType == null)) ||
|
||||
@ -179,7 +179,10 @@ ${dockerignoreHelp}
|
||||
opts['registry-secrets'] = registrySecrets;
|
||||
}
|
||||
|
||||
protected async resolveArchFromDeviceType(sdk: BalenaSDK, opts: FlagsDef) {
|
||||
protected async resolveArchFromDeviceType(
|
||||
sdk: BalenaSDK,
|
||||
opts: PrepareBuildOpts,
|
||||
) {
|
||||
if (opts.deviceType != null && opts.arch == null) {
|
||||
try {
|
||||
const deviceTypeOpts = {
|
||||
@ -212,7 +215,7 @@ ${dockerignoreHelp}
|
||||
}
|
||||
}
|
||||
|
||||
protected async getAppAndResolveArch(opts: FlagsDef) {
|
||||
protected async getAppAndResolveArch(opts: PrepareBuildOpts) {
|
||||
if (opts.fleet) {
|
||||
const { getAppWithArch } = await import('../../utils/helpers');
|
||||
const app = await getAppWithArch(opts.fleet);
|
||||
@ -222,7 +225,7 @@ ${dockerignoreHelp}
|
||||
}
|
||||
}
|
||||
|
||||
protected async prepareBuild(options: FlagsDef) {
|
||||
protected async prepareBuild(options: PrepareBuildOpts) {
|
||||
const { getDocker, generateBuildOpts } = await import('../../utils/docker');
|
||||
const [docker, buildOpts, composeOpts] = await Promise.all([
|
||||
getDocker(options),
|
||||
|
@ -15,9 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags } from '@oclif/core';
|
||||
import { Flags, Command } from '@oclif/core';
|
||||
import type { Interfaces } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, getCliForm, stripIndent } from '../../utils/lazy';
|
||||
import {
|
||||
@ -60,8 +59,6 @@ export default class ConfigGenerateCmd extends Command {
|
||||
'$ balena config generate --fleet myorg/fleet --version 2.12.7 --network wifi --wifiSsid mySsid --wifiKey abcdefgh --appUpdatePollInterval 15',
|
||||
];
|
||||
|
||||
public static usage = 'config generate';
|
||||
|
||||
public static flags = {
|
||||
version: Flags.string({
|
||||
description: 'a balenaOS version',
|
||||
@ -85,7 +82,7 @@ export default class ConfigGenerateCmd extends Command {
|
||||
}),
|
||||
deviceType: Flags.string({
|
||||
description:
|
||||
"device type slug (run 'balena devices supported' for possible values)",
|
||||
"device type slug (run 'balena device-type list' for possible values)",
|
||||
}),
|
||||
'generate-device-api-key': Flags.boolean({
|
||||
description: 'generate a fresh device key for the device',
|
||||
@ -120,7 +117,6 @@ export default class ConfigGenerateCmd extends Command {
|
||||
'expiry date assigned to generated provisioning api key (format: YYYY-MM-DD)',
|
||||
exclusive: ['device'],
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
@ -15,8 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getVisuals, stripIndent } from '../../utils/lazy';
|
||||
|
||||
@ -43,11 +42,8 @@ export default class ConfigInjectCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'config inject <file>';
|
||||
|
||||
public static flags = {
|
||||
drive: cf.driveOrImg,
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static root = true;
|
||||
|
@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Command from '../../command';
|
||||
import { Command } from '@oclif/core';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getVisuals, stripIndent } from '../../utils/lazy';
|
||||
|
||||
@ -36,11 +36,8 @@ export default class ConfigReadCmd extends Command {
|
||||
'$ balena config read --drive balena.img',
|
||||
];
|
||||
|
||||
public static usage = 'config read';
|
||||
|
||||
public static flags = {
|
||||
drive: cf.driveOrImg,
|
||||
help: cf.help,
|
||||
json: cf.json,
|
||||
};
|
||||
|
||||
|
@ -15,8 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import { Flags, Command } from '@oclif/core';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getVisuals, stripIndent } from '../../utils/lazy';
|
||||
|
||||
@ -39,15 +38,12 @@ export default class ConfigReconfigureCmd extends Command {
|
||||
'$ balena config reconfigure --drive balena.img --advanced',
|
||||
];
|
||||
|
||||
public static usage = 'config reconfigure';
|
||||
|
||||
public static flags = {
|
||||
drive: cf.driveOrImg,
|
||||
advanced: Flags.boolean({
|
||||
description: 'show advanced commands',
|
||||
char: 'v',
|
||||
}),
|
||||
help: cf.help,
|
||||
version: Flags.string({
|
||||
description: 'balenaOS version, for example "2.32.0" or "2.44.0+rev1"',
|
||||
}),
|
||||
|
@ -15,8 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getVisuals, stripIndent } from '../../utils/lazy';
|
||||
|
||||
@ -48,11 +47,8 @@ export default class ConfigWriteCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'config write <key> <value>';
|
||||
|
||||
public static flags = {
|
||||
drive: cf.driveOrImg,
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static root = true;
|
||||
|
@ -15,10 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args, Flags } from '@oclif/core';
|
||||
import { Args, Flags, Command } from '@oclif/core';
|
||||
import type { ImageDescriptor } from '@balena/compose/dist/parse';
|
||||
|
||||
import Command from '../../command';
|
||||
import { ExpectedError } from '../../errors';
|
||||
import { getBalenaSdk, getChalk, stripIndent } from '../../utils/lazy';
|
||||
import {
|
||||
@ -62,7 +60,6 @@ interface FlagsDef extends ComposeCliFlags, DockerCliFlags {
|
||||
'release-tag'?: string[];
|
||||
draft: boolean;
|
||||
note?: string;
|
||||
help: void;
|
||||
}
|
||||
|
||||
export default class DeployCmd extends Command {
|
||||
@ -108,8 +105,6 @@ ${dockerignoreHelp}
|
||||
image: Args.string({ description: 'the image to deploy' }),
|
||||
};
|
||||
|
||||
public static usage = 'deploy <fleet> [image]';
|
||||
|
||||
public static flags = {
|
||||
source: Flags.string({
|
||||
description:
|
||||
@ -143,9 +138,6 @@ ${dockerignoreHelp}
|
||||
note: Flags.string({ description: 'The notes for this release' }),
|
||||
...composeCliFlags,
|
||||
...dockerCliFlags,
|
||||
// NOTE: Not supporting -h for help, because of clash with -h in DockerCliFlags
|
||||
// Revisit this in future release.
|
||||
help: Flags.help({}),
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
@ -157,7 +149,9 @@ ${dockerignoreHelp}
|
||||
|
||||
(await import('events')).defaultMaxListeners = 1000;
|
||||
|
||||
const logger = await Command.getLogger();
|
||||
const Logger = await import('../../utils/logger');
|
||||
|
||||
const logger = Logger.getLogger();
|
||||
logger.logDebug('Parsing input...');
|
||||
|
||||
const { fleet, image } = params;
|
||||
@ -374,6 +368,7 @@ ${dockerignoreHelp}
|
||||
!opts.shouldUploadLogs,
|
||||
composeOpts.projectPath,
|
||||
opts.createAsDraft,
|
||||
project.descriptors,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -14,20 +14,23 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Flags } from '@oclif/core';
|
||||
import { Flags, Command } from '@oclif/core';
|
||||
import type * as BalenaSdk from 'balena-sdk';
|
||||
import * as _ from 'lodash';
|
||||
import Command from '../../command';
|
||||
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
||||
import { CommandHelp } from '../../utils/oclif-utils';
|
||||
|
||||
export default class DevicesSupportedCmd extends Command {
|
||||
export default class DeviceTypeListCmd extends Command {
|
||||
public static aliases = ['devices supported'];
|
||||
public static deprecateAliases = true;
|
||||
|
||||
public static description = stripIndent`
|
||||
List the supported device types (like 'raspberrypi3' or 'intel-nuc').
|
||||
List the device types supported by balena (like 'raspberrypi3' or 'intel-nuc').
|
||||
|
||||
List the supported device types (like 'raspberrypi3' or 'intel-nuc').
|
||||
List the device types supported by balena (like 'raspberrypi3' or 'intel-nuc').
|
||||
|
||||
By default, only actively supported device types are listed.
|
||||
The --all option can be used to list all device types, including those that are
|
||||
no longer supported by balena.
|
||||
|
||||
The --json option is recommended when scripting the output of this command,
|
||||
because the JSON format is less likely to change and it better represents data
|
||||
@ -36,25 +39,24 @@ export default class DevicesSupportedCmd extends Command {
|
||||
(https://stedolan.github.io/jq/manual/).
|
||||
`;
|
||||
public static examples = [
|
||||
'$ balena devices supported',
|
||||
'$ balena devices supported --json',
|
||||
'$ balena device-type list',
|
||||
'$ balena device-type list --all',
|
||||
'$ balena device-type list --json',
|
||||
];
|
||||
|
||||
public static usage = (
|
||||
'devices supported ' +
|
||||
new CommandHelp({ args: DevicesSupportedCmd.args }).defaultUsage()
|
||||
).trim();
|
||||
|
||||
public static flags = {
|
||||
help: cf.help,
|
||||
json: Flags.boolean({
|
||||
char: 'j',
|
||||
description: 'produce JSON output instead of tabular output',
|
||||
}),
|
||||
all: Flags.boolean({
|
||||
description: 'include device types no longer supported by balena',
|
||||
default: false,
|
||||
}),
|
||||
};
|
||||
|
||||
public async run() {
|
||||
const { flags: options } = await this.parse(DevicesSupportedCmd);
|
||||
const { flags: options } = await this.parse(DeviceTypeListCmd);
|
||||
const pineOptions = {
|
||||
$select: ['slug', 'name'],
|
||||
$expand: {
|
||||
@ -65,9 +67,11 @@ export default class DevicesSupportedCmd extends Command {
|
||||
},
|
||||
},
|
||||
} satisfies BalenaSdk.PineOptions<BalenaSdk.DeviceType>;
|
||||
const dts = (await getBalenaSdk().models.deviceType.getAllSupported(
|
||||
pineOptions,
|
||||
)) as Array<
|
||||
const dts = (
|
||||
options.all
|
||||
? await getBalenaSdk().models.deviceType.getAll(pineOptions)
|
||||
: await getBalenaSdk().models.deviceType.getAllSupported(pineOptions)
|
||||
) as Array<
|
||||
BalenaSdk.PineTypedResult<BalenaSdk.DeviceType, typeof pineOptions>
|
||||
>;
|
||||
interface DT {
|
@ -15,8 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
|
||||
@ -41,11 +40,8 @@ export default class DeviceDeactivateCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'device deactivate <uuid>';
|
||||
|
||||
public static flags = {
|
||||
yes: cf.yes,
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
@ -15,12 +15,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Flags, Command } from '@oclif/core';
|
||||
import { getCliUx, stripIndent } from '../../utils/lazy';
|
||||
|
||||
export default class ScanCmd extends Command {
|
||||
export default class DeviceDetectCmd extends Command {
|
||||
public static aliases = ['scan'];
|
||||
public static deprecateAliases = true;
|
||||
|
||||
public static description = stripIndent`
|
||||
Scan for balenaOS devices on your local network.
|
||||
|
||||
@ -33,13 +34,11 @@ export default class ScanCmd extends Command {
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
'$ balena scan',
|
||||
'$ balena scan --timeout 120',
|
||||
'$ balena scan --verbose',
|
||||
'$ balena device detect',
|
||||
'$ balena device detect --timeout 120',
|
||||
'$ balena device detect --verbose',
|
||||
];
|
||||
|
||||
public static usage = 'scan';
|
||||
|
||||
public static flags = {
|
||||
verbose: Flags.boolean({
|
||||
default: false,
|
||||
@ -50,7 +49,6 @@ export default class ScanCmd extends Command {
|
||||
char: 't',
|
||||
description: 'scan timeout in seconds',
|
||||
}),
|
||||
help: cf.help,
|
||||
json: Flags.boolean({
|
||||
default: false,
|
||||
char: 'j',
|
||||
@ -73,7 +71,7 @@ export default class ScanCmd extends Command {
|
||||
const dockerPort = 2375;
|
||||
const dockerTimeout = 2000;
|
||||
|
||||
const { flags: options } = await this.parse(ScanCmd);
|
||||
const { flags: options } = await this.parse(DeviceDetectCmd);
|
||||
|
||||
const discoverTimeout =
|
||||
options.timeout != null ? options.timeout * 1000 : undefined;
|
||||
@ -93,7 +91,7 @@ export default class ScanCmd extends Command {
|
||||
try {
|
||||
await docker.ping();
|
||||
return true;
|
||||
} catch (err) {
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
@ -147,10 +145,10 @@ export default class ScanCmd extends Command {
|
||||
if (!options.verbose) {
|
||||
devicesInfo.forEach((d: any) => {
|
||||
d.dockerInfo = _.isObject(d.dockerInfo)
|
||||
? _.pick(d.dockerInfo, ScanCmd.dockerInfoProperties)
|
||||
? _.pick(d.dockerInfo, DeviceDetectCmd.dockerInfoProperties)
|
||||
: d.dockerInfo;
|
||||
d.dockerVersion = _.isObject(d.dockerVersion)
|
||||
? _.pick(d.dockerVersion, ScanCmd.dockerVersionProperties)
|
||||
? _.pick(d.dockerVersion, DeviceDetectCmd.dockerVersionProperties)
|
||||
: d.dockerVersion;
|
||||
});
|
||||
}
|
||||
@ -167,8 +165,9 @@ export default class ScanCmd extends Command {
|
||||
if (!options.json && cmdOutput.length === 0) {
|
||||
console.error(
|
||||
process.platform === 'win32'
|
||||
? ScanCmd.noDevicesFoundMessage + ScanCmd.windowsTipMessage
|
||||
: ScanCmd.noDevicesFoundMessage,
|
||||
? DeviceDetectCmd.noDevicesFoundMessage +
|
||||
DeviceDetectCmd.windowsTipMessage
|
||||
: DeviceDetectCmd.noDevicesFoundMessage,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@ -200,11 +199,11 @@ export default class ScanCmd extends Command {
|
||||
protected static windowsTipMessage = `
|
||||
|
||||
Note for Windows users:
|
||||
The 'scan' command relies on the Bonjour service. Check whether Bonjour is
|
||||
The 'device detect' command relies on the Bonjour service. Check whether Bonjour is
|
||||
installed (Control Panel > Programs and Features). If not, you can download
|
||||
Bonjour for Windows (included with Bonjour Print Services) from here:
|
||||
https://support.apple.com/kb/DL999
|
||||
|
||||
After installing Bonjour, restart your PC and run the 'balena scan' command
|
||||
After installing Bonjour, restart your PC and run the 'balena device detect' command
|
||||
again.`;
|
||||
}
|
@ -15,9 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import { ExpectedError } from '../../errors';
|
||||
|
||||
@ -36,12 +34,6 @@ export default class DeviceIdentifyCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'device identify <uuid>';
|
||||
|
||||
public static flags = {
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
|
@ -15,8 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags, Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import { Flags, Args, Command } from '@oclif/core';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { expandForAppName } from '../../utils/helpers';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
||||
@ -62,11 +61,8 @@ export default class DeviceCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'device <uuid>';
|
||||
|
||||
public static flags = {
|
||||
json: cf.json,
|
||||
help: cf.help,
|
||||
view: Flags.boolean({
|
||||
default: false,
|
||||
description: 'open device dashboard page',
|
||||
@ -81,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');
|
||||
|
@ -15,8 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import { Flags, Command } from '@oclif/core';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
@ -29,7 +28,6 @@ interface FlagsDef {
|
||||
'os-version'?: string;
|
||||
drive?: string;
|
||||
config?: string;
|
||||
help: void;
|
||||
'provisioning-key-name'?: string;
|
||||
'provisioning-key-expiry-date'?: string;
|
||||
}
|
||||
@ -43,17 +41,17 @@ export default class DeviceInitCmd extends Command {
|
||||
This command effectively combines several other balena CLI commands in one,
|
||||
namely:
|
||||
|
||||
'balena device register'
|
||||
'balena os download'
|
||||
'balena os build-config' or 'balena config generate'
|
||||
'balena os configure'
|
||||
'balena device register'
|
||||
'balena os download'
|
||||
'balena os build-config' or 'balena config generate'
|
||||
'balena os configure'
|
||||
'balena os local flash'
|
||||
|
||||
Possible arguments for the '--fleet', '--os-version' and '--drive' options can
|
||||
be listed respectively with the commands:
|
||||
|
||||
'balena fleets'
|
||||
'balena os versions'
|
||||
'balena fleet list'
|
||||
'balena os versions'
|
||||
'balena util available-drives'
|
||||
|
||||
If the '--fleet' or '--drive' options are omitted, interactive menus will be
|
||||
@ -74,8 +72,6 @@ export default class DeviceInitCmd extends Command {
|
||||
'$ balena device init --fleet myFleet --os-version 2.83.21+rev1.prod --drive /dev/disk5 --config config.json --yes',
|
||||
];
|
||||
|
||||
public static usage = 'device init';
|
||||
|
||||
public static flags = {
|
||||
fleet: cf.fleet,
|
||||
yes: cf.yes,
|
||||
@ -103,7 +99,6 @@ export default class DeviceInitCmd extends Command {
|
||||
description:
|
||||
'expiry date assigned to generated provisioning api key (format: YYYY-MM-DD)',
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
@ -119,8 +114,9 @@ export default class DeviceInitCmd extends Command {
|
||||
tmp.setGracefulCleanup();
|
||||
const { downloadOSImage } = await import('../../utils/cloud');
|
||||
const { getApplication } = await import('../../utils/sdk');
|
||||
const Logger = await import('../../utils/logger');
|
||||
|
||||
const logger = await Command.getLogger();
|
||||
const logger = Logger.getLogger();
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
// Get application and
|
||||
@ -159,7 +155,7 @@ export default class DeviceInitCmd extends Command {
|
||||
try {
|
||||
logger.logDebug(`Process failed, removing device ${device.uuid}`);
|
||||
await balena.models.device.remove(device.uuid);
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// Ignore removal failures, and throw original error
|
||||
}
|
||||
throw e;
|
||||
|
@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Command from '../../command';
|
||||
import { Command } from '@oclif/core';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { expandForAppName } from '../../utils/helpers';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
||||
@ -35,7 +35,10 @@ const devicesSelectFields = {
|
||||
],
|
||||
} satisfies PineOptions<Device>;
|
||||
|
||||
export default class DevicesCmd extends Command {
|
||||
export default class DeviceListCmd extends Command {
|
||||
public static aliases = ['devices'];
|
||||
public static deprecateAliases = true;
|
||||
|
||||
public static description = stripIndent`
|
||||
List all devices.
|
||||
|
||||
@ -48,17 +51,14 @@ export default class DevicesCmd extends Command {
|
||||
${jsonInfo.split('\n').join('\n\t\t')}
|
||||
`;
|
||||
public static examples = [
|
||||
'$ balena devices',
|
||||
'$ balena devices --fleet MyFleet',
|
||||
'$ balena devices -f myorg/myfleet',
|
||||
'$ balena device list',
|
||||
'$ balena device list --fleet MyFleet',
|
||||
'$ balena device list -f myorg/myfleet',
|
||||
];
|
||||
|
||||
public static usage = 'devices';
|
||||
|
||||
public static flags = {
|
||||
fleet: cf.fleet,
|
||||
json: cf.json,
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static primary = true;
|
||||
@ -66,7 +66,7 @@ export default class DevicesCmd extends Command {
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
const { flags: options } = await this.parse(DevicesCmd);
|
||||
const { flags: options } = await this.parse(DeviceListCmd);
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
const devicesOptions = {
|
@ -15,9 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags, Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Flags, Args, Command } from '@oclif/core';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
|
||||
export default class DeviceLocalModeCmd extends Command {
|
||||
@ -42,8 +40,6 @@ export default class DeviceLocalModeCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'device local-mode <uuid>';
|
||||
|
||||
public static flags = {
|
||||
enable: Flags.boolean({
|
||||
description: 'enable local mode',
|
||||
@ -57,7 +53,6 @@ export default class DeviceLocalModeCmd extends Command {
|
||||
description: 'output boolean indicating local mode status',
|
||||
exclusive: ['enable', 'disable'],
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
@ -15,15 +15,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags, Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Flags, Args, Command } from '@oclif/core';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import type { LogMessage } from 'balena-sdk';
|
||||
|
||||
const MAX_RETRY = 1000;
|
||||
|
||||
export default class LogsCmd extends Command {
|
||||
export default class DeviceLogsCmd extends Command {
|
||||
public static aliases = ['logs'];
|
||||
public static deprecateAliases = true;
|
||||
|
||||
public static description = stripIndent`
|
||||
Show device logs.
|
||||
|
||||
@ -43,15 +44,15 @@ export default class LogsCmd extends Command {
|
||||
Note: --service and --system flags must come after the device parameter, as per examples.
|
||||
`;
|
||||
public static examples = [
|
||||
'$ balena logs 23c73a1',
|
||||
'$ balena logs 23c73a1 --tail',
|
||||
'$ balena device logs 23c73a1',
|
||||
'$ balena device logs 23c73a1 --tail',
|
||||
'',
|
||||
'$ balena logs 192.168.0.31',
|
||||
'$ balena logs 192.168.0.31 --service my-service',
|
||||
'$ balena logs 192.168.0.31 --service my-service-1 --service my-service-2',
|
||||
'$ balena device logs 192.168.0.31',
|
||||
'$ balena device logs 192.168.0.31 --service my-service',
|
||||
'$ balena device logs 192.168.0.31 --service my-service-1 --service my-service-2',
|
||||
'',
|
||||
'$ balena logs 23c73a1.local --system',
|
||||
'$ balena logs 23c73a1.local --system --service my-service',
|
||||
'$ balena device logs 23c73a1.local --system',
|
||||
'$ balena device logs 23c73a1.local --system --service my-service',
|
||||
];
|
||||
|
||||
public static args = {
|
||||
@ -61,8 +62,6 @@ export default class LogsCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'logs <device>';
|
||||
|
||||
public static flags = {
|
||||
'max-retry': Flags.integer({
|
||||
description: stripIndent`
|
||||
@ -87,13 +86,12 @@ export default class LogsCmd extends Command {
|
||||
'Only show system logs. This can be used in combination with --service.',
|
||||
char: 'S',
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static primary = true;
|
||||
|
||||
public async run() {
|
||||
const { args: params, flags: options } = await this.parse(LogsCmd);
|
||||
const { args: params, flags: options } = await this.parse(DeviceLogsCmd);
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
const { serviceIdToName } = await import('../../utils/cloud');
|
||||
@ -137,7 +135,7 @@ export default class LogsCmd extends Command {
|
||||
logger.logDebug('Checking we can access device');
|
||||
try {
|
||||
await deviceApi.ping();
|
||||
} catch (e) {
|
||||
} catch {
|
||||
const { ExpectedError } = await import('../../errors');
|
||||
throw new ExpectedError(
|
||||
`Cannot access device at address ${params.device}. Device may not be in local mode.`,
|
||||
@ -153,8 +151,9 @@ export default class LogsCmd extends Command {
|
||||
maxAttempts: 1 + (options['max-retry'] ?? MAX_RETRY),
|
||||
});
|
||||
} else {
|
||||
const { checkLoggedIn } = await import('../../utils/patterns');
|
||||
// Logs from cloud
|
||||
await Command.checkLoggedIn();
|
||||
await checkLoggedIn();
|
||||
if (options.tail) {
|
||||
const logStream = await balena.logs.subscribe(params.device, {
|
||||
count: 100,
|
@ -15,14 +15,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import type {
|
||||
BalenaSDK,
|
||||
Device,
|
||||
PineOptions,
|
||||
PineTypedResult,
|
||||
} from 'balena-sdk';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { ExpectedError } from '../../errors';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
@ -54,11 +53,8 @@ export default class DeviceMoveCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'device move <uuid(s)>';
|
||||
|
||||
public static flags = {
|
||||
fleet: cf.fleet,
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
@ -15,13 +15,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags, Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import { Flags, Args, Command } from '@oclif/core';
|
||||
import { ExpectedError } from '../../errors';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
|
||||
export default class NoteCmd extends Command {
|
||||
export default class DeviceNoteCmd extends Command {
|
||||
public static aliases = ['notes'];
|
||||
public static deprecateAliases = true;
|
||||
|
||||
public static description = stripIndent`
|
||||
Set a device note.
|
||||
|
||||
@ -32,8 +34,8 @@ export default class NoteCmd extends Command {
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
'$ balena note "My useful note" --device 7cf02a6',
|
||||
'$ cat note.txt | balena note --device 7cf02a6',
|
||||
'$ balena device note "My useful note" --device 7cf02a6',
|
||||
'$ cat note.txt | balena device note --device 7cf02a6',
|
||||
];
|
||||
|
||||
public static args = {
|
||||
@ -42,27 +44,20 @@ export default class NoteCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'note <|note>';
|
||||
|
||||
public static flags = {
|
||||
device: { exclusive: ['dev'], ...cf.device },
|
||||
dev: Flags.string({
|
||||
exclusive: ['device'],
|
||||
hidden: true,
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public static readStdin = true;
|
||||
|
||||
public async run() {
|
||||
const { args: params, flags: options } = await this.parse(NoteCmd);
|
||||
const { args: params, flags: options } = await this.parse(DeviceNoteCmd);
|
||||
|
||||
params.note = params.note || this.stdin;
|
||||
|
||||
if (params.note.length === 0) {
|
||||
if (params.note?.length === 0) {
|
||||
throw new ExpectedError('Missing note content');
|
||||
}
|
||||
|
||||
@ -75,6 +70,6 @@ export default class NoteCmd extends Command {
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
return balena.models.device.setNote(options.device, params.note);
|
||||
return balena.models.device.setNote(options.device, params.note ?? '');
|
||||
}
|
||||
}
|
@ -15,12 +15,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags, Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import { Flags, Args, Command } from '@oclif/core';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy';
|
||||
import type { Device } from 'balena-sdk';
|
||||
import { ExpectedError } from '../../errors';
|
||||
import { getExpandedProp } from '../../utils/pine';
|
||||
|
||||
export default class DeviceOsUpdateCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
@ -47,8 +47,6 @@ export default class DeviceOsUpdateCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'device os-update <uuid>';
|
||||
|
||||
public static flags = {
|
||||
version: Flags.string({
|
||||
description: 'a balenaOS version',
|
||||
@ -60,7 +58,6 @@ export default class DeviceOsUpdateCmd extends Command {
|
||||
exclusive: ['version'],
|
||||
}),
|
||||
yes: cf.yes,
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
@ -130,27 +127,64 @@ export default class DeviceOsUpdateCmd extends Command {
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const choices = await Promise.all(
|
||||
hupVersionInfo.versions.map(async (version) => {
|
||||
const takeoverRequired =
|
||||
(await sdk.models.os.getOsUpdateType(
|
||||
getExpandedProp(is_of__device_type, 'slug')!,
|
||||
currentOsVersion,
|
||||
version,
|
||||
)) === 'takeover';
|
||||
|
||||
return {
|
||||
name: `${version}${hupVersionInfo.recommended === version ? ' (recommended)' : ''}${takeoverRequired ? ' ADVANCED UPDATE: Requires disk re-partitioning with no rollback option' : ''}`,
|
||||
value: version,
|
||||
};
|
||||
}),
|
||||
);
|
||||
targetOsVersion = await getCliForm().ask({
|
||||
message: 'Target OS version',
|
||||
type: 'list',
|
||||
choices: hupVersionInfo.versions.map((version) => ({
|
||||
name:
|
||||
hupVersionInfo.recommended === version
|
||||
? `${version} (recommended)`
|
||||
: version,
|
||||
value: version,
|
||||
})),
|
||||
choices,
|
||||
});
|
||||
}
|
||||
|
||||
const takeoverRequired =
|
||||
(await sdk.models.os.getOsUpdateType(
|
||||
getExpandedProp(is_of__device_type, 'slug')!,
|
||||
currentOsVersion,
|
||||
targetOsVersion,
|
||||
)) === 'takeover';
|
||||
const patterns = await import('../../utils/patterns');
|
||||
// Warn the user if the update requires a takeover
|
||||
if (takeoverRequired) {
|
||||
await patterns.confirm(
|
||||
options.yes || false,
|
||||
stripIndent`Before you proceed, note that this update process is different from a regular HostOS Update:
|
||||
DATA LOSS: This update requires disk re-partitioning, which will erase all data stored on the device.
|
||||
NO ROLLBACK: Unlike our HostOS update mechanism, this process does not allow reverting to a previous version in case of failure.
|
||||
Make sure to back up all important data before continuing. For more details, check our documentation: https://docs.balena.io/reference/OS/updates/update-process/
|
||||
`,
|
||||
);
|
||||
}
|
||||
// Confirm and start update
|
||||
await patterns.confirm(
|
||||
options.yes || false,
|
||||
'Host OS updates require a device restart when they complete. Are you sure you want to proceed?',
|
||||
);
|
||||
|
||||
await sdk.models.device.startOsUpdate(uuid, targetOsVersion);
|
||||
await patterns.awaitDeviceOsUpdate(uuid, targetOsVersion);
|
||||
await sdk.models.device
|
||||
.startOsUpdate(uuid, targetOsVersion, {
|
||||
runDetached: true,
|
||||
})
|
||||
.then(() => {
|
||||
console.log(
|
||||
`The balena OS update has started. You can keep track of the progress via the dashboard.\n` +
|
||||
`To open the dashboard page related to a device via the CLI, you can use \`balena device UUID --view\``,
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(`Failed to start OS update for device ${uuid}:`, error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -15,9 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import { getExpandedProp } from '../../utils/pine';
|
||||
|
||||
@ -44,12 +42,6 @@ export default class DevicePinCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'device pin <uuid> [releaseToPinTo]';
|
||||
|
||||
public static flags = {
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
@ -59,7 +51,7 @@ export default class DevicePinCmd extends Command {
|
||||
|
||||
const device = await balena.models.device.get(params.uuid, {
|
||||
$expand: {
|
||||
should_be_running__release: {
|
||||
is_pinned_on__release: {
|
||||
$select: 'commit',
|
||||
},
|
||||
belongs_to__application: {
|
||||
@ -69,7 +61,7 @@ export default class DevicePinCmd extends Command {
|
||||
});
|
||||
|
||||
const pinnedRelease = getExpandedProp(
|
||||
device.should_be_running__release,
|
||||
device.is_pinned_on__release,
|
||||
'commit',
|
||||
);
|
||||
const appSlug = getExpandedProp(device.belongs_to__application, 'slug');
|
||||
@ -82,7 +74,7 @@ export default class DevicePinCmd extends Command {
|
||||
pinnedRelease
|
||||
? `This device is currently pinned to ${pinnedRelease}.`
|
||||
: 'This device is not currently pinned to any release.'
|
||||
} \n\nTo see a list of all releases this device can be pinned to, run \`balena releases ${appSlug}\`.`,
|
||||
} \n\nTo see a list of all releases this device can be pinned to, run \`balena release list ${appSlug}\`.`,
|
||||
);
|
||||
} else {
|
||||
await balena.models.device.pinToRelease(params.uuid, releaseToPinTo);
|
||||
|
@ -15,10 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags, Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import { Flags, Args, Command } from '@oclif/core';
|
||||
import { ExpectedError } from '../../errors';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
|
||||
export default class DevicePublicUrlCmd extends Command {
|
||||
@ -44,8 +42,6 @@ export default class DevicePublicUrlCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'device public-url <uuid>';
|
||||
|
||||
public static flags = {
|
||||
enable: Flags.boolean({
|
||||
description: 'enable the public URL',
|
||||
@ -59,7 +55,6 @@ export default class DevicePublicUrlCmd extends Command {
|
||||
description: 'determine if public URL is enabled',
|
||||
exclusive: ['enable', 'disable'],
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
@ -15,9 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy';
|
||||
|
||||
export default class DevicePurgeCmd extends Command {
|
||||
@ -35,8 +33,6 @@ export default class DevicePurgeCmd extends Command {
|
||||
'$ balena device purge 55d43b3,23c73a1',
|
||||
];
|
||||
|
||||
public static usage = 'device purge <uuid>';
|
||||
|
||||
public static args = {
|
||||
uuid: Args.string({
|
||||
description: 'comma-separated list (no blank spaces) of device UUIDs',
|
||||
@ -44,10 +40,6 @@ export default class DevicePurgeCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static flags = {
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
|
@ -15,8 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
|
||||
@ -35,11 +34,8 @@ export default class DeviceRebootCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'device reboot <uuid>';
|
||||
|
||||
public static flags = {
|
||||
force: cf.force,
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
@ -15,9 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Flags, Command } from '@oclif/core';
|
||||
import * as ca from '../../utils/common-args';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
@ -44,8 +42,6 @@ export default class DeviceRegisterCmd extends Command {
|
||||
fleet: ca.fleetRequired,
|
||||
};
|
||||
|
||||
public static usage = 'device register <fleet>';
|
||||
|
||||
public static flags = {
|
||||
uuid: Flags.string({
|
||||
description: 'custom uuid',
|
||||
@ -53,9 +49,8 @@ export default class DeviceRegisterCmd extends Command {
|
||||
}),
|
||||
deviceType: Flags.string({
|
||||
description:
|
||||
"device type slug (run 'balena devices supported' for possible values)",
|
||||
"device type slug (run 'balena device-type list' for possible values)",
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
@ -81,6 +76,6 @@ export default class DeviceRegisterCmd extends Command {
|
||||
options.deviceType,
|
||||
);
|
||||
|
||||
return result && result.uuid;
|
||||
return result.uuid;
|
||||
}
|
||||
}
|
||||
|
@ -15,9 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy';
|
||||
|
||||
export default class DeviceRenameCmd extends Command {
|
||||
@ -43,12 +41,6 @@ export default class DeviceRenameCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'device rename <uuid> [newName]';
|
||||
|
||||
public static flags = {
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
|
@ -15,9 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags, Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Flags, Args, Command } from '@oclif/core';
|
||||
import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy';
|
||||
import type {
|
||||
BalenaSDK,
|
||||
@ -53,15 +51,12 @@ export default class DeviceRestartCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'device restart <uuid>';
|
||||
|
||||
public static flags = {
|
||||
service: Flags.string({
|
||||
description:
|
||||
'comma-separated list (no blank spaces) of service names to restart',
|
||||
char: 's',
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
@ -159,7 +154,7 @@ export default class DeviceRestartCmd extends Command {
|
||||
|
||||
async restartAllServices(balena: BalenaSDK, deviceUuid: string) {
|
||||
// Note: device.restartApplication throws `BalenaDeviceNotFound: Device not found` if device not online.
|
||||
// Need to use device.get first to distinguish between non-existant and offline devices.
|
||||
// Need to use device.get first to distinguish between non-existant and disconnected devices.
|
||||
// Remove this workaround when SDK issue resolved: https://github.com/balena-io/balena-sdk/issues/649
|
||||
const { instanceOf, ExpectedError } = await import('../../errors');
|
||||
try {
|
||||
|
@ -15,8 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
|
||||
@ -43,11 +42,8 @@ export default class DeviceRmCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'device rm <uuid(s)>';
|
||||
|
||||
public static flags = {
|
||||
yes: cf.yes,
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
@ -15,8 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import { ExpectedError } from '../../errors';
|
||||
@ -36,11 +35,8 @@ export default class DeviceShutdownCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'device shutdown <uuid>';
|
||||
|
||||
public static flags = {
|
||||
force: cf.force,
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
@ -15,16 +15,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags, Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Flags, Args, Command } from '@oclif/core';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import {
|
||||
parseAsInteger,
|
||||
validateLocalHostnameOrIp,
|
||||
} from '../../utils/validation';
|
||||
|
||||
export default class SshCmd extends Command {
|
||||
export default class DeviceSSHCmd extends Command {
|
||||
public static aliases = ['ssh'];
|
||||
public static deprecateAliases = true;
|
||||
|
||||
public static description = stripIndent`
|
||||
Open a SSH prompt on a device's host OS or service container.
|
||||
|
||||
@ -53,14 +54,14 @@ export default class SshCmd extends Command {
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
'$ balena ssh MyFleet',
|
||||
'$ balena ssh f49cefd',
|
||||
'$ balena ssh f49cefd my-service',
|
||||
'$ balena ssh f49cefd --port <port>',
|
||||
'$ balena ssh 192.168.0.1 --verbose',
|
||||
'$ balena ssh f49cefd.local my-service',
|
||||
'$ echo "uptime; exit;" | balena ssh f49cefd',
|
||||
'$ echo "uptime; exit;" | balena ssh 192.168.0.1 myService',
|
||||
'$ balena device ssh MyFleet',
|
||||
'$ balena device ssh f49cefd',
|
||||
'$ balena device ssh f49cefd my-service',
|
||||
'$ balena device ssh f49cefd --port <port>',
|
||||
'$ balena device ssh 192.168.0.1 --verbose',
|
||||
'$ balena device ssh f49cefd.local my-service',
|
||||
'$ echo "uptime; exit;" | balena device ssh f49cefd',
|
||||
'$ echo "uptime; exit;" | balena device ssh 192.168.0.1 myService',
|
||||
];
|
||||
|
||||
public static args = {
|
||||
@ -75,15 +76,13 @@ export default class SshCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'ssh <fleetOrDevice> [service]';
|
||||
|
||||
public static flags = {
|
||||
port: Flags.integer({
|
||||
description: stripIndent`
|
||||
SSH server port number (default 22222) if the target is an IP address or .local
|
||||
hostname. Otherwise, port number for the balenaCloud gateway (default 22).`,
|
||||
char: 'p',
|
||||
parse: async (p) => parseAsInteger(p, 'port'),
|
||||
parse: (p) => parseAsInteger(p, 'port'),
|
||||
}),
|
||||
tty: Flags.boolean({
|
||||
default: false,
|
||||
@ -100,38 +99,42 @@ export default class SshCmd extends Command {
|
||||
default: false,
|
||||
description: 'bypass global proxy configuration for the ssh connection',
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static primary = true;
|
||||
public static offlineCompatible = true;
|
||||
|
||||
public async run() {
|
||||
const { args: params, flags: options } = await this.parse(SshCmd);
|
||||
const { args: params, flags: options } = await this.parse(DeviceSSHCmd);
|
||||
|
||||
// Local connection
|
||||
if (validateLocalHostnameOrIp(params.fleetOrDevice)) {
|
||||
const { performLocalDeviceSSH } = await import('../../utils/device/ssh');
|
||||
return await performLocalDeviceSSH({
|
||||
await performLocalDeviceSSH({
|
||||
hostname: params.fleetOrDevice,
|
||||
port: options.port || 'local',
|
||||
forceTTY: options.tty,
|
||||
verbose: options.verbose,
|
||||
service: params.service,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Remote connection
|
||||
const { getProxyConfig } = await import('../../utils/helpers');
|
||||
const { getOnlineTargetDeviceUuid } = await import('../../utils/patterns');
|
||||
const {
|
||||
getOnlineTargetDeviceUuid,
|
||||
checkLoggedIn,
|
||||
checkNotUsingOfflineMode,
|
||||
} = await import('../../utils/patterns');
|
||||
const sdk = getBalenaSdk();
|
||||
|
||||
const proxyConfig = getProxyConfig();
|
||||
const useProxy = !!proxyConfig && !options.noproxy;
|
||||
|
||||
// this will be a tunnelled SSH connection...
|
||||
await Command.checkNotUsingOfflineMode();
|
||||
await Command.checkLoggedIn();
|
||||
checkNotUsingOfflineMode();
|
||||
await checkLoggedIn();
|
||||
const deviceUuid = await getOnlineTargetDeviceUuid(
|
||||
sdk,
|
||||
params.fleetOrDevice,
|
@ -15,9 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy';
|
||||
import type { BalenaSDK } from 'balena-sdk';
|
||||
|
||||
@ -46,12 +44,6 @@ export default class DeviceStartServiceCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'device start-service <uuid>';
|
||||
|
||||
public static flags = {
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
|
@ -15,9 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy';
|
||||
import type { BalenaSDK } from 'balena-sdk';
|
||||
|
||||
@ -46,12 +44,6 @@ export default class DeviceStopServiceCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'device stop-service <uuid>';
|
||||
|
||||
public static flags = {
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
|
@ -15,9 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
|
||||
export default class DeviceTrackFleetCmd extends Command {
|
||||
@ -35,12 +33,6 @@ export default class DeviceTrackFleetCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'device track-fleet <uuid>';
|
||||
|
||||
public static flags = {
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
|
@ -15,20 +15,21 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags, Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import { Flags, Args, Command } from '@oclif/core';
|
||||
import {
|
||||
NoPortsDefinedError,
|
||||
InvalidPortMappingError,
|
||||
ExpectedError,
|
||||
} from '../../errors';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import { lowercaseIfSlug } from '../../utils/normalization';
|
||||
|
||||
import type { Server, Socket } from 'net';
|
||||
|
||||
export default class TunnelCmd extends Command {
|
||||
export default class DeviceTunnelCmd extends Command {
|
||||
public static aliases = ['tunnel'];
|
||||
public static deprecateAliases = true;
|
||||
|
||||
public static description = stripIndent`
|
||||
Tunnel local ports to your balenaOS device.
|
||||
|
||||
@ -55,19 +56,19 @@ export default class TunnelCmd extends Command {
|
||||
|
||||
public static examples = [
|
||||
'# map remote port 22222 to localhost:22222',
|
||||
'$ balena tunnel myFleet -p 22222',
|
||||
'$ balena device tunnel myFleet -p 22222',
|
||||
'',
|
||||
'# map remote port 22222 to localhost:222',
|
||||
'$ balena tunnel 2ead211 -p 22222:222',
|
||||
'$ balena device tunnel 2ead211 -p 22222:222',
|
||||
'',
|
||||
'# map remote port 22222 to any address on your host machine, port 22222',
|
||||
'$ balena tunnel 1546690 -p 22222:0.0.0.0',
|
||||
'$ balena device tunnel 1546690 -p 22222:0.0.0.0',
|
||||
'',
|
||||
'# map remote port 22222 to any address on your host machine, port 222',
|
||||
'$ balena tunnel myFleet -p 22222:0.0.0.0:222',
|
||||
'$ balena device tunnel myFleet -p 22222:0.0.0.0:222',
|
||||
'',
|
||||
'# multiple port tunnels can be specified at any one time',
|
||||
'$ balena tunnel myFleet -p 8080:3000 -p 8081:9000',
|
||||
'$ balena device tunnel myFleet -p 8080:3000 -p 8081:9000',
|
||||
];
|
||||
|
||||
public static args = {
|
||||
@ -78,8 +79,6 @@ export default class TunnelCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'tunnel <deviceOrFleet>';
|
||||
|
||||
public static flags = {
|
||||
port: Flags.string({
|
||||
description:
|
||||
@ -87,16 +86,17 @@ export default class TunnelCmd extends Command {
|
||||
char: 'p',
|
||||
multiple: true,
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static primary = true;
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
const { args: params, flags: options } = await this.parse(TunnelCmd);
|
||||
const { args: params, flags: options } = await this.parse(DeviceTunnelCmd);
|
||||
|
||||
const logger = await Command.getLogger();
|
||||
const Logger = await import('../../utils/logger');
|
||||
|
||||
const logger = Logger.getLogger();
|
||||
const sdk = getBalenaSdk();
|
||||
|
||||
const logConnection = (
|
@ -14,17 +14,16 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Flags } from '@oclif/core';
|
||||
import { Flags, Command } from '@oclif/core';
|
||||
import type { Interfaces } from '@oclif/core';
|
||||
import type * as SDK from 'balena-sdk';
|
||||
import * as _ from 'lodash';
|
||||
import Command from '../../command';
|
||||
import { ExpectedError } from '../../errors';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
|
||||
type FlagsDef = Interfaces.InferredFlags<typeof EnvsCmd.flags>;
|
||||
type FlagsDef = Interfaces.InferredFlags<typeof EnvListCmd.flags>;
|
||||
|
||||
interface EnvironmentVariableInfo extends SDK.EnvironmentVariableBase {
|
||||
fleet?: string | null; // fleet slug
|
||||
@ -46,7 +45,10 @@ interface ServiceEnvironmentVariableInfo
|
||||
serviceName?: string; // service name
|
||||
}
|
||||
|
||||
export default class EnvsCmd extends Command {
|
||||
export default class EnvListCmd extends Command {
|
||||
public static aliases = ['envs'];
|
||||
public static deprecateAliases = true;
|
||||
|
||||
public static description = stripIndent`
|
||||
List the environment or config variables of a fleet, device or service.
|
||||
|
||||
@ -84,18 +86,16 @@ export default class EnvsCmd extends Command {
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
'$ balena envs --fleet myorg/myfleet',
|
||||
'$ balena envs --fleet MyFleet --json',
|
||||
'$ balena envs --fleet MyFleet --service MyService',
|
||||
'$ balena envs --fleet MyFleet --config',
|
||||
'$ balena envs --device 7cf02a6',
|
||||
'$ balena envs --device 7cf02a6 --json',
|
||||
'$ balena envs --device 7cf02a6 --config --json',
|
||||
'$ balena envs --device 7cf02a6 --service MyService',
|
||||
'$ balena env list --fleet myorg/myfleet',
|
||||
'$ balena env list --fleet MyFleet --json',
|
||||
'$ balena env list --fleet MyFleet --service MyService',
|
||||
'$ balena env list --fleet MyFleet --config',
|
||||
'$ balena env list --device 7cf02a6',
|
||||
'$ balena env list --device 7cf02a6 --json',
|
||||
'$ balena env list --device 7cf02a6 --config --json',
|
||||
'$ balena env list --device 7cf02a6 --service MyService',
|
||||
];
|
||||
|
||||
public static usage = 'envs';
|
||||
|
||||
public static flags = {
|
||||
fleet: { ...cf.fleet, exclusive: ['device'] },
|
||||
config: Flags.boolean({
|
||||
@ -105,17 +105,18 @@ export default class EnvsCmd extends Command {
|
||||
exclusive: ['service'],
|
||||
}),
|
||||
device: { ...cf.device, exclusive: ['fleet'] },
|
||||
help: cf.help,
|
||||
json: cf.json,
|
||||
service: { ...cf.service, exclusive: ['config'] },
|
||||
};
|
||||
|
||||
public async run() {
|
||||
const { flags: options } = await this.parse(EnvsCmd);
|
||||
const { flags: options } = await this.parse(EnvListCmd);
|
||||
|
||||
const variables: EnvironmentVariableInfo[] = [];
|
||||
|
||||
await Command.checkLoggedIn();
|
||||
const { checkLoggedIn } = await import('../../utils/patterns');
|
||||
|
||||
await checkLoggedIn();
|
||||
|
||||
if (!options.fleet && !options.device) {
|
||||
throw new ExpectedError('Missing --fleet or --device option');
|
14
src/commands/env/rename.ts
vendored
14
src/commands/env/rename.ts
vendored
@ -14,10 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import * as ec from '../../utils/env-common';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import { parseAsInteger } from '../../utils/validation';
|
||||
@ -44,7 +41,7 @@ export default class EnvRenameCmd extends Command {
|
||||
id: Args.integer({
|
||||
required: true,
|
||||
description: "variable's numeric database ID",
|
||||
parse: async (input) => parseAsInteger(input, 'id'),
|
||||
parse: (input) => parseAsInteger(input, 'id'),
|
||||
}),
|
||||
value: Args.string({
|
||||
required: true,
|
||||
@ -53,19 +50,18 @@ export default class EnvRenameCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'env rename <id> <value>';
|
||||
|
||||
public static flags = {
|
||||
config: ec.booleanConfig,
|
||||
device: ec.booleanDevice,
|
||||
service: ec.booleanService,
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public async run() {
|
||||
const { args: params, flags: opt } = await this.parse(EnvRenameCmd);
|
||||
|
||||
await Command.checkLoggedIn();
|
||||
const { checkLoggedIn } = await import('../../utils/patterns');
|
||||
|
||||
await checkLoggedIn();
|
||||
|
||||
await getBalenaSdk().pine.patch({
|
||||
resource: ec.getVarResourceName(opt.config, opt.device, opt.service),
|
||||
|
12
src/commands/env/rm.ts
vendored
12
src/commands/env/rm.ts
vendored
@ -15,9 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags, Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
|
||||
import { Flags, Args, Command } from '@oclif/core';
|
||||
import * as ec from '../../utils/env-common';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import { parseAsInteger } from '../../utils/validation';
|
||||
@ -48,12 +46,10 @@ export default class EnvRmCmd extends Command {
|
||||
id: Args.integer({
|
||||
required: true,
|
||||
description: "variable's numeric database ID",
|
||||
parse: async (input) => parseAsInteger(input, 'id'),
|
||||
parse: (input) => parseAsInteger(input, 'id'),
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'env rm <id>';
|
||||
|
||||
public static flags = {
|
||||
config: ec.booleanConfig,
|
||||
device: ec.booleanDevice,
|
||||
@ -69,7 +65,9 @@ export default class EnvRmCmd extends Command {
|
||||
public async run() {
|
||||
const { args: params, flags: opt } = await this.parse(EnvRmCmd);
|
||||
|
||||
await Command.checkLoggedIn();
|
||||
const { checkLoggedIn } = await import('../../utils/patterns');
|
||||
|
||||
await checkLoggedIn();
|
||||
|
||||
const { confirm } = await import('../../utils/patterns');
|
||||
await confirm(
|
||||
|
@ -15,9 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import type * as BalenaSdk from 'balena-sdk';
|
||||
import Command from '../../command';
|
||||
import { ExpectedError } from '../../errors';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
@ -26,7 +25,6 @@ import { applicationIdInfo } from '../../utils/messages';
|
||||
interface FlagsDef {
|
||||
fleet?: string;
|
||||
device?: string; // device UUID
|
||||
help: void;
|
||||
quiet: boolean;
|
||||
service?: string; // service name
|
||||
}
|
||||
@ -36,11 +34,14 @@ interface ArgsDef {
|
||||
value?: string;
|
||||
}
|
||||
|
||||
export default class EnvAddCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Add env or config variable to fleets, devices or services.
|
||||
export default class EnvSetCmd extends Command {
|
||||
public static aliases = ['env add'];
|
||||
public static deprecateAliases = true;
|
||||
|
||||
Add an environment or config variable to one or more fleets, devices or
|
||||
public static description = stripIndent`
|
||||
Add or update env or config variable to fleets, devices or services.
|
||||
|
||||
Add or update an environment or config variable to one or more fleets, devices or
|
||||
services, as selected by the respective command-line options. Either the
|
||||
--fleet or the --device option must be provided, and either may be be
|
||||
used alongside the --service option to define a service-specific variable.
|
||||
@ -67,15 +68,15 @@ export default class EnvAddCmd extends Command {
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
'$ balena env add TERM --fleet MyFleet',
|
||||
'$ balena env add EDITOR vim -f myorg/myfleet',
|
||||
'$ balena env add EDITOR vim --fleet MyFleet,MyFleet2',
|
||||
'$ balena env add EDITOR vim --fleet MyFleet --service MyService',
|
||||
'$ balena env add EDITOR vim --fleet MyFleet,MyFleet2 --service MyService,MyService2',
|
||||
'$ balena env add EDITOR vim --device 7cf02a6',
|
||||
'$ balena env add EDITOR vim --device 7cf02a6,d6f1433',
|
||||
'$ balena env add EDITOR vim --device 7cf02a6 --service MyService',
|
||||
'$ balena env add EDITOR vim --device 7cf02a6,d6f1433 --service MyService,MyService2',
|
||||
'$ balena env set TERM --fleet MyFleet',
|
||||
'$ balena env set EDITOR vim -f myorg/myfleet',
|
||||
'$ balena env set EDITOR vim --fleet MyFleet,MyFleet2',
|
||||
'$ balena env set EDITOR vim --fleet MyFleet --service MyService',
|
||||
'$ balena env set EDITOR vim --fleet MyFleet,MyFleet2 --service MyService,MyService2',
|
||||
'$ balena env set EDITOR vim --device 7cf02a6',
|
||||
'$ balena env set EDITOR vim --device 7cf02a6,d6f1433',
|
||||
'$ balena env set EDITOR vim --device 7cf02a6 --service MyService',
|
||||
'$ balena env set EDITOR vim --device 7cf02a6,d6f1433 --service MyService,MyService2',
|
||||
];
|
||||
|
||||
public static args = {
|
||||
@ -92,18 +93,16 @@ export default class EnvAddCmd extends Command {
|
||||
|
||||
// Required for supporting empty string ('') `value` args.
|
||||
public static strict = false;
|
||||
public static usage = 'env add <name> [value]';
|
||||
|
||||
public static flags = {
|
||||
fleet: { ...cf.fleet, exclusive: ['device'] },
|
||||
device: { ...cf.device, exclusive: ['fleet'] },
|
||||
help: cf.help,
|
||||
quiet: cf.quiet,
|
||||
service: cf.service,
|
||||
};
|
||||
|
||||
public async run() {
|
||||
const { args: params, flags: options } = await this.parse(EnvAddCmd);
|
||||
const { args: params, flags: options } = await this.parse(EnvSetCmd);
|
||||
const cmd = this;
|
||||
|
||||
if (!options.fleet && !options.device) {
|
||||
@ -112,7 +111,9 @@ export default class EnvAddCmd extends Command {
|
||||
);
|
||||
}
|
||||
|
||||
await Command.checkLoggedIn();
|
||||
const { checkLoggedIn } = await import('../../utils/patterns');
|
||||
|
||||
await checkLoggedIn();
|
||||
|
||||
if (params.value == null) {
|
||||
params.value = process.env[params.name];
|
@ -15,10 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags, Args } from '@oclif/core';
|
||||
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Flags, Args, Command } from '@oclif/core';
|
||||
import { stripIndent } from '../../utils/lazy';
|
||||
|
||||
export default class FleetCreateCmd extends Command {
|
||||
@ -30,10 +27,10 @@ export default class FleetCreateCmd extends Command {
|
||||
You can specify the organization the fleet should belong to using
|
||||
the \`--organization\` option. The organization's handle, not its name,
|
||||
should be provided. Organization handles can be listed with the
|
||||
\`balena orgs\` command.
|
||||
\`balena organization list\` command.
|
||||
|
||||
The fleet's default device type is specified with the \`--type\` option.
|
||||
The \`balena devices supported\` command can be used to list the available
|
||||
The \`balena device-type list\` command can be used to list the available
|
||||
device types.
|
||||
|
||||
Interactive dropdowns will be shown for selection if no device type or
|
||||
@ -56,8 +53,6 @@ export default class FleetCreateCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'fleet create <name>';
|
||||
|
||||
public static flags = {
|
||||
organization: Flags.string({
|
||||
char: 'o',
|
||||
@ -66,9 +61,8 @@ export default class FleetCreateCmd extends Command {
|
||||
type: Flags.string({
|
||||
char: 't',
|
||||
description:
|
||||
'fleet device type (Check available types with `balena devices supported`)',
|
||||
'fleet device type (Check available types with `balena device-type list`)',
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
@ -15,9 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags } from '@oclif/core';
|
||||
|
||||
import Command from '../../command';
|
||||
import { Flags, Command } from '@oclif/core';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import * as ca from '../../utils/common-args';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
||||
@ -41,10 +39,7 @@ export default class FleetCmd extends Command {
|
||||
fleet: ca.fleetRequired,
|
||||
};
|
||||
|
||||
public static usage = 'fleet <fleet>';
|
||||
|
||||
public static flags = {
|
||||
help: cf.help,
|
||||
view: Flags.boolean({
|
||||
default: false,
|
||||
description: 'open fleet dashboard page',
|
||||
|
@ -16,10 +16,9 @@
|
||||
*/
|
||||
|
||||
import type * as BalenaSdk from 'balena-sdk';
|
||||
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
||||
import { Command } from '@oclif/core';
|
||||
|
||||
interface ExtendedApplication extends ApplicationWithDeviceTypeSlug {
|
||||
device_count: number;
|
||||
@ -27,7 +26,10 @@ interface ExtendedApplication extends ApplicationWithDeviceTypeSlug {
|
||||
device_type?: string;
|
||||
}
|
||||
|
||||
export default class FleetsCmd extends Command {
|
||||
export default class FleetListCmd extends Command {
|
||||
public static aliases = ['fleets'];
|
||||
public static deprecateAliases = true;
|
||||
|
||||
public static description = stripIndent`
|
||||
List all fleets.
|
||||
|
||||
@ -37,12 +39,9 @@ export default class FleetsCmd extends Command {
|
||||
\`balena fleet <fleet>\`
|
||||
`;
|
||||
|
||||
public static examples = ['$ balena fleets'];
|
||||
|
||||
public static usage = 'fleets';
|
||||
public static examples = ['$ balena fleet list'];
|
||||
|
||||
public static flags = {
|
||||
help: cf.help,
|
||||
json: cf.json,
|
||||
};
|
||||
|
||||
@ -50,7 +49,7 @@ export default class FleetsCmd extends Command {
|
||||
public static primary = true;
|
||||
|
||||
public async run() {
|
||||
const { flags: options } = await this.parse(FleetsCmd);
|
||||
const { flags: options } = await this.parse(FleetListCmd);
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
@ -15,9 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import { getExpandedProp } from '../../utils/pine';
|
||||
|
||||
@ -44,12 +42,6 @@ export default class FleetPinCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'fleet pin <slug> [releaseToPinTo]';
|
||||
|
||||
public static flags = {
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
@ -79,7 +71,7 @@ export default class FleetPinCmd extends Command {
|
||||
pinnedRelease
|
||||
? `This fleet is currently pinned to ${pinnedRelease}.`
|
||||
: 'This fleet is not currently pinned to any release.'
|
||||
} \n\nTo see a list of all releases this fleet can be pinned to, run \`balena releases ${slug}\`.`,
|
||||
} \n\nTo see a list of all releases this fleet can be pinned to, run \`balena release list ${slug}\`.`,
|
||||
);
|
||||
} else {
|
||||
await balena.models.application.pinToRelease(slug, releaseToPinTo);
|
||||
|
@ -15,8 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Command } from '@oclif/core';
|
||||
import * as ca from '../../utils/common-args';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
@ -40,12 +39,6 @@ export default class FleetPurgeCmd extends Command {
|
||||
fleet: ca.fleetRequired,
|
||||
};
|
||||
|
||||
public static usage = 'fleet purge <fleet>';
|
||||
|
||||
public static flags = {
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
|
@ -15,10 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import * as ca from '../../utils/common-args';
|
||||
import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
@ -48,12 +45,6 @@ export default class FleetRenameCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'fleet rename <fleet> [newName]';
|
||||
|
||||
public static flags = {
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
|
@ -15,8 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Command } from '@oclif/core';
|
||||
import * as ca from '../../utils/common-args';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
@ -39,12 +38,6 @@ export default class FleetRestartCmd extends Command {
|
||||
fleet: ca.fleetRequired,
|
||||
};
|
||||
|
||||
public static usage = 'fleet restart <fleet>';
|
||||
|
||||
public static flags = {
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
|
@ -15,11 +15,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import * as ca from '../../utils/common-args';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
import { Command } from '@oclif/core';
|
||||
|
||||
export default class FleetRmCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
@ -42,11 +42,8 @@ export default class FleetRmCmd extends Command {
|
||||
fleet: ca.fleetRequired,
|
||||
};
|
||||
|
||||
public static usage = 'fleet rm <fleet>';
|
||||
|
||||
public static flags = {
|
||||
yes: cf.yes,
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
@ -15,9 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
|
||||
export default class FleetTrackLatestCmd extends Command {
|
||||
@ -38,12 +36,6 @@ export default class FleetTrackLatestCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'fleet track-latest <slug>';
|
||||
|
||||
public static flags = {
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
|
@ -15,10 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import { stripIndent } from '../../utils/lazy';
|
||||
import { CommandHelp } from '../../utils/oclif-utils';
|
||||
|
||||
// 'Internal' commands are called during the execution of other commands.
|
||||
// `osinit` is called during `os initialize`
|
||||
@ -48,11 +46,6 @@ export default class OsinitCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = (
|
||||
'internal osinit ' +
|
||||
new CommandHelp({ args: OsinitCmd.args }).defaultUsage()
|
||||
).trim();
|
||||
|
||||
public static hidden = true;
|
||||
public static root = true;
|
||||
public static offlineCompatible = true;
|
||||
|
@ -15,8 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args, Flags } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import { Args, Flags, Command } from '@oclif/core';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
@ -60,16 +59,12 @@ export default class JoinCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
// Hardcoded to preserve camelcase
|
||||
public static usage = 'join [deviceIpOrHostname]';
|
||||
|
||||
public static flags = {
|
||||
fleet: cf.fleet,
|
||||
pollInterval: Flags.integer({
|
||||
description: 'the interval in minutes to check for updates',
|
||||
char: 'i',
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
@ -80,7 +75,8 @@ export default class JoinCmd extends Command {
|
||||
|
||||
const promote = await import('../../utils/promote');
|
||||
const sdk = getBalenaSdk();
|
||||
const logger = await Command.getLogger();
|
||||
const Logger = await import('../../utils/logger');
|
||||
const logger = Logger.getLogger();
|
||||
return promote.join(
|
||||
logger,
|
||||
sdk,
|
||||
|
@ -15,9 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import { stripIndent } from '../../utils/lazy';
|
||||
import { parseAsLocalHostnameOrIp } from '../../utils/validation';
|
||||
|
||||
@ -50,12 +48,6 @@ export default class LeaveCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'leave [deviceIpOrHostname]';
|
||||
|
||||
public static flags = {
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
public static primary = true;
|
||||
|
||||
@ -63,7 +55,8 @@ export default class LeaveCmd extends Command {
|
||||
const { args: params } = await this.parse(LeaveCmd);
|
||||
|
||||
const promote = await import('../../utils/promote');
|
||||
const logger = await Command.getLogger();
|
||||
const Logger = await import('../../utils/logger');
|
||||
const logger = Logger.getLogger();
|
||||
return promote.leave(logger, params.deviceIpOrHostname);
|
||||
}
|
||||
}
|
||||
|
@ -15,10 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import { promisify } from 'util';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import { stripIndent } from '../../utils/lazy';
|
||||
|
||||
export default class LocalConfigureCmd extends Command {
|
||||
@ -40,12 +37,6 @@ export default class LocalConfigureCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'local configure <target>';
|
||||
|
||||
public static flags = {
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static root = true;
|
||||
public static offlineCompatible = true;
|
||||
|
||||
@ -245,7 +236,7 @@ export default class LocalConfigureCmd extends Command {
|
||||
const bootPartition = await getBootPartition(target);
|
||||
|
||||
const files = await imagefs.interact(target, bootPartition, async (_fs) => {
|
||||
return await promisify(_fs.readdir)(this.CONNECTIONS_FOLDER);
|
||||
return await _fs.promises.readdir(this.CONNECTIONS_FOLDER);
|
||||
});
|
||||
|
||||
let connectionFileName;
|
||||
@ -254,13 +245,11 @@ export default class LocalConfigureCmd extends Command {
|
||||
} else if (_.includes(files, 'resin-sample.ignore')) {
|
||||
// Fresh image, new mode, accoding to https://github.com/balena-os/meta-balena/pull/770/files
|
||||
await imagefs.interact(target, bootPartition, async (_fs) => {
|
||||
const readFileAsync = promisify(_fs.readFile);
|
||||
const writeFileAsync = promisify(_fs.writeFile);
|
||||
const contents = await readFileAsync(
|
||||
const contents = await _fs.promises.readFile(
|
||||
`${this.CONNECTIONS_FOLDER}/resin-sample.ignore`,
|
||||
{ encoding: 'utf8' },
|
||||
);
|
||||
return await writeFileAsync(
|
||||
await _fs.promises.writeFile(
|
||||
`${this.CONNECTIONS_FOLDER}/resin-wifi`,
|
||||
contents,
|
||||
);
|
||||
@ -277,13 +266,13 @@ export default class LocalConfigureCmd extends Command {
|
||||
} else {
|
||||
// In case there's no file at all (shouldn't happen normally, but the file might have been removed)
|
||||
await imagefs.interact(target, bootPartition, async (_fs) => {
|
||||
return await promisify(_fs.writeFile)(
|
||||
await _fs.promises.writeFile(
|
||||
`${this.CONNECTIONS_FOLDER}/resin-wifi`,
|
||||
this.CONNECTION_FILE,
|
||||
);
|
||||
});
|
||||
}
|
||||
return await this.getConfigurationSchema(bootPartition, connectionFileName);
|
||||
return this.getConfigurationSchema(bootPartition, connectionFileName);
|
||||
}
|
||||
|
||||
async removeHostname(schema: any) {
|
||||
|
@ -15,9 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import type { BlockDevice } from 'etcher-sdk/build/source-destination';
|
||||
import Command from '../../command';
|
||||
import { ExpectedError } from '../../errors';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getChalk, getVisuals, stripIndent } from '../../utils/lazy';
|
||||
@ -46,12 +45,9 @@ export default class LocalFlashCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'local flash <image>';
|
||||
|
||||
public static flags = {
|
||||
drive: cf.drive,
|
||||
yes: cf.yes,
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static offlineCompatible = true;
|
||||
|
@ -15,9 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags, Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Flags, Args, Command } from '@oclif/core';
|
||||
import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy';
|
||||
import { ExpectedError } from '../../errors';
|
||||
import type { WhoamiResult } from 'balena-sdk';
|
||||
@ -30,7 +28,6 @@ interface FlagsDef {
|
||||
user?: string;
|
||||
password?: string;
|
||||
port?: number;
|
||||
help: void;
|
||||
hideExperimentalWarning: boolean;
|
||||
}
|
||||
|
||||
@ -63,8 +60,6 @@ export default class LoginCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'login';
|
||||
|
||||
public static flags = {
|
||||
web: Flags.boolean({
|
||||
default: false,
|
||||
@ -114,7 +109,6 @@ export default class LoginCmd extends Command {
|
||||
default: false,
|
||||
description: 'Hides warning for experimental features',
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static primary = true;
|
||||
@ -138,7 +132,7 @@ export default class LoginCmd extends Command {
|
||||
// We can safely assume this won't be undefined as doLogin will throw if this call fails
|
||||
// We also don't need to worry too much about the amount of calls to whoami
|
||||
// as these are cached by the SDK
|
||||
const whoamiResult = (await balena.auth.whoami()) as WhoamiResult;
|
||||
const whoamiResult = (await balena.auth.whoami())!;
|
||||
|
||||
if (whoamiResult.actorType !== 'user' && !options.hideExperimentalWarning) {
|
||||
console.info(stripIndent`
|
||||
@ -174,7 +168,7 @@ ${messages.reachingOut}`);
|
||||
|
||||
async doLogin(
|
||||
loginOptions: FlagsDef,
|
||||
balenaUrl: string = 'balena-cloud.com',
|
||||
balenaUrl = 'balena-cloud.com',
|
||||
token?: string,
|
||||
): Promise<void> {
|
||||
// Token
|
||||
|
@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Command from '../../command';
|
||||
import { Command } from '@oclif/core';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
|
||||
export default class LogoutCmd extends Command {
|
||||
@ -26,8 +26,6 @@ export default class LogoutCmd extends Command {
|
||||
`;
|
||||
public static examples = ['$ balena logout'];
|
||||
|
||||
public static usage = 'logout';
|
||||
|
||||
public async run() {
|
||||
await this.parse(LogoutCmd);
|
||||
await getBalenaSdk().auth.logout();
|
||||
|
@ -15,28 +15,24 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Command } from '@oclif/core';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
||||
|
||||
export default class OrgsCmd extends Command {
|
||||
export default class OrganizationListCmd extends Command {
|
||||
public static aliases = ['orgs'];
|
||||
public static deprecateAliases = true;
|
||||
|
||||
public static description = stripIndent`
|
||||
List all organizations.
|
||||
|
||||
list all the organizations that you are a member of.
|
||||
`;
|
||||
public static examples = ['$ balena orgs'];
|
||||
|
||||
public static usage = 'orgs';
|
||||
|
||||
public static flags = {
|
||||
help: cf.help,
|
||||
};
|
||||
public static examples = ['$ balena organization list'];
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
await this.parse(OrgsCmd);
|
||||
await this.parse(OrganizationListCmd);
|
||||
|
||||
const { getOwnOrganizations } = await import('../../utils/sdk');
|
||||
|
@ -15,9 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags, Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Flags, Args, Command } from '@oclif/core';
|
||||
import { getCliForm, stripIndent } from '../../utils/lazy';
|
||||
import * as _ from 'lodash';
|
||||
import type { DeviceTypeJson } from 'balena-sdk';
|
||||
@ -46,8 +44,6 @@ export default class OsBuildConfigCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'os build-config <image> <device-type>';
|
||||
|
||||
public static flags = {
|
||||
advanced: Flags.boolean({
|
||||
description: 'show advanced configuration options',
|
||||
@ -58,7 +54,6 @@ export default class OsBuildConfigCmd extends Command {
|
||||
char: 'o',
|
||||
required: true,
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
@ -15,12 +15,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags, Args } from '@oclif/core';
|
||||
import { Flags, Args, Command } from '@oclif/core';
|
||||
import type { Interfaces } from '@oclif/core';
|
||||
import type * as BalenaSdk from 'balena-sdk';
|
||||
import { promisify } from 'util';
|
||||
import * as _ from 'lodash';
|
||||
import Command from '../../command';
|
||||
import { ExpectedError } from '../../errors';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy';
|
||||
@ -92,8 +90,6 @@ export default class OsConfigureCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'os configure <image>';
|
||||
|
||||
public static flags = {
|
||||
advanced: Flags.boolean({
|
||||
char: 'v',
|
||||
@ -157,7 +153,6 @@ export default class OsConfigureCmd extends Command {
|
||||
'expiry date assigned to generated provisioning api key (format: YYYY-MM-DD)',
|
||||
exclusive: ['config', 'device'],
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
@ -296,7 +291,7 @@ export default class OsConfigureCmd extends Command {
|
||||
|
||||
for (const { name, content } of files) {
|
||||
await imagefs.interact(image, bootPartition, async (_fs) => {
|
||||
return await promisify(_fs.writeFile)(
|
||||
await _fs.promises.writeFile(
|
||||
path.join(CONNECTIONS_FOLDER, name),
|
||||
content,
|
||||
);
|
||||
@ -321,7 +316,9 @@ async function validateOptions(options: FlagsDef) {
|
||||
);
|
||||
}
|
||||
|
||||
await Command.checkLoggedIn();
|
||||
const { checkLoggedIn } = await import('../../utils/patterns');
|
||||
|
||||
await checkLoggedIn();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -15,9 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Flags, Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { Flags, Args, Command } from '@oclif/core';
|
||||
import { stripIndent } from '../../utils/lazy';
|
||||
|
||||
export default class OsDownloadCmd extends Command {
|
||||
@ -25,7 +23,7 @@ export default class OsDownloadCmd extends Command {
|
||||
Download an unconfigured OS image.
|
||||
|
||||
Download an unconfigured OS image for the specified device type.
|
||||
Check available device types with 'balena devices supported'.
|
||||
Check available device types with 'balena device-type list'.
|
||||
|
||||
Note: Currently this command only works with balenaCloud, not openBalena.
|
||||
If using openBalena, please download the OS from: https://www.balena.io/os/
|
||||
@ -62,8 +60,6 @@ export default class OsDownloadCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'os download <type>';
|
||||
|
||||
public static flags = {
|
||||
output: Flags.string({
|
||||
description: 'output path',
|
||||
@ -81,7 +77,6 @@ export default class OsDownloadCmd extends Command {
|
||||
or 'menu-esr' (interactive menu, ESR versions)
|
||||
`,
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public async run() {
|
||||
@ -92,7 +87,8 @@ export default class OsDownloadCmd extends Command {
|
||||
const { isESR } = await import('../../utils/image-manager');
|
||||
if (options.version === 'menu-esr' || isESR(options.version)) {
|
||||
try {
|
||||
await OsDownloadCmd.checkLoggedIn();
|
||||
const { checkLoggedIn } = await import('../../utils/patterns');
|
||||
await checkLoggedIn();
|
||||
} catch (e) {
|
||||
const { ExpectedError, NotLoggedInError } = await import(
|
||||
'../../errors'
|
||||
|
@ -15,8 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Args } from '@oclif/core';
|
||||
import Command from '../../command';
|
||||
import { Args, Command } from '@oclif/core';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getCliForm, stripIndent } from '../../utils/lazy';
|
||||
|
||||
@ -48,13 +47,10 @@ export default class OsInitializeCmd extends Command {
|
||||
}),
|
||||
};
|
||||
|
||||
public static usage = 'os initialize <image>';
|
||||
|
||||
public static flags = {
|
||||
type: cf.deviceType,
|
||||
drive: cf.drive,
|
||||
yes: cf.yes,
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user