Compare commits

..

2 Commits

Author SHA1 Message Date
357866e345 Example on how to test with mocks and stubs
Change-type: patch
2024-09-10 19:11:01 -03:00
629ac9e5e9 Add commands for exporting and importing app/fleet releases.
Change-type: minor
Signed-off-by: Carlo Miguel F. Cruz <carloc@balena.io>
2024-09-10 21:57:06 +08:00
158 changed files with 2818 additions and 3454 deletions

View File

@ -28,7 +28,7 @@ runs:
using: 'composite'
steps:
- name: Download custom source artifact
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
with:
name: custom-${{ github.event.pull_request.head.sha || github.event.head_commit.id }}-${{ runner.os }}-${{ runner.arch }}
path: ${{ runner.temp }}
@ -39,7 +39,7 @@ runs:
run: tar -xf ${{ runner.temp }}/custom.tgz
- name: Setup Node.js
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4
with:
node-version: ${{ inputs.NODE_VERSION }}
cache: npm
@ -66,7 +66,7 @@ runs:
# https://github.com/Apple-Actions/import-codesign-certs
- name: Import Apple code signing certificate
if: runner.os == 'macOS'
uses: apple-actions/import-codesign-certs@8f3fb608891dd2244cdab3d69cd68c0d37a7fe93 # v2
uses: apple-actions/import-codesign-certs@253ddeeac23f2bdad1646faac5c8c2832e800071 # v1
with:
p12-file-base64: ${{ fromJSON(inputs.secrets).APPLE_SIGNING }}
p12-password: ${{ fromJSON(inputs.secrets).APPLE_SIGNING_PASSWORD }}
@ -135,7 +135,7 @@ runs:
XCODE_APP_LOADER_TEAM_ID: ${{ inputs.XCODE_APP_LOADER_TEAM_ID }}
- name: Upload artifacts
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4
with:
name: gh-release-${{ github.event.pull_request.head.sha || github.event.head_commit.id }}-${{ strategy.job-index }}
path: dist

View File

@ -26,7 +26,7 @@ runs:
steps:
# https://github.com/actions/setup-node#caching-global-packages-data
- name: Setup Node.js
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4
with:
node-version: ${{ inputs.NODE_VERSION }}
cache: npm
@ -58,7 +58,7 @@ runs:
run: tar --exclude-vcs -acf ${{ runner.temp }}/custom.tgz .
- name: Upload custom artifact
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4
with:
name: custom-${{ github.event.pull_request.head.sha || github.event.head_commit.id }}-${{ runner.os }}-${{ runner.arch }}
path: ${{ runner.temp }}/custom.tgz

View File

@ -1,459 +1,3 @@
- commits:
- subject: Add alias `organization list` for `orgs` command
hash: 082cce332a497c15b34467f9ed22514ebaafaec9
body: ""
footer:
Change-type: minor
change-type: minor
author: myarmolinsky
nested: []
version: 19.9.0
title: ""
date: 2024-10-18T14:25:39.691Z
- commits:
- subject: Add alias `tag list` for `tags` command
hash: 5eba175bf1a0e016b98757848e0c345489f686c1
body: ""
footer:
Change-type: minor
change-type: minor
author: myarmolinsky
nested: []
version: 19.8.0
title: ""
date: 2024-10-18T13:25:50.462Z
- commits:
- subject: Add alias `env list` for command `envs`
hash: 417c75484b1ac6c216323248b50ba00fc3fe5a7f
body: ""
footer:
Change-type: minor
change-type: minor
author: myarmolinsky
nested: []
version: 19.7.0
title: ""
date: 2024-10-18T12:35:31.416Z
- commits:
- subject: Add `ssh-key` as aliases for all `key` commands
hash: e853b15f12af429265b1e9e20ce60db19438df4b
body: ""
footer:
Change-type: minor
change-type: minor
author: myarmolinsky
nested: []
version: 19.6.0
title: ""
date: 2024-10-18T11:04:26.745Z
- commits:
- subject: Add `key list` alias for `keys` command
hash: 6b5c6e072b426a1e9517376ee756afa31ed96cfa
body: ""
footer:
Change-type: minor
change-type: minor
author: myarmolinsky
nested: []
version: 19.5.0
title: ""
date: 2024-10-17T14:56:38.422Z
- commits:
- subject: Add `release list` alias for `releases` command
hash: 8d1394a77d8cca4f18d162a3f522e80e8092ffa7
body: ""
footer:
Change-type: minor
change-type: minor
author: myarmolinsky
nested: []
version: 19.4.0
title: ""
date: 2024-10-16T19:12:05.274Z
- commits:
- subject: Add alias `fleet list` for `fleets` command
hash: 5243803342203ebac2a04c2fcfe1e9aa4a126a21
body: ""
footer:
Change-type: minor
change-type: minor
author: myarmolinsky
nested: []
version: 19.3.0
title: ""
date: 2024-10-16T18:03:16.667Z
- commits:
- subject: Add alias `api-key list` for command `api-keys`
hash: cad5543863b9f3e2b9c5883083dc3051b9e50ecf
body: ""
footer:
Change-type: minor
change-type: minor
author: myarmolinsky
nested: []
version: 19.2.0
title: ""
date: 2024-10-16T16:11:48.485Z
- commits:
- subject: Remove custom sorting of OS commands in docs in favor of alphabetizing
hash: af2c04540f41428b668ab0890dd498dfd28a4f11
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
version: 19.1.3
title: ""
date: 2024-10-16T14:39:24.641Z
- commits:
- subject: Remove no longer needed dependency `get-stdin`
hash: d323c0742cfbbcc017f11f9fd30a9539b1c46f84
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
- subject: Remove custom override of oclif Command class in favor of `prerun` hook
hash: 9cdde4f6c21ea1593de83107794a5b7323ed4cfe
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
version: 19.1.2
title: ""
date: 2024-10-16T13:35:36.846Z
- commits:
- subject: Deduplicate dependencies
hash: 0eb5c78e33979e7fbe7beb4e407949d53af6004b
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
- subject: Fix changelog entry for v19.1.0
hash: 067232b5c4d757913d4fb1c8f9611ed8eb394124
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
version: 19.1.1
title: ""
date: 2024-10-14T13:06:38.383Z
- commits:
- subject: "Docs: Show aliases for commands"
hash: 12cdb1463860a1ffef4e8ad44dde32f00369e3b5
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
- subject: Deprecate `devices` command in favor of `device list`
hash: b936c51941808db8c832678e11af86e2136cfea6
body: ""
footer:
Change-type: minor
change-type: minor
author: myarmolinsky
nested: []
- subject: Deduplicate dependencies
hash: 87c52c55eda0c7b6f9fa72e20b2298fa9ffefef2
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
version: 19.1.0
title: ""
date: 2024-10-11T17:15:02.716Z
- commits:
- subject: "Docs: Generate CLI command references from file names instead of usage"
hash: f52e6bd8b436d4243e39c9069b8ee885bb6401ff
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
- subject: Use default oclif `USAGE` message for all commands
hash: 0847daba1b58f807a222c5163b8383cc44cd13fc
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
version: 19.0.20
title: ""
date: 2024-10-11T11:48:49.138Z
- commits:
- subject: Deduplicate dependencies
hash: 55dbe42e84ebb14dac9c1da189594e345f69d7cd
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
- subject: Fix update notification release notes link
hash: 3e8bc57fdba35f7cbd4e357c5b8f0395c7bb0eef
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
version: 19.0.19
title: ""
date: 2024-10-11T11:18:22.541Z
- commits:
- subject: "Contributing: No longer request separate folders for plural commands"
hash: 276d61cf6c3dfac44796736ff4570f2aeaec971e
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
version: 19.0.18
title: ""
date: 2024-10-08T15:25:03.766Z
- commits:
- subject: Remove dev dependency `parse-link-header`
hash: da95baa70cc2dc72e6d529fa25c42cd2e1739c10
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
- subject: Remove dev dependency @octokit/rest
hash: a3ec75c2c752f734f07e84e9213887b4aec4e7fd
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
- subject: Remove dev dependency @octokit/plugin-throttling
hash: f6f6be8ee8be80048e621a4e75d2fbecacce47e4
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
- subject: Remove no longer needed references and tests for mixpanel
hash: 09e653692b00777aa56625751110305223bc5917
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
- subject: Remove dev dependency `@types/mixpanel`
hash: 3ac89b236abe3392c185010c3b3851a6923d083a
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
version: 19.0.17
title: ""
date: 2024-10-08T14:04:42.011Z
- commits:
- subject: "compose: Reduce the properties updated to only the necessary"
hash: 7e2b5abe600ab13f5f55fc86f0850b54a66debe5
body: ""
footer:
Change-type: patch
change-type: patch
author: Thodoris Greasidis
nested: []
version: 19.0.16
title: ""
date: 2024-10-08T13:35:01.015Z
- commits:
- subject: Remove unused `mockery` dev dependency
hash: 1245b1c99bab2d504a025f489451710cc140bc55
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
version: 19.0.15
title: ""
date: 2024-10-08T13:11:01.319Z
- commits:
- subject: Temporarily skip broken image-manager tests on Windows and Mac
hash: 77b9514442ab81ef1375a2517eb3e9aab7e724da
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
version: 19.0.14
title: ""
date: 2024-10-08T12:44:14.300Z
- commits:
- subject: Remove extra line from recent changelog entry
hash: 127bd7ec722133d81cd94a5e028d9f2e5219df7c
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
version: 19.0.13
title: ""
date: 2024-09-23T11:35:45.475Z
- commits:
- subject: skip
hash: c7441b06ac97a50d8ffefebff3af55e1d12d4035
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
- subject: Add `image-manager` tests
hash: 251d64eb8831555e2cfc8a54c73701eadb8c4f06
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
- subject: Remove `balena-image-manager` dependency
hash: ff9bb52a20b3f21281189ddfbbe1b800e104be1d
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
- subject: Embed `balena-image-manager` instead of having it as a dependency
hash: c799c3f10d1491227fe770ceace46b26ae209b19
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
version: 19.0.12
title: ""
date: 2024-09-20T17:48:39.881Z
- commits:
- subject: Remove Bluebird as a direct dependency
hash: d39dc5a39ad0ec25e0a690b881c8212699f64162
body: ""
footer:
Change-type: patch
change-type: patch
author: Thodoris Greasidis
nested: []
version: 19.0.11
title: ""
date: 2024-09-18T16:38:55.929Z
- commits:
- subject: Remove package `@resin.io/valid-email`
hash: a2b4f76c94e7fda3f122adf721e0a12d9a0e9164
body: ""
footer:
Change-type: patch
change-type: patch
author: myarmolinsky
nested: []
version: 19.0.10
title: ""
date: 2024-09-12T23:00:13.119Z
- commits:
- subject: Update actions/download-artifact action to v4.1.8
hash: 58f480ad7c097952b3ff4e0a5daebc163f2ce7c1
body: |
Update actions/download-artifact from 4.1.7 to 4.1.8
footer:
Change-type: patch
change-type: patch
author: Self-hosted Renovate Bot
nested: []
version: 19.0.9
title: ""
date: 2024-09-12T16:12:08.049Z
- commits:
- subject: Update actions/upload-artifact digest to 5076954
hash: e101e0f46663585d2999c1bd59c5335a2d012ae4
body: |
Update actions/upload-artifact
footer:
Change-type: patch
change-type: patch
author: Self-hosted Renovate Bot
nested: []
version: 19.0.8
title: ""
date: 2024-09-12T15:07:51.366Z
- commits:
- subject: Update actions/setup-node digest to 1e60f62
hash: 314e8800d0c6dfeade2140125cb3dd996713713e
body: |
Update actions/setup-node
footer:
Change-type: patch
change-type: patch
author: Self-hosted Renovate Bot
nested: []
version: 19.0.7
title: ""
date: 2024-09-12T14:13:18.381Z
- commits:
- subject: Remove moment and moment-duration-format in favor of native time parsing
hash: 707b249e972a6943d75014f487285c0dd8085b15
body: ""
footer:
Change-type: patch
change-type: patch
author: Otavio Jacobi
nested: []
version: 19.0.6
title: ""
date: 2024-09-12T13:47:25.357Z
- commits:
- subject: Update apple-actions/import-codesign-certs action to v2
hash: 9242a3493af4c518c4d1328f19ddf2d95c182af7
body: |
Update apple-actions/import-codesign-certs from 1 to 2
footer:
Change-type: patch
change-type: patch
author: Self-hosted Renovate Bot
nested: []
version: 19.0.5
title: ""
date: 2024-09-10T15:13:23.938Z
- commits:
- subject: Update TypeScript to 5.6.2
hash: 5f92bbc846fe93cc03ebe7717baafe24f17d4e0d
body: ""
footer:
Change-type: patch
change-type: patch
author: Thodoris Greasidis
nested: []
version: 19.0.4
title: ""
date: 2024-09-10T14:44:39.949Z
- commits:
- subject: Reduce use of CJS require() on automation files
hash: facc66e9f97d075610d4383efa92dceb5b4f7acf

