Compare commits

...

98 Commits

Author SHA1 Message Date
d2150c5cb7 v17.2.4 2023-11-06 19:12:02 +00:00
70e113152d Merge pull request #2696 from balena-io/fix-dsutils
Dedupe shrinkwrap
2023-11-06 16:11:13 -03:00
4a64102d67 Dedupe shrinkwrap
Change-type: patch
2023-11-06 14:11:38 -03:00
9bf267166e v17.2.3 2023-11-03 16:34:18 +00:00
f12249bc81 Merge pull request #2694 from balena-io/vipul/add-them-docs
Generate docs for recently supported commands
2023-11-03 16:33:14 +00:00
80d6c71b02 Update CONTRIBUTING.md with docs instructions
Signed-off-by: Vipul Gupta (@vipulgupta2048) <vipulgupta2048@gmail.com>
2023-11-03 20:39:25 +05:30
9ef4117fb8 Generate docs for recently supported commands
Added documentation for `balena fleet pin` and other recently supported commands

Change-type: patch
Signed-off-by: Vipul Gupta (@vipulgupta2048) <vipulgupta2048@gmail.com>
2023-11-03 20:28:19 +05:30
25f3bf1fbe v17.2.2 2023-10-30 12:25:19 +00:00
fe65351666 Merge pull request #2672 from balena-io/bump-eslint
Bump @balena/lint
2023-10-30 12:24:13 +00:00
7d13946c3e Bump balena-lint to 7.2.1
Change-type: patch
2023-10-30 07:45:51 -04:00
1ffa8d38f1 v17.2.1 2023-10-26 21:28:48 +00:00
6914d39370 Merge pull request #2691 from balena-io/bump-balena-sdk
Update balena-sdk and deduped dependencies
2023-10-26 21:27:22 +00:00
04db9c7a91 Update balena-sdk and deduped dependencies
Update balena-sdk from 18.0.0 to 19.0.0
Update balena-preload from 14.0.3 to 15.0.1
Update balena-image-manager from 9.0.2 to 10.0.1

Change-type: patch
2023-10-26 16:27:43 -03:00
c785d01a1c v17.2.0 2023-10-20 13:45:59 +00:00
87ba364f89 Merge pull request #2685 from bbugh/add-releases-json
Add --json option to `release` and `releases`
2023-10-20 10:44:52 -03:00
b7e5915c7a release: Add '--json' option for JSON output
change-type: minor
2023-10-20 07:42:39 -05:00
0cef6b8f87 releases: Add '--json' option for JSON output 2023-10-20 08:05:36 -03:00
71f1dbd80a v17.1.7 2023-10-20 10:49:19 +00:00
36edcf0cb8 Merge pull request #2688 from balena-io/emty-string-env-tag-values
Support empty string values for `env add` and `tag set`
2023-10-20 13:48:23 +03:00
8204dcad93 tag set: Fix using empty string as a value
Change-type: patch
2023-10-20 13:11:12 +03:00
03bcb6cff7 env add: Fix using empty string as a value
Change-type: patch
2023-10-20 13:11:12 +03:00
66b6eed57c Add tests for empty & non-provided value for the env add command
Change-type: patch
2023-10-20 13:11:12 +03:00
99b0f2c022 Add tests for the tag set command
Change-type: patch
2023-10-20 13:06:39 +03:00
947a9bade7 v17.1.6 2023-10-18 18:16:11 +00:00
15ea601d68 Merge pull request #2686 from balena-io/klutchell-patch-3
Allow custom actions for external contributors
2023-10-18 18:14:54 +00:00
ce888966dd Update npm shrinkwrap
Signed-off-by: Kyle Harding <kyle@balena.io>
2023-10-18 13:04:02 -04:00
3fc6eb7a47 Allow custom actions for external contributors
Change-type: patch
2023-10-18 12:53:43 -04:00
73aebf10bf v17.1.5 2023-10-05 10:54:36 +00:00
292c372eb4 Merge pull request #2682 from balena-io/fix-ssh-service-stdin
Fix ssh ignoreStdin device argument
2023-10-05 07:53:30 -03:00
dc3261d9c7 Fix ssh ignoreStdin device argument
Change-type: patch
2023-10-05 07:05:53 -03:00
301d0ab3a0 v17.1.4 2023-09-29 12:40:52 +00:00
efe0a018d7 Merge pull request #2679 from balena-io/bump-oclif-to-latest
Bump oclif to ^3.17.1
2023-09-29 12:39:46 +00:00
fa2a232e5f Bump oclif to ^3.17.1
Change-type: patch
2023-09-28 18:35:59 -03:00
da1f022df9 v17.1.3 2023-09-26 13:58:00 +00:00
8e6a6b81d9 Merge pull request #2671 from balena-io/stop-depending-on-old-oclif-libs
Stop depending on old oclif libs and migrate to @oclif/core directly
2023-09-26 13:56:38 +00:00
77906c4152 Move to @oclif/core v2
Change-type: patch
2023-09-26 09:50:39 -03:00
26bc68753b Remove direct dependency to @oclif/config
Change-type: patch
2023-09-26 09:48:13 -03:00
af6b263f7a v17.1.2 2023-09-26 12:14:31 +00:00
a27e216e44 Merge pull request #2678 from balena-io/fix-preload-with-commit-hash
Fix preload with commit hash
2023-09-26 12:13:24 +00:00
50e1efa448 Fix preload with commit hash
Update balena-preload from 14.0.2 to 14.0.3

Change-type: patch
2023-09-26 07:45:56 -03:00
519ac0383a v17.1.1 2023-09-05 15:06:14 +00:00
3d0ef9bc4f Merge pull request #2669 from balena-io/fixes-local-flash-on-unix
Fix local flash on unix environments
2023-09-05 15:05:16 +00:00
49e23464f9 Fix local flash on unix environments
Update etcher-sdk from 8.5.3 to 8.7.0
Change-type: patch
2023-09-05 11:26:06 -03:00
a1c9b4b80e v17.1.0 2023-09-05 13:01:03 +00:00
2b1be3e5d9 Merge pull request #2647 from balena-io/update-oclif
Update oclif, improve help command
2023-09-05 08:59:52 -04:00
e46378ec51 Update oclif, improve help command
Change-type: minor
2023-09-04 21:07:05 -03:00
27ee9c85e7 v17.0.0 2023-08-29 11:45:54 +00:00
21b6ec46e3 Merge pull request #2667 from balena-io/bump-cli-to-node-v18
Update to Node 18
2023-08-29 11:44:48 +00:00
817ce5dc96 Update to Node 18
Change-type: major
2023-08-29 07:35:53 -03:00
d9af28bca7 v16.8.0 2023-08-25 17:47:44 +00:00
8646be7979 Merge pull request #2665 from balena-io/accept-device-application-keys-as-experimental-feature
Accept device & application keys on login as experimental feature
2023-08-25 17:46:52 +00:00
14ba287e0d Accept device & application keys on login as experimental feature
Change-type: minor
2023-08-23 11:12:06 -03:00
1671e46d99 v16.7.9 2023-08-22 13:33:30 +00:00
507333c463 Merge pull request #2663 from balena-io/bumps-sdk-to-v18
Update balena-sdk to v18
2023-08-22 10:32:34 -03:00
8b320d3e9e Update balena-sdk to v18
Update balena-sdk from 17.21.1 to 18.0.0
Update balena-preload from 14.0.0 to 14.0.2
Update balena-image-manager from 9.0.0 to 9.0.2
Change-type: patch
2023-08-22 09:38:39 -03:00
e1be268749 v16.7.8 2023-08-22 11:16:38 +00:00
1a0019e6d0 Merge pull request #2662 from balena-io/otaviojacobi/bumps-to-sdk-17-12-1
Update balena-sdk to ^17.12.1
2023-08-22 11:15:33 +00:00
e79cdb671f Update balena-settings-storage to 8.1.0
Update balena-settings-storage from 7.0.0 to 8.1.0
Change-type: patch
2023-08-21 14:42:28 -03:00
f38e643cf0 env: Stop fetching unnecessary app fields
Change-type: patch
2023-08-21 14:39:16 -03:00
b8e190cd1d Remove redundant envs documentation
Change-type: patch
2023-08-21 14:39:16 -03:00
9cca654bd5 Update balena-sdk to 17.12.1
Update balena-sdk from 17.8.0 to 17.12.1
Change-type: patch
2023-08-21 14:39:16 -03:00
35177e2d2f v16.7.7 2023-08-21 16:56:33 +00:00
1a24b193e7 Merge pull request #2664 from balena-io/otavio-revert-flowzone-to-master
Revert flowzone to master
2023-08-21 16:55:31 +00:00
272915192b Revert flowzone to master
Change-type: patch
2023-08-17 20:22:53 -03:00
96774f4c52 v16.7.6 2023-07-24 13:38:06 +00:00
a034f585ba Merge pull request #2656 from balena-io/fix-app-create
app create: Fix halting with a deprecation warning
2023-07-24 13:37:00 +00:00
365d95c36b app create: Fix halting with a deprecation warning
Change-type: patch
2023-07-21 10:02:32 +03:00
c6313c08ae v16.7.5 2023-07-21 06:59:22 +00:00
f5764c4659 Merge pull request #2655 from balena-io/abstract-fleet-app-block-create
Abstract the fleet/app/block create commands
2023-07-21 09:58:36 +03:00
aff094575b Abstract the fleet/app/block create commands
Change-type: patch
2023-07-20 16:14:52 +03:00
4aaaf64f8d v16.7.4 2023-07-20 10:41:09 +00:00
7b88ce273f Merge pull request #2654 from balena-io/move-discontinued-dt
move: Include fleets of discontinued device types in the fleet selection
2023-07-20 10:40:12 +00:00
b011af89ad move: Include fleets of discontinued device types in the fleet selection
Change-type: patch
2023-07-20 13:03:54 +03:00
1bf8c1bfe7 v16.7.3 2023-07-20 08:30:09 +00:00
2b39d5d111 Merge pull request #2653 from balena-io/promote-discontinued-dt
promote: Allow joining fleets of discontinued device types
2023-07-20 08:29:20 +00:00
98663af7f6 Rerun npm-shrinkwrap.json deduplication 2023-07-19 19:44:43 +03:00
5628824bee promote: Allow joining fleets of discontinued device types
Change-type: patch
2023-07-19 19:17:27 +03:00
d12d7996bc v16.7.2 2023-07-19 01:43:55 +00:00
0dcf4cbff6 Merge pull request #2650 from balena-io/bump-balena-compose
Update balena-compose to v3.0.2
2023-07-19 01:42:56 +00:00
884e37d242 Update balena-compose to v3.0.2
Update balena-compose to v3.0.2

That release removes the use of the `cachefrom` on pull tasks, which
there is good evidence to suggest is the cause of #2165

Change-type: patch
2023-07-18 18:14:56 -04:00
f4a24e26c3 v16.7.1 2023-07-18 20:27:07 +00:00
122eccf3dc Merge pull request #2652 from balena-io/update-balena-sdk-17.8.0
Update balena-sdk to 17.8.0
2023-07-18 20:26:16 +00:00
bd598788dc Update balena-sdk to 17.8.0
Update balena-sdk from 17.0.0 to 17.8.0

Change-type: patch
2023-07-18 22:48:54 +03:00
406482b4da v16.7.0 2023-07-17 19:59:52 +00:00
a381c97ca9 Merge pull request #2649 from balena-io/preload-no-pin-device-to-release
preload: Add the --no-pin-device-to-release flag to avoid interactive questions
2023-07-17 19:59:04 +00:00
8ce78ba33c Rerun npm-shrinkwrap.json deduplication 2023-07-17 11:25:02 +03:00
f53f148c89 preload: Add the --no-pin-device-to-release flag to avoid interactive questions
Change-type: minor
See: https://balena.zulipchat.com/#narrow/stream/345746-aspect.2Fproduct/topic/Level.20-.20need.20thoughts.20on.20questions.20.26.20feature.20suggestions
2023-07-17 11:19:03 +03:00
0086feb645 v16.6.6 2023-07-10 17:16:08 +00:00
4ee55b049f Merge pull request #2646 from balena-io/reduce-lodash-usage
Reduce lodash usage in common user interaction patterns
2023-07-10 17:15:14 +00:00
90c6f121cc Rerun npm-shrinkwrap.json deduplication 2023-07-10 19:36:36 +03:00
d3c27ae859 Reduce lodash usage in common user interaction patterns
Change-type: patch
2023-07-10 17:19:01 +03:00
8f39c1de6c v16.6.5 2023-07-09 21:29:55 +00:00
4df1831187 Merge pull request #2645 from balena-io/application-create-hostApp-based-supported-DTs
fleet/block/app create: Fetch the supported device types using the hostApps
2023-07-09 21:29:02 +00:00
2bce761ace Rerun npm-shrinkwrap.json deduplication 2023-07-07 20:17:53 +03:00
d78b76aceb fleet/block/app create: Fetch the supported device types using the hostApps
Change-type: patch
See: https://balena.zulipchat.com/#narrow/stream/360838-balena-io.2Fos.2Fdevices/topic/state.20field.20in.20device-type.2Ejson
See: https://balena.fibery.io/Organisation/Improvements-849#Improvements/Stop-relying-on-device-types-v1-device-type.json-for-unrelated-things-257
2023-07-07 19:57:36 +03:00
f07f6b84d4 v16.6.4 2023-07-06 13:58:27 +00:00
d297a10570 Merge pull request #2643 from balena-io/bump-balena-compose
Bump balena-compose to v2.3.0
2023-07-06 13:57:37 +00:00
9d0b82122a Bump balena-compose to v2.3.0
This allows the the CLI to use docker registry config when querying the
images manifest.

Relates-to: balena-io-modules/balena-compose#31
Change-type: patch
2023-07-05 15:46:42 -04:00
173 changed files with 24887 additions and 14748 deletions

2
.eslintignore Normal file
View File