View File

@ -4,134 +4,6 @@ All notable changes to this project will be documented in this file
automatically by Versionist. DO NOT EDIT THIS FILE MANUALLY!
This project adheres to [Semantic Versioning](http://semver.org/).
## 19.9.0 - 2024-10-18
* Add alias `organization list` for `orgs` command [myarmolinsky]
## 19.8.0 - 2024-10-18
* Add alias `tag list` for `tags` command [myarmolinsky]
## 19.7.0 - 2024-10-18
* Add alias `env list` for command `envs` [myarmolinsky]
## 19.6.0 - 2024-10-18
* Add `ssh-key` as aliases for all `key` commands [myarmolinsky]
## 19.5.0 - 2024-10-17
* Add `key list` alias for `keys` command [myarmolinsky]
## 19.4.0 - 2024-10-16
* Add `release list` alias for `releases` command [myarmolinsky]
## 19.3.0 - 2024-10-16
* Add alias `fleet list` for `fleets` command [myarmolinsky]
## 19.2.0 - 2024-10-16
* Add alias `api-key list` for command `api-keys` [myarmolinsky]
## 19.1.3 - 2024-10-16
* Remove custom sorting of OS commands in docs in favor of alphabetizing [myarmolinsky]
## 19.1.2 - 2024-10-16
* Remove no longer needed dependency `get-stdin` [myarmolinsky]
* Remove custom override of oclif Command class in favor of `prerun` hook [myarmolinsky]
## 19.1.1 - 2024-10-14
* Deduplicate dependencies [myarmolinsky]
* Fix changelog entry for v19.1.0 [myarmolinsky]
## 19.1.0 - 2024-10-11
* Docs: Show aliases for commands [myarmolinsky]
* Add alias `device list` for `devices` command [myarmolinsky]
* Deduplicate dependencies [myarmolinsky]
## 19.0.20 - 2024-10-11
* Docs: Generate CLI command references from file names instead of usage [myarmolinsky]
* Use default oclif `USAGE` message for all commands [myarmolinsky]
## 19.0.19 - 2024-10-11
* Deduplicate dependencies [myarmolinsky]
* Fix update notification release notes link [myarmolinsky]
## 19.0.18 - 2024-10-08
* Contributing: No longer request separate folders for plural commands [myarmolinsky]
## 19.0.17 - 2024-10-08
* Remove dev dependency `parse-link-header` [myarmolinsky]
* Remove dev dependency @octokit/rest [myarmolinsky]
* Remove dev dependency @octokit/plugin-throttling [myarmolinsky]
* Remove no longer needed references and tests for mixpanel [myarmolinsky]
* Remove dev dependency `@types/mixpanel` [myarmolinsky]
## 19.0.16 - 2024-10-08
* compose: Reduce the properties updated to only the necessary [Thodoris Greasidis]
## 19.0.15 - 2024-10-08
* Remove unused `mockery` dev dependency [myarmolinsky]
## 19.0.14 - 2024-10-08
* Temporarily skip broken image-manager tests on Windows and Mac [myarmolinsky]
## 19.0.13 - 2024-09-23
* Remove extra line from recent changelog entry [myarmolinsky]
## 19.0.12 - 2024-09-20
* Add `image-manager` tests [myarmolinsky]
* Remove `balena-image-manager` dependency [myarmolinsky]
* Embed `balena-image-manager` instead of having it as a dependency [myarmolinsky]
## 19.0.11 - 2024-09-18
* Remove Bluebird as a direct dependency [Thodoris Greasidis]
## 19.0.10 - 2024-09-12
* Remove package `@resin.io/valid-email` [myarmolinsky]
## 19.0.9 - 2024-09-12
* Update actions/download-artifact action to v4.1.8 [Self-hosted Renovate Bot]
## 19.0.8 - 2024-09-12
* Update actions/upload-artifact digest to 5076954 [Self-hosted Renovate Bot]
## 19.0.7 - 2024-09-12
* Update actions/setup-node digest to 1e60f62 [Self-hosted Renovate Bot]
## 19.0.6 - 2024-09-12
* Remove moment and moment-duration-format in favor of native time parsing [Otavio Jacobi]
## 19.0.5 - 2024-09-10
* Update apple-actions/import-codesign-certs action to v2 [Self-hosted Renovate Bot]
## 19.0.4 - 2024-09-10
* Update TypeScript to 5.6.2 [Thodoris Greasidis]
## 19.0.3 - 2024-09-05
* Reduce use of CJS require() on automation files [Otavio Jacobi]

View File

@ -133,6 +133,7 @@ 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

View File

@ -19,6 +19,7 @@ import type { JsonVersions } from '../src/commands/version/index';
import { run as oclifRun } from '@oclif/core';
import * as archiver from 'archiver';
import * as Bluebird from 'bluebird';
import { exec, execFile } from 'child_process';
import * as filehound from 'filehound';
import type { Stats } from 'fs';
@ -41,7 +42,6 @@ import {
const execFileAsync = promisify(execFile);
const execAsync = promisify(exec);
const rimrafAsync = promisify(rimraf);
export const packageJSON = loadPackageJson();
export const version = 'v' + packageJSON.version;
@ -517,7 +517,7 @@ export async function buildOclifInstaller() {
}
for (const dir of dirs) {
console.log(`rimraf(${dir})`);
await rimrafAsync(dir);
await Bluebird.fromCallback((cb) => rimraf(dir, cb));
}
console.log('=======================================================');
console.log(`oclif ${packCmd} ${packOpts.join(' ')}`);

View File

@ -36,6 +36,9 @@ import { GlobSync } from 'glob';
* This dictionary is the source of truth that creates the docs config which is used
* to generate the CLI documentation. By default, the folder name will be used.
*
* Resources with plural names needs to have 2 sections if they have commands like:
* "fleet, fleets" or "device, devices" or "tag, tags"
*
*/
interface Category {
@ -52,13 +55,16 @@ interface Documentation {
// Mapping folders names to custom headings in the docs
const commandHeadings: { [key: string]: string } = {
'api-key': 'API Keys',
'api-keys': 'API Keys',
login: 'Authentication',
whoami: 'Authentication',
logout: 'Authentication',
env: 'Environment Variables',
envs: 'Environment Variables',
help: 'Help and Version',
'ssh-key': 'SSH Keys',
organization: 'Organizations',
key: 'SSH Keys',
keys: 'SSH Keys',
orgs: 'Organizations',
os: 'OS',
util: 'Utilities',
ssh: 'Network',

View File

@ -26,7 +26,7 @@ export interface Document {
export interface Category {
title: string;
commands: Array<OclifCommand & { name: string }>;
commands: OclifCommand[];
}
export { OclifCommand };

View File

@ -18,6 +18,7 @@ import * as path from 'path';
import { getCapitanoDoc } from './capitanodoc';
import type { Category, Document, OclifCommand } from './doc-types';
import * as markdown from './markdown';
import { stripIndent } from '../../src/utils/lazy';
/**
* Generates the markdown document (as a string) for the CLI documentation
@ -38,7 +39,7 @@ export async function renderMarkdown(): Promise<string> {
};
for (const jsFilename of commandCategory.files) {
category.commands.push(await importOclifCommands(jsFilename));
category.commands.push(...(await importOclifCommands(jsFilename)));
}
result.categories.push(category);
}
@ -46,23 +47,51 @@ export async function renderMarkdown(): Promise<string> {
return markdown.render(result);
}
async function importOclifCommands(jsFilename: string) {
const command = (await import(path.join(process.cwd(), jsFilename)))
.default as OclifCommand;
// Help is now managed via a plugin
// This fake command allows capitanodoc to include help in docs
class FakeHelpCommand {
description = stripIndent`
List balena commands, or get detailed help for a specific command.
return {
...command,
// build/commands/device/index.js -> device
// build/commands/device/list.js -> device list
name: jsFilename
.split('/')
.slice(2)
.join(' ')
.split('.')
.slice(0, 1)
.join(' ')
.split(' index')[0],
} as Category['commands'][0];
List balena commands, or get detailed help for a specific command.
`;
examples = [
'$ balena help',
'$ balena help login',
'$ balena help os download',
];
args = {
command: {
description: 'command to show help for',
},
};
usage = 'help [command]';
flags = {
verbose: {
description: 'show additional commands',
char: '-v',
},
};
}
async function importOclifCommands(
jsFilename: string,
): Promise<OclifCommand[]> {
// TODO: Currently oclif commands with no `usage` overridden will cause
// an error when parsed. This should be improved so that `usage` does not have
// to be overridden if not necessary.
const command: OclifCommand =
jsFilename === 'help'
? (new FakeHelpCommand() as unknown as OclifCommand)
: ((await import(path.join(process.cwd(), jsFilename)))
.default as OclifCommand);
return [command];
}
/**

View File

@ -18,20 +18,12 @@ import { Parser } from '@oclif/core';
import * as ent from 'ent';
import * as _ from 'lodash';
import { getManualSortCompareFunction } from '../../src/utils/helpers';
import { capitanoizeOclifUsage } from '../../src/utils/oclif-utils';
import type { Category, Document } from './doc-types';
import type { Category, Document, OclifCommand } from './doc-types';
function renderOclifCommand(command: Category['commands'][0]): string[] {
const result = [`## ${ent.encode(command.name || '')}`];
if (command.aliases?.length) {
result.push('### Aliases');
result.push(command.aliases.map((alias) => `- \`${alias}\``).join('\n'));
result.push(
`\nTo use one of the aliases, replace \`${command.name}\` with the alias.`,
);
}
result.push('### Description');
function renderOclifCommand(command: OclifCommand): string[] {
const result = [`## ${ent.encode(command.usage || '')}`];
const description = (command.description || '')
.split('\n')
.slice(1) // remove the first line, which oclif uses as help header
@ -88,7 +80,7 @@ function renderToc(categories: Category[]): string[] {
result.push(
category.commands
.map((command) => {
const signature = capitanoizeOclifUsage(command.name);
const signature = capitanoizeOclifUsage(command.usage);
return `\t- [${ent.encode(signature)}](${getAnchor(signature)})`;
})
.join('\n'),
@ -97,7 +89,33 @@ function renderToc(categories: Category[]): string[] {
return result;
}
const manualCategorySorting: { [category: string]: string[] } = {
'Environment Variables': ['envs', 'env rm', 'env add', 'env rename'],
OS: [
'os versions',
'os download',
'os build config',
'os configure',
'os initialize',
],
};
function sortCommands(doc: Document): void {
for (const category of doc.categories) {
if (category.title in manualCategorySorting) {
category.commands = category.commands.sort(
getManualSortCompareFunction<OclifCommand, string>(
manualCategorySorting[category.title],
(cmd: OclifCommand, x: string) =>
(cmd.usage || '').toString().replace(/\W+/g, ' ').includes(x),
),
);
}
}
}
export function render(doc: Document) {
sortCommands(doc);
const result = [
`# ${doc.title}`,
doc.introduction,

215
automation/deploy-bin.ts Normal file
View File

@ -0,0 +1,215 @@
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as _ from 'lodash';
import * as semver from 'semver';
import { Octokit } from '@octokit/rest';
import { throttling } from '@octokit/plugin-throttling';
const { GITHUB_TOKEN } = process.env;
/** Return a cached Octokit instance, creating a new one as needed. */
const getOctokit = _.once(function () {
const OctokitConstructor = Octokit.plugin(throttling);
return new OctokitConstructor({
auth: GITHUB_TOKEN,
throttle: {
onRateLimit: (retryAfter: number, options: any) => {
console.warn(
`Request quota exhausted for request ${options.method} ${options.url}`,
);
// retries 3 times
if (options.request.retryCount < 3) {
console.log(`Retrying after ${retryAfter} seconds!`);
return true;
}
},
onAbuseLimit: (_retryAfter: number, options: any) => {
// does not retry, only logs a warning
console.warn(
`Abuse detected for request ${options.method} ${options.url}`,
);
},
},
});
});
/**
* Extract pagination information (current page, total pages, ordinal number)
* from the 'link' response header (example below), using the parse-link-header
* npm package:
* "link": "<https://api.github.com/repositories/187370853/releases?per_page=2&page=2>; rel=\"next\",
* <https://api.github.com/repositories/187370853/releases?per_page=2&page=3>; rel=\"last\""
*
* @param response Octokit response object (including response.headers.link)
* @param perPageDefault Default per_page pagination value if missing in URL
* @return Object where 'page' is the current page number (1-based),
* 'pages' is the total number of pages, and 'ordinal' is the ordinal number
* (3rd, 4th, 5th...) of the first item in the current page.
*/
async function getPageNumbers(
response: any,
perPageDefault: number,
): Promise<{ page: number; pages: number; ordinal: number }> {
const res = { page: 1, pages: 1, ordinal: 1 };
if (!response.headers.link) {
return res;
}
const parse = await import('parse-link-header');
const parsed = parse(response.headers.link);
if (parsed == null) {
throw new Error(`Failed to parse link header: '${response.headers.link}'`);
}
let perPage = perPageDefault;
if (parsed.next) {
if (parsed.next.per_page) {
perPage = parseInt(parsed.next.per_page, 10);
}
res.page = parseInt(parsed.next.page!, 10) - 1;
res.pages = parseInt(parsed.last!.page!, 10);
} else {
if (parsed.prev!.per_page) {
perPage = parseInt(parsed.prev!.per_page, 10);
}
res.page = res.pages = parseInt(parsed.prev!.page!, 10) + 1;
}
res.ordinal = (res.page - 1) * perPage + 1;
return res;
}
/**
* Iterate over every GitHub release in the given owner/repo, check whether
* its tag_name matches against the affectedVersions semver spec, and if so
* replace its release description (body) with the given newDescription value.
* @param owner GitHub repo owner, e.g. 'balena-io' or 'pdcastro'
* @param repo GitHub repo, e.g. 'balena-cli'
* @param affectedVersions Semver spec, e.g. '2.6.1 - 7.10.9 || 8.0.0'
* @param newDescription New release description (body)
* @param editID Short string present in newDescription, e.g. '[AA101]', that
* can be searched to determine whether that release has already been updated.
*/
async function updateGitHubReleaseDescriptions(
owner: string,
repo: string,
affectedVersions: string,
newDescription: string,
editID: string,
) {
const perPage = 30;
const octokit = getOctokit();
const options = octokit.repos.listReleases.endpoint.merge({
owner,
repo,
per_page: perPage,
});
let errCount = 0;
type Release =
import('@octokit/rest').RestEndpointMethodTypes['repos']['listReleases']['response']['data'][0];
for await (const response of octokit.paginate.iterator<Release>(options)) {
const {
page: thisPage,
pages: totalPages,
ordinal,
} = await getPageNumbers(response, perPage);
let i = 0;
for (const cliRelease of response.data) {
const prefix = `[#${ordinal + i++} pg ${thisPage}/${totalPages}]`;
if (!cliRelease.id) {
console.error(
`${prefix} Error: missing release ID (errCount=${++errCount})`,
);
continue;
}
const skipMsg = `${prefix} skipping release "${cliRelease.tag_name}" (${cliRelease.id})`;
if (cliRelease.draft === true) {
console.info(`${skipMsg}: draft release`);
continue;
} else if (cliRelease.body && cliRelease.body.includes(editID)) {
console.info(`${skipMsg}: already updated`);
continue;
} else if (!semver.satisfies(cliRelease.tag_name, affectedVersions)) {
console.info(`${skipMsg}: outside version range`);
continue;
} else {
const updatedRelease = {
owner,
repo,
release_id: cliRelease.id,
body: newDescription,
};
let oldBodyPreview = cliRelease.body;
if (oldBodyPreview) {
oldBodyPreview = oldBodyPreview.replace(/\s+/g, ' ').trim();
if (oldBodyPreview.length > 12) {
oldBodyPreview = oldBodyPreview.substring(0, 9) + '...';
}
}
console.info(
`${prefix} updating release "${cliRelease.tag_name}" (${cliRelease.id}) old body="${oldBodyPreview}"`,
);
try {
await octokit.repos.updateRelease(updatedRelease);
} catch (err) {
console.error(
`${skipMsg}: Error: ${err.message} (count=${++errCount})`,
);
continue;
}
}
}
}
}
/**
* Add a warning description to CLI releases affected by a mixpanel tracking
* security issue (#1359). This function can be executed "manually" with the
* following command line:
*
* npx ts-node --type-check -P automation/tsconfig.json automation/run.ts fix1359
*/
export async function updateDescriptionOfReleasesAffectedByIssue1359() {
// Run only on Linux/Node10, instead of all platform/Node combinations.
// (It could have been any other platform, as long as it only runs once.)
if (process.platform !== 'linux' || semver.major(process.version) !== 10) {
return;
}
const owner = 'balena-io';
const repo = 'balena-cli';
const affectedVersions =
'2.6.1 - 7.10.9 || 8.0.0 - 8.1.0 || 9.0.0 - 9.15.6 || 10.0.0 - 10.17.5 || 11.0.0 - 11.7.2';
const editID = '[AA100]';
let newDescription = `
Please note: the "login" command in this release is affected by a
security issue fixed in versions
[7.10.10](https://github.com/balena-io/balena-cli/releases/tag/v7.10.10),
[8.1.1](https://github.com/balena-io/balena-cli/releases/tag/v8.1.1),
[9.15.7](https://github.com/balena-io/balena-cli/releases/tag/v9.15.7),
[10.17.6](https://github.com/balena-io/balena-cli/releases/tag/v10.17.6),
[11.7.3](https://github.com/balena-io/balena-cli/releases/tag/v11.7.3)
and later. If you need to use this version, avoid passing your password,
keys or tokens as command-line arguments. ${editID}`;
// remove line breaks and collapse white space
newDescription = newDescription.replace(/\s+/g, ' ').trim();
await updateGitHubReleaseDescriptions(
owner,
repo,
affectedVersions,
newDescription,
editID,
);
}

View File

@ -24,6 +24,7 @@ import {
signFilesForNotarization,
testShrinkwrap,
} from './build-bin';
import { updateDescriptionOfReleasesAffectedByIssue1359 } from './deploy-bin';
// DEBUG set to falsy for negative values else is truthy
process.env.DEBUG = ['0', 'no', 'false', '', undefined].includes(
@ -53,6 +54,7 @@ async function parse(args?: string[]) {
'sign:binaries': signFilesForNotarization,
'catch-uncommitted': catchUncommitted,
'test-shrinkwrap': testShrinkwrap,
fix1359: updateDescriptionOfReleasesAffectedByIssue1359,
};
for (const arg of args) {
if (!Object.hasOwn(commands, arg)) {

View File

@ -8,23 +8,22 @@ _balena() {
local context state line curcontext="$curcontext"
# Valid top-level completions
main_commands=( api-key app block build config deploy device devices env fleet internal join leave local login logout logs notes organization os preload push release scan settings ssh ssh-key support tag tunnel util version whoami )
main_commands=( api-key api-keys app block build config deploy device devices env envs fleet fleets internal join key keys leave local login logout logs notes orgs os preload push release releases scan settings ssh support tag tags tunnel util version whoami )
# Sub-completions
api_key_cmds=( generate list revoke )
api_key_cmds=( generate revoke )
app_cmds=( create )
block_cmds=( create )
config_cmds=( generate inject read reconfigure write )
device_cmds=( deactivate identify init list local-mode move os-update pin public-url purge reboot register rename restart rm shutdown start-service stop-service track-fleet )
device_cmds=( deactivate identify init local-mode move os-update pin public-url purge reboot register rename restart rm shutdown start-service stop-service track-fleet )
devices_cmds=( supported )
env_cmds=( add list rename rm )
fleet_cmds=( create list pin purge rename restart rm track-latest )
env_cmds=( add rename rm )
fleet_cmds=( create pin purge rename restart rm track-latest )
internal_cmds=( osinit )
key_cmds=( add rm )
local_cmds=( configure flash )
organization_cmds=( list )
os_cmds=( build-config configure download initialize versions )
release_cmds=( finalize invalidate list validate )
ssh_key_cmds=( add list rm )
tag_cmds=( list rm set )
release_cmds=( export finalize import invalidate validate )
tag_cmds=( rm set )
_arguments -C \
@ -70,21 +69,18 @@ _balena_sec_cmds() {
"internal")
_describe -t internal_cmds 'internal_cmd' internal_cmds "$@" && ret=0
;;
"key")
_describe -t key_cmds 'key_cmd' key_cmds "$@" && ret=0
;;
"local")
_describe -t local_cmds 'local_cmd' local_cmds "$@" && ret=0
;;
"organization")
_describe -t organization_cmds 'organization_cmd' organization_cmds "$@" && ret=0
;;
"os")
_describe -t os_cmds 'os_cmd' os_cmds "$@" && ret=0
;;
"release")
_describe -t release_cmds 'release_cmd' release_cmds "$@" && ret=0
;;
"ssh-key")
_describe -t ssh_key_cmds 'ssh-key_cmd' ssh_key_cmds "$@" && ret=0
;;
"tag")
_describe -t tag_cmds 'tag_cmd' tag_cmds "$@" && ret=0
;;

View File

@ -7,23 +7,22 @@ _balena_complete()
local cur prev
# Valid top-level completions
main_commands="api-key app block build config deploy device devices env fleet internal join leave local login logout logs notes organization os preload push release scan settings ssh ssh-key support tag tunnel util version whoami"
main_commands="api-key api-keys app block build config deploy device devices env envs fleet fleets internal join key keys leave local login logout logs notes orgs os preload push release releases scan settings ssh support tag tags tunnel util version whoami"
# Sub-completions
api_key_cmds="generate list revoke"
api_key_cmds="generate revoke"
app_cmds="create"
block_cmds="create"
config_cmds="generate inject read reconfigure write"
device_cmds="deactivate identify init list local-mode move os-update pin public-url purge reboot register rename restart rm shutdown start-service stop-service track-fleet"
device_cmds="deactivate identify init local-mode move os-update pin public-url purge reboot register rename restart rm shutdown start-service stop-service track-fleet"
devices_cmds="supported"
env_cmds="add list rename rm"
fleet_cmds="create list pin purge rename restart rm track-latest"
env_cmds="add rename rm"
fleet_cmds="create pin purge rename restart rm track-latest"
internal_cmds="osinit"
key_cmds="add rm"
local_cmds="configure flash"
organization_cmds="list"
os_cmds="build-config configure download initialize versions"
release_cmds="finalize invalidate list validate"
ssh_key_cmds="add list rm"
tag_cmds="list rm set"
release_cmds="export finalize import invalidate validate"
tag_cmds="rm set"
@ -64,21 +63,18 @@ _balena_complete()
internal)
COMPREPLY=( $(compgen -W "$internal_cmds" -- $cur) )
;;
key)
COMPREPLY=( $(compgen -W "$key_cmds" -- $cur) )
;;
local)
COMPREPLY=( $(compgen -W "$local_cmds" -- $cur) )
;;
organization)
COMPREPLY=( $(compgen -W "$organization_cmds" -- $cur) )
;;
os)
COMPREPLY=( $(compgen -W "$os_cmds" -- $cur) )
;;
release)
COMPREPLY=( $(compgen -W "$release_cmds" -- $cur) )
;;
ssh-key)
COMPREPLY=( $(compgen -W "$ssh_key_cmds" -- $cur) )
;;
tag)
COMPREPLY=( $(compgen -W "$tag_cmds" -- $cur) )
;;

File diff suppressed because it is too large Load Diff

765
npm-shrinkwrap.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "balena-cli",
"version": "19.9.0",
"version": "19.0.3",
"description": "The official balena Command Line Interface",
"main": "./build/app.js",
"homepage": "https://github.com/balena-io/balena-cli",
@ -99,7 +99,7 @@
"helpClass": "./build/help",
"topicSeparator": " ",
"hooks": {
"prerun": "./build/hooks/prerun",
"prerun": "./build/hooks/prerun/track",
"command_not_found": "./build/hooks/command-not-found/suggest"
},
"additionalHelpFlags": [
@ -113,6 +113,8 @@
"devDependencies": {
"@balena/lint": "^8.0.0",
"@electron/notarize": "^2.0.0",
"@octokit/plugin-throttling": "^3.5.1",
"@octokit/rest": "^18.6.7",
"@types/archiver": "^6.0.2",
"@types/bluebird": "^3.5.36",
"@types/body-parser": "^1.19.2",
@ -136,13 +138,14 @@
"@types/jsonwebtoken": "^9.0.6",
"@types/klaw": "^3.0.6",
"@types/lodash": "^4.14.178",
"@types/mime": "^3.0.4",
"@types/mixpanel": "^2.14.3",
"@types/mocha": "^10.0.7",
"@types/mock-fs": "^4.13.4",
"@types/mock-require": "^2.0.1",
"@types/moment-duration-format": "^2.2.3",
"@types/ndjson": "^2.0.1",
"@types/node": "^20.0.0",
"@types/node-cleanup": "^2.1.2",
"@types/parse-link-header": "^2.0.3",
"@types/prettyjson": "^0.0.33",
"@types/progress-stream": "^2.0.2",
"@types/request": "^2.48.7",
@ -176,34 +179,38 @@
"intercept-stdout": "^0.1.2",
"jsonwebtoken": "^9.0.0",
"klaw": "^4.1.0",
"mkdirp": "^3.0.1",
"mocha": "^10.6.0",
"mock-fs": "^5.2.0",
"mock-require": "^3.0.3",
"nock": "^13.2.1",
"oclif": "^4.14.0",
"parse-link-header": "^2.0.0",
"rewire": "^7.0.0",
"simple-git": "^3.14.1",
"sinon": "^18.0.0",
"string-to-stream": "^3.0.1",
"ts-node": "^10.4.0",
"typescript": "^5.6.2"
"typescript": "^5.5.2"
},
"dependencies": {
"@balena/compose": "^4.0.1",
"@balena/dockerignore": "^1.0.2",
"@balena/env-parsing": "^1.1.8",
"@balena/es-version": "^1.0.1",
"@balena/release-bundle": "^0.5.2",
"@oclif/core": "^4.0.8",
"@resin.io/valid-email": "^0.1.0",
"@sentry/node": "^6.16.1",
"balena-config-json": "^4.2.0",
"balena-device-init": "^7.0.1",
"balena-errors": "^4.7.3",
"balena-image-fs": "^7.0.6",
"balena-image-manager": "^10.0.1",
"balena-preload": "^15.0.6",
"balena-sdk": "^19.7.3",
"balena-semver": "^2.3.0",
"balena-settings-client": "^5.0.2",
"balena-settings-storage": "^8.1.0",
"bluebird": "^3.7.2",
"body-parser": "^1.19.1",
"bonjour-service": "^1.2.1",
"chalk": "^3.0.0",
@ -221,6 +228,7 @@
"fast-boot2": "^1.1.0",
"fast-levenshtein": "^3.0.0",
"filenamify": "^4.3.0",
"get-stdin": "^8.0.0",
"glob": "^7.2.0",
"global-agent": "^2.2.0",
"global-tunnel-ng": "^2.1.1",
@ -233,8 +241,8 @@
"JSONStream": "^1.0.3",
"livepush": "^3.5.1",
"lodash": "^4.17.21",
"mime": "^2.4.6",
"mkdirp": "^3.0.1",
"moment": "^2.29.1",
"moment-duration-format": "^2.3.2",
"ndjson": "^2.0.0",
"node-cleanup": "^2.1.2",
"node-unzip-2": "^0.2.8",
@ -245,7 +253,7 @@
"reconfix": "^1.0.0-v0-1-0-fork-46760acff4d165f5238bfac5e464256ef1944476",
"request": "^2.88.2",
"resin-cli-form": "^3.0.0",
"resin-cli-visuals": "^2.0.1",
"resin-cli-visuals": "^2.0.0",
"resin-doodles": "^0.2.0",
"resin-stream-logger": "^0.1.2",
"rimraf": "^3.0.2",
@ -273,6 +281,6 @@
}
},
"versionist": {
"publishedAt": "2024-10-18T14:25:40.555Z"
"publishedAt": "2024-09-05T12:34:09.871Z"
}
}