@ -0,0 +1,2 @@
/completion/*
/bin/*

21
.eslintrc.js Normal file
View File

@ -0,0 +1,21 @@
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: '^_' }],
},
};

4
.gitattributes vendored
View File

@ -4,6 +4,10 @@
*.* -eol
*.sh text eol=lf
.dockerignore eol=lf
Dockerfile eol=lf
Dockerfile.* eol=lf
* text=auto eol=lf
# lf for the docs as it's auto-generated and will otherwise trigger an uncommited error on windows
docs/balena-cli.md text eol=lf

View File

@ -18,7 +18,7 @@ inputs:
default: 'accounts+apple@balena.io'
NODE_VERSION:
type: string
default: '16.x'
default: '18.x'
VERBOSE:
type: string
default: 'true'

View File

@ -15,7 +15,7 @@ inputs:
# --- custom environment
NODE_VERSION:
type: string
default: '16.x'
default: '18.x'
VERBOSE:
type: string
default: "true"
@ -31,6 +31,12 @@ runs:
node-version: ${{ inputs.NODE_VERSION }}
cache: npm
- name: Set up Python 3.11
if: runner.os == 'macOS'
uses: actions/setup-python@d27e3f3d7c64b4bbf8e4abfb9b63b83e846e0435 # v4
with:
python-version: "3.11"
- name: Test release
shell: bash
run: |
@ -49,7 +55,7 @@ runs:
- name: Compress custom source
shell: pwsh
run: tar -acf ${{ runner.temp }}/custom.tgz .
run: tar --exclude-vcs -acf ${{ runner.temp }}/custom.tgz .
- name: Upload custom artifact
uses: actions/upload-artifact@v3

View File

@ -11,7 +11,7 @@ on:
jobs:
flowzone:
name: Flowzone
uses: product-os/flowzone/.github/workflows/flowzone.yml@v4.7.1
uses: product-os/flowzone/.github/workflows/flowzone.yml@master
# prevent duplicate workflow executions for pull_request and pull_request_target
if: |
(
@ -27,3 +27,4 @@ jobs:
repo_config: true
repo_description: "The official balena CLI tool."
github_prerelease: false
restrict_custom_actions: false

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -123,6 +123,20 @@ The README file is manually edited, but subsections are automatically extracted
`docs/balena-cli.md` by the `getCapitanoDoc()` function in
[`automation/capitanodoc/capitanodoc.ts`](https://github.com/balena-io/balena-cli/blob/master/automation/capitanodoc/capitanodoc.ts).
**IMPORTANT**
The file [`capitanodoc.ts`](https://github.com/balena-io/balena-cli/blob/master/automation/capitanodoc/capitanodoc.ts) lists
commands to generate documentation from. At the moment, it's manually updated and maintained alphabetically.
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
The `INSTALL*.md` and `TROUBLESHOOTING.md` files are also manually edited.
## Patches folder

View File

@ -78,8 +78,8 @@ If you are a Node.js developer, you may wish to install the balena CLI via [npm]
The npm installation involves building native (platform-specific) binary modules, which require
some development tools to be installed first, as follows.
> **The balena CLI currently requires Node.js version 16.**
> **Versions 17 and later are not yet fully supported.**
> **The balena CLI currently requires Node.js version 18.**
> **Versions 19 and later are not yet fully supported.**
### Install development tools
@ -89,7 +89,7 @@ some development tools to be installed first, as follows.
$ sudo apt-get update && sudo apt-get -y install curl python3 git make g++
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
$ . ~/.bashrc
$ nvm install 16
$ nvm install 18
```
The `curl` command line above uses
@ -106,7 +106,7 @@ recommended.
```sh
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
$ . ~/.bashrc
$ nvm install 16
$ nvm install 18
```
#### **Windows** (not WSL)
@ -114,7 +114,7 @@ $ nvm install 16
Install:
* If you'd like the ability to switch between Node.js versions, install
- Node.js v16 from the [Nodejs.org releases page](https://nodejs.org/en/download/releases/).
- Node.js v18 from the [Nodejs.org releases page](https://nodejs.org/en/download/releases/).
[nvm-windows](https://github.com/coreybutler/nvm-windows#node-version-manager-nvm-for-windows)
instead.
* The [MSYS2 shell](https://www.msys2.org/), which provides `git`, `make`, `g++` and more:

View File

@ -17,7 +17,7 @@
import type { JsonVersions } from '../lib/commands/version';
import { run as oclifRun } from 'oclif';
import { run as oclifRun } from '@oclif/core';
import * as archiver from 'archiver';
import * as Bluebird from 'bluebird';
import { execFile } from 'child_process';
@ -25,11 +25,11 @@ import * as filehound from 'filehound';
import { Stats } from 'fs';
import * as fs from 'fs-extra';
import * as klaw from 'klaw';
import * as _ from 'lodash';
import * as path from 'path';
import * as rimraf from 'rimraf';
import * as semver from 'semver';
import { promisify } from 'util';
import { notarize } from '@electron/notarize';
import { stripIndent } from '../build/utils/lazy';
import {
@ -206,7 +206,6 @@ async function buildPkg() {
const paths: Array<[string, string[], string[]]> = [
// [platform, [source path], [destination path]]
['*', ['open', 'xdg-open'], ['xdg-open']],
['*', ['opn', 'xdg-open'], ['xdg-open-402']],
['darwin', ['denymount', 'bin', 'denymount'], ['denymount']],
];
await Promise.all(
@ -471,8 +470,6 @@ async function notarizeMacInstaller(): Promise<void> {
const appleIdPassword = process.env.XCODE_APP_LOADER_PASSWORD;
if (appleIdPassword && teamId) {
const { notarize } = await import('@electron/notarize');
// https://github.com/electron/notarize#readme
await notarize({
tool: 'notarytool',
teamId,
@ -494,9 +491,10 @@ export async function buildOclifInstaller() {
let packOpts = ['-r', ROOT];
if (process.platform === 'darwin') {
packOS = 'macos';
packOpts = packOpts.concat('--targets', 'darwin-x64');
} else if (process.platform === 'win32') {
packOS = 'win';
packOpts = packOpts.concat('-t', 'win32-x64');
packOpts = packOpts.concat('--targets', 'win32-x64');
}
if (packOS) {
console.log(`Building oclif installer for CLI ${version}`);
@ -514,10 +512,11 @@ export async function buildOclifInstaller() {
await signFilesForNotarization();
}
console.log('=======================================================');
console.log(`oclif "${packCmd}" "${packOpts.join('" "')}"`);
console.log(`oclif ${packCmd} ${packOpts.join(' ')}`);
console.log(`cwd="${process.cwd()}" ROOT="${ROOT}"`);
console.log('=======================================================');
await oclifRun([packCmd].concat(...packOpts));
const oclifPath = path.join(ROOT, 'node_modules', 'oclif');
await oclifRun([packCmd].concat(...packOpts), oclifPath);
await renameInstallerFiles();
// The Windows installer is explicitly signed here (oclif doesn't do it).
// The macOS installer is automatically signed by oclif (which runs the

View File

@ -24,27 +24,35 @@ import { MarkdownFileParser } from './utils';
*
* The `getCapitanoDoc` function in this module parses README.md and adds
* some content to this object.
*
* IMPORTANT
*
* Only build files listed here will be documented by Capitano
* Make sure to add your files in alphabetical order
*
* Resources with plural names needs to have 2 sections if they have commands like:
* "fleet, fleets" or "device, devices" or "tag, tags"
*
*/
const capitanoDoc = {
title: 'balena CLI Documentation',
introduction: '',
categories: [
{
title: 'API keys',
files: ['build/commands/api-key/generate.js'],
title: 'API Key',
files: ['build/commands/api-keys/index.js'],
},
{
title: 'Fleet',
title: 'API Keys',
files: [
'build/commands/fleets.js',
'build/commands/fleet/index.js',
'build/commands/fleet/create.js',
'build/commands/fleet/purge.js',
'build/commands/fleet/rename.js',
'build/commands/fleet/restart.js',
'build/commands/fleet/rm.js',
'build/commands/api-key/generate.js',
'build/commands/api-key/revoke.js',
],
},
{
title: 'App',
files: ['build/commands/app/create.js'],
},
{
title: 'Authentication',
files: [
@ -53,18 +61,35 @@ const capitanoDoc = {
'build/commands/whoami.js',
],
},
{
title: 'Block',
files: ['build/commands/app/create.js'],
},
{
title: 'Config',
files: [
'build/commands/config/generate.js',
'build/commands/config/inject.js',
'build/commands/config/read.js',
'build/commands/config/reconfigure.js',
'build/commands/config/write.js',
],
},
{
title: 'Deploy',
files: ['build/commands/build.js', 'build/commands/deploy.js'],
},
{
title: 'Device',
files: [
'build/commands/devices/index.js',
'build/commands/devices/supported.js',
'build/commands/device/index.js',
'build/commands/device/deactivate.js',
'build/commands/device/identify.js',
'build/commands/device/index.js',
'build/commands/device/init.js',
'build/commands/device/local-mode.js',
'build/commands/device/move.js',
'build/commands/device/os-update.js',
'build/commands/device/pin.js',
'build/commands/device/public-url.js',
'build/commands/device/purge.js',
'build/commands/device/reboot.js',
@ -73,44 +98,54 @@ const capitanoDoc = {
'build/commands/device/restart.js',
'build/commands/device/rm.js',
'build/commands/device/shutdown.js',
'build/commands/device/track-fleet.js',
],
},
{
title: 'Releases',
title: 'Devices',
files: [
'build/commands/releases.js',
'build/commands/release/index.js',
'build/commands/release/finalize.js',
'build/commands/devices/index.js',
'build/commands/devices/supported.js',
],
},
{
title: 'Environment Variables',
title: 'Environment Variable',
files: [
'build/commands/envs.js',
'build/commands/env/add.js',
'build/commands/env/rename.js',
'build/commands/env/rm.js',
],
},
{
title: 'Tags',
title: 'Environment Variables',
files: ['build/commands/envs.js'],
},
{
title: 'Fleet',
files: [
'build/commands/tags.js',
'build/commands/tag/rm.js',
'build/commands/tag/set.js',
'build/commands/fleet/create.js',
'build/commands/fleet/index.js',
'build/commands/fleet/pin.js',
'build/commands/fleet/purge.js',
'build/commands/fleet/rename.js',
'build/commands/fleet/restart.js',
'build/commands/fleet/rm.js',
'build/commands/fleet/track-latest.js',
],
},
{
title: 'Fleets',
files: ['build/commands/fleets.js'],
},
{
title: 'Help and Version',
files: ['help', 'build/commands/version.js'],
},
{
title: 'Keys',
title: 'Local',
files: [
'build/commands/keys.js',
'build/commands/key/index.js',
'build/commands/key/add.js',
'build/commands/key/rm.js',
'build/commands/local/configure.js',
'build/commands/local/flash.js',
],
},
{
@ -129,24 +164,18 @@ const capitanoDoc = {
title: 'Notes',
files: ['build/commands/note.js'],
},
{
title: 'Organizations',
files: ['build/commands/orgs.js'],
},
{
title: 'OS',
files: [
'build/commands/os/build-config.js',
'build/commands/os/configure.js',
'build/commands/os/versions.js',
'build/commands/os/download.js',
'build/commands/os/initialize.js',
],
},
{
title: 'Config',
files: [
'build/commands/config/generate.js',
'build/commands/config/inject.js',
'build/commands/config/read.js',
'build/commands/config/reconfigure.js',
'build/commands/config/write.js',
'build/commands/os/versions.js',
],
},
{
@ -157,33 +186,56 @@ const capitanoDoc = {
title: 'Push',
files: ['build/commands/push.js'],
},
{
title: 'Settings',
files: ['build/commands/settings.js'],
},
{
title: 'Local',
files: [
'build/commands/local/configure.js',
'build/commands/local/flash.js',
],
},
{
title: 'Deploy',
files: ['build/commands/build.js', 'build/commands/deploy.js'],
},
{
title: 'Platform',
files: ['build/commands/join.js', 'build/commands/leave.js'],
},
{
title: 'Utilities',
files: ['build/commands/util/available-drives.js'],
title: 'Release',
files: [
'build/commands/release/finalize.js',
'build/commands/release/index.js',
'build/commands/release/invalidate.js',
'build/commands/release/validate.js',
],
},
{
title: 'Releases',
files: ['build/commands/releases.js'],
},
{
title: 'Settings',
files: ['build/commands/settings.js'],
},
{
title: 'Support',
files: ['build/commands/support.js'],
},
{
title: 'SSH Key',
files: [
'build/commands/key/add.js',
'build/commands/key/index.js',
'build/commands/key/rm.js',
],
},
{
title: 'SSH Keys',
files: ['build/commands/keys.js'],
},
{
title: 'Tags',
files: ['build/commands/tag/rm.js', 'build/commands/tag/set.js'],
},
{
title: 'Tags',
files: ['build/commands/tags.js'],
},
{
title: 'Utilities',
files: ['build/commands/util/available-drives.js'],
},
],
};

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Command as OclifCommandClass } from '@oclif/command';
import { Command as OclifCommandClass } from '@oclif/core';
type OclifCommand = typeof OclifCommandClass;

View File

@ -62,12 +62,11 @@ class FakeHelpCommand {
'$ balena help os download',
];
args = [
{
name: 'command',
args = {
command: {
description: 'command to show help for',
},
];
};
usage = 'help [command]';
@ -105,5 +104,5 @@ async function printMarkdown() {
}
}
// tslint:disable-next-line:no-floating-promises
// eslint-disable-next-line @typescript-eslint/no-floating-promises
printMarkdown();

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { flagUsages } from '@oclif/parser';
import { Parser } from '@oclif/core';
import * as ent from 'ent';
import * as _ from 'lodash';
@ -37,8 +37,8 @@ function renderOclifCommand(command: OclifCommand): string[] {
if (!_.isEmpty(command.args)) {
result.push('### Arguments');
for (const arg of command.args!) {
result.push(`#### ${arg.name.toUpperCase()}`, arg.description || '');
for (const [name, arg] of Object.entries(command.args!)) {
result.push(`#### ${name.toUpperCase()}`, arg.description || '');
}
}
@ -49,7 +49,7 @@ function renderOclifCommand(command: OclifCommand): string[] {
continue;
}
flag.name = name;
const flagUsage = flagUsages([flag])
const flagUsage = Parser.flagUsages([flag])
.map(([usage, _description]) => usage)
.join()
.trim();

View File

@ -15,7 +15,7 @@
* limitations under the License.
*/
// tslint:disable-next-line:import-blacklist
// eslint-disable-next-line no-restricted-imports
import { stripIndent } from 'common-tags';
import * as _ from 'lodash';
import { promises as fs } from 'fs';
@ -82,5 +82,5 @@ async function run() {
}
}
// tslint:disable-next-line:no-floating-promises
// eslint-disable-next-line @typescript-eslint/no-floating-promises
run();

View File

@ -23,8 +23,8 @@ function parseSemver(version) {
* @param {string} v2
*/
function semverGte(v1, v2) {
let v1Array = parseSemver(v1);
let v2Array = parseSemver(v2);
const v1Array = parseSemver(v1);
const v2Array = parseSemver(v2);
for (let i = 0; i < 3; i++) {
if (v1Array[i] < v2Array[i]) {
return false;

View File

@ -30,7 +30,7 @@ const { GITHUB_TOKEN } = process.env;
export async function createGitHubRelease() {
console.log(`Publishing release ${version} to GitHub`);
const publishRelease = await import('publish-release');
const ghRelease = await Bluebird.fromCallback(
const ghRelease = (await Bluebird.fromCallback(
publishRelease.bind(null, {
token: GITHUB_TOKEN || '',
owner: 'balena-io',
@ -40,7 +40,7 @@ export async function createGitHubRelease() {
reuseRelease: true,
assets: finalReleaseAssets[process.platform],
}),
);
)) as { html_url: any };
console.log(`Release ${version} successful: ${ghRelease.html_url}`);
}
@ -154,7 +154,7 @@ async function updateGitHubReleaseDescriptions(
) {
const perPage = 30;
const octokit = getOctokit();
const options = await octokit.repos.listReleases.endpoint.merge({
const options = octokit.repos.listReleases.endpoint.merge({
owner,
repo,
per_page: perPage,

View File

@ -60,7 +60,7 @@ async function parse(args?: string[]) {
release,
};
for (const arg of args) {
if (!commands.hasOwnProperty(arg)) {
if (!Object.hasOwn(commands, arg)) {
throw new Error(`command unknown: ${arg}`);
}
}
@ -103,5 +103,5 @@ export async function run(args?: string[]) {
}
}
// tslint:disable-next-line:no-floating-promises
// eslint-disable-next-line @typescript-eslint/no-floating-promises
run();

View File

@ -136,5 +136,5 @@ async function main() {
}
}
// tslint:disable-next-line:no-floating-promises
// eslint-disable-next-line @typescript-eslint/no-floating-promises
main();

View File

@ -16,7 +16,6 @@
*/
import { spawn } from 'child_process';
import * as _ from 'lodash';
import * as path from 'path';
export const ROOT = path.join(__dirname, '..');

View File

@ -1,7 +1,5 @@
#!/usr/bin/env node
// tslint:disable:no-var-requires
// We boost the threadpool size as ext2fs can deadlock with some
// operations otherwise, if the pool runs out.
process.env.UV_THREADPOOL_SIZE = '64';

View File

@ -5,8 +5,6 @@
// Before opening a PR you should build and test your changes using bin/balena
// ****************************************************************************
// tslint:disable:no-var-requires
// We boost the threadpool size as ext2fs can deadlock with some
// operations otherwise, if the pool runs out.
process.env.UV_THREADPOOL_SIZE = '64';

View File

@ -31,8 +31,8 @@ if (fs.existsSync(commandsFilePath)) {
const commandsJson = JSON.parse(fs.readFileSync(commandsFilePath, 'utf8'));
var mainCommands = [];
var additionalCommands = [];
const mainCommands = [];
const additionalCommands = [];
for (const key of Object.keys(commandsJson.commands)) {
const cmd = key.split(':');
if (cmd.length > 1) {
@ -72,8 +72,8 @@ fs.readFile(bashFilePathIn, 'utf8', function (err, data) {
/\$main_commands\$/g,
'main_commands="' + mainCommandsStr + '"',
);
var subCommands = [];
var prevElement = additionalCommands[0][0];
let subCommands = [];
let prevElement = additionalCommands[0][0];
additionalCommands.forEach(function (element) {
if (element[0] === prevElement) {
subCommands.push(element[1]);
@ -134,8 +134,8 @@ fs.readFile(zshFilePathIn, 'utf8', function (err, data) {
/\$main_commands\$/g,
'main_commands=( ' + mainCommandsStr + ' )',
);
var subCommands = [];
var prevElement = additionalCommands[0][0];
let subCommands = [];
let prevElement = additionalCommands[0][0];
additionalCommands.forEach(function (element) {
if (element[0] === prevElement) {
subCommands.push(element[1]);

File diff suppressed because it is too large Load Diff

View File

@ -24,6 +24,7 @@ import {
} from './preparser';
import { CliSettings } from './utils/bootstrap';
import { onceAsync } from './utils/lazy';
import { run as mainRun } from '@oclif/core';
/**
* Sentry.io setup
@ -114,10 +115,9 @@ async function oclifRun(command: string[], options: AppOptions) {
}
const runPromise = (async function (shouldFlush: boolean) {
const { CustomMain } = await import('./utils/oclif-utils');
let isEEXIT = false;
try {
await CustomMain.run(command);
await mainRun(command, options.configPath);
} catch (error) {
// oclif sometimes exits with ExitError code EEXIT 0 (not an error),
// for example the `balena help` command.
@ -130,7 +130,7 @@ async function oclifRun(command: string[], options: AppOptions) {
}
}
if (shouldFlush) {
await import('@oclif/command/flush');
await import('@oclif/core/flush');
}
// TODO: figure out why we need to call fast-boot stop() here, in
// addition to calling it in the main `run()` function in this file.

View File

@ -15,7 +15,7 @@
* limitations under the License.
*/
import Command from '@oclif/command';
import { Command } from '@oclif/core';
import {
InsufficientPrivilegesError,
NotAvailableInOfflineModeError,

View File

@ -15,20 +15,12 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Args } from '@oclif/core';
import Command from '../../command';
import { ExpectedError } from '../../errors';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
interface FlagsDef {
help: void;
}
interface ArgsDef {
name: string;
}
export default class GenerateCmd extends Command {
public static description = stripIndent`
Generate a new balenaCloud API key.
@ -41,24 +33,23 @@ export default class GenerateCmd extends Command {
`;
public static examples = ['$ balena api-key generate "Jenkins Key"'];
public static args = [
{
name: 'name',
public static args = {
name: Args.string({
description: 'the API key name',
required: true,
},
];
}),
};
public static usage = 'api-key generate <name>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
};
public static authenticated = true;
public async run() {
const { args: params } = this.parse<FlagsDef, ArgsDef>(GenerateCmd);
const { args: params } = await this.parse(GenerateCmd);
let key;
try {

View File

@ -15,19 +15,11 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
interface FlagsDef {
help: void;
}
interface ArgsDef {
ids: string;
}
export default class RevokeCmd extends Command {
public static description = stripIndent`
Revoke balenaCloud API keys.
@ -42,39 +34,34 @@ export default class RevokeCmd extends Command {
'$ balena api-key revoke 123,124,456',
];
public static args = [
{
name: 'ids',
public static args = {
ids: Args.string({
description: 'the API key ids',
required: true,
},
];
}),
};
public static usage = 'api-key revoke <ids>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
};
public static authenticated = true;
public async run() {
const { args: params } = this.parse<FlagsDef, ArgsDef>(RevokeCmd);
const { args: params } = await this.parse(RevokeCmd);
try {
const apiKeyIds = params.ids.split(',');
if (apiKeyIds.filter((apiKeyId) => !apiKeyId.match(/^\d+$/)).length > 0) {
console.log('API key ids must be positive integers');
return;
}
await Promise.all(
apiKeyIds.map(
async (id) => await getBalenaSdk().models.apiKey.revoke(Number(id)),
),
);
console.log('Successfully revoked the given API keys');
} catch (e) {
throw e;
const apiKeyIds = params.ids.split(',');
if (apiKeyIds.filter((apiKeyId) => !apiKeyId.match(/^\d+$/)).length > 0) {
console.log('API key ids must be positive integers');
return;
}
await Promise.all(
apiKeyIds.map(
async (id) => await getBalenaSdk().models.apiKey.revoke(Number(id)),
),
);
console.log('Successfully revoked the given API keys');
}
}

View File

@ -15,17 +15,11 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Flags } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
interface FlagsDef {
help: void;
user?: void;
fleet?: string;
}
export default class ApiKeysCmd extends Command {
public static description = stripIndent`
Print a list of balenaCloud API keys.
@ -36,13 +30,11 @@ export default class ApiKeysCmd extends Command {
`;
public static examples = ['$ balena api-keys'];
public static args = [];
public static usage = 'api-keys';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
user: flags.boolean({
user: Flags.boolean({
char: 'u',
description: 'show API keys for your user',
}),
@ -52,44 +44,40 @@ export default class ApiKeysCmd extends Command {
public static authenticated = true;
public async run() {
const { flags: options } = this.parse<FlagsDef, {}>(ApiKeysCmd);
const { flags: options } = await this.parse(ApiKeysCmd);
try {
const { getApplication } = await import('../../utils/sdk');
const actorId = options.fleet
? (
await getApplication(getBalenaSdk(), options.fleet, {
$select: 'actor',
})
).actor
: await getBalenaSdk().auth.getUserActorId();
const keys = await getBalenaSdk().pine.get({
resource: 'api_key',
options: {
$select: ['id', 'created_at', 'name', 'description', 'expiry_date'],
$filter: {
is_of__actor: actorId,
...(options.user
? {
name: {
$ne: null,
},
}
: {}),
},
$orderby: 'name asc',
const { getApplication } = await import('../../utils/sdk');
const actorId = options.fleet
? (
await getApplication(getBalenaSdk(), options.fleet, {
$select: 'actor',
})
).actor
: await getBalenaSdk().auth.getActorId();
const keys = await getBalenaSdk().pine.get({
resource: 'api_key',
options: {
$select: ['id', 'created_at', 'name', 'description', 'expiry_date'],
$filter: {
is_of__actor: actorId,
...(options.user
? {
name: {
$ne: null,
},
}
: {}),
},
});
const fields = ['id', 'name', 'created_at', 'description', 'expiry_date'];
const _ = await import('lodash');
console.log(
getVisuals().table.horizontal(
keys.map((key) => _.mapValues(key, (val) => val ?? 'N/a')),
fields,
),
);
} catch (e) {
throw e;
}
$orderby: 'name asc',
},
});
const fields = ['id', 'name', 'created_at', 'description', 'expiry_date'];
const _ = await import('lodash');
console.log(
getVisuals().table.horizontal(
keys.map((key) => _.mapValues(key, (val) => val ?? 'N/a')),
fields,
),
);
}
}

View File

@ -15,22 +15,11 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import { ExpectedError } from '../../errors';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
interface FlagsDef {
organization?: string;
type?: string; // application device type
help: void;
}
interface ArgsDef {
name: string;
}
import { stripIndent } from '../../utils/lazy';
export default class AppCreateCmd extends Command {
public static description = stripIndent`
@ -60,22 +49,21 @@ export default class AppCreateCmd extends Command {
'$ balena app create MyApp -o myorg --type raspberry-pi',
];
public static args = [
{
name: 'name',
public static args = {
name: Args.string({
description: 'app name',
required: true,
},
];
}),
};
public static usage = 'app create <name>';
public static flags: flags.Input<FlagsDef> = {
organization: flags.string({
public static flags = {
organization: Flags.string({
char: 'o',
description: 'handle of the organization the app should belong to',
}),
type: flags.string({
type: Flags.string({
char: 't',
description:
'app device type (Check available types with `balena devices supported`)',
@ -86,65 +74,10 @@ export default class AppCreateCmd extends Command {
public static authenticated = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
AppCreateCmd,
);
const { args: params, flags: options } = await this.parse(AppCreateCmd);
// Ascertain device type
const deviceType =
options.type ||
(await (await import('../../utils/patterns')).selectDeviceType());
// Ascertain organization
const organization =
options.organization?.toLowerCase() || (await this.getOrganization());
// Create application
try {
const application = await getBalenaSdk().models.application.create({
name: params.name,
deviceType,
organization,
applicationClass: 'app',
});
// Output
console.log(
`App created: slug "${application.slug}", device type "${deviceType}"`,
);
} catch (err) {
if ((err.message || '').toLowerCase().includes('unique')) {
// BalenaRequestError: Request error: "organization" and "app_name" must be unique.
throw new ExpectedError(
`Error: An app or block or fleet with the name "${params.name}" already exists in organization "${organization}".`,
);
} else if ((err.message || '').toLowerCase().includes('unauthorized')) {
// BalenaRequestError: Request error: Unauthorized
throw new ExpectedError(
`Error: You are not authorized to create apps in organization "${organization}".`,
);
}
throw err;
}
}
async getOrganization() {
const { getOwnOrganizations } = await import('../../utils/sdk');
const organizations = await getOwnOrganizations(getBalenaSdk(), {
$select: ['name', 'handle'],
});
if (organizations.length === 0) {
// User is not a member of any organizations (should not happen).
throw new Error('This account is not a member of any organizations');
} else if (organizations.length === 1) {
// User is a member of only one organization - use this.
return organizations[0].handle;
} else {
// User is a member of multiple organizations -
const { selectOrganization } = await import('../../utils/patterns');
return selectOrganization(organizations);
}
await (
await import('../../utils/application-create')
).applicationCreateBase('app', options, params);
}
}

View File

@ -15,22 +15,11 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import { ExpectedError } from '../../errors';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
interface FlagsDef {
organization?: string;
type?: string; // application device type
help: void;
}
interface ArgsDef {
name: string;
}
import { stripIndent } from '../../utils/lazy';
export default class BlockCreateCmd extends Command {
public static description = stripIndent`
@ -60,22 +49,21 @@ export default class BlockCreateCmd extends Command {
'$ balena block create MyBlock -o myorg --type raspberry-pi',
];
public static args = [
{
name: 'name',
public static args = {
name: Args.string({
description: 'block name',
required: true,
},
];
}),
};
public static usage = 'block create <name>';
public static flags: flags.Input<FlagsDef> = {
organization: flags.string({
public static flags = {
organization: Flags.string({
char: 'o',
description: 'handle of the organization the block should belong to',
}),
type: flags.string({
type: Flags.string({
char: 't',
description:
'block device type (Check available types with `balena devices supported`)',
@ -86,65 +74,10 @@ export default class BlockCreateCmd extends Command {
public static authenticated = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
BlockCreateCmd,
);
const { args: params, flags: options } = await this.parse(BlockCreateCmd);
// Ascertain device type
const deviceType =
options.type ||
(await (await import('../../utils/patterns')).selectDeviceType());
// Ascertain organization
const organization =
options.organization?.toLowerCase() || (await this.getOrganization());
// Create application
try {
const application = await getBalenaSdk().models.application.create({
name: params.name,
deviceType,
organization,
applicationClass: 'block',
});
// Output
console.log(
`Block created: slug "${application.slug}", device type "${deviceType}"`,
);
} catch (err) {
if ((err.message || '').toLowerCase().includes('unique')) {
// BalenaRequestError: Request error: "organization" and "app_name" must be unique.
throw new ExpectedError(
`Error: An app or block or fleet with the name "${params.name}" already exists in organization "${organization}".`,
);
} else if ((err.message || '').toLowerCase().includes('unauthorized')) {
// BalenaRequestError: Request error: Unauthorized
throw new ExpectedError(
`Error: You are not authorized to create blocks in organization "${organization}".`,
);
}
throw err;
}
}
async getOrganization() {
const { getOwnOrganizations } = await import('../../utils/sdk');
const organizations = await getOwnOrganizations(getBalenaSdk(), {
$select: ['name', 'handle'],
});
if (organizations.length === 0) {
// User is not a member of any organizations (should not happen).
throw new Error('This account is not a member of any organizations');
} else if (organizations.length === 1) {
// User is a member of only one organization - use this.
return organizations[0].handle;
} else {
// User is a member of multiple organizations -
const { selectOrganization } = await import('../../utils/patterns');
return selectOrganization(organizations);
}
await (
await import('../../utils/application-create')
).applicationCreateBase('block', options, params);
}
}

View File

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Args, Flags } from '@oclif/core';
import Command from '../command';
import { getBalenaSdk } from '../utils/lazy';
import * as cf from '../utils/common-flags';
@ -31,6 +31,9 @@ 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 {
arch?: string;
deviceType?: string;
@ -39,10 +42,6 @@ interface FlagsDef extends ComposeCliFlags, DockerCliFlags {
help: void;
}
interface ArgsDef {
source?: string;
}
export default class BuildCmd extends Command {
public static description = `\
Build a project locally.
@ -74,21 +73,18 @@ ${dockerignoreHelp}
'$ balena build --dockerHost my.docker.host --dockerPort 2376 --ca ca.pem --key key.pem --cert cert.pem -f myFleet',
];
public static args = [
{
name: 'source',
description: 'path of project source directory',
},
];
public static args = {
source: Args.string({ description: 'path of project source directory' }),
};
public static usage = 'build [source]';
public static flags: flags.Input<FlagsDef> = {
arch: flags.string({
public static flags = {
arch: Flags.string({
description: 'the architecture to build for',
char: 'A',
}),
deviceType: flags.string({
deviceType: Flags.string({
description: 'the type of device this build is for',
char: 'd',
}),
@ -97,15 +93,13 @@ ${dockerignoreHelp}
...dockerCliFlags,
// NOTE: Not supporting -h for help, because of clash with -h in DockerCliFlags
// Revisit this in future release.
help: flags.help({}),
help: Flags.help({}),
};
public static primary = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
BuildCmd,
);
const { args: params, flags: options } = await this.parse(BuildCmd);
await Command.checkLoggedInIf(!!options.fleet);

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Flags } 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';
@ -26,26 +27,6 @@ import {
} from '../../utils/messages';
import type { BalenaSDK, PineDeferred } from 'balena-sdk';
interface FlagsDef {
version: string; // OS version
fleet?: string;
dev?: boolean; // balenaOS development variant
secureBoot?: boolean;
device?: string;
deviceApiKey?: string;
deviceType?: string;
'generate-device-api-key': boolean;
output?: string;
// Options for non-interactive configuration
network?: string;
wifiSsid?: string;
wifiKey?: string;
appUpdatePollInterval?: string;
'provisioning-key-name'?: string;
'provisioning-key-expiry-date'?: string;
help: void;
}
export default class ConfigGenerateCmd extends Command {
public static description = stripIndent`
Generate a config.json file.
@ -81,8 +62,8 @@ export default class ConfigGenerateCmd extends Command {
public static usage = 'config generate';
public static flags: flags.Input<FlagsDef> = {
version: flags.string({
public static flags = {
version: Flags.string({
description: 'a balenaOS version',
required: true,
}),
@ -97,44 +78,44 @@ export default class ConfigGenerateCmd extends Command {
'provisioning-key-expiry-date',
],
},
deviceApiKey: flags.string({
deviceApiKey: Flags.string({
description:
'custom device key - note that this is only supported on balenaOS 2.0.3+',
char: 'k',
}),
deviceType: flags.string({
deviceType: Flags.string({
description:
"device type slug (run 'balena devices supported' for possible values)",
}),
'generate-device-api-key': flags.boolean({
'generate-device-api-key': Flags.boolean({
description: 'generate a fresh device key for the device',
}),
output: flags.string({
output: Flags.string({
description: 'path of output file',
char: 'o',
}),
// Options for non-interactive configuration
network: flags.string({
network: Flags.string({
description: 'the network type to use: ethernet or wifi',
options: ['ethernet', 'wifi'],
}),
wifiSsid: flags.string({
wifiSsid: Flags.string({
description:
'the wifi ssid to use (used only if --network is set to wifi)',
}),
wifiKey: flags.string({
wifiKey: Flags.string({
description:
'the wifi key to use (used only if --network is set to wifi)',
}),
appUpdatePollInterval: flags.string({
appUpdatePollInterval: Flags.string({
description:
'supervisor cloud polling interval in minutes (e.g. for device variables)',
}),
'provisioning-key-name': flags.string({
'provisioning-key-name': Flags.string({
description: 'custom key name assigned to generated provisioning api key',
exclusive: ['device'],
}),
'provisioning-key-expiry-date': flags.string({
'provisioning-key-expiry-date': Flags.string({
description:
'expiry date assigned to generated provisioning api key (format: YYYY-MM-DD)',
exclusive: ['device'],
@ -155,7 +136,7 @@ export default class ConfigGenerateCmd extends Command {
}
public async run() {
const { flags: options } = this.parse<FlagsDef, {}>(ConfigGenerateCmd);
const { flags: options } = await this.parse(ConfigGenerateCmd);
const balena = getBalenaSdk();
await this.validateOptions(options);
@ -266,7 +247,9 @@ export default class ConfigGenerateCmd extends Command {
protected readonly deviceTypeNotAllowedMessage =
'The --deviceType option can only be used alongside the --fleet option';
protected async validateOptions(options: FlagsDef) {
protected async validateOptions(
options: Interfaces.InferredFlags<typeof ConfigGenerateCmd.flags>,
) {
const { ExpectedError } = await import('../../errors');
if (options.device == null && options.fleet == null) {

View File

@ -15,21 +15,11 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getVisuals, stripIndent } from '../../utils/lazy';
interface FlagsDef {
type?: string;
drive?: string;
help: void;
}
interface ArgsDef {
file: string;
}
export default class ConfigInjectCmd extends Command {
public static description = stripIndent`
Inject a config.json file to a balenaOS image or attached media.
@ -46,17 +36,16 @@ export default class ConfigInjectCmd extends Command {
'$ balena config inject my/config.json --drive /dev/disk2',
];
public static args = [
{
name: 'file',
public static args = {
file: Args.string({
description: 'the path to the config.json file to inject',
required: true,
},
];
}),
};
public static usage = 'config inject <file>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
drive: cf.driveOrImg,
help: cf.help,
};
@ -65,9 +54,7 @@ export default class ConfigInjectCmd extends Command {
public static offlineCompatible = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
ConfigInjectCmd,
);
const { args: params, flags: options } = await this.parse(ConfigInjectCmd);
const { safeUmount } = await import('../../utils/umount');

View File

@ -15,18 +15,10 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getVisuals, stripIndent } from '../../utils/lazy';
interface FlagsDef {
type?: string;
drive?: string;
help: void;
json: boolean;
}
export default class ConfigReadCmd extends Command {
public static description = stripIndent`
Read the config.json file of a balenaOS image or attached media.
@ -46,7 +38,7 @@ export default class ConfigReadCmd extends Command {
public static usage = 'config read';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
drive: cf.driveOrImg,
help: cf.help,
json: cf.json,
@ -56,7 +48,7 @@ export default class ConfigReadCmd extends Command {
public static offlineCompatible = true;
public async run() {
const { flags: options } = this.parse<FlagsDef, {}>(ConfigReadCmd);
const { flags: options } = await this.parse(ConfigReadCmd);
const { safeUmount } = await import('../../utils/umount');

View File

@ -15,19 +15,11 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Flags } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getVisuals, stripIndent } from '../../utils/lazy';
interface FlagsDef {
type?: string;
drive?: string;
advanced: boolean;
help: void;
version?: string;
}
export default class ConfigReconfigureCmd extends Command {
public static description = stripIndent`
Interactively reconfigure a balenaOS image file or attached media.
@ -49,14 +41,14 @@ export default class ConfigReconfigureCmd extends Command {
public static usage = 'config reconfigure';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
drive: cf.driveOrImg,
advanced: flags.boolean({
advanced: Flags.boolean({
description: 'show advanced commands',
char: 'v',
}),
help: cf.help,
version: flags.string({
version: Flags.string({
description: 'balenaOS version, for example "2.32.0" or "2.44.0+rev1"',
}),
};
@ -65,7 +57,7 @@ export default class ConfigReconfigureCmd extends Command {
public static root = true;
public async run() {
const { flags: options } = this.parse<FlagsDef, {}>(ConfigReconfigureCmd);
const { flags: options } = await this.parse(ConfigReconfigureCmd);
const { safeUmount } = await import('../../utils/umount');

View File

@ -15,22 +15,11 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getVisuals, stripIndent } from '../../utils/lazy';
interface FlagsDef {
type?: string;
drive?: string;
help: void;
}
interface ArgsDef {
key: string;
value: string;
}
export default class ConfigWriteCmd extends Command {
public static description = stripIndent`
Write a key-value pair to the config.json file of an OS image or attached media.
@ -48,22 +37,20 @@ export default class ConfigWriteCmd extends Command {
'$ balena config write --drive balena.img os.network.connectivity.interval 300',
];
public static args = [
{
name: 'key',
public static args = {
key: Args.string({
description: 'the key of the config parameter to write',
required: true,
},
{
name: 'value',
}),
value: Args.string({
description: 'the value of the config parameter to write',
required: true,
},
];
}),
};
public static usage = 'config write <key> <value>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
drive: cf.driveOrImg,
help: cf.help,
};
@ -72,9 +59,7 @@ export default class ConfigWriteCmd extends Command {
public static offlineCompatible = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
ConfigWriteCmd,
);
const { args: params, flags: options } = await this.parse(ConfigWriteCmd);
const { denyMount, safeUmount } = await import('../../utils/umount');

View File

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Args, Flags } from '@oclif/core';
import type { ImageDescriptor } from '@balena/compose/dist/parse';
import Command from '../command';
@ -52,6 +52,9 @@ interface ApplicationWithArch {
application_type: [Pick<ApplicationType, 'slug' | 'supports_multicontainer'>];
}
// 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 {
source?: string;
build: boolean;
@ -62,11 +65,6 @@ interface FlagsDef extends ComposeCliFlags, DockerCliFlags {
help: void;
}
interface ArgsDef {
fleet: string;
image?: string;
}
export default class DeployCmd extends Command {
public static description = `\
Deploy a single image or a multicontainer project to a balena fleet.
@ -105,31 +103,28 @@ ${dockerignoreHelp}
'$ balena deploy myFleet myRepo/myImage --release-tag key1 "" key2 "value2 with spaces"',
];
public static args = [
ca.fleetRequired,
{
name: 'image',
description: 'the image to deploy',
},
];
public static args = {
fleet: ca.fleetRequired,
image: Args.string({ description: 'the image to deploy' }),
};
public static usage = 'deploy <fleet> [image]';
public static flags: flags.Input<FlagsDef> = {
source: flags.string({
public static flags = {
source: Flags.string({
description:
'specify an alternate source directory; default is the working directory',
char: 's',
}),
build: flags.boolean({
build: Flags.boolean({
description: 'force a rebuild before deploy',
char: 'b',
}),
nologupload: flags.boolean({
nologupload: Flags.boolean({
description:
"don't upload build logs to the dashboard with image (if building)",
}),
'release-tag': flags.string({
'release-tag': Flags.string({
description: stripIndent`
Set release tags if the image deployment is successful. Multiple
arguments may be provided, alternating tag keys and values (see examples).
@ -137,7 +132,7 @@ ${dockerignoreHelp}
`,
multiple: true,
}),
draft: flags.boolean({
draft: Flags.boolean({
description: stripIndent`
Deploy the release as a draft. Draft releases are ignored
by the 'track latest' release policy but can be used through release pinning.
@ -145,12 +140,12 @@ ${dockerignoreHelp}
as final by default unless this option is given.`,
default: false,
}),
note: flags.string({ description: 'The notes for this release' }),
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({}),
help: Flags.help({}),
};
public static authenticated = true;
@ -158,9 +153,7 @@ ${dockerignoreHelp}
public static primary = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
DeployCmd,
);
const { args: params, flags: options } = await this.parse(DeployCmd);
(await import('events')).defaultMaxListeners = 1000;
@ -190,7 +183,7 @@ ${dockerignoreHelp}
);
if (image) {
options['registry-secrets'] = await getRegistrySecrets(
(options as FlagsDef)['registry-secrets'] = await getRegistrySecrets(
sdk,
options['registry-secrets'],
);
@ -203,7 +196,7 @@ ${dockerignoreHelp}
registrySecretsPath: options['registry-secrets'],
});
options.dockerfile = dockerfilePath;
options['registry-secrets'] = registrySecrets;
(options as FlagsDef)['registry-secrets'] = registrySecrets;
}
const helpers = await import('../utils/helpers');
@ -212,7 +205,7 @@ ${dockerignoreHelp}
const dockerUtils = await import('../utils/docker');
const [docker, buildOpts, composeOpts] = await Promise.all([
dockerUtils.getDocker(options),
dockerUtils.generateBuildOpts(options),
dockerUtils.generateBuildOpts(options as FlagsDef),
compose.generateOpts(options),
]);
@ -346,9 +339,9 @@ ${dockerignoreHelp}
);
logger.logWarn(msg);
const [token, username, url, options] = await Promise.all([
const [token, { username }, url, options] = await Promise.all([
sdk.auth.getToken(),
sdk.auth.whoami(),
sdk.auth.getUserInfo(),
sdk.settings.get('balenaUrl'),
{
// opts.appName may be prefixed by 'owner/', unlike opts.app.app_name
@ -371,8 +364,8 @@ ${dockerignoreHelp}
$select: ['commit'],
});
} else {
const [userId, auth, apiEndpoint] = await Promise.all([
sdk.auth.getUserId(),
const [{ id: userId }, auth, apiEndpoint] = await Promise.all([
sdk.auth.getUserInfo(),
sdk.auth.getToken(),
sdk.settings.get('apiUrl'),
]);

View File

@ -15,21 +15,11 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import type { IArg } from '@oclif/parser/lib/args';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
interface FlagsDef {
yes: boolean;
help: void;
}
interface ArgsDef {
uuid: string;
}
export default class DeviceDeactivateCmd extends Command {
public static description = stripIndent`
Deactivate a device.
@ -44,17 +34,16 @@ export default class DeviceDeactivateCmd extends Command {
'$ balena device deactivate 7cf02a6 --yes',
];
public static args: Array<IArg<any>> = [
{
name: 'uuid',
public static args = {
uuid: Args.string({
description: 'the UUID of the device to be deactivated',
required: true,
},
];
}),
};
public static usage = 'device deactivate <uuid>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
yes: cf.yes,
help: cf.help,
};
@ -62,9 +51,8 @@ export default class DeviceDeactivateCmd extends Command {
public static authenticated = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
DeviceDeactivateCmd,
);
const { args: params, flags: options } =
await this.parse(DeviceDeactivateCmd);
const balena = getBalenaSdk();
const patterns = await import('../../utils/patterns');

View File

@ -15,21 +15,12 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import type { IArg } from '@oclif/parser/lib/args';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { ExpectedError } from '../../errors';
interface FlagsDef {
help: void;
}
interface ArgsDef {
uuid: string;
}
export default class DeviceIdentifyCmd extends Command {
public static description = stripIndent`
Identify a device.
@ -38,24 +29,23 @@ export default class DeviceIdentifyCmd extends Command {
`;
public static examples = ['$ balena device identify 23c73a1'];
public static args: Array<IArg<any>> = [
{
name: 'uuid',
public static args = {
uuid: Args.string({
description: 'the uuid of the device to identify',
required: true,
},
];
}),
};
public static usage = 'device identify <uuid>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
};
public static authenticated = true;
public async run() {
const { args: params } = this.parse<FlagsDef, ArgsDef>(DeviceIdentifyCmd);
const { args: params } = await this.parse(DeviceIdentifyCmd);
const balena = getBalenaSdk();

View File

@ -15,8 +15,7 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { IArg } from '@oclif/parser/lib/args';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { expandForAppName } from '../../utils/helpers';
@ -41,15 +40,6 @@ interface ExtendedDevice extends DeviceWithDeviceType {
undervoltage_detected?: boolean;
}
interface FlagsDef {
help: void;
view: boolean;
}
interface ArgsDef {
uuid: string;
}
export default class DeviceCmd extends Command {
public static description = stripIndent`
Show info about a single device.
@ -61,19 +51,18 @@ export default class DeviceCmd extends Command {
'$ balena device 7cf02a6 --view',
];
public static args: Array<IArg<any>> = [
{
name: 'uuid',
public static args = {
uuid: Args.string({
description: 'the device uuid',
required: true,
},
];
}),
};
public static usage = 'device <uuid>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
view: flags.boolean({
view: Flags.boolean({
default: false,
description: 'open device dashboard page',
}),
@ -83,9 +72,7 @@ export default class DeviceCmd extends Command {
public static primary = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
DeviceCmd,
);
const { args: params, flags: options } = await this.parse(DeviceCmd);
const balena = getBalenaSdk();

View File

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Flags } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
@ -76,14 +76,14 @@ export default class DeviceInitCmd extends Command {
public static usage = 'device init';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
fleet: cf.fleet,
yes: cf.yes,
advanced: flags.boolean({
advanced: Flags.boolean({
char: 'v',
description: 'show advanced configuration options',
}),
'os-version': flags.string({
'os-version': Flags.string({
description: stripIndent`
exact version number, or a valid semver range,
or 'latest' (includes pre-releases),
@ -93,13 +93,13 @@ export default class DeviceInitCmd extends Command {
`,
}),
drive: cf.drive,
config: flags.string({
config: Flags.string({
description: 'path to the config JSON file, see `balena os build-config`',
}),
'provisioning-key-name': flags.string({
'provisioning-key-name': Flags.string({
description: 'custom key name assigned to generated provisioning api key',
}),
'provisioning-key-expiry-date': flags.string({
'provisioning-key-expiry-date': Flags.string({
description:
'expiry date assigned to generated provisioning api key (format: YYYY-MM-DD)',
}),
@ -109,7 +109,7 @@ export default class DeviceInitCmd extends Command {
public static authenticated = true;
public async run() {
const { flags: options } = this.parse<FlagsDef, {}>(DeviceInitCmd);
const { flags: options } = await this.parse(DeviceInitCmd);
// Imports
const { promisify } = await import('util');

View File

@ -15,23 +15,11 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import type { IArg } from '@oclif/parser/lib/args';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
interface FlagsDef {
enable: boolean;
disable: boolean;
status: boolean;
help?: void;
}
interface ArgsDef {
uuid: string | number;
}
export default class DeviceLocalModeCmd extends Command {
public static description = stripIndent`
Get or manage the local mode status for a device.
@ -47,26 +35,25 @@ export default class DeviceLocalModeCmd extends Command {
'$ balena device local-mode 23c73a1 --status',
];
public static args: Array<IArg<any>> = [
{
name: 'uuid',
public static args = {
uuid: Args.string({
description: 'the uuid of the device to manage',
required: true,
},
];
}),
};
public static usage = 'device local-mode <uuid>';
public static flags: flags.Input<FlagsDef> = {
enable: flags.boolean({
public static flags = {
enable: Flags.boolean({
description: 'enable local mode',
exclusive: ['disable', 'status'],
}),
disable: flags.boolean({
disable: Flags.boolean({
description: 'disable local mode',
exclusive: ['enable', 'status'],
}),
status: flags.boolean({
status: Flags.boolean({
description: 'output boolean indicating local mode status',
exclusive: ['enable', 'disable'],
}),
@ -76,9 +63,8 @@ export default class DeviceLocalModeCmd extends Command {
public static authenticated = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
DeviceLocalModeCmd,
);
const { args: params, flags: options } =
await this.parse(DeviceLocalModeCmd);
const balena = getBalenaSdk();

View File

@ -15,12 +15,10 @@
* limitations under the License.
*/
import type { flags } from '@oclif/command';
import type { IArg } from '@oclif/parser/lib/args';
import { Args } from '@oclif/core';
import type {
BalenaSDK,
Device,
DeviceType,
PineOptions,
PineTypedResult,
} from 'balena-sdk';
@ -30,15 +28,6 @@ import { ExpectedError } from '../../errors';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { applicationIdInfo } from '../../utils/messages';
interface FlagsDef {
fleet?: string;
help: void;
}
interface ArgsDef {
uuid: string;
}
export default class DeviceMoveCmd extends Command {
public static description = stripIndent`
Move one or more devices to another fleet.
@ -57,18 +46,17 @@ export default class DeviceMoveCmd extends Command {
'$ balena device move 7cf02a6 -f myorg/mynewfleet',
];
public static args: Array<IArg<any>> = [
{
name: 'uuid',
public static args = {
uuid: Args.string({
description:
'comma-separated list (no blank spaces) of device UUIDs to be moved',
required: true,
},
];
}),
};
public static usage = 'device move <uuid(s)>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
fleet: cf.fleet,
help: cf.help,
};
@ -103,9 +91,7 @@ export default class DeviceMoveCmd extends Command {
}
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
DeviceMoveCmd,
);
const { args: params, flags: options } = await this.parse(DeviceMoveCmd);
const balena = getBalenaSdk();
@ -138,7 +124,6 @@ export default class DeviceMoveCmd extends Command {
balena: BalenaSDK,
devices: Awaited<ReturnType<typeof this.getDevices>>,
) {
const { getExpandedProp } = await import('../../utils/pine');
// deduplicate the slugs
const deviceCpuArchs = Array.from(
new Set(
@ -148,48 +133,44 @@ export default class DeviceMoveCmd extends Command {
),
);
const deviceTypeOptions = {
$select: 'slug',
$expand: {
is_of__cpu_architecture: {
$select: 'slug',
},
const allCpuArches = await balena.pine.get({
resource: 'cpu_architecture',
options: {
$select: ['id', 'slug'],
},
} satisfies PineOptions<DeviceType>;
const deviceTypes = (await balena.models.deviceType.getAllSupported(
deviceTypeOptions,
)) as Array<PineTypedResult<DeviceType, typeof deviceTypeOptions>>;
});
const compatibleDeviceTypeSlugs = new Set(
deviceTypes
.filter((deviceType) => {
const deviceTypeArch = getExpandedProp(
deviceType.is_of__cpu_architecture,
'slug',
)!;
return deviceCpuArchs.every((deviceCpuArch) =>
balena.models.os.isArchitectureCompatibleWith(
deviceCpuArch,
deviceTypeArch,
),
);
})
.map((deviceType) => deviceType.slug),
);
const compatibleCpuArchIds = allCpuArches
.filter((cpuArch) => {
return deviceCpuArchs.every((deviceCpuArch) =>
balena.models.os.isArchitectureCompatibleWith(
deviceCpuArch,
cpuArch.slug,
),
);
})
.map((deviceType) => deviceType.id);
const patterns = await import('../../utils/patterns');
try {
const application = await patterns.selectApplication(
(app) =>
compatibleDeviceTypeSlugs.has(app.is_for__device_type[0].slug) &&
devices.some(
(device) => device.belongs_to__application.__id !== app.id,
),
{
is_for__device_type: {
$any: {
$alias: 'dt',
$expr: {
dt: {
is_of__cpu_architecture: { $in: compatibleCpuArchIds },
},
},
},
},
},
true,
);
return application;
} catch (err) {
if (!compatibleDeviceTypeSlugs.size) {
if (!compatibleCpuArchIds.length) {
throw new ExpectedError(
`${err.message}\nDo all devices have a compatible architecture?`,
);

View File

@ -15,24 +15,13 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import type { IArg } from '@oclif/parser/lib/args';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy';
import type { Device } from 'balena-sdk';
import { ExpectedError } from '../../errors';
interface FlagsDef {
version?: string;
yes: boolean;
help: void;
}
interface ArgsDef {
uuid: string;
}
export default class DeviceOsUpdateCmd extends Command {
public static description = stripIndent`
Start a Host OS update for a device.
@ -50,18 +39,17 @@ export default class DeviceOsUpdateCmd extends Command {
'$ balena device os-update 23c73a1 --version 2.31.0+rev1.prod',
];
public static args: Array<IArg<any>> = [
{
name: 'uuid',
public static args = {
uuid: Args.string({
description: 'the uuid of the device to update',
required: true,
},
];
}),
};
public static usage = 'device os-update <uuid>';
public static flags: flags.Input<FlagsDef> = {
version: flags.string({
public static flags = {
version: Flags.string({
description: 'a balenaOS version',
}),
yes: cf.yes,
@ -71,9 +59,8 @@ export default class DeviceOsUpdateCmd extends Command {
public static authenticated = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
DeviceOsUpdateCmd,
);
const { args: params, flags: options } =
await this.parse(DeviceOsUpdateCmd);
const sdk = getBalenaSdk();

View File

@ -15,22 +15,12 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import type { IArg } from '@oclif/parser/lib/args';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { getExpandedProp } from '../../utils/pine';
interface FlagsDef {
help: void;
}
interface ArgsDef {
uuid: string;
releaseToPinTo?: string;
}
export default class DevicePinCmd extends Command {
public static description = stripIndent`
Pin a device to a release.
@ -44,28 +34,26 @@ export default class DevicePinCmd extends Command {
'$ balena device pin 7cf02a6 91165e5',
];
public static args: Array<IArg<any>> = [
{
name: 'uuid',
public static args = {
uuid: Args.string({
description: 'the uuid of the device to pin to a release',
required: true,
},
{
name: 'releaseToPinTo',
}),
releaseToPinTo: Args.string({
description: 'the commit of the release for the device to get pinned to',
},
];
}),
};
public static usage = 'device pin <uuid> [releaseToPinTo]';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
};
public static authenticated = true;
public async run() {
const { args: params } = this.parse<FlagsDef, ArgsDef>(DevicePinCmd);
const { args: params } = await this.parse(DevicePinCmd);
const balena = getBalenaSdk();

View File

@ -15,24 +15,12 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import type { IArg } from '@oclif/parser/lib/args';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import { ExpectedError } from '../../errors';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
interface FlagsDef {
enable: boolean;
disable: boolean;
status: boolean;
help?: void;
}
interface ArgsDef {
uuid: string;
}
export default class DevicePublicUrlCmd extends Command {
public static description = stripIndent`
Get or manage the public URL for a device.
@ -49,26 +37,25 @@ export default class DevicePublicUrlCmd extends Command {
'$ balena device public-url 23c73a1 --status',
];
public static args: Array<IArg<any>> = [
{
name: 'uuid',
public static args = {
uuid: Args.string({
description: 'the uuid of the device to manage',
required: true,
},
];
}),
};
public static usage = 'device public-url <uuid>';
public static flags: flags.Input<FlagsDef> = {
enable: flags.boolean({
public static flags = {
enable: Flags.boolean({
description: 'enable the public URL',
exclusive: ['disable', 'status'],
}),
disable: flags.boolean({
disable: Flags.boolean({
description: 'disable the public URL',
exclusive: ['enable', 'status'],
}),
status: flags.boolean({
status: Flags.boolean({
description: 'determine if public URL is enabled',
exclusive: ['enable', 'disable'],
}),
@ -78,9 +65,8 @@ export default class DevicePublicUrlCmd extends Command {
public static authenticated = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
DevicePublicUrlCmd,
);
const { args: params, flags: options } =
await this.parse(DevicePublicUrlCmd);
const balena = getBalenaSdk();

View File

@ -15,20 +15,11 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import type { IArg } from '@oclif/parser/lib/args';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy';
interface FlagsDef {
help: void;
}
interface ArgsDef {
uuid: string;
}
export default class DevicePurgeCmd extends Command {
public static description = stripIndent`
Purge data from a device.
@ -46,22 +37,21 @@ export default class DevicePurgeCmd extends Command {
public static usage = 'device purge <uuid>';
public static args: Array<IArg<any>> = [
{
name: 'uuid',
public static args = {
uuid: Args.string({
description: 'comma-separated list (no blank spaces) of device UUIDs',
required: true,
},
];
}),
};
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
};
public static authenticated = true;
public async run() {
const { args: params } = this.parse<FlagsDef, ArgsDef>(DevicePurgeCmd);
const { args: params } = await this.parse(DevicePurgeCmd);
const balena = getBalenaSdk();
const ux = getCliUx();

View File

@ -15,21 +15,11 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import type { IArg } from '@oclif/parser/lib/args';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
interface FlagsDef {
force: boolean;
help: void;
}
interface ArgsDef {
uuid: string;
}
export default class DeviceRebootCmd extends Command {
public static description = stripIndent`
Restart a device.
@ -38,17 +28,16 @@ export default class DeviceRebootCmd extends Command {
`;
public static examples = ['$ balena device reboot 23c73a1'];
public static args: Array<IArg<any>> = [
{
name: 'uuid',
public static args = {
uuid: Args.string({
description: 'the uuid of the device to reboot',
required: true,
},
];
}),
};
public static usage = 'device reboot <uuid>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
force: cf.force,
help: cf.help,
};
@ -56,9 +45,7 @@ export default class DeviceRebootCmd extends Command {
public static authenticated = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
DeviceRebootCmd,
);
const { args: params, flags: options } = await this.parse(DeviceRebootCmd);
const balena = getBalenaSdk();

View File

@ -15,24 +15,13 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import type { IArg } from '@oclif/parser/lib/args';
import { Flags } from '@oclif/core';
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';
interface FlagsDef {
uuid?: string;
deviceType?: string;
help: void;
}
interface ArgsDef {
fleet: string;
}
export default class DeviceRegisterCmd extends Command {
public static description = stripIndent`
Register a new device.
@ -51,16 +40,18 @@ export default class DeviceRegisterCmd extends Command {
'$ balena device register myorg/myfleet --uuid <uuid> --deviceType <deviceTypeSlug>',
];
public static args: Array<IArg<any>> = [ca.fleetRequired];
public static args = {
fleet: ca.fleetRequired,
};
public static usage = 'device register <fleet>';
public static flags: flags.Input<FlagsDef> = {
uuid: flags.string({
public static flags = {
uuid: Flags.string({
description: 'custom uuid',
char: 'u',
}),
deviceType: flags.string({
deviceType: Flags.string({
description:
"device type slug (run 'balena devices supported' for possible values)",
}),
@ -70,9 +61,8 @@ export default class DeviceRegisterCmd extends Command {
public static authenticated = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
DeviceRegisterCmd,
);
const { args: params, flags: options } =
await this.parse(DeviceRegisterCmd);
const { getApplication } = await import('../../utils/sdk');

View File

@ -15,21 +15,11 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import type { IArg } from '@oclif/parser/lib/args';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy';
interface FlagsDef {
help: void;
}
interface ArgsDef {
uuid: string;
newName?: string;
}
export default class DeviceRenameCmd extends Command {
public static description = stripIndent`
Rename a device.
@ -43,28 +33,26 @@ export default class DeviceRenameCmd extends Command {
'$ balena device rename 7cf02a6 MyPi',
];
public static args: Array<IArg<any>> = [
{
name: 'uuid',
public static args = {
uuid: Args.string({
description: 'the uuid of the device to rename',
required: true,
},
{
name: 'newName',
}),
newName: Args.string({
description: 'the new name for the device',
},
];
}),
};
public static usage = 'device rename <uuid> [newName]';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
};
public static authenticated = true;
public async run() {
const { args: params } = this.parse<FlagsDef, ArgsDef>(DeviceRenameCmd);
const { args: params } = await this.parse(DeviceRenameCmd);
const balena = getBalenaSdk();

View File

@ -15,8 +15,7 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import type { IArg } from '@oclif/parser/lib/args';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy';
@ -26,15 +25,6 @@ import type {
CurrentServiceWithCommit,
} from 'balena-sdk';
interface FlagsDef {
help: void;
service?: string;
}
interface ArgsDef {
uuid: string;
}
export default class DeviceRestartCmd extends Command {
public static description = stripIndent`
Restart containers on a device.
@ -55,19 +45,18 @@ export default class DeviceRestartCmd extends Command {
'$ balena device restart 23c73a1 -s myService1,myService2',
];
public static args: Array<IArg<any>> = [
{
name: 'uuid',
public static args = {
uuid: Args.string({
description:
'comma-separated list (no blank spaces) of device UUIDs to restart',
required: true,
},
];
}),
};
public static usage = 'device restart <uuid>';
public static flags: flags.Input<FlagsDef> = {
service: flags.string({
public static flags = {
service: Flags.string({
description:
'comma-separated list (no blank spaces) of service names to restart',
char: 's',
@ -78,9 +67,7 @@ export default class DeviceRestartCmd extends Command {
public static authenticated = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
DeviceRestartCmd,
);
const { args: params, flags: options } = await this.parse(DeviceRestartCmd);
const balena = getBalenaSdk();
const ux = getCliUx();

View File

@ -15,21 +15,11 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import type { IArg } from '@oclif/parser/lib/args';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
interface FlagsDef {
yes: boolean;
help: void;
}
interface ArgsDef {
uuid: string;
}
export default class DeviceRmCmd extends Command {
public static description = stripIndent`
Remove one or more devices.
@ -45,18 +35,17 @@ export default class DeviceRmCmd extends Command {
'$ balena device rm 7cf02a6 --yes',
];
public static args: Array<IArg<any>> = [
{
name: 'uuid',
public static args = {
uuid: Args.string({
description:
'comma-separated list (no blank spaces) of device UUIDs to be removed',
required: true,
},
];
}),
};
public static usage = 'device rm <uuid(s)>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
yes: cf.yes,
help: cf.help,
};
@ -64,9 +53,7 @@ export default class DeviceRmCmd extends Command {
public static authenticated = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
DeviceRmCmd,
);
const { args: params, flags: options } = await this.parse(DeviceRmCmd);
const balena = getBalenaSdk();
const patterns = await import('../../utils/patterns');

View File

@ -15,22 +15,12 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import type { IArg } from '@oclif/parser/lib/args';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { ExpectedError } from '../../errors';
interface FlagsDef {
force: boolean;
help: void;
}
interface ArgsDef {
uuid: string;
}
export default class DeviceShutdownCmd extends Command {
public static description = stripIndent`
Shutdown a device.
@ -39,17 +29,16 @@ export default class DeviceShutdownCmd extends Command {
`;
public static examples = ['$ balena device shutdown 23c73a1'];
public static args: Array<IArg<any>> = [
{
name: 'uuid',
public static args = {
uuid: Args.string({
description: 'the uuid of the device to shutdown',
required: true,
},
];
}),
};
public static usage = 'device shutdown <uuid>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
force: cf.force,
help: cf.help,
};
@ -57,9 +46,8 @@ export default class DeviceShutdownCmd extends Command {
public static authenticated = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
DeviceShutdownCmd,
);
const { args: params, flags: options } =
await this.parse(DeviceShutdownCmd);
const balena = getBalenaSdk();

View File

@ -15,20 +15,11 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import type { IArg } from '@oclif/parser/lib/args';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
interface FlagsDef {
help: void;
}
interface ArgsDef {
uuid: string;
}
export default class DeviceTrackFleetCmd extends Command {
public static description = stripIndent`
Make a device track the fleet's pinned release.
@ -37,24 +28,23 @@ export default class DeviceTrackFleetCmd extends Command {
`;
public static examples = ['$ balena device track-fleet 7cf02a6'];
public static args: Array<IArg<any>> = [
{
name: 'uuid',
public static args = {
uuid: Args.string({
description: "the uuid of the device to make track the fleet's release",
required: true,
},
];
}),
};
public static usage = 'device track-fleet <uuid>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
};
public static authenticated = true;
public async run() {
const { args: params } = this.parse<FlagsDef, ArgsDef>(DeviceTrackFleetCmd);
const { args: params } = await this.parse(DeviceTrackFleetCmd);
const balena = getBalenaSdk();

View File

@ -15,7 +15,6 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { expandForAppName } from '../../utils/helpers';
@ -24,12 +23,6 @@ import { applicationIdInfo, jsonInfo } from '../../utils/messages';
import type { Device, PineOptions } from 'balena-sdk';
interface FlagsDef {
fleet?: string;
help: void;
json: boolean;
}
const devicesSelectFields = {
$select: [
'id',
@ -62,7 +55,7 @@ export default class DevicesCmd extends Command {
public static usage = 'devices';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
fleet: cf.fleet,
json: cf.json,
help: cf.help,
@ -73,7 +66,7 @@ export default class DevicesCmd extends Command {
public static authenticated = true;
public async run() {
const { flags: options } = this.parse<FlagsDef, {}>(DevicesCmd);
const { flags: options } = await this.parse(DevicesCmd);
const balena = getBalenaSdk();
const devicesOptions = {

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Flags } from '@oclif/core';
import type * as BalenaSdk from 'balena-sdk';
import * as _ from 'lodash';
import Command from '../../command';
@ -23,11 +23,6 @@ import * as cf from '../../utils/common-flags';
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
import { CommandHelp } from '../../utils/oclif-utils';
interface FlagsDef {
help: void;
json?: boolean;
}
export default class DevicesSupportedCmd extends Command {
public static description = stripIndent`
List the supported device types (like 'raspberrypi3' or 'intel-nuc').
@ -50,16 +45,16 @@ export default class DevicesSupportedCmd extends Command {
new CommandHelp({ args: DevicesSupportedCmd.args }).defaultUsage()
).trim();
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
json: flags.boolean({
json: Flags.boolean({
char: 'j',
description: 'produce JSON output instead of tabular output',
}),
};
public async run() {
const { flags: options } = this.parse<FlagsDef, {}>(DevicesSupportedCmd);
const { flags: options } = await this.parse(DevicesSupportedCmd);
const pineOptions = {
$select: ['slug', 'name'],
$expand: {

View File

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Args } from '@oclif/core';
import type * as BalenaSdk from 'balena-sdk';
import Command from '../../command';
import { ExpectedError } from '../../errors';
@ -78,23 +78,23 @@ export default class EnvAddCmd extends Command {
'$ balena env add EDITOR vim --device 7cf02a6,d6f1433 --service MyService,MyService2',
];
public static args = [
{
name: 'name',
public static args = {
name: Args.string({
required: true,
description: 'environment or config variable name',
},
{
name: 'value',
}),
value: Args.string({
required: false,
description:
"variable value; if omitted, use value from this process' environment",
},
];
}),
};
// Required for supporting empty string ('') `value` args.
public static strict = false;
public static usage = 'env add <name> [value]';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
fleet: { ...cf.fleet, exclusive: ['device'] },
device: { ...cf.device, exclusive: ['fleet'] },
help: cf.help,
@ -103,9 +103,7 @@ export default class EnvAddCmd extends Command {
};
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
EnvAddCmd,
);
const { args: params, flags: options } = await this.parse(EnvAddCmd);
const cmd = this;
if (!options.fleet && !options.device) {
@ -268,6 +266,7 @@ async function getServiceIdForApp(
): Promise<number> {
let serviceId: number | undefined;
const services = await sdk.models.service.getAllByApplication(appSlug, {
$select: 'id',
$filter: { service_name: serviceName },
});
if (services.length > 0) {

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
@ -22,20 +22,6 @@ import * as ec from '../../utils/env-common';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { parseAsInteger } from '../../utils/validation';
type IArg<T> = import('@oclif/parser').args.IArg<T>;
interface FlagsDef {
config: boolean;
device: boolean;
service: boolean;
help: void;
}
interface ArgsDef {
id: number;
value: string;
}
export default class EnvRenameCmd extends Command {
public static description = stripIndent`
Change the value of a config or env var for a fleet, device or service.
@ -54,24 +40,22 @@ export default class EnvRenameCmd extends Command {
'$ balena env rename 678678 1 --device --config',
];
public static args: Array<IArg<any>> = [
{
name: 'id',
public static args = {
id: Args.integer({
required: true,
description: "variable's numeric database ID",
parse: (input) => parseAsInteger(input, 'id'),
},
{
name: 'value',
parse: async (input) => parseAsInteger(input, 'id'),
}),
value: Args.string({
required: true,
description:
"variable value; if omitted, use value from this process' environment",
},
];
}),
};
public static usage = 'env rename <id> <value>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
config: ec.booleanConfig,
device: ec.booleanDevice,
service: ec.booleanService,
@ -79,9 +63,7 @@ export default class EnvRenameCmd extends Command {
};
public async run() {
const { args: params, flags: opt } = this.parse<FlagsDef, ArgsDef>(
EnvRenameCmd,
);
const { args: params, flags: opt } = await this.parse(EnvRenameCmd);
await Command.checkLoggedIn();

View File

@ -15,26 +15,13 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import * as ec from '../../utils/env-common';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { parseAsInteger } from '../../utils/validation';
type IArg<T> = import('@oclif/parser').args.IArg<T>;
interface FlagsDef {
config: boolean;
device: boolean;
service: boolean;
yes: boolean;
}
interface ArgsDef {
id: number;
}
export default class EnvRmCmd extends Command {
public static description = stripIndent`
Remove a config or env var from a fleet, device or service.
@ -57,22 +44,21 @@ export default class EnvRmCmd extends Command {
'$ balena env rm 789789 --device --service --yes',
];
public static args: Array<IArg<any>> = [
{
name: 'id',
public static args = {
id: Args.integer({
required: true,
description: "variable's numeric database ID",
parse: (input) => parseAsInteger(input, 'id'),
},
];
parse: async (input) => parseAsInteger(input, 'id'),
}),
};
public static usage = 'env rm <id>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
config: ec.booleanConfig,
device: ec.booleanDevice,
service: ec.booleanService,
yes: flags.boolean({
yes: Flags.boolean({
char: 'y',
description:
'do not prompt for confirmation before deleting the variable',
@ -81,9 +67,7 @@ export default class EnvRmCmd extends Command {
};
public async run() {
const { args: params, flags: opt } = this.parse<FlagsDef, ArgsDef>(
EnvRmCmd,
);
const { args: params, flags: opt } = await this.parse(EnvRmCmd);
await Command.checkLoggedIn();

View File

@ -14,7 +14,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Flags } from '@oclif/core';
import type { Interfaces } from '@oclif/core';
import type * as SDK from 'balena-sdk';
import * as _ from 'lodash';
import Command from '../command';
@ -23,14 +24,7 @@ import * as cf from '../utils/common-flags';
import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy';
import { applicationIdInfo } from '../utils/messages';
interface FlagsDef {
fleet?: string;
config: boolean;
device?: string; // device UUID
json: boolean;
help: void;
service?: string; // service name
}
type FlagsDef = Interfaces.InferredFlags<typeof EnvsCmd.flags>;
interface EnvironmentVariableInfo extends SDK.EnvironmentVariableBase {
fleet?: string | null; // fleet slug
@ -93,7 +87,6 @@ export default class EnvsCmd extends Command {
'$ balena envs --fleet myorg/myfleet',
'$ balena envs --fleet MyFleet --json',
'$ balena envs --fleet MyFleet --service MyService',
'$ balena envs --fleet MyFleet --service MyService',
'$ balena envs --fleet MyFleet --config',
'$ balena envs --device 7cf02a6',
'$ balena envs --device 7cf02a6 --json',
@ -103,9 +96,9 @@ export default class EnvsCmd extends Command {
public static usage = 'envs';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
fleet: { ...cf.fleet, exclusive: ['device'] },
config: flags.boolean({
config: Flags.boolean({
default: false,
char: 'c',
description: 'show configuration variables only',
@ -118,7 +111,7 @@ export default class EnvsCmd extends Command {
};
public async run() {
const { flags: options } = this.parse<FlagsDef, {}>(EnvsCmd);
const { flags: options } = await this.parse(EnvsCmd);
const variables: EnvironmentVariableInfo[] = [];
@ -209,6 +202,7 @@ async function validateServiceName(
fleetSlug: string,
) {
const services = await sdk.models.service.getAllByApplication(fleetSlug, {
$select: 'id',
$filter: { service_name: serviceName },
});
if (services.length === 0) {
@ -234,9 +228,10 @@ async function getAppVars(
if (!fleetSlug) {
return appVars;
}
const vars = await sdk.models.application[
options.config ? 'configVar' : 'envVar'
].getAllByApplication(fleetSlug);
const vars =
await sdk.models.application[
options.config ? 'configVar' : 'envVar'
].getAllByApplication(fleetSlug);
fillInInfoFields(vars, fleetSlug);
appVars.push(...vars);
if (!options.config) {
@ -275,9 +270,8 @@ async function getDeviceVars(
const printedUUID = options.json ? fullUUID : options.device!;
const deviceVars: EnvironmentVariableInfo[] = [];
if (options.config) {
const deviceConfigVars = await sdk.models.device.configVar.getAllByDevice(
fullUUID,
);
const deviceConfigVars =
await sdk.models.device.configVar.getAllByDevice(fullUUID);
fillInInfoFields(deviceConfigVars, fleetSlug, printedUUID);
deviceVars.push(...deviceConfigVars);
} else {
@ -302,9 +296,8 @@ async function getDeviceVars(
fillInInfoFields(deviceServiceVars, fleetSlug, printedUUID);
deviceVars.push(...deviceServiceVars);
const deviceEnvVars = await sdk.models.device.envVar.getAllByDevice(
fullUUID,
);
const deviceEnvVars =
await sdk.models.device.envVar.getAllByDevice(fullUUID);
fillInInfoFields(deviceEnvVars, fleetSlug, printedUUID);
deviceVars.push(...deviceEnvVars);
}

View File

@ -15,22 +15,11 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import { ExpectedError } from '../../errors';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
interface FlagsDef {
organization?: string;
type?: string; // application device type
help: void;
}
interface ArgsDef {
name: string;
}
import { stripIndent } from '../../utils/lazy';
export default class FleetCreateCmd extends Command {
public static description = stripIndent`
@ -60,22 +49,21 @@ export default class FleetCreateCmd extends Command {
'$ balena fleet create MyFleet -o myorg --type raspberry-pi',
];
public static args = [
{
name: 'name',
public static args = {
name: Args.string({
description: 'fleet name',
required: true,
},
];
}),
};
public static usage = 'fleet create <name>';
public static flags: flags.Input<FlagsDef> = {
organization: flags.string({
public static flags = {
organization: Flags.string({
char: 'o',
description: 'handle of the organization the fleet should belong to',
}),
type: flags.string({
type: Flags.string({
char: 't',
description:
'fleet device type (Check available types with `balena devices supported`)',
@ -86,64 +74,10 @@ export default class FleetCreateCmd extends Command {
public static authenticated = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
FleetCreateCmd,
);
const { args: params, flags: options } = await this.parse(FleetCreateCmd);
// Ascertain device type
const deviceType =
options.type ||
(await (await import('../../utils/patterns')).selectDeviceType());
// Ascertain organization
const organization =
options.organization?.toLowerCase() || (await this.getOrganization());
// Create application
try {
const application = await getBalenaSdk().models.application.create({
name: params.name,
deviceType,
organization,
});
// Output
console.log(
`Fleet created: slug "${application.slug}", device type "${deviceType}"`,
);
} catch (err) {
if ((err.message || '').toLowerCase().includes('unique')) {
// BalenaRequestError: Request error: "organization" and "app_name" must be unique.
throw new ExpectedError(
`Error: An app or block or fleet with the name "${params.name}" already exists in organization "${organization}".`,
);
} else if ((err.message || '').toLowerCase().includes('unauthorized')) {
// BalenaRequestError: Request error: Unauthorized
throw new ExpectedError(
`Error: You are not authorized to create fleets in organization "${organization}".`,
);
}
throw err;
}
}
async getOrganization() {
const { getOwnOrganizations } = await import('../../utils/sdk');
const organizations = await getOwnOrganizations(getBalenaSdk(), {
$select: ['name', 'handle'],
});
if (organizations.length === 0) {
// User is not a member of any organizations (should not happen).
throw new Error('This account is not a member of any organizations');
} else if (organizations.length === 1) {
// User is a member of only one organization - use this.
return organizations[0].handle;
} else {
// User is a member of multiple organizations -
const { selectOrganization } = await import('../../utils/patterns');
return selectOrganization(organizations);
}
await (
await import('../../utils/application-create')
).applicationCreateBase('fleet', options, params);
}
}

View File

@ -15,24 +15,13 @@
* limitations under the License.
*/
import type { flags as flagsType } from '@oclif/command';
import { flags } from '@oclif/command';
import { Flags } from '@oclif/core';
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 type { DataOutputOptions } from '../../framework';
interface FlagsDef extends DataOutputOptions {
help: void;
view: boolean;
}
interface ArgsDef {
fleet: string;
}
export default class FleetCmd extends Command {
public static description = stripIndent`
@ -48,13 +37,15 @@ export default class FleetCmd extends Command {
'$ balena fleet myorg/myfleet --view',
];
public static args = [ca.fleetRequired];
public static args = {
fleet: ca.fleetRequired,
};
public static usage = 'fleet <fleet>';
public static flags: flagsType.Input<FlagsDef> = {
public static flags = {
help: cf.help,
view: flags.boolean({
view: Flags.boolean({
default: false,
description: 'open fleet dashboard page',
}),
@ -65,9 +56,7 @@ export default class FleetCmd extends Command {
public static primary = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
FleetCmd,
);
const { args: params, flags: options } = await this.parse(FleetCmd);
const { getApplication } = await import('../../utils/sdk');

View File

@ -15,22 +15,12 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import type { IArg } from '@oclif/parser/lib/args';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { getExpandedProp } from '../../utils/pine';
interface FlagsDef {
help: void;
}
interface ArgsDef {
slug: string;
releaseToPinTo?: string;
}
export default class FleetPinCmd extends Command {
public static description = stripIndent`
Pin a fleet to a release.
@ -44,28 +34,26 @@ export default class FleetPinCmd extends Command {
'$ balena fleet pin myorg/myfleet 91165e5',
];
public static args: Array<IArg<any>> = [
{
name: 'slug',
public static args = {
slug: Args.string({
description: 'the slug of the fleet to pin to a release',
required: true,
},
{
name: 'releaseToPinTo',
}),
releaseToPinTo: Args.string({
description: 'the commit of the release for the fleet to get pinned to',
},
];
}),
};
public static usage = 'fleet pin <slug> [releaseToPinTo]';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
};
public static authenticated = true;
public async run() {
const { args: params } = this.parse<FlagsDef, ArgsDef>(FleetPinCmd);
const { args: params } = await this.parse(FleetPinCmd);
const balena = getBalenaSdk();

View File

@ -15,22 +15,12 @@
* limitations under the License.
*/
import type { flags } from '@oclif/command';
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';
interface FlagsDef {
help: void;
}
interface ArgsDef {
fleet: string;
}
export default class FleetPurgeCmd extends Command {
public static description = stripIndent`
Purge data from a fleet.
@ -46,18 +36,20 @@ export default class FleetPurgeCmd extends Command {
'$ balena fleet purge myorg/myfleet',
];
public static args = [ca.fleetRequired];
public static args = {
fleet: ca.fleetRequired,
};
public static usage = 'fleet purge <fleet>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
};
public static authenticated = true;
public async run() {
const { args: params } = this.parse<FlagsDef, ArgsDef>(FleetPurgeCmd);
const { args: params } = await this.parse(FleetPurgeCmd);
const { getApplication } = await import('../../utils/sdk');

View File

@ -15,7 +15,7 @@
* limitations under the License.
*/
import type { flags } from '@oclif/command';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
@ -23,15 +23,6 @@ import * as ca from '../../utils/common-args';
import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy';
import { applicationIdInfo } from '../../utils/messages';
interface FlagsDef {
help: void;
}
interface ArgsDef {
fleet: string;
newName?: string;
}
export default class FleetRenameCmd extends Command {
public static description = stripIndent`
Rename a fleet.
@ -50,24 +41,23 @@ export default class FleetRenameCmd extends Command {
'$ balena fleet rename myorg/oldname NewName',
];
public static args = [
ca.fleetRequired,
{
name: 'newName',
public static args = {
fleet: ca.fleetRequired,
newName: Args.string({
description: 'the new name for the fleet',
},
];
}),
};
public static usage = 'fleet rename <fleet> [newName]';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
};
public static authenticated = true;
public async run() {
const { args: params } = this.parse<FlagsDef, ArgsDef>(FleetRenameCmd);
const { args: params } = await this.parse(FleetRenameCmd);
const { validateApplicationName } = await import('../../utils/validation');
const { ExpectedError } = await import('../../errors');

View File

@ -15,22 +15,12 @@
* limitations under the License.
*/
import type { flags } from '@oclif/command';
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';
interface FlagsDef {
help: void;
}
interface ArgsDef {
fleet: string;
}
export default class FleetRestartCmd extends Command {
public static description = stripIndent`
Restart a fleet.
@ -45,18 +35,20 @@ export default class FleetRestartCmd extends Command {
'$ balena fleet restart myorg/myfleet',
];
public static args = [ca.fleetRequired];
public static args = {
fleet: ca.fleetRequired,
};
public static usage = 'fleet restart <fleet>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
};
public static authenticated = true;
public async run() {
const { args: params } = this.parse<FlagsDef, ArgsDef>(FleetRestartCmd);
const { args: params } = await this.parse(FleetRestartCmd);
const { getApplication } = await import('../../utils/sdk');

View File

@ -15,23 +15,12 @@
* limitations under the License.
*/
import type { flags } from '@oclif/command';
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';
interface FlagsDef {
yes: boolean;
help: void;
}
interface ArgsDef {
fleet: string;
}
export default class FleetRmCmd extends Command {
public static description = stripIndent`
Remove a fleet.
@ -49,11 +38,13 @@ export default class FleetRmCmd extends Command {
'$ balena fleet rm myorg/myfleet',
];
public static args = [ca.fleetRequired];
public static args = {
fleet: ca.fleetRequired,
};
public static usage = 'fleet rm <fleet>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
yes: cf.yes,
help: cf.help,
};
@ -61,9 +52,7 @@ export default class FleetRmCmd extends Command {
public static authenticated = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
FleetRmCmd,
);
const { args: params, flags: options } = await this.parse(FleetRmCmd);
const { confirm } = await import('../../utils/patterns');
const { getApplication } = await import('../../utils/sdk');

View File

@ -15,20 +15,11 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import type { IArg } from '@oclif/parser/lib/args';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
interface FlagsDef {
help: void;
}
interface ArgsDef {
slug: string;
}
export default class FleetTrackLatestCmd extends Command {
public static description = stripIndent`
Make this fleet track the latest release.
@ -40,24 +31,23 @@ export default class FleetTrackLatestCmd extends Command {
'$ balena fleet track-latest myfleet',
];
public static args: Array<IArg<any>> = [
{
name: 'slug',
public static args = {
slug: Args.string({
description: 'the slug of the fleet to make track the latest release',
required: true,
},
];
}),
};
public static usage = 'fleet track-latest <slug>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
};
public static authenticated = true;
public async run() {
const { args: params } = this.parse<FlagsDef, ArgsDef>(FleetTrackLatestCmd);
const { args: params } = await this.parse(FleetTrackLatestCmd);
const balena = getBalenaSdk();

View File

@ -15,24 +15,18 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import type * as BalenaSdk from 'balena-sdk';
import Command from '../command';
import * as cf from '../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../utils/lazy';
import type { DataSetOutputOptions } from '../framework';
interface ExtendedApplication extends ApplicationWithDeviceType {
interface ExtendedApplication extends ApplicationWithDeviceTypeSlug {
device_count: number;
online_devices: number;
device_type?: string;
}
interface FlagsDef extends DataSetOutputOptions {
help: void;
verbose?: boolean;
}
export default class FleetsCmd extends Command {
public static description = stripIndent`
List all fleets.
@ -47,7 +41,7 @@ export default class FleetsCmd extends Command {
public static usage = 'fleets';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
...cf.dataSetOutputFlags,
help: cf.help,
};
@ -56,19 +50,24 @@ export default class FleetsCmd extends Command {
public static primary = true;
public async run() {
const { flags: options } = this.parse<FlagsDef, {}>(FleetsCmd);
const { flags: options } = await this.parse(FleetsCmd);
const balena = getBalenaSdk();
const pineOptions = {
$select: ['id', 'app_name', 'slug'],
$expand: {
is_for__device_type: { $select: 'slug' },
owns__device: { $select: 'is_online' },
},
} satisfies BalenaSdk.PineOptions<BalenaSdk.Application>;
// Get applications
const applications =
(await balena.models.application.getAllDirectlyAccessible({
$select: ['id', 'app_name', 'slug'],
$expand: {
is_for__device_type: { $select: 'slug' },
owns__device: { $select: 'is_online' },
},
})) as ExtendedApplication[];
(await balena.models.application.getAllDirectlyAccessible(
pineOptions,
)) as Array<
BalenaSdk.PineTypedResult<BalenaSdk.Application, typeof pineOptions>
> as ExtendedApplication[];
// Add extended properties
applications.forEach((application) => {

View File

@ -15,6 +15,7 @@
* limitations under the License.
*/
import { Args } from '@oclif/core';
import Command from '../../command';
import { stripIndent } from '../../utils/lazy';
import { CommandHelp } from '../../utils/oclif-utils';
@ -27,12 +28,6 @@ import { CommandHelp } from '../../utils/oclif-utils';
// - https://github.com/balena-io/balena-cli/pull/1455#discussion_r334308357
// - https://github.com/balena-io/balena-cli/pull/1455#discussion_r334308526
interface ArgsDef {
image: string;
type: string;
config: string;
}
export default class OsinitCmd extends Command {
public static description = stripIndent`
Do actual init of the device with the preconfigured os image.
@ -41,20 +36,17 @@ export default class OsinitCmd extends Command {
Use \`balena os initialize <image>\` instead.
`;
public static args = [
{
name: 'image',
public static args = {
image: Args.string({
required: true,
},
{
name: 'type',
}),
type: Args.string({
required: true,
},
{
name: 'config',
}),
config: Args.string({
required: true,
},
];
}),
};
public static usage = (
'internal osinit ' +
@ -66,7 +58,7 @@ export default class OsinitCmd extends Command {
public static offlineCompatible = true;
public async run() {
const { args: params } = this.parse<{}, ArgsDef>(OsinitCmd);
const { args: params } = await this.parse(OsinitCmd);
const config = JSON.parse(params.config);

View File

@ -15,23 +15,13 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Args, Flags } from '@oclif/core';
import Command from '../command';
import * as cf from '../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../utils/lazy';
import { applicationIdInfo } from '../utils/messages';
import { parseAsLocalHostnameOrIp } from '../utils/validation';
interface FlagsDef {
fleet?: string;
pollInterval?: number;
help?: void;
}
interface ArgsDef {
deviceIpOrHostname?: string;
}
export default class JoinCmd extends Command {
public static description = stripIndent`
Move a local device to a fleet on another balena server.
@ -63,20 +53,19 @@ export default class JoinCmd extends Command {
'$ balena join 192.168.1.25 --fleet MyFleet',
];
public static args = [
{
name: 'deviceIpOrHostname',
public static args = {
deviceIpOrHostname: Args.string({
description: 'the IP or hostname of device',
parse: parseAsLocalHostnameOrIp,
},
];
}),
};
// Hardcoded to preserve camelcase
public static usage = 'join [deviceIpOrHostname]';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
fleet: cf.fleet,
pollInterval: flags.integer({
pollInterval: Flags.integer({
description: 'the interval in minutes to check for updates',
char: 'i',
}),
@ -87,9 +76,7 @@ export default class JoinCmd extends Command {
public static primary = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
JoinCmd,
);
const { args: params, flags: options } = await this.parse(JoinCmd);
const promote = await import('../utils/promote');
const sdk = getBalenaSdk();

View File

@ -15,21 +15,12 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Args } from '@oclif/core';
import Command from '../../command';
import { ExpectedError } from '../../errors';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
interface FlagsDef {
help: void;
}
interface ArgsDef {
name: string;
path: string;
}
export default class KeyAddCmd extends Command {
public static description = stripIndent`
Add an SSH key to balenaCloud.
@ -60,21 +51,19 @@ export default class KeyAddCmd extends Command {
'$ balena key add Main %userprofile%.sshid_rsa.pub',
];
public static args = [
{
name: 'name',
public static args = {
name: Args.string({
description: 'the SSH key name',
required: true,
},
{
name: `path`,
}),
path: Args.string({
description: `the path to the public key file`,
},
];
}),
};
public static usage = 'key add <name> [path]';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
};
@ -83,7 +72,7 @@ export default class KeyAddCmd extends Command {
public static readStdin = true;
public async run() {
const { args: params } = this.parse<FlagsDef, ArgsDef>(KeyAddCmd);
const { args: params } = await this.parse(KeyAddCmd);
let key: string;
if (params.path != null) {

View File

@ -15,22 +15,12 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
import { parseAsInteger } from '../../utils/validation';
type IArg<T> = import('@oclif/parser').args.IArg<T>;
interface FlagsDef {
help: void;
}
interface ArgsDef {
id: number;
}
export default class KeyCmd extends Command {
public static description = stripIndent`
Display an SSH key.
@ -40,25 +30,24 @@ export default class KeyCmd extends Command {
public static examples = ['$ balena key 17'];
public static args: Array<IArg<any>> = [
{
name: 'id',
public static args = {
id: Args.integer({
description: 'balenaCloud ID for the SSH key',
parse: (x) => parseAsInteger(x, 'id'),
parse: async (x) => parseAsInteger(x, 'id'),
required: true,
},
];
}),
};
public static usage = 'key <id>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
};
public static authenticated = true;
public async run() {
const { args: params } = this.parse<{}, ArgsDef>(KeyCmd);
const { args: params } = await this.parse(KeyCmd);
const key = await getBalenaSdk().models.key.get(params.id);

View File

@ -15,23 +15,12 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { parseAsInteger } from '../../utils/validation';
type IArg<T> = import('@oclif/parser').args.IArg<T>;
interface FlagsDef {
yes: boolean;
help: void;
}
interface ArgsDef {
id: number;
}
export default class KeyRmCmd extends Command {
public static description = stripIndent`
Remove an SSH key from balenaCloud.
@ -43,18 +32,17 @@ export default class KeyRmCmd extends Command {
public static examples = ['$ balena key rm 17', '$ balena key rm 17 --yes'];
public static args: Array<IArg<any>> = [
{
name: 'id',
public static args = {
id: Args.integer({
description: 'balenaCloud ID for the SSH key',
parse: (x) => parseAsInteger(x, 'id'),
parse: async (x) => parseAsInteger(x, 'id'),
required: true,
},
];
}),
};
public static usage = 'key rm <id>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
yes: cf.yes,
help: cf.help,
};
@ -62,9 +50,7 @@ export default class KeyRmCmd extends Command {
public static authenticated = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
KeyRmCmd,
);
const { args: params, flags: options } = await this.parse(KeyRmCmd);
const patterns = await import('../../utils/patterns');

View File

@ -15,15 +15,10 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import Command from '../command';
import * as cf from '../utils/common-flags';
import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy';
interface FlagsDef {
help: void;
}
export default class KeysCmd extends Command {
public static description = stripIndent`
List the SSH keys in balenaCloud.
@ -34,14 +29,14 @@ export default class KeysCmd extends Command {
public static usage = 'keys';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
};
public static authenticated = true;
public async run() {
this.parse<FlagsDef, {}>(KeysCmd);
await this.parse(KeysCmd);
const keys = await getBalenaSdk().models.key.getAll();

View File

@ -15,20 +15,12 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Args } from '@oclif/core';
import Command from '../command';
import * as cf from '../utils/common-flags';
import { stripIndent } from '../utils/lazy';
import { parseAsLocalHostnameOrIp } from '../utils/validation';
interface FlagsDef {
help?: void;
}
interface ArgsDef {
deviceIpOrHostname?: string;
}
export default class LeaveCmd extends Command {
public static description = stripIndent`
Remove a local device from its balena fleet.
@ -51,17 +43,16 @@ export default class LeaveCmd extends Command {
'$ balena leave 192.168.1.25',
];
public static args = [
{
name: 'deviceIpOrHostname',
public static args = {
deviceIpOrHostname: Args.string({
description: 'the device IP or hostname',
parse: parseAsLocalHostnameOrIp,
},
];
}),
};
public static usage = 'leave [deviceIpOrHostname]';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
};
@ -69,7 +60,7 @@ export default class LeaveCmd extends Command {
public static primary = true;
public async run() {
const { args: params } = this.parse<FlagsDef, ArgsDef>(LeaveCmd);
const { args: params } = await this.parse(LeaveCmd);
const promote = await import('../utils/promote');
const logger = await Command.getLogger();

View File

@ -15,20 +15,12 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Args } from '@oclif/core';
import { promisify } from 'util';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { stripIndent } from '../../utils/lazy';
interface FlagsDef {
help: void;
}
interface ArgsDef {
target: string;
}
export default class LocalConfigureCmd extends Command {
public static description = stripIndent`
(Re)configure a balenaOS drive or image.
@ -41,17 +33,16 @@ export default class LocalConfigureCmd extends Command {
'$ balena local configure path/to/image.img',
];
public static args = [
{
name: 'target',
public static args = {
target: Args.string({
description: 'path of drive or image to configure',
required: true,
},
];
}),
};
public static usage = 'local configure <target>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
};
@ -59,7 +50,7 @@ export default class LocalConfigureCmd extends Command {
public static offlineCompatible = true;
public async run() {
const { args: params } = this.parse<FlagsDef, ArgsDef>(LocalConfigureCmd);
const { args: params } = await this.parse(LocalConfigureCmd);
const reconfix = await import('reconfix');
const { denyMount, safeUmount } = await import('../../utils/umount');

View File

@ -15,23 +15,13 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Args } 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';
interface FlagsDef {
yes: boolean;
drive?: string;
help: void;
}
interface ArgsDef {
image: string;
}
export default class LocalFlashCmd extends Command {
public static description = stripIndent`
Flash an image to a drive.
@ -49,17 +39,16 @@ export default class LocalFlashCmd extends Command {
'$ balena local flash path/to/balenaos.img --drive /dev/disk2 --yes',
];
public static args = [
{
name: 'image',
public static args = {
image: Args.string({
description: 'path to OS image',
required: true,
},
];
}),
};
public static usage = 'local flash <image>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
drive: cf.drive,
yes: cf.yes,
help: cf.help,
@ -68,9 +57,7 @@ export default class LocalFlashCmd extends Command {
public static offlineCompatible = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
LocalFlashCmd,
);
const { args: params, flags: options } = await this.parse(LocalFlashCmd);
if (process.platform === 'linux') {
const { promisify } = await import('util');

View File

@ -15,11 +15,12 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Flags, Args } from '@oclif/core';
import Command from '../command';
import * as cf from '../utils/common-flags';
import { getBalenaSdk, stripIndent, getCliForm } from '../utils/lazy';
import { ExpectedError } from '../errors';
import type { WhoamiResult } from 'balena-sdk';
interface FlagsDef {
token: boolean;
@ -30,10 +31,7 @@ interface FlagsDef {
password?: string;
port?: number;
help: void;
}
interface ArgsDef {
token?: string;
hideExperimentalWarning: boolean;
}
export default class LoginCmd extends Command {
@ -59,37 +57,34 @@ export default class LoginCmd extends Command {
'$ balena login --credentials --email johndoe@gmail.com --password secret',
];
public static args = [
{
// Capitano allowed -t to be type boolean|string, which oclif does not.
// So -t is now bool, and we check first arg for token content.
name: 'token',
public static args = {
token: Args.string({
hidden: true,
},
];
}),
};
public static usage = 'login';
public static flags: flags.Input<FlagsDef> = {
web: flags.boolean({
public static flags = {
web: Flags.boolean({
default: false,
char: 'w',
description: 'web-based login',
exclusive: ['token', 'credentials'],
}),
token: flags.boolean({
token: Flags.boolean({
default: false,
char: 't',
description: 'session token or API key',
exclusive: ['web', 'credentials'],
}),
credentials: flags.boolean({
credentials: Flags.boolean({
default: false,
char: 'c',
description: 'credential-based login',
exclusive: ['web', 'token'],
}),
email: flags.string({
email: Flags.string({
char: 'e',
description: 'email',
exclusive: ['user'],
@ -97,32 +92,35 @@ export default class LoginCmd extends Command {
}),
// Capitano version of this command had a second alias for email, 'u'.
// Using an oclif hidden flag to support the same behaviour.
user: flags.string({
user: Flags.string({
char: 'u',
hidden: true,
exclusive: ['email'],
dependsOn: ['credentials'],
}),
password: flags.string({
password: Flags.string({
char: 'p',
description: 'password',
dependsOn: ['credentials'],
}),
port: flags.integer({
port: Flags.integer({
char: 'P',
description:
'TCP port number of local HTTP login server (--web auth only)',
dependsOn: ['web'],
}),
hideExperimentalWarning: Flags.boolean({
char: 'H',
default: false,
description: 'Hides warning for experimental features',
}),
help: cf.help,
};
public static primary = true;
public async run() {
const { flags: options, args: params } = this.parse<FlagsDef, ArgsDef>(
LoginCmd,
);
const { flags: options, args: params } = await this.parse(LoginCmd);
const balena = getBalenaSdk();
const messages = await import('../utils/messages');
@ -137,9 +135,24 @@ export default class LoginCmd extends Command {
console.log(`\nLogging in to ${balenaUrl}`);
await this.doLogin(options, balenaUrl, params.token);
const username = await balena.auth.whoami();
// 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;
console.info(`Successfully logged in as: ${username}`);
if (whoamiResult.actorType !== 'user' && !options.hideExperimentalWarning) {
console.info(stripIndent`
----------------------------------------------------------------------------------------
You are logging in with a ${whoamiResult.actorType} key.
This is an experimental feature and many features of the CLI might not work as expected.
We sure hope you know what you are doing.
----------------------------------------------------------------------------------------
`);
}
console.info(
`Successfully logged in as: ${this.getLoggedInMessage(whoamiResult)}`,
);
console.info(`\
Find out about the available commands by running:
@ -149,6 +162,16 @@ Find out about the available commands by running:
${messages.reachingOut}`);
}
private getLoggedInMessage(whoami: WhoamiResult): string {
if (whoami.actorType === 'user') {
return whoami.username;
}
const identifier =
whoami.actorType === 'device' ? whoami.uuid : whoami.slug;
return `${whoami.actorType} ${identifier}`;
}
async doLogin(
loginOptions: FlagsDef,
balenaUrl: string = 'balena-cloud.com',
@ -165,7 +188,14 @@ ${messages.reachingOut}`);
}
const balena = getBalenaSdk();
await balena.auth.loginWithToken(token!);
if (!(await balena.auth.whoami())) {
try {
if (!(await balena.auth.whoami())) {
throw new ExpectedError('Token authentication failed');
}
} catch (err) {
if (process.env.DEBUG) {
console.error(`Get user info failed with: ${err.message}`);
}
throw new ExpectedError('Token authentication failed');
}
return;

View File

@ -29,7 +29,7 @@ export default class LogoutCmd extends Command {
public static usage = 'logout';
public async run() {
this.parse<{}, {}>(LogoutCmd);
await this.parse(LogoutCmd);
await getBalenaSdk().auth.logout();
}
}

View File

@ -15,24 +15,11 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Flags, Args } from '@oclif/core';
import Command from '../command';
import * as cf from '../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../utils/lazy';
import { LogMessage } from 'balena-sdk';
import { IArg } from '@oclif/parser/lib/args';
interface FlagsDef {
'max-retry'?: number;
tail?: boolean;
service?: string[];
system?: boolean;
help: void;
}
interface ArgsDef {
device: string;
}
const MAX_RETRY = 1000;
@ -67,35 +54,34 @@ export default class LogsCmd extends Command {
'$ balena logs 23c73a1.local --system --service my-service',
];
public static args: Array<IArg<any>> = [
{
name: 'device',
public static args = {
device: Args.string({
description: 'device UUID, IP, or .local address',
required: true,
},
];
}),
};
public static usage = 'logs <device>';
public static flags: flags.Input<FlagsDef> = {
'max-retry': flags.integer({
public static flags = {
'max-retry': Flags.integer({
description: stripIndent`
Maximum number of reconnection attempts on "connection lost" errors
(use 0 to disable auto reconnection).`,
}),
tail: flags.boolean({
tail: Flags.boolean({
default: false,
description: 'continuously stream output',
char: 't',
}),
service: flags.string({
service: Flags.string({
description: stripIndent`
Reject logs not originating from this service.
This can be used in combination with --system or other --service flags.`,
char: 's',
multiple: true,
}),
system: flags.boolean({
system: Flags.boolean({
default: false,
description:
'Only show system logs. This can be used in combination with --service.',
@ -107,9 +93,7 @@ export default class LogsCmd extends Command {
public static primary = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
LogsCmd,
);
const { args: params, flags: options } = await this.parse(LogsCmd);
const balena = getBalenaSdk();
const { serviceIdToName } = await import('../utils/cloud');

View File

@ -15,22 +15,12 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Flags, Args } from '@oclif/core';
import Command from '../command';
import { ExpectedError } from '../errors';
import * as cf from '../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../utils/lazy';
interface FlagsDef {
device?: string; // device UUID
dev?: string; // Alias for device.
help: void;
}
interface ArgsDef {
note: string;
}
export default class NoteCmd extends Command {
public static description = stripIndent`
Set a device note.
@ -46,18 +36,17 @@ export default class NoteCmd extends Command {
'$ cat note.txt | balena note --device 7cf02a6',
];
public static args = [
{
name: 'note',
public static args = {
note: Args.string({
description: 'note content',
},
];
}),
};
public static usage = 'note <|note>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
device: { exclusive: ['dev'], ...cf.device },
dev: flags.string({
dev: Flags.string({
exclusive: ['device'],
hidden: true,
}),
@ -69,9 +58,7 @@ export default class NoteCmd extends Command {
public static readStdin = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
NoteCmd,
);
const { args: params, flags: options } = await this.parse(NoteCmd);
params.note = params.note || this.stdin;

View File

@ -15,15 +15,10 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import Command from '../command';
import * as cf from '../utils/common-flags';
import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy';
interface FlagsDef {
help: void;
}
export default class OrgsCmd extends Command {
public static description = stripIndent`
List all organizations.
@ -34,14 +29,14 @@ export default class OrgsCmd extends Command {
public static usage = 'orgs';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
};
public static authenticated = true;
public async run() {
this.parse<FlagsDef, {}>(OrgsCmd);
await this.parse(OrgsCmd);
const { getOwnOrganizations } = await import('../utils/sdk');

View File

@ -15,24 +15,13 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getCliForm, stripIndent } from '../../utils/lazy';
import * as _ from 'lodash';
import type { DeviceTypeJson } from 'balena-sdk';
interface FlagsDef {
advanced: boolean;
output: string;
help: void;
}
interface ArgsDef {
image: string;
'device-type': string;
}
export default class OsBuildConfigCmd extends Command {
public static description = stripIndent`
Prepare a configuration file for use by the 'os configure' command.
@ -46,27 +35,25 @@ export default class OsBuildConfigCmd extends Command {
'$ balena os configure ../path/rpi3.img --device 7cf02a6 --config rpi3-config.json',
];
public static args = [
{
name: 'image',
public static args = {
image: Args.string({
description: 'os image',
required: true,
},
{
name: 'device-type',
}),
'device-type': Args.string({
description: 'device type',
required: true,
},
];
}),
};
public static usage = 'os build-config <image> <device-type>';
public static flags: flags.Input<FlagsDef> = {
advanced: flags.boolean({
public static flags = {
advanced: Flags.boolean({
description: 'show advanced configuration options',
char: 'v',
}),
output: flags.string({
output: Flags.string({
description: 'path to output JSON file',
char: 'o',
required: true,
@ -77,9 +64,7 @@ export default class OsBuildConfigCmd extends Command {
public static authenticated = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
OsBuildConfigCmd,
);
const { args: params, flags: options } = await this.parse(OsBuildConfigCmd);
const { writeFile } = (await import('fs')).promises;

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Flags, Args } from '@oclif/core';
import type { Interfaces } from '@oclif/core';
import type * as BalenaSdk from 'balena-sdk';
import { promisify } from 'util';
import * as _ from 'lodash';
@ -31,29 +32,7 @@ import {
const CONNECTIONS_FOLDER = '/system-connections';
interface FlagsDef {
advanced?: boolean;
fleet?: string;
config?: string;
'config-app-update-poll-interval'?: number;
'config-network'?: string;
'config-wifi-key'?: string;
'config-wifi-ssid'?: string;
dev?: boolean; // balenaOS development variant
secureBoot?: boolean;
device?: string; // device UUID
'device-type'?: string;
help?: void;
version?: string;
'system-connection': string[];
'initial-device-name'?: string;
'provisioning-key-name'?: string;
'provisioning-key-expiry-date'?: string;
}
interface ArgsDef {
image: string;
}
type FlagsDef = Interfaces.InferredFlags<typeof OsConfigureCmd.flags>;
interface Answers {
appUpdatePollInterval: number; // in minutes
@ -111,40 +90,39 @@ export default class OsConfigureCmd extends Command {
'$ balena os configure ../path/rpi3.img -f MyFinFleet --device-type raspberrypi3 --config myWifiConfig.json',
];
public static args = [
{
name: 'image',
public static args = {
image: Args.string({
required: true,
description: 'path to a balenaOS image file, e.g. "rpi3.img"',
},
];
}),
};
public static usage = 'os configure <image>';
public static flags: flags.Input<FlagsDef> = {
advanced: flags.boolean({
public static flags = {
advanced: Flags.boolean({
char: 'v',
description:
'ask advanced configuration questions (when in interactive mode)',
}),
fleet: { ...cf.fleet, exclusive: ['device'] },
config: flags.string({
config: Flags.string({
description:
'path to a pre-generated config.json file to be injected in the OS image',
exclusive: ['provisioning-key-name', 'provisioning-key-expiry-date'],
}),
'config-app-update-poll-interval': flags.integer({
'config-app-update-poll-interval': Flags.integer({
description:
'supervisor cloud polling interval in minutes (e.g. for variable updates)',
}),
'config-network': flags.string({
'config-network': Flags.string({
description: 'device network type (non-interactive configuration)',
options: ['ethernet', 'wifi'],
}),
'config-wifi-key': flags.string({
'config-wifi-key': Flags.string({
description: 'WiFi key (password) (non-interactive configuration)',
}),
'config-wifi-ssid': flags.string({
'config-wifi-ssid': Flags.string({
description: 'WiFi SSID (network name) (non-interactive configuration)',
}),
dev: cf.dev,
@ -157,29 +135,29 @@ export default class OsConfigureCmd extends Command {
'provisioning-key-expiry-date',
],
},
'device-type': flags.string({
'device-type': Flags.string({
description:
'device type slug (e.g. "raspberrypi3") to override the fleet device type',
}),
'initial-device-name': flags.string({
'initial-device-name': Flags.string({
description:
'This option will set the device name when the device provisions',
}),
version: flags.string({
version: Flags.string({
description: 'balenaOS version, for example "2.32.0" or "2.44.0+rev1"',
}),
'system-connection': flags.string({
'system-connection': Flags.string({
multiple: true,
char: 'c',
required: false,
description:
"paths to local files to place into the 'system-connections' directory",
}),
'provisioning-key-name': flags.string({
'provisioning-key-name': Flags.string({
description: 'custom key name assigned to generated provisioning api key',
exclusive: ['config', 'device'],
}),
'provisioning-key-expiry-date': flags.string({
'provisioning-key-expiry-date': Flags.string({
description:
'expiry date assigned to generated provisioning api key (format: YYYY-MM-DD)',
exclusive: ['config', 'device'],
@ -190,9 +168,7 @@ export default class OsConfigureCmd extends Command {
public static authenticated = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
OsConfigureCmd,
);
const { args: params, flags: options } = await this.parse(OsConfigureCmd);
await validateOptions(options);
@ -204,7 +180,7 @@ export default class OsConfigureCmd extends Command {
const helpers = await import('../../utils/helpers');
const { getApplication } = await import('../../utils/sdk');
let app: ApplicationWithDeviceType | undefined;
let app: ApplicationWithDeviceTypeSlug | undefined;
let device;
let deviceTypeSlug: string;
@ -223,7 +199,7 @@ export default class OsConfigureCmd extends Command {
$expand: {
is_for__device_type: { $select: 'slug' },
},
})) as ApplicationWithDeviceType;
})) as ApplicationWithDeviceTypeSlug;
await checkDeviceTypeCompatibility(options, app);
deviceTypeSlug =
options['device-type'] || app.is_for__device_type[0].slug;

View File

@ -15,21 +15,11 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { stripIndent } from '../../utils/lazy';
interface FlagsDef {
output: string;
version?: string;
help: void;
}
interface ArgsDef {
type: string;
}
export default class OsDownloadCmd extends Command {
public static description = stripIndent`
Download an unconfigured OS image.
@ -65,23 +55,22 @@ export default class OsDownloadCmd extends Command {
'$ balena os download raspberrypi3 -o ../foo/bar/raspberry-pi.img --version menu-esr',
];
public static args = [
{
name: 'type',
public static args = {
type: Args.string({
description: 'the device type',
required: true,
},
];
}),
};
public static usage = 'os download <type>';
public static flags: flags.Input<FlagsDef> = {
output: flags.string({
public static flags = {
output: Flags.string({
description: 'output path',
char: 'o',
required: true,
}),
version: flags.string({
version: Flags.string({
description: stripIndent`
version number (ESR or non-ESR versions),
or semver range (non-ESR versions only),
@ -96,9 +85,7 @@ export default class OsDownloadCmd extends Command {
};
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
OsDownloadCmd,
);
const { args: params, flags: options } = await this.parse(OsDownloadCmd);
// balenaOS ESR versions require user authentication
if (options.version) {

View File

@ -15,22 +15,11 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getCliForm, stripIndent } from '../../utils/lazy';
interface FlagsDef {
type: string;
drive?: string;
yes: boolean;
help: void;
}
interface ArgsDef {
image: string;
}
const INIT_WARNING_MESSAGE = `
Note: Initializing the device may ask for administrative permissions
@ -52,17 +41,16 @@ export default class OsInitializeCmd extends Command {
'$ balena os initialize ../path/rpi.img --type raspberry-pi',
];
public static args = [
{
name: 'image',
public static args = {
image: Args.string({
description: 'path to OS image',
required: true,
},
];
}),
};
public static usage = 'os initialize <image>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
type: cf.deviceType,
drive: cf.drive,
yes: cf.yes,
@ -72,9 +60,7 @@ export default class OsInitializeCmd extends Command {
public static authenticated = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
OsInitializeCmd,
);
const { args: params, flags: options } = await this.parse(OsInitializeCmd);
const { getManifest, sudo } = await import('../../utils/helpers');

View File

@ -15,20 +15,11 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { stripIndent } from '../../utils/lazy';
interface FlagsDef {
esr?: boolean;
help: void;
}
interface ArgsDef {
type: string;
}
export default class OsVersionsCmd extends Command {
public static description = stripIndent`
Show available balenaOS versions for the given device type.
@ -42,28 +33,25 @@ export default class OsVersionsCmd extends Command {
public static examples = ['$ balena os versions raspberrypi3'];
public static args = [
{
name: 'type',
public static args = {
type: Args.string({
description: 'device type',
required: true,
},
];
}),
};
public static usage = 'os versions <type>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
esr: flags.boolean({
esr: Flags.boolean({
description: 'select balenaOS ESR versions',
default: false,
}),
};
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
OsVersionsCmd,
);
const { args: params, flags: options } = await this.parse(OsVersionsCmd);
const { formatOsVersion, getOsVersions } = await import(
'../../utils/cloud'

View File

@ -25,11 +25,10 @@ import {
stripIndent,
} from '../utils/lazy';
import { applicationIdInfo } from '../utils/messages';
import type { DockerConnectionCliFlags } from '../utils/docker';
import { dockerConnectionCliFlags } from '../utils/docker';
import { parseAsInteger } from '../utils/validation';
import { flags } from '@oclif/command';
import { Flags, Args } from '@oclif/core';
import * as _ from 'lodash';
import type {
Application,
@ -41,21 +40,6 @@ import type {
} from 'balena-sdk';
import type { Preloader } from 'balena-preload';
interface FlagsDef extends DockerConnectionCliFlags {
fleet?: string;
commit?: string;
'splash-image'?: string;
'dont-check-arch': boolean;
'pin-device-to-release': boolean;
'additional-space'?: number;
'add-certificate'?: string[];
help: void;
}
interface ArgsDef {
image: string;
}
export default class PreloadCmd extends Command {
public static description = stripIndent`
Preload a release on a disk image (or Edison zip archive).
@ -89,19 +73,18 @@ export default class PreloadCmd extends Command {
'$ balena preload balena.img',
];
public static args = [
{
name: 'image',
public static args = {
image: Args.string({
description: 'the image file path',
required: true,
},
];
}),
};
public static usage = 'preload <image>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
fleet: cf.fleet,
commit: flags.string({
commit: Flags.string({
description: `\
The commit hash of the release to preload. Use "current" to specify the current
release (ignored if no appId is given). The current release is usually also the
@ -112,27 +95,27 @@ https://github.com/balena-io-examples/staged-releases\
`,
char: 'c',
}),
'splash-image': flags.string({
'splash-image': Flags.string({
description: 'path to a png image to replace the splash screen',
char: 's',
}),
'dont-check-arch': flags.boolean({
'dont-check-arch': Flags.boolean({
default: false,
description:
'disable architecture compatibility check between image and fleet',
}),
'pin-device-to-release': flags.boolean({
default: false,
'pin-device-to-release': Flags.boolean({
allowNo: true,
description:
'pin the preloaded device to the preloaded release on provision',
char: 'p',
}),
'additional-space': flags.integer({
'additional-space': Flags.integer({
description:
'expand the image by this amount of bytes instead of automatically estimating the required amount',
parse: (x) => parseAsInteger(x, 'additional-space'),
parse: async (x) => parseAsInteger(x, 'additional-space'),
}),
'add-certificate': flags.string({
'add-certificate': Flags.string({
description: `\
Add the given certificate (in PEM format) to /etc/ssl/certs in the preloading container.
The file name must end with '.crt' and must not be already contained in the preloader's
@ -144,14 +127,14 @@ Can be repeated to add multiple certificates.\
...dockerConnectionCliFlags,
// Redefining --dockerPort here (defined already in dockerConnectionCliFlags)
// without -p alias, to avoid clash with -p alias of pin-device-to-release
dockerPort: flags.integer({
dockerPort: Flags.integer({
description:
'Docker daemon TCP port number (hint: 2375 for balena devices)',
parse: (p) => parseAsInteger(p, 'dockerPort'),
parse: async (p) => parseAsInteger(p, 'dockerPort'),
}),
// Not supporting -h for help, because of clash with -h in DockerCliFlags
// Revisit this in future release.
help: flags.help({}),
help: Flags.help({}),
};
public static authenticated = true;
@ -159,9 +142,7 @@ Can be repeated to add multiple certificates.\
public static primary = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
PreloadCmd,
);
const { args: params, flags: options } = await this.parse(PreloadCmd);
const balena = getBalenaSdk();
const balenaPreload = await import('balena-preload');
@ -230,7 +211,7 @@ Can be repeated to add multiple certificates.\
const splashImage = options['splash-image'];
const additionalSpace = options['additional-space'];
const dontCheckArch = options['dont-check-arch'] || false;
const pinDevice = options['pin-device-to-release'] || false;
const pinDevice = options['pin-device-to-release'];
if (dontCheckArch && !fleetSlug) {
throw new ExpectedError(
@ -257,7 +238,7 @@ Can be repeated to add multiple certificates.\
splashImage,
undefined, // TODO: Currently always undefined, investigate approach in ssh command.
dontCheckArch,
pinDevice,
pinDevice ?? false,
certificates,
additionalSpace,
);
@ -409,9 +390,8 @@ Can be repeated to add multiple certificates.\
);
applicationInfoSpinner.start();
const applications = await this.getApplicationsWithSuccessfulBuilds(
deviceTypeSlug,
);
const applications =
await this.getApplicationsWithSuccessfulBuilds(deviceTypeSlug);
applicationInfoSpinner.stop();
if (applications.length === 0) {
throw new ExpectedError(
@ -450,14 +430,14 @@ Can be repeated to add multiple certificates.\
async offerToDisableAutomaticUpdates(
application: Pick<Application, 'id' | 'should_track_latest_release'>,
commit: string,
pinDevice: boolean,
pinDevice: boolean | undefined,
) {
const balena = getBalenaSdk();
if (
this.isCurrentCommit(commit) ||
!application.should_track_latest_release ||
pinDevice
pinDevice != null
) {
return;
}
@ -476,8 +456,9 @@ through the web dashboard or programatically through the balena API / SDK.
Documentation about release policies and pinning can be found at:
https://www.balena.io/docs/learn/deploy/release-strategy/release-policy/
Alternatively, the --pin-device-to-release flag may be used to pin only the
preloaded device to the selected release.
Alternatively, the --pin-device-to-release or --no-pin-device-to-release flags may be used
to avoid this interactive confirmation and pin only the preloaded device to the selected release
or keep it unpinned respectively.
Would you like to disable automatic updates for this fleet now?\
`;
@ -511,7 +492,7 @@ Would you like to disable automatic updates for this fleet now?\
options: {
slug?: string;
commit?: string;
pinDevice: boolean;
pinDevice?: boolean;
},
) {
await preloader.prepare();

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Flags, Args } from '@oclif/core';
import type { Interfaces } from '@oclif/core';
import Command from '../command';
import * as cf from '../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../utils/lazy';
@ -34,30 +35,7 @@ enum BuildTarget {
Device,
}
interface ArgsDef {
fleetOrDevice: string;
}
interface FlagsDef {
source: string;
emulated: boolean;
dockerfile?: string; // DeviceDeployOptions.dockerfilePath (alternative Dockerfile)
nocache: boolean;
pull: boolean;
'noparent-check': boolean;
'registry-secrets'?: string;
nolive: boolean;
detached: boolean;
service?: string[];
system: boolean;
env?: string[];
'noconvert-eol': boolean;
'multi-dockerignore': boolean;
'release-tag'?: string[];
draft: boolean;
note?: string;
help: void;
}
type FlagsDef = Interfaces.InferredFlags<typeof PushCmd.flags>;
export default class PushCmd extends Command {
public static description = stripIndent`
@ -112,27 +90,26 @@ export default class PushCmd extends Command {
'$ balena push 23c73a1.local --system --service my-service',
];
public static args = [
{
name: 'fleetOrDevice',
public static args = {
fleetOrDevice: Args.string({
description:
'fleet name or slug, or local device IP address or ".local" hostname',
required: true,
parse: lowercaseIfSlug,
},
];
}),
};
public static usage = 'push <fleetOrDevice>';
public static flags: flags.Input<FlagsDef> = {
source: flags.string({
public static flags = {
source: Flags.string({
description: stripIndent`
Source directory to be sent to balenaCloud or balenaOS device
(default: current working dir)`,
char: 's',
default: '.',
}),
emulated: flags.boolean({
emulated: Flags.boolean({
description: stripIndent`
Don't use the faster, native balenaCloud ARM builders; force slower QEMU ARM
emulation on Intel x86-64 builders. This flag is sometimes used to investigate
@ -140,11 +117,11 @@ export default class PushCmd extends Command {
char: 'e',
default: false,
}),
dockerfile: flags.string({
dockerfile: Flags.string({
description:
'Alternative Dockerfile name/path, relative to the source folder',
}),
nocache: flags.boolean({
nocache: Flags.boolean({
description: stripIndent`
Don't use cached layers of previously built images for this project. This
ensures that the latest base image and packages are pulled. Note that build
@ -155,18 +132,18 @@ export default class PushCmd extends Command {
char: 'c',
default: false,
}),
pull: flags.boolean({
pull: Flags.boolean({
description: stripIndent`
When pushing to a local device, force the base images to be pulled again.
Currently this option is ignored when pushing to the balenaCloud builders.`,
default: false,
}),
'noparent-check': flags.boolean({
'noparent-check': Flags.boolean({
description: stripIndent`
Disable project validation check of 'docker-compose.yml' file in parent folder`,
default: false,
}),
'registry-secrets': flags.string({
'registry-secrets': Flags.string({
description: stripIndent`
Path to a local YAML or JSON file containing Docker registry passwords used
to pull base images. Note that if registry-secrets are not provided on the
@ -174,7 +151,7 @@ export default class PushCmd extends Command {
used (usually $HOME/.balena/secrets.yml|.json)`,
char: 'R',
}),
nolive: flags.boolean({
nolive: Flags.boolean({
description: stripIndent`
Don't run a live session on this push. The filesystem will not be monitored,
and changes will not be synchronized to any running containers. Note that both
@ -182,7 +159,7 @@ export default class PushCmd extends Command {
initial build has completed.`,
default: false,
}),
detached: flags.boolean({
detached: Flags.boolean({
description: stripIndent`
When pushing to the cloud, this option will cause the build to start, then
return execution back to the shell, with the status and release ID (if
@ -191,20 +168,20 @@ export default class PushCmd extends Command {
char: 'd',
default: false,
}),
service: flags.string({
service: Flags.string({
description: stripIndent`
Reject logs not originating from this service.
This can be used in combination with --system and other --service flags.
Only valid when pushing to a local mode device.`,
multiple: true,
}),
system: flags.boolean({
system: Flags.boolean({
description: stripIndent`
Only show system logs. This can be used in combination with --service.
Only valid when pushing to a local mode device.`,
default: false,
}),
env: flags.string({
env: Flags.string({
description: stripIndent`
When performing a push to device, run the built containers with environment
variables provided with this argument. Environment variables can be applied
@ -216,17 +193,17 @@ export default class PushCmd extends Command {
`,
multiple: true,
}),
'noconvert-eol': flags.boolean({
'noconvert-eol': Flags.boolean({
description: `Don't convert line endings from CRLF (Windows format) to LF (Unix format).`,
default: false,
}),
'multi-dockerignore': flags.boolean({
'multi-dockerignore': Flags.boolean({
description:
'Have each service use its own .dockerignore file. See "balena help push".',
char: 'm',
default: false,
}),
'release-tag': flags.string({
'release-tag': Flags.string({
description: stripIndent`
Set release tags if the image build is successful (balenaCloud only). Multiple
arguments may be provided, alternating tag keys and values (see examples).
@ -235,7 +212,7 @@ export default class PushCmd extends Command {
multiple: true,
exclusive: ['detached'],
}),
draft: flags.boolean({
draft: Flags.boolean({
description: stripIndent`
Instruct the builder to create the release as a draft. Draft releases are ignored
by the 'track latest' release policy but can be used through release pinning.
@ -243,16 +220,14 @@ export default class PushCmd extends Command {
as final by default unless this option is given.`,
default: false,
}),
note: flags.string({ description: 'The notes for this release' }),
note: Flags.string({ description: 'The notes for this release' }),
help: cf.help,
};
public static primary = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
PushCmd,
);
const { args: params, flags: options } = await this.parse(PushCmd);
const logger = await Command.getLogger();
logger.logDebug(`Using build source directory: ${options.source} `);

View File

@ -15,19 +15,10 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { commitOrIdArg } from '.';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { tryAsInteger } from '../../utils/validation';
interface FlagsDef {
help: void;
}
interface ArgsDef {
commitOrId: string | number;
}
export default class ReleaseFinalizeCmd extends Command {
public static description = stripIndent`
@ -51,23 +42,21 @@ export default class ReleaseFinalizeCmd extends Command {
public static usage = 'release finalize <commitOrId>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
};
public static args = [
{
name: 'commitOrId',
public static args = {
commitOrId: commitOrIdArg({
description: 'the commit or ID of the release to finalize',
required: true,
parse: (commitOrId: string) => tryAsInteger(commitOrId),
},
];
}),
};
public static authenticated = true;
public async run() {
const { args: params } = this.parse<FlagsDef, ArgsDef>(ReleaseFinalizeCmd);
const { args: params } = await this.parse(ReleaseFinalizeCmd);
const balena = getBalenaSdk();

View File

@ -15,64 +15,62 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Flags, Args, type Interfaces } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
import type * as BalenaSdk from 'balena-sdk';
import jsyaml = require('js-yaml');
import { tryAsInteger } from '../../utils/validation';
import { jsonInfo } from '../../utils/messages';
interface FlagsDef {
help: void;
composition?: boolean;
}
export const commitOrIdArg = Args.custom({
parse: async (commitOrId: string) => tryAsInteger(commitOrId),
});
interface ArgsDef {
commitOrId: string | number;
}
type FlagsDef = Interfaces.InferredFlags<typeof ReleaseCmd.flags>;
export default class ReleaseCmd extends Command {
public static description = stripIndent`
Get info for a release.
${jsonInfo.split('\n').join('\n\t\t')}
`;
public static examples = [
'$ balena release a777f7345fe3d655c1c981aa642e5555',
'$ balena release 1234567',
'$ balena release d3f3151f5ad25ca6b070aa4d08296aca --json',
];
public static usage = 'release <commitOrId>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
json: cf.json,
help: cf.help,
composition: flags.boolean({
composition: Flags.boolean({
default: false,
char: 'c',
description: 'Return the release composition',
}),
};
public static args = [
{
name: 'commitOrId',
public static args = {
commitOrId: commitOrIdArg({
description: 'the commit or ID of the release to get information',
required: true,
parse: (commitOrId: string) => tryAsInteger(commitOrId),
},
];
}),
};
public static authenticated = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
ReleaseCmd,
);
const { args: params, flags: options } = await this.parse(ReleaseCmd);
const balena = getBalenaSdk();
if (options.composition) {
await this.showComposition(params.commitOrId, balena);
} else {
await this.showReleaseInfo(params.commitOrId, balena);
await this.showReleaseInfo(params.commitOrId, balena, options);
}
}
@ -90,6 +88,7 @@ export default class ReleaseCmd extends Command {
async showReleaseInfo(
commitOrId: string | number,
balena: BalenaSdk.BalenaSDK,
options: FlagsDef,
) {
const fields: Array<keyof BalenaSdk.Release> = [
'id',
@ -104,7 +103,7 @@ export default class ReleaseCmd extends Command {
];
const release = await balena.models.release.get(commitOrId, {
$select: fields,
...(!options.json && { $select: fields }),
$expand: {
release_tag: {
$select: ['tag_key', 'value'],
@ -112,17 +111,21 @@ export default class ReleaseCmd extends Command {
},
});
const tagStr = release
.release_tag!.map((t) => `${t.tag_key}=${t.value}`)
.join('\n');
if (options.json) {
console.log(JSON.stringify(release, null, 4));
} else {
const tagStr = release
.release_tag!.map((t) => `${t.tag_key}=${t.value}`)
.join('\n');
const _ = await import('lodash');
const values = _.mapValues(
release,
(val) => val ?? 'N/a',
) as Dictionary<string>;
values['tags'] = tagStr;
const _ = await import('lodash');
const values = _.mapValues(
release,
(val) => val ?? 'N/a',
) as Dictionary<string>;
values['tags'] = tagStr;
console.log(getVisuals().table.vertical(values, [...fields, 'tags']));
console.log(getVisuals().table.vertical(values, [...fields, 'tags']));
}
}
}

View File

@ -15,19 +15,10 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { commitOrIdArg } from '.';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { tryAsInteger } from '../../utils/validation';
interface FlagsDef {
help: void;
}
interface ArgsDef {
commitOrId: string | number;
}
export default class ReleaseInvalidateCmd extends Command {
public static description = stripIndent`
@ -46,25 +37,21 @@ export default class ReleaseInvalidateCmd extends Command {
public static usage = 'release invalidate <commitOrId>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
};
public static args = [
{
name: 'commitOrId',
public static args = {
commitOrId: commitOrIdArg({
description: 'the commit or ID of the release to invalidate',
required: true,
parse: (commitOrId: string) => tryAsInteger(commitOrId),
},
];
}),
};
public static authenticated = true;
public async run() {
const { args: params } = this.parse<FlagsDef, ArgsDef>(
ReleaseInvalidateCmd,
);
const { args: params } = await this.parse(ReleaseInvalidateCmd);
const balena = getBalenaSdk();

View File

@ -15,19 +15,10 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { commitOrIdArg } from '.';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { tryAsInteger } from '../../utils/validation';
interface FlagsDef {
help: void;
}
interface ArgsDef {
commitOrId: string | number;
}
export default class ReleaseValidateCmd extends Command {
public static description = stripIndent`
@ -45,23 +36,21 @@ export default class ReleaseValidateCmd extends Command {
public static usage = 'release validate <commitOrId>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
};
public static args = [
{
name: 'commitOrId',
public static args = {
commitOrId: commitOrIdArg({
description: 'the commit or ID of the release to validate',
required: true,
parse: (commitOrId: string) => tryAsInteger(commitOrId),
},
];
}),
};
public static authenticated = true;
public async run() {
const { args: params } = this.parse<FlagsDef, ArgsDef>(ReleaseValidateCmd);
const { args: params } = await this.parse(ReleaseValidateCmd);
const balena = getBalenaSdk();

View File

@ -15,20 +15,13 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Args } from '@oclif/core';
import Command from '../command';
import * as cf from '../utils/common-flags';
import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy';
import { applicationNameNote } from '../utils/messages';
import type * as BalenaSdk from 'balena-sdk';
interface FlagsDef {
help: void;
}
interface ArgsDef {
fleet: string;
}
import { jsonInfo } from '../utils/messages';
export default class ReleasesCmd extends Command {
public static description = stripIndent`
@ -37,27 +30,32 @@ export default class ReleasesCmd extends Command {
List all releases of the given fleet.
${applicationNameNote.split('\n').join('\n\t\t')}
${jsonInfo.split('\n').join('\n\t\t')}
`;
public static examples = ['$ balena releases myorg/myfleet'];
public static examples = [
'$ balena releases myorg/myfleet',
'$ balena releases myorg/myfleet --json',
];
public static usage = 'releases <fleet>';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
json: cf.json,
help: cf.help,
};
public static args = [
{
name: 'fleet',
public static args = {
fleet: Args.string({
description: 'fleet name or slug (preferred)',
required: true,
},
];
}),
};
public static authenticated = true;
public async run() {
const { args: params } = this.parse<FlagsDef, ArgsDef>(ReleasesCmd);
const { args: params, flags: options } = await this.parse(ReleasesCmd);
const fields: Array<keyof BalenaSdk.Release> = [
'id',
@ -73,15 +71,27 @@ export default class ReleasesCmd extends Command {
const releases = await balena.models.release.getAllByApplication(
await getFleetSlug(balena, params.fleet),
{ $select: fields },
options.json
? {
$expand: {
release_tag: {
$select: ['tag_key', 'value'],
},
},
}
: { $select: fields },
);
const _ = await import('lodash');
console.log(
getVisuals().table.horizontal(
releases.map((rel) => _.mapValues(rel, (val) => val ?? 'N/a')),
fields,
),
);
if (options.json) {
console.log(JSON.stringify(releases, null, 4));
} else {
const _ = await import('lodash');
console.log(
getVisuals().table.horizontal(
releases.map((rel) => _.mapValues(rel, (val) => val ?? 'N/a')),
fields,
),
);
}
}
}

View File

@ -15,18 +15,11 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Flags } from '@oclif/core';
import Command from '../command';
import * as cf from '../utils/common-flags';
import { getCliUx, stripIndent } from '../utils/lazy';
interface FlagsDef {
json?: boolean;
verbose: boolean;
timeout?: number;
help: void;
}
export default class ScanCmd extends Command {
public static description = stripIndent`
Scan for balenaOS devices on your local network.
@ -47,18 +40,18 @@ export default class ScanCmd extends Command {
public static usage = 'scan';
public static flags: flags.Input<FlagsDef> = {
verbose: flags.boolean({
public static flags = {
verbose: Flags.boolean({
default: false,
char: 'v',
description: 'display full info',
}),
timeout: flags.integer({
timeout: Flags.integer({
char: 't',
description: 'scan timeout in seconds',
}),
help: cf.help,
json: flags.boolean({
json: Flags.boolean({
default: false,
char: 'j',
description: 'produce JSON output instead of tabular output',
@ -78,7 +71,7 @@ export default class ScanCmd extends Command {
const dockerPort = 2375;
const dockerTimeout = 2000;
const { flags: options } = this.parse<FlagsDef, {}>(ScanCmd);
const { flags: options } = await this.parse(ScanCmd);
const discoverTimeout =
options.timeout != null ? options.timeout * 1000 : undefined;

View File

@ -15,15 +15,10 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import Command from '../command';
import * as cf from '../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../utils/lazy';
interface FlagsDef {
help: void;
}
export default class SettingsCmd extends Command {
public static description = stripIndent`
Print current settings.
@ -34,12 +29,12 @@ export default class SettingsCmd extends Command {
public static usage = 'settings';
public static flags: flags.Input<FlagsDef> = {
public static flags = {
help: cf.help,
};
public async run() {
this.parse<FlagsDef, {}>(SettingsCmd);
await this.parse(SettingsCmd);
const settings = await getBalenaSdk().settings.getAll();

View File

@ -15,25 +15,12 @@
* limitations under the License.
*/
import { flags } from '@oclif/command';
import { Flags, Args } from '@oclif/core';
import Command from '../command';
import * as cf from '../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../utils/lazy';
import { parseAsInteger, validateLocalHostnameOrIp } from '../utils/validation';
interface FlagsDef {
port?: number;
tty: boolean;
verbose: boolean;
noproxy: boolean;
help: void;
}
interface ArgsDef {
fleetOrDevice: string;
service?: string;
}
export default class SshCmd extends Command {
public static description = stripIndent`
Open a SSH prompt on a device's host OS or service container.
@ -73,41 +60,40 @@ export default class SshCmd extends Command {
'$ echo "uptime; exit;" | balena ssh 192.168.0.1 myService',
];
public static args = [
{
name: 'fleetOrDevice',
public static args = {
fleetOrDevice: Args.string({
description: 'fleet name/slug, device uuid, or address of local device',
required: true,
},
{
name: 'service',
}),
service: Args.string({
description: 'service name, if connecting to a container',
required: false,
},
];
ignoreStdin: true,
}),
};
public static usage = 'ssh <fleetOrDevice> [service]';
public static flags: flags.Input<FlagsDef> = {
port: flags.integer({
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: (p) => parseAsInteger(p, 'port'),
parse: async (p) => parseAsInteger(p, 'port'),
}),
tty: flags.boolean({
tty: Flags.boolean({
default: false,
description:
'force pseudo-terminal allocation (bypass TTY autodetection for stdin)',
char: 't',
}),
verbose: flags.boolean({
verbose: Flags.boolean({
default: false,
description: 'increase verbosity',
char: 'v',
}),
noproxy: flags.boolean({
noproxy: Flags.boolean({
default: false,
description: 'bypass global proxy configuration for the ssh connection',
}),
@ -118,9 +104,7 @@ export default class SshCmd extends Command {
public static offlineCompatible = true;
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
SshCmd,
);
const { args: params, flags: options } = await this.parse(SshCmd);
// Local connection
if (validateLocalHostnameOrIp(params.fleetOrDevice)) {
@ -152,9 +136,9 @@ export default class SshCmd extends Command {
const { which } = await import('../utils/which');
const [whichProxytunnel, username, proxyUrl] = await Promise.all([
const [whichProxytunnel, { username }, proxyUrl] = await Promise.all([
useProxy ? which('proxytunnel', false) : undefined,
sdk.auth.whoami(),
sdk.auth.getUserInfo(),
// note that `proxyUrl` refers to the balenaCloud "resin-proxy"
// service, currently "balena-devices.com", rather than some
// local proxy server URL
@ -208,7 +192,7 @@ export default class SshCmd extends Command {
port: options.port || 'cloud',
proxyCommand,
service: params.service,
username: username!,
username,
});
}

Some files were not shown because too many files have changed in this diff Show More