View File

@ -153,7 +153,7 @@ async function oclifRun(command: string[], options: AppOptions) {
}
})(!options.noFlush);
const { trackPromise } = await import('./hooks/prerun');
const { trackPromise } = await import('./hooks/prerun/track');
await Promise.all([trackPromise, deprecationPromise, runPromise]);
}

170
src/command.ts Normal file
View File

@ -0,0 +1,170 @@
/**
* @license
* Copyright 2020 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Command } from '@oclif/core';
import {
InsufficientPrivilegesError,
NotAvailableInOfflineModeError,
} from './errors';
import { stripIndent } from './utils/lazy';
export default abstract class BalenaCommand extends Command {
/**
* When set to true, command will be listed in `help`,
* otherwise listed in `help --verbose` with secondary commands.
*/
public static primary = false;
/**
* Require elevated privileges to run.
* When set to true, command will exit with an error
* if executed without root on Mac/Linux
* or if executed by non-Administrator on Windows.
*/
public static root = false;
/**
* Require authentication to run.
* When set to true, command will exit with an error
* if user is not already logged in.
*/
public static authenticated = false;
/**
* Require an internet connection to run.
* When set to true, command will exit with an error
* if user is running in offline mode (BALENARC_OFFLINE_MODE).
*/
public static offlineCompatible = false;
/**
* Accept piped input.
* When set to true, command will read from stdin during init
* and make contents available on member `stdin`.
*/
public static readStdin = false;
public stdin: string;
/**
* Throw InsufficientPrivilegesError if not root on Mac/Linux
* or non-Administrator on Windows.
*
* Called automatically if `root=true`.
* Can be called explicitly by command implementation, if e.g.:
* - check should only be done conditionally
* - other code needs to execute before check
*/
protected static async checkElevatedPrivileges() {
const isElevated = await (await import('is-elevated'))();
if (!isElevated) {
throw new InsufficientPrivilegesError(
'You need root/admin privileges to run this command',
);
}
}
/**
* Throw NotLoggedInError if not logged in.
*
* Called automatically if `authenticated=true`.
* Can be called explicitly by command implementation, if e.g.:
* - check should only be done conditionally
* - other code needs to execute before check
*
* Note, currently public to allow use outside of derived commands
* (as some command implementations require this. Can be made protected
* if this changes).
*
* @throws {NotLoggedInError}
*/
public static async checkLoggedIn() {
await (await import('./utils/patterns')).checkLoggedIn();
}
/**
* Throw NotLoggedInError if not logged in when condition true.
*
* @param {boolean} doCheck - will check if true.
* @throws {NotLoggedInError}
*/
public static async checkLoggedInIf(doCheck: boolean) {
if (doCheck) {
await this.checkLoggedIn();
}
}
/**
* Throw NotAvailableInOfflineModeError if in offline mode.
*
* Called automatically if `onlineOnly=true`.
* Can be called explicitly by command implementation, if e.g.:
* - check should only be done conditionally
* - other code needs to execute before check
*
* Note, currently public to allow use outside of derived commands
* (as some command implementations require this. Can be made protected
* if this changes).
*
* @throws {NotAvailableInOfflineModeError}
*/
public static checkNotUsingOfflineMode() {
if (process.env.BALENARC_OFFLINE_MODE) {
throw new NotAvailableInOfflineModeError(stripIndent`
This command requires an internet connection, and cannot be used in offline mode.
To leave offline mode, unset the BALENARC_OFFLINE_MODE environment variable.
`);
}
}
/**
* Read stdin contents and make available to command.
*
* This approach could be improved in the future to automatically set argument
* values from stdin based in configuration, minimising command implementation.
*/
protected async getStdin() {
this.stdin = await (await import('get-stdin'))();
}
/**
* Get a logger instance.
*/
protected static async getLogger() {
return (await import('./utils/logger')).getLogger();
}
protected async init() {
const ctr = this.constructor as typeof BalenaCommand;
if (ctr.root) {
await BalenaCommand.checkElevatedPrivileges();
}
if (ctr.authenticated) {
await BalenaCommand.checkLoggedIn();
}
if (!ctr.offlineCompatible) {
BalenaCommand.checkNotUsingOfflineMode();
}
if (ctr.readStdin) {
await this.getStdin();
}
}
}

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
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';
@ -39,6 +40,8 @@ export default class GenerateCmd extends Command {
}),
};
public static usage = 'api-key generate <name>';
public static flags = {
help: cf.help,
};

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
@ -40,6 +41,8 @@ export default class RevokeCmd extends Command {
}),
};
public static usage = 'api-key revoke <ids>';
public static flags = {
help: cf.help,
};

View File

@ -15,13 +15,12 @@
* limitations under the License.
*/
import { Flags, Command } from '@oclif/core';
import { Flags } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
export default class APIKeyListCmd extends Command {
public static aliases = ['api-keys'];
export default class ApiKeysCmd extends Command {
public static description = stripIndent`
Print a list of balenaCloud API keys.
@ -29,7 +28,9 @@ export default class APIKeyListCmd extends Command {
Print a list of balenaCloud API keys for the current user or for a specific fleet with the \`--fleet\` option.
`;
public static examples = ['$ balena api-key list'];
public static examples = ['$ balena api-keys'];
public static usage = 'api-keys';
public static flags = {
help: cf.help,
@ -43,7 +44,7 @@ export default class APIKeyListCmd extends Command {
public static authenticated = true;
public async run() {
const { flags: options } = await this.parse(APIKeyListCmd);
const { flags: options } = await this.parse(ApiKeysCmd);
const { getApplication } = await import('../../utils/sdk');
const actorId = options.fleet

View File

@ -15,7 +15,9 @@
* limitations under the License.
*/
import { Flags, Args, Command } from '@oclif/core';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { stripIndent } from '../../utils/lazy';
@ -28,7 +30,7 @@ export default class AppCreateCmd extends Command {
You can specify the organization the app should belong to using
the \`--organization\` option. The organization's handle, not its name,
should be provided. Organization handles can be listed with the
\`balena organization list\` command.
\`balena orgs\` command.
The app's default device type is specified with the \`--type\` option.
The \`balena devices supported\` command can be used to list the available
@ -54,6 +56,8 @@ export default class AppCreateCmd extends Command {
}),
};
public static usage = 'app create <name>';
public static flags = {
organization: Flags.string({
char: 'o',

View File

@ -15,7 +15,9 @@
* limitations under the License.
*/
import { Flags, Args, Command } from '@oclif/core';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { stripIndent } from '../../utils/lazy';
@ -28,7 +30,7 @@ export default class BlockCreateCmd extends Command {
You can specify the organization the block should belong to using
the \`--organization\` option. The organization's handle, not its name,
should be provided. Organization handles can be listed with the
\`balena organization list\` command.
\`balena orgs\` command.
The block's default device type is specified with the \`--type\` option.
The \`balena devices supported\` command can be used to list the available
@ -54,6 +56,8 @@ export default class BlockCreateCmd extends Command {
}),
};
public static usage = 'block create <name>';
public static flags = {
organization: Flags.string({
char: 'o',

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Flags, Command } from '@oclif/core';
import { Args, Flags } from '@oclif/core';
import Command from '../../command';
import { getBalenaSdk } from '../../utils/lazy';
import * as cf from '../../utils/common-flags';
import * as compose from '../../utils/compose';
@ -83,6 +84,8 @@ ${dockerignoreHelp}
source: Args.string({ description: 'path of project source directory' }),
};
public static usage = 'build [source]';
public static flags = {
arch: Flags.string({
description: 'the architecture to build for',
@ -105,16 +108,13 @@ ${dockerignoreHelp}
public async run() {
const { args: params, flags: options } = await this.parse(BuildCmd);
const Logger = await import('../../utils/logger');
const { checkLoggedInIf } = await import('../../utils/patterns');
await checkLoggedInIf(!!options.fleet);
await Command.checkLoggedInIf(!!options.fleet);
(await import('events')).defaultMaxListeners = 1000;
const sdk = getBalenaSdk();
const logger = Logger.getLogger();
const logger = await Command.getLogger();
logger.logDebug('Parsing input...');
// `build` accepts `source` as a parameter, but compose expects it as an option

View File

@ -15,8 +15,9 @@
* limitations under the License.
*/
import { Flags, Command } from '@oclif/core';
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';
import {
@ -59,6 +60,8 @@ export default class ConfigGenerateCmd extends Command {
'$ balena config generate --fleet myorg/fleet --version 2.12.7 --network wifi --wifiSsid mySsid --wifiKey abcdefgh --appUpdatePollInterval 15',
];
public static usage = 'config generate';
public static flags = {
version: Flags.string({
description: 'a balenaOS version',

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getVisuals, stripIndent } from '../../utils/lazy';
@ -42,6 +43,8 @@ export default class ConfigInjectCmd extends Command {
}),
};
public static usage = 'config inject <file>';
public static flags = {
drive: cf.driveOrImg,
help: cf.help,

View File

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Command } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getVisuals, stripIndent } from '../../utils/lazy';
@ -36,6 +36,8 @@ export default class ConfigReadCmd extends Command {
'$ balena config read --drive balena.img',
];
public static usage = 'config read';
public static flags = {
drive: cf.driveOrImg,
help: cf.help,

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Flags, Command } from '@oclif/core';
import { Flags } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getVisuals, stripIndent } from '../../utils/lazy';
@ -38,6 +39,8 @@ export default class ConfigReconfigureCmd extends Command {
'$ balena config reconfigure --drive balena.img --advanced',
];
public static usage = 'config reconfigure';
public static flags = {
drive: cf.driveOrImg,
advanced: Flags.boolean({

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getVisuals, stripIndent } from '../../utils/lazy';
@ -47,6 +48,8 @@ export default class ConfigWriteCmd extends Command {
}),
};
public static usage = 'config write <key> <value>';
public static flags = {
drive: cf.driveOrImg,
help: cf.help,

View File

@ -15,8 +15,10 @@
* limitations under the License.
*/
import { Args, Flags, Command } from '@oclif/core';
import { Args, Flags } from '@oclif/core';
import type { ImageDescriptor } from '@balena/compose/dist/parse';
import Command from '../../command';
import { ExpectedError } from '../../errors';
import { getBalenaSdk, getChalk, stripIndent } from '../../utils/lazy';
import {
@ -106,6 +108,8 @@ ${dockerignoreHelp}
image: Args.string({ description: 'the image to deploy' }),
};
public static usage = 'deploy <fleet> [image]';
public static flags = {
source: Flags.string({
description:
@ -153,9 +157,7 @@ ${dockerignoreHelp}
(await import('events')).defaultMaxListeners = 1000;
const Logger = await import('../../utils/logger');
const logger = Logger.getLogger();
const logger = await Command.getLogger();
logger.logDebug('Parsing input...');
const { fleet, image } = params;

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
@ -40,6 +41,8 @@ export default class DeviceDeactivateCmd extends Command {
}),
};
public static usage = 'device deactivate <uuid>';
public static flags = {
yes: cf.yes,
help: cf.help,

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
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';
@ -35,6 +36,8 @@ export default class DeviceIdentifyCmd extends Command {
}),
};
public static usage = 'device identify <uuid>';
public static flags = {
help: cf.help,
};

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Flags, Args, Command } from '@oclif/core';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { expandForAppName } from '../../utils/helpers';
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
@ -61,6 +62,8 @@ export default class DeviceCmd extends Command {
}),
};
public static usage = 'device <uuid>';
public static flags = {
json: cf.json,
help: cf.help,

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Flags, Command } from '@oclif/core';
import { 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';
@ -42,17 +43,17 @@ export default class DeviceInitCmd extends Command {
This command effectively combines several other balena CLI commands in one,
namely:
'balena device register'
'balena os download'
'balena os build-config' or 'balena config generate'
'balena os configure'
'balena device register'
'balena os download'
'balena os build-config' or 'balena config generate'
'balena os configure'
'balena os local flash'
Possible arguments for the '--fleet', '--os-version' and '--drive' options can
be listed respectively with the commands:
'balena fleet list'
'balena os versions'
'balena fleets'
'balena os versions'
'balena util available-drives'
If the '--fleet' or '--drive' options are omitted, interactive menus will be
@ -73,6 +74,8 @@ export default class DeviceInitCmd extends Command {
'$ balena device init --fleet myFleet --os-version 2.83.21+rev1.prod --drive /dev/disk5 --config config.json --yes',
];
public static usage = 'device init';
public static flags = {
fleet: cf.fleet,
yes: cf.yes,
@ -116,9 +119,8 @@ export default class DeviceInitCmd extends Command {
tmp.setGracefulCleanup();
const { downloadOSImage } = await import('../../utils/cloud');
const { getApplication } = await import('../../utils/sdk');
const Logger = await import('../../utils/logger');
const logger = Logger.getLogger();
const logger = await Command.getLogger();
const balena = getBalenaSdk();
// Get application and

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Flags, Args, Command } from '@oclif/core';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
@ -41,6 +42,8 @@ export default class DeviceLocalModeCmd extends Command {
}),
};
public static usage = 'device local-mode <uuid>';
public static flags = {
enable: Flags.boolean({
description: 'enable local mode',

View File

@ -15,13 +15,14 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
import { Args } from '@oclif/core';
import type {
BalenaSDK,
Device,
PineOptions,
PineTypedResult,
} from 'balena-sdk';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { ExpectedError } from '../../errors';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
@ -53,6 +54,8 @@ export default class DeviceMoveCmd extends Command {
}),
};
public static usage = 'device move <uuid(s)>';
public static flags = {
fleet: cf.fleet,
help: cf.help,

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Flags, Args, Command } from '@oclif/core';
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';
@ -46,6 +47,8 @@ export default class DeviceOsUpdateCmd extends Command {
}),
};
public static usage = 'device os-update <uuid>';
public static flags = {
version: Flags.string({
description: 'a balenaOS version',

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
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';
@ -43,6 +44,8 @@ export default class DevicePinCmd extends Command {
}),
};
public static usage = 'device pin <uuid> [releaseToPinTo]';
public static flags = {
help: cf.help,
};
@ -79,7 +82,7 @@ export default class DevicePinCmd extends Command {
pinnedRelease
? `This device is currently pinned to ${pinnedRelease}.`
: 'This device is not currently pinned to any release.'
} \n\nTo see a list of all releases this device can be pinned to, run \`balena release list ${appSlug}\`.`,
} \n\nTo see a list of all releases this device can be pinned to, run \`balena releases ${appSlug}\`.`,
);
} else {
await balena.models.device.pinToRelease(params.uuid, releaseToPinTo);

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Flags, Args, Command } from '@oclif/core';
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';
@ -43,6 +44,8 @@ export default class DevicePublicUrlCmd extends Command {
}),
};
public static usage = 'device public-url <uuid>';
public static flags = {
enable: Flags.boolean({
description: 'enable the public URL',

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy';
@ -34,6 +35,8 @@ export default class DevicePurgeCmd extends Command {
'$ balena device purge 55d43b3,23c73a1',
];
public static usage = 'device purge <uuid>';
public static args = {
uuid: Args.string({
description: 'comma-separated list (no blank spaces) of device UUIDs',

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
@ -34,6 +35,8 @@ export default class DeviceRebootCmd extends Command {
}),
};
public static usage = 'device reboot <uuid>';
public static flags = {
force: cf.force,
help: cf.help,

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Flags, Command } from '@oclif/core';
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';
@ -43,6 +44,8 @@ export default class DeviceRegisterCmd extends Command {
fleet: ca.fleetRequired,
};
public static usage = 'device register <fleet>';
public static flags = {
uuid: Flags.string({
description: 'custom uuid',

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy';
@ -42,6 +43,8 @@ export default class DeviceRenameCmd extends Command {
}),
};
public static usage = 'device rename <uuid> [newName]';
public static flags = {
help: cf.help,
};

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Flags, Args, Command } from '@oclif/core';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy';
import type {
@ -52,6 +53,8 @@ export default class DeviceRestartCmd extends Command {
}),
};
public static usage = 'device restart <uuid>';
public static flags = {
service: Flags.string({
description:

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
@ -42,6 +43,8 @@ export default class DeviceRmCmd extends Command {
}),
};
public static usage = 'device rm <uuid(s)>';
public static flags = {
yes: cf.yes,
help: cf.help,

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
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';
@ -35,6 +36,8 @@ export default class DeviceShutdownCmd extends Command {
}),
};
public static usage = 'device shutdown <uuid>';
public static flags = {
force: cf.force,
help: cf.help,

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy';
import type { BalenaSDK } from 'balena-sdk';
@ -45,6 +46,8 @@ export default class DeviceStartServiceCmd extends Command {
}),
};
public static usage = 'device start-service <uuid>';
public static flags = {
help: cf.help,
};

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy';
import type { BalenaSDK } from 'balena-sdk';
@ -45,6 +46,8 @@ export default class DeviceStopServiceCmd extends Command {
}),
};
public static usage = 'device stop-service <uuid>';
public static flags = {
help: cf.help,
};

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
@ -34,6 +35,8 @@ export default class DeviceTrackFleetCmd extends Command {
}),
};
public static usage = 'device track-fleet <uuid>';
public static flags = {
help: cf.help,
};

View File

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Command } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { expandForAppName } from '../../utils/helpers';
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
@ -35,9 +35,7 @@ const devicesSelectFields = {
],
} satisfies PineOptions<Device>;
export default class DeviceListCmd extends Command {
public static aliases = ['devices'];
export default class DevicesCmd extends Command {
public static description = stripIndent`
List all devices.
@ -50,11 +48,13 @@ export default class DeviceListCmd extends Command {
${jsonInfo.split('\n').join('\n\t\t')}
`;
public static examples = [
'$ balena device list',
'$ balena device list --fleet MyFleet',
'$ balena device list -f myorg/myfleet',
'$ balena devices',
'$ balena devices --fleet MyFleet',
'$ balena devices -f myorg/myfleet',
];
public static usage = 'devices';
public static flags = {
fleet: cf.fleet,
json: cf.json,
@ -66,7 +66,7 @@ export default class DeviceListCmd extends Command {
public static authenticated = true;
public async run() {
const { flags: options } = await this.parse(DeviceListCmd);
const { flags: options } = await this.parse(DevicesCmd);
const balena = getBalenaSdk();
const devicesOptions = {

View File

@ -14,11 +14,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Flags, Command } from '@oclif/core';
import { Flags } from '@oclif/core';
import type * as BalenaSdk from 'balena-sdk';
import * as _ from 'lodash';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
import { CommandHelp } from '../../utils/oclif-utils';
export default class DevicesSupportedCmd extends Command {
public static description = stripIndent`
@ -37,6 +40,11 @@ export default class DevicesSupportedCmd extends Command {
'$ balena devices supported --json',
];
public static usage = (
'devices supported ' +
new CommandHelp({ args: DevicesSupportedCmd.args }).defaultUsage()
).trim();
public static flags = {
help: cf.help,
json: Flags.boolean({

View File

@ -15,8 +15,9 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
import { Args } from '@oclif/core';
import type * as BalenaSdk from 'balena-sdk';
import Command from '../../command';
import { ExpectedError } from '../../errors';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
@ -91,6 +92,7 @@ export default class EnvAddCmd extends Command {
// Required for supporting empty string ('') `value` args.
public static strict = false;
public static usage = 'env add <name> [value]';
public static flags = {
fleet: { ...cf.fleet, exclusive: ['device'] },
@ -110,9 +112,7 @@ export default class EnvAddCmd extends Command {
);
}
const { checkLoggedIn } = await import('../../utils/patterns');
await checkLoggedIn();
await Command.checkLoggedIn();
if (params.value == null) {
params.value = process.env[params.name];

View File

@ -14,7 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import * as ec from '../../utils/env-common';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
@ -51,6 +53,8 @@ export default class EnvRenameCmd extends Command {
}),
};
public static usage = 'env rename <id> <value>';
public static flags = {
config: ec.booleanConfig,
device: ec.booleanDevice,
@ -61,9 +65,7 @@ export default class EnvRenameCmd extends Command {
public async run() {
const { args: params, flags: opt } = await this.parse(EnvRenameCmd);
const { checkLoggedIn } = await import('../../utils/patterns');
await checkLoggedIn();
await Command.checkLoggedIn();
await getBalenaSdk().pine.patch({
resource: ec.getVarResourceName(opt.config, opt.device, opt.service),

View File

@ -15,7 +15,9 @@
* limitations under the License.
*/
import { Flags, Args, Command } from '@oclif/core';
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';
@ -50,6 +52,8 @@ export default class EnvRmCmd extends Command {
}),
};
public static usage = 'env rm <id>';
public static flags = {
config: ec.booleanConfig,
device: ec.booleanDevice,
@ -65,9 +69,7 @@ export default class EnvRmCmd extends Command {
public async run() {
const { args: params, flags: opt } = await this.parse(EnvRmCmd);
const { checkLoggedIn } = await import('../../utils/patterns');
await checkLoggedIn();
await Command.checkLoggedIn();
const { confirm } = await import('../../utils/patterns');
await confirm(

View File

@ -14,16 +14,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Flags, Command } from '@oclif/core';
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';
import { ExpectedError } from '../../errors';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
import { applicationIdInfo } from '../../utils/messages';
type FlagsDef = Interfaces.InferredFlags<typeof EnvListCmd.flags>;
type FlagsDef = Interfaces.InferredFlags<typeof EnvsCmd.flags>;
interface EnvironmentVariableInfo extends SDK.EnvironmentVariableBase {
fleet?: string | null; // fleet slug
@ -45,9 +46,7 @@ interface ServiceEnvironmentVariableInfo
serviceName?: string; // service name
}
export default class EnvListCmd extends Command {
public static aliases = ['envs'];
export default class EnvsCmd extends Command {
public static description = stripIndent`
List the environment or config variables of a fleet, device or service.
@ -85,16 +84,18 @@ export default class EnvListCmd extends Command {
`;
public static examples = [
'$ balena env list --fleet myorg/myfleet',
'$ balena env list --fleet MyFleet --json',
'$ balena env list --fleet MyFleet --service MyService',
'$ balena env list --fleet MyFleet --config',
'$ balena env list --device 7cf02a6',
'$ balena env list --device 7cf02a6 --json',
'$ balena env list --device 7cf02a6 --config --json',
'$ balena env list --device 7cf02a6 --service MyService',
'$ balena envs --fleet myorg/myfleet',
'$ balena envs --fleet MyFleet --json',
'$ balena envs --fleet MyFleet --service MyService',
'$ balena envs --fleet MyFleet --config',
'$ balena envs --device 7cf02a6',
'$ balena envs --device 7cf02a6 --json',
'$ balena envs --device 7cf02a6 --config --json',
'$ balena envs --device 7cf02a6 --service MyService',
];
public static usage = 'envs';
public static flags = {
fleet: { ...cf.fleet, exclusive: ['device'] },
config: Flags.boolean({
@ -110,13 +111,11 @@ export default class EnvListCmd extends Command {
};
public async run() {
const { flags: options } = await this.parse(EnvListCmd);
const { flags: options } = await this.parse(EnvsCmd);
const variables: EnvironmentVariableInfo[] = [];
const { checkLoggedIn } = await import('../../utils/patterns');
await checkLoggedIn();
await Command.checkLoggedIn();
if (!options.fleet && !options.device) {
throw new ExpectedError('Missing --fleet or --device option');

View File

@ -15,7 +15,9 @@
* limitations under the License.
*/
import { Flags, Args, Command } from '@oclif/core';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { stripIndent } from '../../utils/lazy';
@ -28,7 +30,7 @@ export default class FleetCreateCmd extends Command {
You can specify the organization the fleet should belong to using
the \`--organization\` option. The organization's handle, not its name,
should be provided. Organization handles can be listed with the
\`balena organization list\` command.
\`balena orgs\` command.
The fleet's default device type is specified with the \`--type\` option.
The \`balena devices supported\` command can be used to list the available
@ -54,6 +56,8 @@ export default class FleetCreateCmd extends Command {
}),
};
public static usage = 'fleet create <name>';
public static flags = {
organization: Flags.string({
char: 'o',

View File

@ -15,7 +15,9 @@
* limitations under the License.
*/
import { Flags, Command } from '@oclif/core';
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, getVisuals, stripIndent } from '../../utils/lazy';
@ -39,6 +41,8 @@ export default class FleetCmd extends Command {
fleet: ca.fleetRequired,
};
public static usage = 'fleet <fleet>';
public static flags = {
help: cf.help,
view: Flags.boolean({

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
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';
@ -43,6 +44,8 @@ export default class FleetPinCmd extends Command {
}),
};
public static usage = 'fleet pin <slug> [releaseToPinTo]';
public static flags = {
help: cf.help,
};
@ -76,7 +79,7 @@ export default class FleetPinCmd extends Command {
pinnedRelease
? `This fleet is currently pinned to ${pinnedRelease}.`
: 'This fleet is not currently pinned to any release.'
} \n\nTo see a list of all releases this fleet can be pinned to, run \`balena release list ${slug}\`.`,
} \n\nTo see a list of all releases this fleet can be pinned to, run \`balena releases ${slug}\`.`,
);
} else {
await balena.models.application.pinToRelease(slug, releaseToPinTo);

View File

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Command } 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';
@ -40,6 +40,8 @@ export default class FleetPurgeCmd extends Command {
fleet: ca.fleetRequired,
};
public static usage = 'fleet purge <fleet>';
public static flags = {
help: cf.help,
};

View File

@ -15,7 +15,9 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import * as ca from '../../utils/common-args';
import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy';
@ -46,6 +48,8 @@ export default class FleetRenameCmd extends Command {
}),
};
public static usage = 'fleet rename <fleet> [newName]';
public static flags = {
help: cf.help,
};

View File

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Command } 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';
@ -39,6 +39,8 @@ export default class FleetRestartCmd extends Command {
fleet: ca.fleetRequired,
};
public static usage = 'fleet restart <fleet>';
public static flags = {
help: cf.help,
};

View File

@ -15,11 +15,11 @@
* limitations under the License.
*/
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import * as ca from '../../utils/common-args';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { applicationIdInfo } from '../../utils/messages';
import { Command } from '@oclif/core';
export default class FleetRmCmd extends Command {
public static description = stripIndent`
@ -42,6 +42,8 @@ export default class FleetRmCmd extends Command {
fleet: ca.fleetRequired,
};
public static usage = 'fleet rm <fleet>';
public static flags = {
yes: cf.yes,
help: cf.help,

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
@ -37,6 +38,8 @@ export default class FleetTrackLatestCmd extends Command {
}),
};
public static usage = 'fleet track-latest <slug>';
public static flags = {
help: cf.help,
};

View File

@ -16,9 +16,10 @@
*/
import type * as BalenaSdk from 'balena-sdk';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
import { Command } from '@oclif/core';
interface ExtendedApplication extends ApplicationWithDeviceTypeSlug {
device_count: number;
@ -26,9 +27,7 @@ interface ExtendedApplication extends ApplicationWithDeviceTypeSlug {
device_type?: string;
}
export default class FleetListCmd extends Command {
public static aliases = ['fleets'];
export default class FleetsCmd extends Command {
public static description = stripIndent`
List all fleets.
@ -38,7 +37,9 @@ export default class FleetListCmd extends Command {
\`balena fleet <fleet>\`
`;
public static examples = ['$ balena fleet list'];
public static examples = ['$ balena fleets'];
public static usage = 'fleets';
public static flags = {
help: cf.help,
@ -49,7 +50,7 @@ export default class FleetListCmd extends Command {
public static primary = true;
public async run() {
const { flags: options } = await this.parse(FleetListCmd);
const { flags: options } = await this.parse(FleetsCmd);
const balena = getBalenaSdk();

View File

@ -15,8 +15,10 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
import { Args } from '@oclif/core';
import Command from '../../command';
import { stripIndent } from '../../utils/lazy';
import { CommandHelp } from '../../utils/oclif-utils';
// 'Internal' commands are called during the execution of other commands.
// `osinit` is called during `os initialize`
@ -46,6 +48,11 @@ export default class OsinitCmd extends Command {
}),
};
public static usage = (
'internal osinit ' +
new CommandHelp({ args: OsinitCmd.args }).defaultUsage()
).trim();
public static hidden = true;
public static root = true;
public static offlineCompatible = true;

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Flags, Command } from '@oclif/core';
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';
@ -59,6 +60,9 @@ export default class JoinCmd extends Command {
}),
};
// Hardcoded to preserve camelcase
public static usage = 'join [deviceIpOrHostname]';
public static flags = {
fleet: cf.fleet,
pollInterval: Flags.integer({
@ -76,8 +80,7 @@ export default class JoinCmd extends Command {
const promote = await import('../../utils/promote');
const sdk = getBalenaSdk();
const Logger = await import('../../utils/logger');
const logger = Logger.getLogger();
const logger = await Command.getLogger();
return promote.join(
logger,
sdk,

View File

@ -15,14 +15,13 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
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';
export default class SSHKeyAddCmd extends Command {
public static aliases = ['key add'];
export default class KeyAddCmd extends Command {
public static description = stripIndent`
Add an SSH key to balenaCloud.
@ -30,7 +29,7 @@ export default class SSHKeyAddCmd extends Command {
If \`path\` is omitted, the command will attempt to read the SSH key from stdin.
About SSH keys
About SSH keys
An "SSH key" actually consists of a public/private key pair. A typical name
for the private key file is "id_rsa", and a typical name for the public key
file is "id_rsa.pub". Both key files are saved to your computer (with the
@ -38,7 +37,7 @@ export default class SSHKeyAddCmd extends Command {
saved to your balena account. This means that if you change computers or
otherwise lose the private key, you cannot recover the private key through
your balena account. You can however add new keys, and delete the old ones.
To generate a new SSH key pair, a nice guide can be found in GitHub's docs:
https://help.github.com/en/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent
Skip the step about adding the key to a GitHub account, and instead add it to
@ -46,10 +45,10 @@ export default class SSHKeyAddCmd extends Command {
`;
public static examples = [
'$ balena ssh-key add Main ~/.ssh/id_rsa.pub',
'$ cat ~/.ssh/id_rsa.pub | balena ssh-key add Main',
'$ balena key add Main ~/.ssh/id_rsa.pub',
'$ cat ~/.ssh/id_rsa.pub | balena key add Main',
'# Windows 10 (cmd.exe prompt) example',
'$ balena ssh-key add Main %userprofile%.sshid_rsa.pub',
'$ balena key add Main %userprofile%.sshid_rsa.pub',
];
public static args = {
@ -62,19 +61,25 @@ export default class SSHKeyAddCmd extends Command {
}),
};
public static usage = 'key add <name> [path]';
public static flags = {
help: cf.help,
};
public static authenticated = true;
public static readStdin = true;
public async run() {
const { args: params } = await this.parse(SSHKeyAddCmd);
const { args: params } = await this.parse(KeyAddCmd);
let key: string;
if (params.path != null) {
const { readFile } = (await import('fs')).promises;
key = await readFile(params.path, 'utf8');
} else if (this.stdin.length > 0) {
key = this.stdin;
} else {
throw new ExpectedError('No public key file or path provided.');
}

View File

@ -15,21 +15,20 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
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';
export default class SSHKeyCmd extends Command {
public static aliases = ['key'];
export default class KeyCmd extends Command {
public static description = stripIndent`
Display an SSH key.
Display a single SSH key registered in balenaCloud for the logged in user.
`;
public static examples = ['$ balena ssh-key 17'];
public static examples = ['$ balena key 17'];
public static args = {
id: Args.integer({
@ -39,6 +38,8 @@ export default class SSHKeyCmd extends Command {
}),
};
public static usage = 'key <id>';
public static flags = {
help: cf.help,
};
@ -46,7 +47,7 @@ export default class SSHKeyCmd extends Command {
public static authenticated = true;
public async run() {
const { args: params } = await this.parse(SSHKeyCmd);
const { args: params } = await this.parse(KeyCmd);
const key = await getBalenaSdk().models.key.get(params.id);

View File

@ -15,14 +15,13 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
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';
export default class SSHKeyRmCmd extends Command {
public static aliases = ['key rm'];
export default class KeyRmCmd extends Command {
public static description = stripIndent`
Remove an SSH key from balenaCloud.
@ -31,10 +30,7 @@ export default class SSHKeyRmCmd extends Command {
The --yes option may be used to avoid interactive confirmation.
`;
public static examples = [
'$ balena ssh-key rm 17',
'$ balena ssh-key rm 17 --yes',
];
public static examples = ['$ balena key rm 17', '$ balena key rm 17 --yes'];
public static args = {
id: Args.integer({
@ -44,6 +40,8 @@ export default class SSHKeyRmCmd extends Command {
}),
};
public static usage = 'key rm <id>';
public static flags = {
yes: cf.yes,
help: cf.help,
@ -52,7 +50,7 @@ export default class SSHKeyRmCmd extends Command {
public static authenticated = true;
public async run() {
const { args: params, flags: options } = await this.parse(SSHKeyRmCmd);
const { args: params, flags: options } = await this.parse(KeyRmCmd);
const patterns = await import('../../utils/patterns');

View File

@ -15,19 +15,19 @@
* limitations under the License.
*/
import { Command } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
export default class SSHKeyListCmd extends Command {
public static aliases = ['keys', 'key list'];
export default class KeysCmd extends Command {
public static description = stripIndent`
List the SSH keys in balenaCloud.
List all SSH keys registered in balenaCloud for the logged in user.
`;
public static examples = ['$ balena ssh-key list'];
public static examples = ['$ balena keys'];
public static usage = 'keys';
public static flags = {
help: cf.help,
@ -36,7 +36,7 @@ export default class SSHKeyListCmd extends Command {
public static authenticated = true;
public async run() {
await this.parse(SSHKeyListCmd);
await this.parse(KeysCmd);
const keys = await getBalenaSdk().models.key.getAll();

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
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';
@ -49,6 +50,8 @@ export default class LeaveCmd extends Command {
}),
};
public static usage = 'leave [deviceIpOrHostname]';
public static flags = {
help: cf.help,
};
@ -60,8 +63,7 @@ export default class LeaveCmd extends Command {
const { args: params } = await this.parse(LeaveCmd);
const promote = await import('../../utils/promote');
const Logger = await import('../../utils/logger');
const logger = Logger.getLogger();
const logger = await Command.getLogger();
return promote.leave(logger, params.deviceIpOrHostname);
}
}

View File

@ -15,8 +15,9 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
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';
@ -39,6 +40,8 @@ export default class LocalConfigureCmd extends Command {
}),
};
public static usage = 'local configure <target>';
public static flags = {
help: cf.help,
};

View File

@ -15,8 +15,9 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
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';
@ -45,6 +46,8 @@ export default class LocalFlashCmd extends Command {
}),
};
public static usage = 'local flash <image>';
public static flags = {
drive: cf.drive,
yes: cf.yes,

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Flags, Args, Command } from '@oclif/core';
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';
@ -62,6 +63,8 @@ export default class LoginCmd extends Command {
}),
};
public static usage = 'login';
public static flags = {
web: Flags.boolean({
default: false,

View File

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Command } from '@oclif/core';
import Command from '../../command';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
export default class LogoutCmd extends Command {
@ -26,6 +26,8 @@ export default class LogoutCmd extends Command {
`;
public static examples = ['$ balena logout'];
public static usage = 'logout';
public async run() {
await this.parse(LogoutCmd);
await getBalenaSdk().auth.logout();

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Flags, Args, Command } from '@oclif/core';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import type { LogMessage } from 'balena-sdk';
@ -60,6 +61,8 @@ export default class LogsCmd extends Command {
}),
};
public static usage = 'logs <device>';
public static flags = {
'max-retry': Flags.integer({
description: stripIndent`
@ -150,9 +153,8 @@ export default class LogsCmd extends Command {
maxAttempts: 1 + (options['max-retry'] ?? MAX_RETRY),
});
} else {
const { checkLoggedIn } = await import('../../utils/patterns');
// Logs from cloud
await checkLoggedIn();
await Command.checkLoggedIn();
if (options.tail) {
const logStream = await balena.logs.subscribe(params.device, {
count: 100,

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Flags, Args, Command } from '@oclif/core';
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';
@ -41,6 +42,8 @@ export default class NoteCmd extends Command {
}),
};
public static usage = 'note <|note>';
public static flags = {
device: { exclusive: ['dev'], ...cf.device },
dev: Flags.string({
@ -52,10 +55,14 @@ export default class NoteCmd extends Command {
public static authenticated = true;
public static readStdin = true;
public async run() {
const { args: params, flags: options } = await this.parse(NoteCmd);
if (params.note?.length === 0) {
params.note = params.note || this.stdin;
if (params.note.length === 0) {
throw new ExpectedError('Missing note content');
}
@ -68,6 +75,6 @@ export default class NoteCmd extends Command {
const balena = getBalenaSdk();
return balena.models.device.setNote(options.device, params.note ?? '');
return balena.models.device.setNote(options.device, params.note);
}
}

View File

@ -15,19 +15,19 @@
* limitations under the License.
*/
import { Command } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
export default class OrganizationListCmd extends Command {
public static aliases = ['orgs'];
export default class OrgsCmd extends Command {
public static description = stripIndent`
List all organizations.
list all the organizations that you are a member of.
`;
public static examples = ['$ balena organization list'];
public static examples = ['$ balena orgs'];
public static usage = 'orgs';
public static flags = {
help: cf.help,
@ -36,7 +36,7 @@ export default class OrganizationListCmd extends Command {
public static authenticated = true;
public async run() {
await this.parse(OrganizationListCmd);
await this.parse(OrgsCmd);
const { getOwnOrganizations } = await import('../../utils/sdk');

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Flags, Args, Command } from '@oclif/core';
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';
@ -45,6 +46,8 @@ export default class OsBuildConfigCmd extends Command {
}),
};
public static usage = 'os build-config <image> <device-type>';
public static flags = {
advanced: Flags.boolean({
description: 'show advanced configuration options',

View File

@ -15,11 +15,12 @@
* limitations under the License.
*/
import { Flags, Args, Command } from '@oclif/core';
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';
import Command from '../../command';
import { ExpectedError } from '../../errors';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy';
@ -91,6 +92,8 @@ export default class OsConfigureCmd extends Command {
}),
};
public static usage = 'os configure <image>';
public static flags = {
advanced: Flags.boolean({
char: 'v',
@ -318,9 +321,7 @@ async function validateOptions(options: FlagsDef) {
);
}
const { checkLoggedIn } = await import('../../utils/patterns');
await checkLoggedIn();
await Command.checkLoggedIn();
}
/**

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Flags, Args, Command } from '@oclif/core';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { stripIndent } from '../../utils/lazy';
@ -61,6 +62,8 @@ export default class OsDownloadCmd extends Command {
}),
};
public static usage = 'os download <type>';
public static flags = {
output: Flags.string({
description: 'output path',
@ -86,11 +89,10 @@ export default class OsDownloadCmd extends Command {
// balenaOS ESR versions require user authentication
if (options.version) {
const { isESR } = await import('../../utils/image-manager');
const { isESR } = await import('balena-image-manager');
if (options.version === 'menu-esr' || isESR(options.version)) {
try {
const { checkLoggedIn } = await import('../../utils/patterns');
await checkLoggedIn();
await OsDownloadCmd.checkLoggedIn();
} catch (e) {
const { ExpectedError, NotLoggedInError } = await import(
'../../errors'

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
import { Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getCliForm, stripIndent } from '../../utils/lazy';
@ -47,6 +48,8 @@ export default class OsInitializeCmd extends Command {
}),
};
public static usage = 'os initialize <image>';
public static flags = {
type: cf.deviceType,
drive: cf.drive,

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Flags, Args, Command } from '@oclif/core';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { stripIndent } from '../../utils/lazy';
@ -39,6 +40,8 @@ export default class OsVersionsCmd extends Command {
}),
};
public static usage = 'os versions <type>';
public static flags = {
help: cf.help,
esr: Flags.boolean({

View File

@ -15,6 +15,7 @@
* limitations under the License.
*/
import Command from '../../command';
import { ExpectedError } from '../../errors';
import * as cf from '../../utils/common-flags';
import {
@ -26,7 +27,8 @@ import {
import { applicationIdInfo } from '../../utils/messages';
import { dockerConnectionCliFlags } from '../../utils/docker';
import { parseAsInteger } from '../../utils/validation';
import { Flags, Args, Command } from '@oclif/core';
import { Flags, Args } from '@oclif/core';
import * as _ from 'lodash';
import type {
Application,
@ -78,6 +80,8 @@ export default class PreloadCmd extends Command {
}),
};
public static usage = 'preload <image>';
public static flags = {
fleet: cf.fleet,
commit: Flags.string({

View File

@ -15,8 +15,9 @@
* limitations under the License.
*/
import { Flags, Args, Command } from '@oclif/core';
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';
import { dockerignoreHelp, registrySecretsHelp } from '../../utils/messages';
@ -98,6 +99,8 @@ export default class PushCmd extends Command {
}),
};
public static usage = 'push <fleetOrDevice>';
public static flags = {
source: Flags.string({
description: stripIndent`
@ -226,9 +229,7 @@ export default class PushCmd extends Command {
public async run() {
const { args: params, flags: options } = await this.parse(PushCmd);
const Logger = await import('../../utils/logger');
const logger = Logger.getLogger();
const logger = await Command.getLogger();
logger.logDebug(`Using build source directory: ${options.source} `);
const sdk = getBalenaSdk();
@ -295,9 +296,7 @@ export default class PushCmd extends Command {
options['release-tag'] ?? [],
);
const { checkLoggedIn } = await import('../../utils/patterns');
await checkLoggedIn();
await Command.checkLoggedIn();
const [token, baseUrl] = await Promise.all([
sdk.auth.getToken(),
sdk.settings.get('balenaUrl'),

View File

@ -0,0 +1,116 @@
/**
* @license
* Copyright 2016-2024 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { commitOrIdArg } from '.';
import { Flags } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { create } from '@balena/release-bundle';
import * as fs from 'fs/promises';
import * as semver from 'balena-semver';
import { ExpectedError } from '../../errors';
export default class ReleaseExportCmd extends Command {
public static description = stripIndent`
Exports a release into a file.
Exporting a release to a file allows you to import an exact
copy of the original release into another app.
If the SemVer of a release is provided using the --version option,
the first argument is assumed to be the fleet's slug.
Only successful releases can be exported.
`;
public static examples = [
'$ balena release export a777f7345fe3d655c1c981aa642e5555 -o ../path/to/release.tar',
'$ balena release export myOrg/myFleet --version 1.2.3 -o ../path/to/release.tar',
];
public static usage = 'release export <commitOrId>';
public static flags = {
output: Flags.string({
description: 'output path',
char: 'o',
required: true,
}),
version: Flags.string({
description: 'version of the release to export from the specified fleet',
}),
help: cf.help,
};
public static args = {
commitOrId: commitOrIdArg({
description: 'commit, ID, or version of the release to export',
required: true,
}),
};
public static authenticated = true;
public async run() {
const { args: params, flags: options } = await this.parse(ReleaseExportCmd);
const balena = getBalenaSdk();
let release: balenaSdk.Release;
if (typeof options.version === 'string') {
const application = params.commitOrId;
const parsedVersion = semver.parse(options.version);
if (parsedVersion == null) {
throw new ExpectedError(
`Release of ${application} with version ${options.version} could not be exported; version must be valid SemVer.`,
);
} else {
const rawVersion =
parsedVersion.build.length === 0
? parsedVersion.version
: `${parsedVersion.version}+${parsedVersion.build[0]}`;
release = await balena.models.release.get(
{ application, rawVersion },
{ $select: ['id'] },
);
}
} else {
release = await balena.models.release.get(params.commitOrId, {
$select: ['id'],
});
}
try {
const releaseBundle = await create({
sdk: balena,
releaseId: release.id,
});
await fs.writeFile(options.output, releaseBundle);
const versionInfo =
typeof options.version === 'string'
? ` version ${options.version}`
: '';
console.log(
`Release ${params.commitOrId}${versionInfo} has been exported to ${options.output}.`,
);
} catch (error) {
throw new ExpectedError(
`Release ${params.commitOrId} could not be exported: ${error.message}`,
);
}
}
}

View File

@ -15,8 +15,8 @@
* limitations under the License.
*/
import { Command } from '@oclif/core';
import { commitOrIdArg } from '.';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
@ -40,6 +40,8 @@ export default class ReleaseFinalizeCmd extends Command {
'$ balena release finalize 1234567',
];
public static usage = 'release finalize <commitOrId>';
public static flags = {
help: cf.help,
};

View File

@ -0,0 +1,103 @@
/**
* @license
* Copyright 2016-2024 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { apply } from '@balena/release-bundle';
import { createReadStream } from 'fs';
import { ExpectedError } from '../../errors';
export default class ReleaseImportCmd extends Command {
public static description = stripIndent`
Imports a release from a file to an app or fleet. The revision field of the release
is automatically omitted when importing a release. The backend will auto-increment
the revision field of the imported release if a release exists with the same semver.
A release will not be imported if a successful release with the same commit already
exists.
To export a release to a file, use 'balena release export'.
Use the --override-version option to specify the version
of the imported release, overriding the one saved in the file.
`;
public static examples = [
'$ balena release import ../path/to/release.tar myFleet',
'$ balena release import ../path/to/release.tar myOrg/myFleet',
'$ balena release import ../path/to/release.tar myOrg/myFleet --override-version 1.2.3',
];
public static usage = 'release import <file> <fleet>';
public static flags = {
'override-version': Flags.string({
description:
'Imports this release with the specified version overriding the version in the file.',
required: false,
}),
help: cf.help,
};
public static args = {
bundle: Args.string({
required: true,
description: 'path to a file, e.g. "./release.tar"',
}),
fleet: Args.string({
required: true,
description:
'fleet that the release will be imported to, e.g. "myOrg/myFleet"',
}),
};
public static authenticated = true;
public async run() {
const { args: params, flags: options } = await this.parse(ReleaseImportCmd);
const balena = getBalenaSdk();
const bundle = createReadStream(params.bundle).on('error', () => {
throw new ExpectedError(
`Release bundle ${params.bundle} does not exist or is not accessible.`,
);
});
try {
const application = await balena.models.application.get(params.fleet, {
$select: ['id'],
});
if (application == null) {
throw new ExpectedError(`Fleet ${params.fleet} not found.`);
}
await apply({
sdk: balena,
application: application.id,
stream: bundle,
version: options['override-version'],
});
console.log(
`Release bundle ${params.bundle} has been imported to ${params.fleet}.`,
);
} catch (error) {
throw new ExpectedError(
`Could not import release bundle ${params.bundle} to fleet ${params.fleet}: ${error.message}`,
);
}
}
}

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Flags, Args, type Interfaces, Command } from '@oclif/core';
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';
@ -41,6 +42,8 @@ export default class ReleaseCmd extends Command {
'$ balena release d3f3151f5ad25ca6b070aa4d08296aca --json',
];
public static usage = 'release <commitOrId>';
public static flags = {
json: cf.json,
help: cf.help,

View File

@ -15,8 +15,8 @@
* limitations under the License.
*/
import { Command } from '@oclif/core';
import { commitOrIdArg } from '.';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
@ -35,6 +35,8 @@ export default class ReleaseInvalidateCmd extends Command {
'$ balena release invalidate 1234567',
];
public static usage = 'release invalidate <commitOrId>';
public static flags = {
help: cf.help,
};

View File

@ -15,8 +15,8 @@
* limitations under the License.
*/
import { Command } from '@oclif/core';
import { commitOrIdArg } from '.';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
@ -34,6 +34,8 @@ export default class ReleaseValidateCmd extends Command {
'$ balena release validate 1234567',
];
public static usage = 'release validate <commitOrId>';
public static flags = {
help: cf.help,
};

View File

@ -15,16 +15,15 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
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';
import { jsonInfo } from '../../utils/messages';
export default class ReleaseListCmd extends Command {
public static aliases = ['releases'];
export default class ReleasesCmd extends Command {
public static description = stripIndent`
List all releases of a fleet.
@ -35,10 +34,12 @@ export default class ReleaseListCmd extends Command {
${jsonInfo.split('\n').join('\n\t\t')}
`;
public static examples = [
'$ balena release list myorg/myfleet',
'$ balena release list myorg/myfleet --json',
'$ balena releases myorg/myfleet',
'$ balena releases myorg/myfleet --json',
];
public static usage = 'releases <fleet>';
public static flags = {
json: cf.json,
help: cf.help,
@ -54,7 +55,7 @@ export default class ReleaseListCmd extends Command {
public static authenticated = true;
public async run() {
const { args: params, flags: options } = await this.parse(ReleaseListCmd);
const { args: params, flags: options } = await this.parse(ReleasesCmd);
const fields: Array<keyof BalenaSdk.Release> = [
'id',

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Flags, Command } from '@oclif/core';
import { Flags } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getCliUx, stripIndent } from '../../utils/lazy';
@ -37,6 +38,8 @@ export default class ScanCmd extends Command {
'$ balena scan --verbose',
];
public static usage = 'scan';
public static flags = {
verbose: Flags.boolean({
default: false,

View File

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Command } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
@ -27,6 +27,8 @@ export default class SettingsCmd extends Command {
`;
public static examples = ['$ balena settings'];
public static usage = 'settings';
public static flags = {
help: cf.help,
};

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Flags, Args, Command } from '@oclif/core';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import {
@ -74,6 +75,8 @@ export default class SshCmd extends Command {
}),
};
public static usage = 'ssh <fleetOrDevice> [service]';
public static flags = {
port: Flags.integer({
description: stripIndent`
@ -120,19 +123,15 @@ export default class SshCmd extends Command {
// Remote connection
const { getProxyConfig } = await import('../../utils/helpers');
const {
getOnlineTargetDeviceUuid,
checkLoggedIn,
checkNotUsingOfflineMode,
} = await import('../../utils/patterns');
const { getOnlineTargetDeviceUuid } = await import('../../utils/patterns');
const sdk = getBalenaSdk();
const proxyConfig = getProxyConfig();
const useProxy = !!proxyConfig && !options.noproxy;
// this will be a tunnelled SSH connection...
await checkNotUsingOfflineMode();
await checkLoggedIn();
await Command.checkNotUsingOfflineMode();
await Command.checkLoggedIn();
const deviceUuid = await getOnlineTargetDeviceUuid(
sdk,
params.fleetOrDevice,

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Flags, Args, Command } from '@oclif/core';
import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import { ExpectedError } from '../../errors';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy';
@ -51,6 +52,8 @@ export default class SupportCmd extends Command {
}),
};
public static usage = 'support <action>';
public static flags = {
device: Flags.string({
description: 'comma-separated list (no spaces) of device UUIDs',

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
import { Args } 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';
@ -44,6 +45,8 @@ export default class TagRmCmd extends Command {
}),
};
public static usage = 'tag rm <tagKey>';
public static flags = {
fleet: {
...cf.fleet,

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
import { Args, Command } from '@oclif/core';
import { Args } 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';
@ -57,6 +58,7 @@ export default class TagSetCmd extends Command {
// Required for supporting empty string ('') `value` args.
public static strict = false;
public static usage = 'tag set <tagKey> [value]';
public static flags = {
fleet: {

View File

@ -15,15 +15,13 @@
* limitations under the License.
*/
import { Command } from '@oclif/core';
import Command from '../../command';
import { ExpectedError } from '../../errors';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
import { applicationIdInfo } from '../../utils/messages';
export default class TagListCmd extends Command {
public static aliases = ['tags'];
export default class TagsCmd extends Command {
public static description = stripIndent`
List all tags for a fleet, device or release.
@ -33,13 +31,15 @@ export default class TagListCmd extends Command {
`;
public static examples = [
'$ balena tag list --fleet MyFleet',
'$ balena tag list -f myorg/myfleet',
'$ balena tag list --device 7cf02a6',
'$ balena tag list --release 1234',
'$ balena tag list --release b376b0e544e9429483b656490e5b9443b4349bd6',
'$ balena tags --fleet MyFleet',
'$ balena tags -f myorg/myfleet',
'$ balena tags --device 7cf02a6',
'$ balena tags --release 1234',
'$ balena tags --release b376b0e544e9429483b656490e5b9443b4349bd6',
];
public static usage = 'tags';
public static flags = {
fleet: {
...cf.fleet,
@ -59,7 +59,7 @@ export default class TagListCmd extends Command {
public static authenticated = true;
public async run() {
const { flags: options } = await this.parse(TagListCmd);
const { flags: options } = await this.parse(TagsCmd);
const balena = getBalenaSdk();
@ -107,6 +107,6 @@ export default class TagListCmd extends Command {
See the help page for examples:
$ balena help tag list
$ balena help tags
`;
}

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