diff --git a/automation/capitanodoc/doc-types.d.ts b/automation/capitanodoc/doc-types.d.ts index 3d37511d..ab211688 100644 --- a/automation/capitanodoc/doc-types.d.ts +++ b/automation/capitanodoc/doc-types.d.ts @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Command as OclifCommandClass } from '@oclif/command'; +import { Command as OclifCommandClass } from '@oclif/core'; type OclifCommand = typeof OclifCommandClass; diff --git a/automation/capitanodoc/index.ts b/automation/capitanodoc/index.ts index 8b2d05db..3a6eea3b 100644 --- a/automation/capitanodoc/index.ts +++ b/automation/capitanodoc/index.ts @@ -62,12 +62,11 @@ class FakeHelpCommand { '$ balena help os download', ]; - args = [ - { - name: 'command', + args = { + command: { description: 'command to show help for', }, - ]; + }; usage = 'help [command]'; diff --git a/automation/capitanodoc/markdown.ts b/automation/capitanodoc/markdown.ts index 2fd48b9e..24d8b94d 100644 --- a/automation/capitanodoc/markdown.ts +++ b/automation/capitanodoc/markdown.ts @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { flagUsages } from '@oclif/parser'; +import { Parser } from '@oclif/core'; import * as ent from 'ent'; import * as _ from 'lodash'; @@ -37,8 +37,8 @@ function renderOclifCommand(command: OclifCommand): string[] { if (!_.isEmpty(command.args)) { result.push('### Arguments'); - for (const arg of command.args!) { - result.push(`#### ${arg.name.toUpperCase()}`, arg.description || ''); + for (const [name, arg] of Object.entries(command.args!)) { + result.push(`#### ${name.toUpperCase()}`, arg.description || ''); } } @@ -49,7 +49,7 @@ function renderOclifCommand(command: OclifCommand): string[] { continue; } flag.name = name; - const flagUsage = flagUsages([flag]) + const flagUsage = Parser.flagUsages([flag]) .map(([usage, _description]) => usage) .join() .trim(); diff --git a/lib/app.ts b/lib/app.ts index 972f6330..4db14d7a 100644 --- a/lib/app.ts +++ b/lib/app.ts @@ -24,6 +24,7 @@ import { } from './preparser'; import { CliSettings } from './utils/bootstrap'; import { onceAsync } from './utils/lazy'; +import { run as mainRun } from '@oclif/core'; /** * Sentry.io setup @@ -114,10 +115,9 @@ async function oclifRun(command: string[], options: AppOptions) { } const runPromise = (async function (shouldFlush: boolean) { - const { CustomMain } = await import('./utils/oclif-utils'); let isEEXIT = false; try { - await CustomMain.run(command); + await mainRun(command, options.configPath); } catch (error) { // oclif sometimes exits with ExitError code EEXIT 0 (not an error), // for example the `balena help` command. @@ -130,7 +130,7 @@ async function oclifRun(command: string[], options: AppOptions) { } } if (shouldFlush) { - await import('@oclif/command/flush'); + await import('@oclif/core/flush'); } // TODO: figure out why we need to call fast-boot stop() here, in // addition to calling it in the main `run()` function in this file. diff --git a/lib/command.ts b/lib/command.ts index de9b6979..1b260a35 100644 --- a/lib/command.ts +++ b/lib/command.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import Command from '@oclif/command'; +import { Command } from '@oclif/core'; import { InsufficientPrivilegesError, NotAvailableInOfflineModeError, diff --git a/lib/commands/api-key/generate.ts b/lib/commands/api-key/generate.ts index f5b353c0..c9213b67 100644 --- a/lib/commands/api-key/generate.ts +++ b/lib/commands/api-key/generate.ts @@ -15,20 +15,12 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Args } from '@oclif/core'; import Command from '../../command'; import { ExpectedError } from '../../errors'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -interface FlagsDef { - help: void; -} - -interface ArgsDef { - name: string; -} - export default class GenerateCmd extends Command { public static description = stripIndent` Generate a new balenaCloud API key. @@ -41,24 +33,23 @@ export default class GenerateCmd extends Command { `; public static examples = ['$ balena api-key generate "Jenkins Key"']; - public static args = [ - { - name: 'name', + public static args = { + name: Args.string({ description: 'the API key name', required: true, - }, - ]; + }), + }; public static usage = 'api-key generate '; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; public static authenticated = true; public async run() { - const { args: params } = this.parse(GenerateCmd); + const { args: params } = await this.parse(GenerateCmd); let key; try { diff --git a/lib/commands/api-key/revoke.ts b/lib/commands/api-key/revoke.ts index b84715f4..a358f690 100644 --- a/lib/commands/api-key/revoke.ts +++ b/lib/commands/api-key/revoke.ts @@ -15,19 +15,11 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -interface FlagsDef { - help: void; -} - -interface ArgsDef { - ids: string; -} - export default class RevokeCmd extends Command { public static description = stripIndent` Revoke balenaCloud API keys. @@ -42,24 +34,23 @@ export default class RevokeCmd extends Command { '$ balena api-key revoke 123,124,456', ]; - public static args = [ - { - name: 'ids', + public static args = { + ids: Args.string({ description: 'the API key ids', required: true, - }, - ]; + }), + }; public static usage = 'api-key revoke '; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; public static authenticated = true; public async run() { - const { args: params } = this.parse(RevokeCmd); + const { args: params } = await this.parse(RevokeCmd); try { const apiKeyIds = params.ids.split(','); diff --git a/lib/commands/api-keys/index.ts b/lib/commands/api-keys/index.ts index 6fc1d07f..e0b8dfa2 100644 --- a/lib/commands/api-keys/index.ts +++ b/lib/commands/api-keys/index.ts @@ -15,17 +15,11 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy'; -interface FlagsDef { - help: void; - user?: void; - fleet?: string; -} - export default class ApiKeysCmd extends Command { public static description = stripIndent` Print a list of balenaCloud API keys. @@ -36,13 +30,11 @@ export default class ApiKeysCmd extends Command { `; public static examples = ['$ balena api-keys']; - public static args = []; - public static usage = 'api-keys'; - public static flags: flags.Input = { + public static flags = { help: cf.help, - user: flags.boolean({ + user: Flags.boolean({ char: 'u', description: 'show API keys for your user', }), @@ -52,7 +44,7 @@ export default class ApiKeysCmd extends Command { public static authenticated = true; public async run() { - const { flags: options } = this.parse(ApiKeysCmd); + const { flags: options } = await this.parse(ApiKeysCmd); try { const { getApplication } = await import('../../utils/sdk'); diff --git a/lib/commands/app/create.ts b/lib/commands/app/create.ts index ef1ec7f8..5d82e33c 100644 --- a/lib/commands/app/create.ts +++ b/lib/commands/app/create.ts @@ -15,12 +15,11 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags, Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { stripIndent } from '../../utils/lazy'; -import { ArgsDef, FlagsDef } from '../../utils/application-create'; export default class AppCreateCmd extends Command { public static description = stripIndent` @@ -50,22 +49,21 @@ export default class AppCreateCmd extends Command { '$ balena app create MyApp -o myorg --type raspberry-pi', ]; - public static args = [ - { - name: 'name', + public static args = { + name: Args.string({ description: 'app name', required: true, - }, - ]; + }), + }; public static usage = 'app create '; - public static flags: flags.Input = { - organization: flags.string({ + public static flags = { + organization: Flags.string({ char: 'o', description: 'handle of the organization the app should belong to', }), - type: flags.string({ + type: Flags.string({ char: 't', description: 'app device type (Check available types with `balena devices supported`)', @@ -76,9 +74,7 @@ export default class AppCreateCmd extends Command { public static authenticated = true; public async run() { - const { args: params, flags: options } = this.parse( - AppCreateCmd, - ); + const { args: params, flags: options } = await this.parse(AppCreateCmd); await ( await import('../../utils/application-create') diff --git a/lib/commands/block/create.ts b/lib/commands/block/create.ts index d85bfc92..efb5ced7 100644 --- a/lib/commands/block/create.ts +++ b/lib/commands/block/create.ts @@ -15,22 +15,12 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags, Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { stripIndent } from '../../utils/lazy'; -interface FlagsDef { - organization?: string; - type?: string; // application device type - help: void; -} - -interface ArgsDef { - name: string; -} - export default class BlockCreateCmd extends Command { public static description = stripIndent` Create an block. @@ -59,22 +49,21 @@ export default class BlockCreateCmd extends Command { '$ balena block create MyBlock -o myorg --type raspberry-pi', ]; - public static args = [ - { - name: 'name', + public static args = { + name: Args.string({ description: 'block name', required: true, - }, - ]; + }), + }; public static usage = 'block create '; - public static flags: flags.Input = { - organization: flags.string({ + public static flags = { + organization: Flags.string({ char: 'o', description: 'handle of the organization the block should belong to', }), - type: flags.string({ + type: Flags.string({ char: 't', description: 'block device type (Check available types with `balena devices supported`)', @@ -85,9 +74,7 @@ export default class BlockCreateCmd extends Command { public static authenticated = true; public async run() { - const { args: params, flags: options } = this.parse( - BlockCreateCmd, - ); + const { args: params, flags: options } = await this.parse(BlockCreateCmd); await ( await import('../../utils/application-create') diff --git a/lib/commands/build.ts b/lib/commands/build.ts index 26d38173..72efca76 100644 --- a/lib/commands/build.ts +++ b/lib/commands/build.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Args, Flags } from '@oclif/core'; import Command from '../command'; import { getBalenaSdk } from '../utils/lazy'; import * as cf from '../utils/common-flags'; @@ -31,6 +31,9 @@ import { buildProject, composeCliFlags } from '../utils/compose_ts'; import type { BuildOpts, DockerCliFlags } from '../utils/docker'; import { dockerCliFlags } from '../utils/docker'; +// TODO: For this special one we can't use Interfaces.InferredFlags/InferredArgs +// because of the 'registry-secrets' type which is defined in the actual code +// as a path (string | undefined) but then the cli turns it into an object interface FlagsDef extends ComposeCliFlags, DockerCliFlags { arch?: string; deviceType?: string; @@ -39,10 +42,6 @@ interface FlagsDef extends ComposeCliFlags, DockerCliFlags { help: void; } -interface ArgsDef { - source?: string; -} - export default class BuildCmd extends Command { public static description = `\ Build a project locally. @@ -74,21 +73,18 @@ ${dockerignoreHelp} '$ balena build --dockerHost my.docker.host --dockerPort 2376 --ca ca.pem --key key.pem --cert cert.pem -f myFleet', ]; - public static args = [ - { - name: 'source', - description: 'path of project source directory', - }, - ]; + public static args = { + source: Args.string({ description: 'path of project source directory' }), + }; public static usage = 'build [source]'; - public static flags: flags.Input = { - arch: flags.string({ + public static flags = { + arch: Flags.string({ description: 'the architecture to build for', char: 'A', }), - deviceType: flags.string({ + deviceType: Flags.string({ description: 'the type of device this build is for', char: 'd', }), @@ -97,15 +93,13 @@ ${dockerignoreHelp} ...dockerCliFlags, // NOTE: Not supporting -h for help, because of clash with -h in DockerCliFlags // Revisit this in future release. - help: flags.help({}), + help: Flags.help({}), }; public static primary = true; public async run() { - const { args: params, flags: options } = this.parse( - BuildCmd, - ); + const { args: params, flags: options } = await this.parse(BuildCmd); await Command.checkLoggedInIf(!!options.fleet); diff --git a/lib/commands/config/generate.ts b/lib/commands/config/generate.ts index 92844659..59d77fb8 100644 --- a/lib/commands/config/generate.ts +++ b/lib/commands/config/generate.ts @@ -15,7 +15,8 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags } from '@oclif/core'; +import type { Interfaces } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, getCliForm, stripIndent } from '../../utils/lazy'; @@ -26,26 +27,6 @@ import { } from '../../utils/messages'; import type { BalenaSDK, PineDeferred } from 'balena-sdk'; -interface FlagsDef { - version: string; // OS version - fleet?: string; - dev?: boolean; // balenaOS development variant - secureBoot?: boolean; - device?: string; - deviceApiKey?: string; - deviceType?: string; - 'generate-device-api-key': boolean; - output?: string; - // Options for non-interactive configuration - network?: string; - wifiSsid?: string; - wifiKey?: string; - appUpdatePollInterval?: string; - 'provisioning-key-name'?: string; - 'provisioning-key-expiry-date'?: string; - help: void; -} - export default class ConfigGenerateCmd extends Command { public static description = stripIndent` Generate a config.json file. @@ -81,8 +62,8 @@ export default class ConfigGenerateCmd extends Command { public static usage = 'config generate'; - public static flags: flags.Input = { - version: flags.string({ + public static flags = { + version: Flags.string({ description: 'a balenaOS version', required: true, }), @@ -97,44 +78,44 @@ export default class ConfigGenerateCmd extends Command { 'provisioning-key-expiry-date', ], }, - deviceApiKey: flags.string({ + deviceApiKey: Flags.string({ description: 'custom device key - note that this is only supported on balenaOS 2.0.3+', char: 'k', }), - deviceType: flags.string({ + deviceType: Flags.string({ description: "device type slug (run 'balena devices supported' for possible values)", }), - 'generate-device-api-key': flags.boolean({ + 'generate-device-api-key': Flags.boolean({ description: 'generate a fresh device key for the device', }), - output: flags.string({ + output: Flags.string({ description: 'path of output file', char: 'o', }), // Options for non-interactive configuration - network: flags.string({ + network: Flags.string({ description: 'the network type to use: ethernet or wifi', options: ['ethernet', 'wifi'], }), - wifiSsid: flags.string({ + wifiSsid: Flags.string({ description: 'the wifi ssid to use (used only if --network is set to wifi)', }), - wifiKey: flags.string({ + wifiKey: Flags.string({ description: 'the wifi key to use (used only if --network is set to wifi)', }), - appUpdatePollInterval: flags.string({ + appUpdatePollInterval: Flags.string({ description: 'supervisor cloud polling interval in minutes (e.g. for device variables)', }), - 'provisioning-key-name': flags.string({ + 'provisioning-key-name': Flags.string({ description: 'custom key name assigned to generated provisioning api key', exclusive: ['device'], }), - 'provisioning-key-expiry-date': flags.string({ + 'provisioning-key-expiry-date': Flags.string({ description: 'expiry date assigned to generated provisioning api key (format: YYYY-MM-DD)', exclusive: ['device'], @@ -155,7 +136,7 @@ export default class ConfigGenerateCmd extends Command { } public async run() { - const { flags: options } = this.parse(ConfigGenerateCmd); + const { flags: options } = await this.parse(ConfigGenerateCmd); const balena = getBalenaSdk(); await this.validateOptions(options); @@ -266,7 +247,9 @@ export default class ConfigGenerateCmd extends Command { protected readonly deviceTypeNotAllowedMessage = 'The --deviceType option can only be used alongside the --fleet option'; - protected async validateOptions(options: FlagsDef) { + protected async validateOptions( + options: Interfaces.InferredFlags, + ) { const { ExpectedError } = await import('../../errors'); if (options.device == null && options.fleet == null) { diff --git a/lib/commands/config/inject.ts b/lib/commands/config/inject.ts index 58557571..6e344f70 100644 --- a/lib/commands/config/inject.ts +++ b/lib/commands/config/inject.ts @@ -15,21 +15,11 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getVisuals, stripIndent } from '../../utils/lazy'; -interface FlagsDef { - type?: string; - drive?: string; - help: void; -} - -interface ArgsDef { - file: string; -} - export default class ConfigInjectCmd extends Command { public static description = stripIndent` Inject a config.json file to a balenaOS image or attached media. @@ -46,17 +36,16 @@ export default class ConfigInjectCmd extends Command { '$ balena config inject my/config.json --drive /dev/disk2', ]; - public static args = [ - { - name: 'file', + public static args = { + file: Args.string({ description: 'the path to the config.json file to inject', required: true, - }, - ]; + }), + }; public static usage = 'config inject '; - public static flags: flags.Input = { + public static flags = { drive: cf.driveOrImg, help: cf.help, }; @@ -65,9 +54,7 @@ export default class ConfigInjectCmd extends Command { public static offlineCompatible = true; public async run() { - const { args: params, flags: options } = this.parse( - ConfigInjectCmd, - ); + const { args: params, flags: options } = await this.parse(ConfigInjectCmd); const { safeUmount } = await import('../../utils/umount'); diff --git a/lib/commands/config/read.ts b/lib/commands/config/read.ts index fae37747..333fea87 100644 --- a/lib/commands/config/read.ts +++ b/lib/commands/config/read.ts @@ -15,18 +15,10 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getVisuals, stripIndent } from '../../utils/lazy'; -interface FlagsDef { - type?: string; - drive?: string; - help: void; - json: boolean; -} - export default class ConfigReadCmd extends Command { public static description = stripIndent` Read the config.json file of a balenaOS image or attached media. @@ -46,7 +38,7 @@ export default class ConfigReadCmd extends Command { public static usage = 'config read'; - public static flags: flags.Input = { + public static flags = { drive: cf.driveOrImg, help: cf.help, json: cf.json, @@ -56,7 +48,7 @@ export default class ConfigReadCmd extends Command { public static offlineCompatible = true; public async run() { - const { flags: options } = this.parse(ConfigReadCmd); + const { flags: options } = await this.parse(ConfigReadCmd); const { safeUmount } = await import('../../utils/umount'); diff --git a/lib/commands/config/reconfigure.ts b/lib/commands/config/reconfigure.ts index 670d0e67..be9ab466 100644 --- a/lib/commands/config/reconfigure.ts +++ b/lib/commands/config/reconfigure.ts @@ -15,19 +15,11 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getVisuals, stripIndent } from '../../utils/lazy'; -interface FlagsDef { - type?: string; - drive?: string; - advanced: boolean; - help: void; - version?: string; -} - export default class ConfigReconfigureCmd extends Command { public static description = stripIndent` Interactively reconfigure a balenaOS image file or attached media. @@ -49,14 +41,14 @@ export default class ConfigReconfigureCmd extends Command { public static usage = 'config reconfigure'; - public static flags: flags.Input = { + public static flags = { drive: cf.driveOrImg, - advanced: flags.boolean({ + advanced: Flags.boolean({ description: 'show advanced commands', char: 'v', }), help: cf.help, - version: flags.string({ + version: Flags.string({ description: 'balenaOS version, for example "2.32.0" or "2.44.0+rev1"', }), }; @@ -65,7 +57,7 @@ export default class ConfigReconfigureCmd extends Command { public static root = true; public async run() { - const { flags: options } = this.parse(ConfigReconfigureCmd); + const { flags: options } = await this.parse(ConfigReconfigureCmd); const { safeUmount } = await import('../../utils/umount'); diff --git a/lib/commands/config/write.ts b/lib/commands/config/write.ts index 474bcbea..e1323f5f 100644 --- a/lib/commands/config/write.ts +++ b/lib/commands/config/write.ts @@ -15,22 +15,11 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getVisuals, stripIndent } from '../../utils/lazy'; -interface FlagsDef { - type?: string; - drive?: string; - help: void; -} - -interface ArgsDef { - key: string; - value: string; -} - export default class ConfigWriteCmd extends Command { public static description = stripIndent` Write a key-value pair to the config.json file of an OS image or attached media. @@ -48,22 +37,20 @@ export default class ConfigWriteCmd extends Command { '$ balena config write --drive balena.img os.network.connectivity.interval 300', ]; - public static args = [ - { - name: 'key', + public static args = { + key: Args.string({ description: 'the key of the config parameter to write', required: true, - }, - { - name: 'value', + }), + value: Args.string({ description: 'the value of the config parameter to write', required: true, - }, - ]; + }), + }; public static usage = 'config write '; - public static flags: flags.Input = { + public static flags = { drive: cf.driveOrImg, help: cf.help, }; @@ -72,9 +59,7 @@ export default class ConfigWriteCmd extends Command { public static offlineCompatible = true; public async run() { - const { args: params, flags: options } = this.parse( - ConfigWriteCmd, - ); + const { args: params, flags: options } = await this.parse(ConfigWriteCmd); const { denyMount, safeUmount } = await import('../../utils/umount'); diff --git a/lib/commands/deploy.ts b/lib/commands/deploy.ts index c44f7602..abc84683 100644 --- a/lib/commands/deploy.ts +++ b/lib/commands/deploy.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Args, Flags } from '@oclif/core'; import type { ImageDescriptor } from '@balena/compose/dist/parse'; import Command from '../command'; @@ -52,6 +52,9 @@ interface ApplicationWithArch { application_type: [Pick]; } +// TODO: For this special one we can't use Interfaces.InferredFlags/InferredArgs +// because of the 'registry-secrets' type which is defined in the actual code +// as a path (string | undefined) but then the cli turns it into an object interface FlagsDef extends ComposeCliFlags, DockerCliFlags { source?: string; build: boolean; @@ -62,11 +65,6 @@ interface FlagsDef extends ComposeCliFlags, DockerCliFlags { help: void; } -interface ArgsDef { - fleet: string; - image?: string; -} - export default class DeployCmd extends Command { public static description = `\ Deploy a single image or a multicontainer project to a balena fleet. @@ -105,31 +103,28 @@ ${dockerignoreHelp} '$ balena deploy myFleet myRepo/myImage --release-tag key1 "" key2 "value2 with spaces"', ]; - public static args = [ - ca.fleetRequired, - { - name: 'image', - description: 'the image to deploy', - }, - ]; + public static args = { + fleet: ca.fleetRequired, + image: Args.string({ description: 'the image to deploy' }), + }; public static usage = 'deploy [image]'; - public static flags: flags.Input = { - source: flags.string({ + public static flags = { + source: Flags.string({ description: 'specify an alternate source directory; default is the working directory', char: 's', }), - build: flags.boolean({ + build: Flags.boolean({ description: 'force a rebuild before deploy', char: 'b', }), - nologupload: flags.boolean({ + nologupload: Flags.boolean({ description: "don't upload build logs to the dashboard with image (if building)", }), - 'release-tag': flags.string({ + 'release-tag': Flags.string({ description: stripIndent` Set release tags if the image deployment is successful. Multiple arguments may be provided, alternating tag keys and values (see examples). @@ -137,7 +132,7 @@ ${dockerignoreHelp} `, multiple: true, }), - draft: flags.boolean({ + draft: Flags.boolean({ description: stripIndent` Deploy the release as a draft. Draft releases are ignored by the 'track latest' release policy but can be used through release pinning. @@ -145,12 +140,12 @@ ${dockerignoreHelp} as final by default unless this option is given.`, default: false, }), - note: flags.string({ description: 'The notes for this release' }), + note: Flags.string({ description: 'The notes for this release' }), ...composeCliFlags, ...dockerCliFlags, // NOTE: Not supporting -h for help, because of clash with -h in DockerCliFlags // Revisit this in future release. - help: flags.help({}), + help: Flags.help({}), }; public static authenticated = true; @@ -158,9 +153,7 @@ ${dockerignoreHelp} public static primary = true; public async run() { - const { args: params, flags: options } = this.parse( - DeployCmd, - ); + const { args: params, flags: options } = await this.parse(DeployCmd); (await import('events')).defaultMaxListeners = 1000; @@ -190,7 +183,7 @@ ${dockerignoreHelp} ); if (image) { - options['registry-secrets'] = await getRegistrySecrets( + (options as FlagsDef)['registry-secrets'] = await getRegistrySecrets( sdk, options['registry-secrets'], ); @@ -203,7 +196,7 @@ ${dockerignoreHelp} registrySecretsPath: options['registry-secrets'], }); options.dockerfile = dockerfilePath; - options['registry-secrets'] = registrySecrets; + (options as FlagsDef)['registry-secrets'] = registrySecrets; } const helpers = await import('../utils/helpers'); @@ -212,7 +205,7 @@ ${dockerignoreHelp} const dockerUtils = await import('../utils/docker'); const [docker, buildOpts, composeOpts] = await Promise.all([ dockerUtils.getDocker(options), - dockerUtils.generateBuildOpts(options), + dockerUtils.generateBuildOpts(options as FlagsDef), compose.generateOpts(options), ]); diff --git a/lib/commands/device/deactivate.ts b/lib/commands/device/deactivate.ts index beca1415..584758bd 100644 --- a/lib/commands/device/deactivate.ts +++ b/lib/commands/device/deactivate.ts @@ -15,21 +15,11 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; -import type { IArg } from '@oclif/parser/lib/args'; +import { Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -interface FlagsDef { - yes: boolean; - help: void; -} - -interface ArgsDef { - uuid: string; -} - export default class DeviceDeactivateCmd extends Command { public static description = stripIndent` Deactivate a device. @@ -44,17 +34,16 @@ export default class DeviceDeactivateCmd extends Command { '$ balena device deactivate 7cf02a6 --yes', ]; - public static args: Array> = [ - { - name: 'uuid', + public static args = { + uuid: Args.string({ description: 'the UUID of the device to be deactivated', required: true, - }, - ]; + }), + }; public static usage = 'device deactivate '; - public static flags: flags.Input = { + public static flags = { yes: cf.yes, help: cf.help, }; @@ -62,7 +51,7 @@ export default class DeviceDeactivateCmd extends Command { public static authenticated = true; public async run() { - const { args: params, flags: options } = this.parse( + const { args: params, flags: options } = await this.parse( DeviceDeactivateCmd, ); diff --git a/lib/commands/device/identify.ts b/lib/commands/device/identify.ts index 88537624..7766a088 100644 --- a/lib/commands/device/identify.ts +++ b/lib/commands/device/identify.ts @@ -15,21 +15,12 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; -import type { IArg } from '@oclif/parser/lib/args'; +import { Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { ExpectedError } from '../../errors'; -interface FlagsDef { - help: void; -} - -interface ArgsDef { - uuid: string; -} - export default class DeviceIdentifyCmd extends Command { public static description = stripIndent` Identify a device. @@ -38,24 +29,23 @@ export default class DeviceIdentifyCmd extends Command { `; public static examples = ['$ balena device identify 23c73a1']; - public static args: Array> = [ - { - name: 'uuid', + public static args = { + uuid: Args.string({ description: 'the uuid of the device to identify', required: true, - }, - ]; + }), + }; public static usage = 'device identify '; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; public static authenticated = true; public async run() { - const { args: params } = this.parse(DeviceIdentifyCmd); + const { args: params } = await this.parse(DeviceIdentifyCmd); const balena = getBalenaSdk(); diff --git a/lib/commands/device/index.ts b/lib/commands/device/index.ts index 2201977e..99c23769 100644 --- a/lib/commands/device/index.ts +++ b/lib/commands/device/index.ts @@ -15,8 +15,7 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; -import { IArg } from '@oclif/parser/lib/args'; +import { Flags, Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { expandForAppName } from '../../utils/helpers'; @@ -41,15 +40,6 @@ interface ExtendedDevice extends DeviceWithDeviceType { undervoltage_detected?: boolean; } -interface FlagsDef { - help: void; - view: boolean; -} - -interface ArgsDef { - uuid: string; -} - export default class DeviceCmd extends Command { public static description = stripIndent` Show info about a single device. @@ -61,19 +51,18 @@ export default class DeviceCmd extends Command { '$ balena device 7cf02a6 --view', ]; - public static args: Array> = [ - { - name: 'uuid', + public static args = { + uuid: Args.string({ description: 'the device uuid', required: true, - }, - ]; + }), + }; public static usage = 'device '; - public static flags: flags.Input = { + public static flags = { help: cf.help, - view: flags.boolean({ + view: Flags.boolean({ default: false, description: 'open device dashboard page', }), @@ -83,9 +72,7 @@ export default class DeviceCmd extends Command { public static primary = true; public async run() { - const { args: params, flags: options } = this.parse( - DeviceCmd, - ); + const { args: params, flags: options } = await this.parse(DeviceCmd); const balena = getBalenaSdk(); diff --git a/lib/commands/device/init.ts b/lib/commands/device/init.ts index d0d861b6..b60fe073 100644 --- a/lib/commands/device/init.ts +++ b/lib/commands/device/init.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; @@ -76,14 +76,14 @@ export default class DeviceInitCmd extends Command { public static usage = 'device init'; - public static flags: flags.Input = { + public static flags = { fleet: cf.fleet, yes: cf.yes, - advanced: flags.boolean({ + advanced: Flags.boolean({ char: 'v', description: 'show advanced configuration options', }), - 'os-version': flags.string({ + 'os-version': Flags.string({ description: stripIndent` exact version number, or a valid semver range, or 'latest' (includes pre-releases), @@ -93,13 +93,13 @@ export default class DeviceInitCmd extends Command { `, }), drive: cf.drive, - config: flags.string({ + config: Flags.string({ description: 'path to the config JSON file, see `balena os build-config`', }), - 'provisioning-key-name': flags.string({ + 'provisioning-key-name': Flags.string({ description: 'custom key name assigned to generated provisioning api key', }), - 'provisioning-key-expiry-date': flags.string({ + 'provisioning-key-expiry-date': Flags.string({ description: 'expiry date assigned to generated provisioning api key (format: YYYY-MM-DD)', }), @@ -109,7 +109,7 @@ export default class DeviceInitCmd extends Command { public static authenticated = true; public async run() { - const { flags: options } = this.parse(DeviceInitCmd); + const { flags: options } = await this.parse(DeviceInitCmd); // Imports const { promisify } = await import('util'); diff --git a/lib/commands/device/local-mode.ts b/lib/commands/device/local-mode.ts index f18fc31c..4a54fc49 100644 --- a/lib/commands/device/local-mode.ts +++ b/lib/commands/device/local-mode.ts @@ -15,23 +15,11 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; -import type { IArg } from '@oclif/parser/lib/args'; +import { Flags, Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -interface FlagsDef { - enable: boolean; - disable: boolean; - status: boolean; - help?: void; -} - -interface ArgsDef { - uuid: string | number; -} - export default class DeviceLocalModeCmd extends Command { public static description = stripIndent` Get or manage the local mode status for a device. @@ -47,26 +35,25 @@ export default class DeviceLocalModeCmd extends Command { '$ balena device local-mode 23c73a1 --status', ]; - public static args: Array> = [ - { - name: 'uuid', + public static args = { + uuid: Args.string({ description: 'the uuid of the device to manage', required: true, - }, - ]; + }), + }; public static usage = 'device local-mode '; - public static flags: flags.Input = { - enable: flags.boolean({ + public static flags = { + enable: Flags.boolean({ description: 'enable local mode', exclusive: ['disable', 'status'], }), - disable: flags.boolean({ + disable: Flags.boolean({ description: 'disable local mode', exclusive: ['enable', 'status'], }), - status: flags.boolean({ + status: Flags.boolean({ description: 'output boolean indicating local mode status', exclusive: ['enable', 'disable'], }), @@ -76,7 +63,7 @@ export default class DeviceLocalModeCmd extends Command { public static authenticated = true; public async run() { - const { args: params, flags: options } = this.parse( + const { args: params, flags: options } = await this.parse( DeviceLocalModeCmd, ); diff --git a/lib/commands/device/move.ts b/lib/commands/device/move.ts index b670ac5f..20e4c9af 100644 --- a/lib/commands/device/move.ts +++ b/lib/commands/device/move.ts @@ -15,8 +15,7 @@ * limitations under the License. */ -import type { flags } from '@oclif/command'; -import type { IArg } from '@oclif/parser/lib/args'; +import { Args } from '@oclif/core'; import type { BalenaSDK, Device, @@ -29,15 +28,6 @@ import { ExpectedError } from '../../errors'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { applicationIdInfo } from '../../utils/messages'; -interface FlagsDef { - fleet?: string; - help: void; -} - -interface ArgsDef { - uuid: string; -} - export default class DeviceMoveCmd extends Command { public static description = stripIndent` Move one or more devices to another fleet. @@ -56,18 +46,17 @@ export default class DeviceMoveCmd extends Command { '$ balena device move 7cf02a6 -f myorg/mynewfleet', ]; - public static args: Array> = [ - { - name: 'uuid', + public static args = { + uuid: Args.string({ description: 'comma-separated list (no blank spaces) of device UUIDs to be moved', required: true, - }, - ]; + }), + }; public static usage = 'device move '; - public static flags: flags.Input = { + public static flags = { fleet: cf.fleet, help: cf.help, }; @@ -102,9 +91,7 @@ export default class DeviceMoveCmd extends Command { } public async run() { - const { args: params, flags: options } = this.parse( - DeviceMoveCmd, - ); + const { args: params, flags: options } = await this.parse(DeviceMoveCmd); const balena = getBalenaSdk(); diff --git a/lib/commands/device/os-update.ts b/lib/commands/device/os-update.ts index fa0df5cd..82aa194d 100644 --- a/lib/commands/device/os-update.ts +++ b/lib/commands/device/os-update.ts @@ -15,24 +15,13 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; -import type { IArg } from '@oclif/parser/lib/args'; +import { Flags, Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy'; import type { Device } from 'balena-sdk'; import { ExpectedError } from '../../errors'; -interface FlagsDef { - version?: string; - yes: boolean; - help: void; -} - -interface ArgsDef { - uuid: string; -} - export default class DeviceOsUpdateCmd extends Command { public static description = stripIndent` Start a Host OS update for a device. @@ -50,18 +39,17 @@ export default class DeviceOsUpdateCmd extends Command { '$ balena device os-update 23c73a1 --version 2.31.0+rev1.prod', ]; - public static args: Array> = [ - { - name: 'uuid', + public static args = { + uuid: Args.string({ description: 'the uuid of the device to update', required: true, - }, - ]; + }), + }; public static usage = 'device os-update '; - public static flags: flags.Input = { - version: flags.string({ + public static flags = { + version: Flags.string({ description: 'a balenaOS version', }), yes: cf.yes, @@ -71,7 +59,7 @@ export default class DeviceOsUpdateCmd extends Command { public static authenticated = true; public async run() { - const { args: params, flags: options } = this.parse( + const { args: params, flags: options } = await this.parse( DeviceOsUpdateCmd, ); diff --git a/lib/commands/device/pin.ts b/lib/commands/device/pin.ts index 281364c4..51f582bf 100644 --- a/lib/commands/device/pin.ts +++ b/lib/commands/device/pin.ts @@ -15,22 +15,12 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; -import type { IArg } from '@oclif/parser/lib/args'; +import { Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { getExpandedProp } from '../../utils/pine'; -interface FlagsDef { - help: void; -} - -interface ArgsDef { - uuid: string; - releaseToPinTo?: string; -} - export default class DevicePinCmd extends Command { public static description = stripIndent` Pin a device to a release. @@ -44,28 +34,26 @@ export default class DevicePinCmd extends Command { '$ balena device pin 7cf02a6 91165e5', ]; - public static args: Array> = [ - { - name: 'uuid', + public static args = { + uuid: Args.string({ description: 'the uuid of the device to pin to a release', required: true, - }, - { - name: 'releaseToPinTo', + }), + releaseToPinTo: Args.string({ description: 'the commit of the release for the device to get pinned to', - }, - ]; + }), + }; public static usage = 'device pin [releaseToPinTo]'; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; public static authenticated = true; public async run() { - const { args: params } = this.parse(DevicePinCmd); + const { args: params } = await this.parse(DevicePinCmd); const balena = getBalenaSdk(); diff --git a/lib/commands/device/public-url.ts b/lib/commands/device/public-url.ts index a728501b..e7a3d475 100644 --- a/lib/commands/device/public-url.ts +++ b/lib/commands/device/public-url.ts @@ -15,24 +15,12 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; -import type { IArg } from '@oclif/parser/lib/args'; +import { Flags, Args } from '@oclif/core'; import Command from '../../command'; import { ExpectedError } from '../../errors'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -interface FlagsDef { - enable: boolean; - disable: boolean; - status: boolean; - help?: void; -} - -interface ArgsDef { - uuid: string; -} - export default class DevicePublicUrlCmd extends Command { public static description = stripIndent` Get or manage the public URL for a device. @@ -49,26 +37,25 @@ export default class DevicePublicUrlCmd extends Command { '$ balena device public-url 23c73a1 --status', ]; - public static args: Array> = [ - { - name: 'uuid', + public static args = { + uuid: Args.string({ description: 'the uuid of the device to manage', required: true, - }, - ]; + }), + }; public static usage = 'device public-url '; - public static flags: flags.Input = { - enable: flags.boolean({ + public static flags = { + enable: Flags.boolean({ description: 'enable the public URL', exclusive: ['disable', 'status'], }), - disable: flags.boolean({ + disable: Flags.boolean({ description: 'disable the public URL', exclusive: ['enable', 'status'], }), - status: flags.boolean({ + status: Flags.boolean({ description: 'determine if public URL is enabled', exclusive: ['enable', 'disable'], }), @@ -78,7 +65,7 @@ export default class DevicePublicUrlCmd extends Command { public static authenticated = true; public async run() { - const { args: params, flags: options } = this.parse( + const { args: params, flags: options } = await this.parse( DevicePublicUrlCmd, ); diff --git a/lib/commands/device/purge.ts b/lib/commands/device/purge.ts index 77456140..a40d00e6 100644 --- a/lib/commands/device/purge.ts +++ b/lib/commands/device/purge.ts @@ -15,20 +15,11 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; -import type { IArg } from '@oclif/parser/lib/args'; +import { Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy'; -interface FlagsDef { - help: void; -} - -interface ArgsDef { - uuid: string; -} - export default class DevicePurgeCmd extends Command { public static description = stripIndent` Purge data from a device. @@ -46,22 +37,21 @@ export default class DevicePurgeCmd extends Command { public static usage = 'device purge '; - public static args: Array> = [ - { - name: 'uuid', + public static args = { + uuid: Args.string({ description: 'comma-separated list (no blank spaces) of device UUIDs', required: true, - }, - ]; + }), + }; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; public static authenticated = true; public async run() { - const { args: params } = this.parse(DevicePurgeCmd); + const { args: params } = await this.parse(DevicePurgeCmd); const balena = getBalenaSdk(); const ux = getCliUx(); diff --git a/lib/commands/device/reboot.ts b/lib/commands/device/reboot.ts index f3934cd4..daef864b 100644 --- a/lib/commands/device/reboot.ts +++ b/lib/commands/device/reboot.ts @@ -15,21 +15,11 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; -import type { IArg } from '@oclif/parser/lib/args'; +import { Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -interface FlagsDef { - force: boolean; - help: void; -} - -interface ArgsDef { - uuid: string; -} - export default class DeviceRebootCmd extends Command { public static description = stripIndent` Restart a device. @@ -38,17 +28,16 @@ export default class DeviceRebootCmd extends Command { `; public static examples = ['$ balena device reboot 23c73a1']; - public static args: Array> = [ - { - name: 'uuid', + public static args = { + uuid: Args.string({ description: 'the uuid of the device to reboot', required: true, - }, - ]; + }), + }; public static usage = 'device reboot '; - public static flags: flags.Input = { + public static flags = { force: cf.force, help: cf.help, }; @@ -56,9 +45,7 @@ export default class DeviceRebootCmd extends Command { public static authenticated = true; public async run() { - const { args: params, flags: options } = this.parse( - DeviceRebootCmd, - ); + const { args: params, flags: options } = await this.parse(DeviceRebootCmd); const balena = getBalenaSdk(); diff --git a/lib/commands/device/register.ts b/lib/commands/device/register.ts index b3af039e..b3ad8c03 100644 --- a/lib/commands/device/register.ts +++ b/lib/commands/device/register.ts @@ -15,24 +15,13 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; -import type { IArg } from '@oclif/parser/lib/args'; +import { Flags } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import * as ca from '../../utils/common-args'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { applicationIdInfo } from '../../utils/messages'; -interface FlagsDef { - uuid?: string; - deviceType?: string; - help: void; -} - -interface ArgsDef { - fleet: string; -} - export default class DeviceRegisterCmd extends Command { public static description = stripIndent` Register a new device. @@ -51,16 +40,18 @@ export default class DeviceRegisterCmd extends Command { '$ balena device register myorg/myfleet --uuid --deviceType ', ]; - public static args: Array> = [ca.fleetRequired]; + public static args = { + fleet: ca.fleetRequired, + }; public static usage = 'device register '; - public static flags: flags.Input = { - uuid: flags.string({ + public static flags = { + uuid: Flags.string({ description: 'custom uuid', char: 'u', }), - deviceType: flags.string({ + deviceType: Flags.string({ description: "device type slug (run 'balena devices supported' for possible values)", }), @@ -70,7 +61,7 @@ export default class DeviceRegisterCmd extends Command { public static authenticated = true; public async run() { - const { args: params, flags: options } = this.parse( + const { args: params, flags: options } = await this.parse( DeviceRegisterCmd, ); diff --git a/lib/commands/device/rename.ts b/lib/commands/device/rename.ts index 31149578..be6c38f4 100644 --- a/lib/commands/device/rename.ts +++ b/lib/commands/device/rename.ts @@ -15,21 +15,11 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; -import type { IArg } from '@oclif/parser/lib/args'; +import { Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy'; -interface FlagsDef { - help: void; -} - -interface ArgsDef { - uuid: string; - newName?: string; -} - export default class DeviceRenameCmd extends Command { public static description = stripIndent` Rename a device. @@ -43,28 +33,26 @@ export default class DeviceRenameCmd extends Command { '$ balena device rename 7cf02a6 MyPi', ]; - public static args: Array> = [ - { - name: 'uuid', + public static args = { + uuid: Args.string({ description: 'the uuid of the device to rename', required: true, - }, - { - name: 'newName', + }), + newName: Args.string({ description: 'the new name for the device', - }, - ]; + }), + }; public static usage = 'device rename [newName]'; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; public static authenticated = true; public async run() { - const { args: params } = this.parse(DeviceRenameCmd); + const { args: params } = await this.parse(DeviceRenameCmd); const balena = getBalenaSdk(); diff --git a/lib/commands/device/restart.ts b/lib/commands/device/restart.ts index 9c8aad52..71f35f7b 100644 --- a/lib/commands/device/restart.ts +++ b/lib/commands/device/restart.ts @@ -15,8 +15,7 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; -import type { IArg } from '@oclif/parser/lib/args'; +import { Flags, Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy'; @@ -26,15 +25,6 @@ import type { CurrentServiceWithCommit, } from 'balena-sdk'; -interface FlagsDef { - help: void; - service?: string; -} - -interface ArgsDef { - uuid: string; -} - export default class DeviceRestartCmd extends Command { public static description = stripIndent` Restart containers on a device. @@ -55,19 +45,18 @@ export default class DeviceRestartCmd extends Command { '$ balena device restart 23c73a1 -s myService1,myService2', ]; - public static args: Array> = [ - { - name: 'uuid', + public static args = { + uuid: Args.string({ description: 'comma-separated list (no blank spaces) of device UUIDs to restart', required: true, - }, - ]; + }), + }; public static usage = 'device restart '; - public static flags: flags.Input = { - service: flags.string({ + public static flags = { + service: Flags.string({ description: 'comma-separated list (no blank spaces) of service names to restart', char: 's', @@ -78,9 +67,7 @@ export default class DeviceRestartCmd extends Command { public static authenticated = true; public async run() { - const { args: params, flags: options } = this.parse( - DeviceRestartCmd, - ); + const { args: params, flags: options } = await this.parse(DeviceRestartCmd); const balena = getBalenaSdk(); const ux = getCliUx(); diff --git a/lib/commands/device/rm.ts b/lib/commands/device/rm.ts index 53fb6960..6f3cc482 100644 --- a/lib/commands/device/rm.ts +++ b/lib/commands/device/rm.ts @@ -15,21 +15,11 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; -import type { IArg } from '@oclif/parser/lib/args'; +import { Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -interface FlagsDef { - yes: boolean; - help: void; -} - -interface ArgsDef { - uuid: string; -} - export default class DeviceRmCmd extends Command { public static description = stripIndent` Remove one or more devices. @@ -45,18 +35,17 @@ export default class DeviceRmCmd extends Command { '$ balena device rm 7cf02a6 --yes', ]; - public static args: Array> = [ - { - name: 'uuid', + public static args = { + uuid: Args.string({ description: 'comma-separated list (no blank spaces) of device UUIDs to be removed', required: true, - }, - ]; + }), + }; public static usage = 'device rm '; - public static flags: flags.Input = { + public static flags = { yes: cf.yes, help: cf.help, }; @@ -64,9 +53,7 @@ export default class DeviceRmCmd extends Command { public static authenticated = true; public async run() { - const { args: params, flags: options } = this.parse( - DeviceRmCmd, - ); + const { args: params, flags: options } = await this.parse(DeviceRmCmd); const balena = getBalenaSdk(); const patterns = await import('../../utils/patterns'); diff --git a/lib/commands/device/shutdown.ts b/lib/commands/device/shutdown.ts index 664c9133..b819a892 100644 --- a/lib/commands/device/shutdown.ts +++ b/lib/commands/device/shutdown.ts @@ -15,22 +15,12 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; -import type { IArg } from '@oclif/parser/lib/args'; +import { Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { ExpectedError } from '../../errors'; -interface FlagsDef { - force: boolean; - help: void; -} - -interface ArgsDef { - uuid: string; -} - export default class DeviceShutdownCmd extends Command { public static description = stripIndent` Shutdown a device. @@ -39,17 +29,16 @@ export default class DeviceShutdownCmd extends Command { `; public static examples = ['$ balena device shutdown 23c73a1']; - public static args: Array> = [ - { - name: 'uuid', + public static args = { + uuid: Args.string({ description: 'the uuid of the device to shutdown', required: true, - }, - ]; + }), + }; public static usage = 'device shutdown '; - public static flags: flags.Input = { + public static flags = { force: cf.force, help: cf.help, }; @@ -57,7 +46,7 @@ export default class DeviceShutdownCmd extends Command { public static authenticated = true; public async run() { - const { args: params, flags: options } = this.parse( + const { args: params, flags: options } = await this.parse( DeviceShutdownCmd, ); diff --git a/lib/commands/device/track-fleet.ts b/lib/commands/device/track-fleet.ts index 571e57c8..edb164ef 100644 --- a/lib/commands/device/track-fleet.ts +++ b/lib/commands/device/track-fleet.ts @@ -15,20 +15,11 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; -import type { IArg } from '@oclif/parser/lib/args'; +import { Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -interface FlagsDef { - help: void; -} - -interface ArgsDef { - uuid: string; -} - export default class DeviceTrackFleetCmd extends Command { public static description = stripIndent` Make a device track the fleet's pinned release. @@ -37,24 +28,23 @@ export default class DeviceTrackFleetCmd extends Command { `; public static examples = ['$ balena device track-fleet 7cf02a6']; - public static args: Array> = [ - { - name: 'uuid', + public static args = { + uuid: Args.string({ description: "the uuid of the device to make track the fleet's release", required: true, - }, - ]; + }), + }; public static usage = 'device track-fleet '; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; public static authenticated = true; public async run() { - const { args: params } = this.parse(DeviceTrackFleetCmd); + const { args: params } = await this.parse(DeviceTrackFleetCmd); const balena = getBalenaSdk(); diff --git a/lib/commands/devices/index.ts b/lib/commands/devices/index.ts index 9a028d25..e7196c42 100644 --- a/lib/commands/devices/index.ts +++ b/lib/commands/devices/index.ts @@ -15,7 +15,6 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { expandForAppName } from '../../utils/helpers'; @@ -24,12 +23,6 @@ import { applicationIdInfo, jsonInfo } from '../../utils/messages'; import type { Device, PineOptions } from 'balena-sdk'; -interface FlagsDef { - fleet?: string; - help: void; - json: boolean; -} - const devicesSelectFields = { $select: [ 'id', @@ -62,7 +55,7 @@ export default class DevicesCmd extends Command { public static usage = 'devices'; - public static flags: flags.Input = { + public static flags = { fleet: cf.fleet, json: cf.json, help: cf.help, @@ -73,7 +66,7 @@ export default class DevicesCmd extends Command { public static authenticated = true; public async run() { - const { flags: options } = this.parse(DevicesCmd); + const { flags: options } = await this.parse(DevicesCmd); const balena = getBalenaSdk(); const devicesOptions = { diff --git a/lib/commands/devices/supported.ts b/lib/commands/devices/supported.ts index e17e635e..93305a41 100644 --- a/lib/commands/devices/supported.ts +++ b/lib/commands/devices/supported.ts @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags } from '@oclif/core'; import type * as BalenaSdk from 'balena-sdk'; import * as _ from 'lodash'; import Command from '../../command'; @@ -23,11 +23,6 @@ import * as cf from '../../utils/common-flags'; import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy'; import { CommandHelp } from '../../utils/oclif-utils'; -interface FlagsDef { - help: void; - json?: boolean; -} - export default class DevicesSupportedCmd extends Command { public static description = stripIndent` List the supported device types (like 'raspberrypi3' or 'intel-nuc'). @@ -50,16 +45,16 @@ export default class DevicesSupportedCmd extends Command { new CommandHelp({ args: DevicesSupportedCmd.args }).defaultUsage() ).trim(); - public static flags: flags.Input = { + public static flags = { help: cf.help, - json: flags.boolean({ + json: Flags.boolean({ char: 'j', description: 'produce JSON output instead of tabular output', }), }; public async run() { - const { flags: options } = this.parse(DevicesSupportedCmd); + const { flags: options } = await this.parse(DevicesSupportedCmd); const pineOptions = { $select: ['slug', 'name'], $expand: { diff --git a/lib/commands/env/add.ts b/lib/commands/env/add.ts index cfbc17d4..318717da 100644 --- a/lib/commands/env/add.ts +++ b/lib/commands/env/add.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Args } from '@oclif/core'; import type * as BalenaSdk from 'balena-sdk'; import Command from '../../command'; import { ExpectedError } from '../../errors'; @@ -78,23 +78,21 @@ export default class EnvAddCmd extends Command { '$ balena env add EDITOR vim --device 7cf02a6,d6f1433 --service MyService,MyService2', ]; - public static args = [ - { - name: 'name', + public static args = { + name: Args.string({ required: true, description: 'environment or config variable name', - }, - { - name: 'value', + }), + value: Args.string({ required: false, description: "variable value; if omitted, use value from this process' environment", - }, - ]; + }), + }; public static usage = 'env add [value]'; - public static flags: flags.Input = { + public static flags = { fleet: { ...cf.fleet, exclusive: ['device'] }, device: { ...cf.device, exclusive: ['fleet'] }, help: cf.help, @@ -103,9 +101,7 @@ export default class EnvAddCmd extends Command { }; public async run() { - const { args: params, flags: options } = this.parse( - EnvAddCmd, - ); + const { args: params, flags: options } = await this.parse(EnvAddCmd); const cmd = this; if (!options.fleet && !options.device) { diff --git a/lib/commands/env/rename.ts b/lib/commands/env/rename.ts index 78f528e5..6f9a3b99 100644 --- a/lib/commands/env/rename.ts +++ b/lib/commands/env/rename.ts @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; @@ -22,20 +22,6 @@ import * as ec from '../../utils/env-common'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { parseAsInteger } from '../../utils/validation'; -type IArg = import('@oclif/parser').args.IArg; - -interface FlagsDef { - config: boolean; - device: boolean; - service: boolean; - help: void; -} - -interface ArgsDef { - id: number; - value: string; -} - export default class EnvRenameCmd extends Command { public static description = stripIndent` Change the value of a config or env var for a fleet, device or service. @@ -54,24 +40,22 @@ export default class EnvRenameCmd extends Command { '$ balena env rename 678678 1 --device --config', ]; - public static args: Array> = [ - { - name: 'id', + public static args = { + id: Args.integer({ required: true, description: "variable's numeric database ID", - parse: (input) => parseAsInteger(input, 'id'), - }, - { - name: 'value', + parse: async (input) => parseAsInteger(input, 'id'), + }), + value: Args.string({ required: true, description: "variable value; if omitted, use value from this process' environment", - }, - ]; + }), + }; public static usage = 'env rename '; - public static flags: flags.Input = { + public static flags = { config: ec.booleanConfig, device: ec.booleanDevice, service: ec.booleanService, @@ -79,9 +63,7 @@ export default class EnvRenameCmd extends Command { }; public async run() { - const { args: params, flags: opt } = this.parse( - EnvRenameCmd, - ); + const { args: params, flags: opt } = await this.parse(EnvRenameCmd); await Command.checkLoggedIn(); diff --git a/lib/commands/env/rm.ts b/lib/commands/env/rm.ts index 3435d329..d3589001 100644 --- a/lib/commands/env/rm.ts +++ b/lib/commands/env/rm.ts @@ -15,26 +15,13 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags, Args } from '@oclif/core'; import Command from '../../command'; import * as ec from '../../utils/env-common'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { parseAsInteger } from '../../utils/validation'; -type IArg = import('@oclif/parser').args.IArg; - -interface FlagsDef { - config: boolean; - device: boolean; - service: boolean; - yes: boolean; -} - -interface ArgsDef { - id: number; -} - export default class EnvRmCmd extends Command { public static description = stripIndent` Remove a config or env var from a fleet, device or service. @@ -57,22 +44,21 @@ export default class EnvRmCmd extends Command { '$ balena env rm 789789 --device --service --yes', ]; - public static args: Array> = [ - { - name: 'id', + public static args = { + id: Args.integer({ required: true, description: "variable's numeric database ID", - parse: (input) => parseAsInteger(input, 'id'), - }, - ]; + parse: async (input) => parseAsInteger(input, 'id'), + }), + }; public static usage = 'env rm '; - public static flags: flags.Input = { + public static flags = { config: ec.booleanConfig, device: ec.booleanDevice, service: ec.booleanService, - yes: flags.boolean({ + yes: Flags.boolean({ char: 'y', description: 'do not prompt for confirmation before deleting the variable', @@ -81,9 +67,7 @@ export default class EnvRmCmd extends Command { }; public async run() { - const { args: params, flags: opt } = this.parse( - EnvRmCmd, - ); + const { args: params, flags: opt } = await this.parse(EnvRmCmd); await Command.checkLoggedIn(); diff --git a/lib/commands/envs.ts b/lib/commands/envs.ts index ca1ab9d9..355c6f07 100644 --- a/lib/commands/envs.ts +++ b/lib/commands/envs.ts @@ -14,7 +14,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags } from '@oclif/core'; +import type { Interfaces } from '@oclif/core'; import type * as SDK from 'balena-sdk'; import * as _ from 'lodash'; import Command from '../command'; @@ -23,14 +24,7 @@ import * as cf from '../utils/common-flags'; import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy'; import { applicationIdInfo } from '../utils/messages'; -interface FlagsDef { - fleet?: string; - config: boolean; - device?: string; // device UUID - json: boolean; - help: void; - service?: string; // service name -} +type FlagsDef = Interfaces.InferredFlags; interface EnvironmentVariableInfo extends SDK.EnvironmentVariableBase { fleet?: string | null; // fleet slug @@ -102,9 +96,9 @@ export default class EnvsCmd extends Command { public static usage = 'envs'; - public static flags: flags.Input = { + public static flags = { fleet: { ...cf.fleet, exclusive: ['device'] }, - config: flags.boolean({ + config: Flags.boolean({ default: false, char: 'c', description: 'show configuration variables only', @@ -117,7 +111,7 @@ export default class EnvsCmd extends Command { }; public async run() { - const { flags: options } = this.parse(EnvsCmd); + const { flags: options } = await this.parse(EnvsCmd); const variables: EnvironmentVariableInfo[] = []; diff --git a/lib/commands/fleet/create.ts b/lib/commands/fleet/create.ts index dc3ad1bc..2d10c221 100644 --- a/lib/commands/fleet/create.ts +++ b/lib/commands/fleet/create.ts @@ -15,12 +15,11 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags, Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { stripIndent } from '../../utils/lazy'; -import { ArgsDef, FlagsDef } from '../../utils/application-create'; export default class FleetCreateCmd extends Command { public static description = stripIndent` @@ -50,22 +49,21 @@ export default class FleetCreateCmd extends Command { '$ balena fleet create MyFleet -o myorg --type raspberry-pi', ]; - public static args = [ - { - name: 'name', + public static args = { + name: Args.string({ description: 'fleet name', required: true, - }, - ]; + }), + }; public static usage = 'fleet create '; - public static flags: flags.Input = { - organization: flags.string({ + public static flags = { + organization: Flags.string({ char: 'o', description: 'handle of the organization the fleet should belong to', }), - type: flags.string({ + type: Flags.string({ char: 't', description: 'fleet device type (Check available types with `balena devices supported`)', @@ -76,9 +74,7 @@ export default class FleetCreateCmd extends Command { public static authenticated = true; public async run() { - const { args: params, flags: options } = this.parse( - FleetCreateCmd, - ); + const { args: params, flags: options } = await this.parse(FleetCreateCmd); await ( await import('../../utils/application-create') diff --git a/lib/commands/fleet/index.ts b/lib/commands/fleet/index.ts index 87b3989a..b59c135a 100644 --- a/lib/commands/fleet/index.ts +++ b/lib/commands/fleet/index.ts @@ -15,24 +15,13 @@ * limitations under the License. */ -import type { flags as flagsType } from '@oclif/command'; -import { flags } from '@oclif/command'; +import { Flags } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import * as ca from '../../utils/common-args'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { applicationIdInfo } from '../../utils/messages'; -import type { DataOutputOptions } from '../../framework'; - -interface FlagsDef extends DataOutputOptions { - help: void; - view: boolean; -} - -interface ArgsDef { - fleet: string; -} export default class FleetCmd extends Command { public static description = stripIndent` @@ -48,13 +37,15 @@ export default class FleetCmd extends Command { '$ balena fleet myorg/myfleet --view', ]; - public static args = [ca.fleetRequired]; + public static args = { + fleet: ca.fleetRequired, + }; public static usage = 'fleet '; - public static flags: flagsType.Input = { + public static flags = { help: cf.help, - view: flags.boolean({ + view: Flags.boolean({ default: false, description: 'open fleet dashboard page', }), @@ -65,9 +56,7 @@ export default class FleetCmd extends Command { public static primary = true; public async run() { - const { args: params, flags: options } = this.parse( - FleetCmd, - ); + const { args: params, flags: options } = await this.parse(FleetCmd); const { getApplication } = await import('../../utils/sdk'); diff --git a/lib/commands/fleet/pin.ts b/lib/commands/fleet/pin.ts index b0b81172..2b9f6cc1 100644 --- a/lib/commands/fleet/pin.ts +++ b/lib/commands/fleet/pin.ts @@ -15,22 +15,12 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; -import type { IArg } from '@oclif/parser/lib/args'; +import { Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { getExpandedProp } from '../../utils/pine'; -interface FlagsDef { - help: void; -} - -interface ArgsDef { - slug: string; - releaseToPinTo?: string; -} - export default class FleetPinCmd extends Command { public static description = stripIndent` Pin a fleet to a release. @@ -44,28 +34,26 @@ export default class FleetPinCmd extends Command { '$ balena fleet pin myorg/myfleet 91165e5', ]; - public static args: Array> = [ - { - name: 'slug', + public static args = { + slug: Args.string({ description: 'the slug of the fleet to pin to a release', required: true, - }, - { - name: 'releaseToPinTo', + }), + releaseToPinTo: Args.string({ description: 'the commit of the release for the fleet to get pinned to', - }, - ]; + }), + }; public static usage = 'fleet pin [releaseToPinTo]'; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; public static authenticated = true; public async run() { - const { args: params } = this.parse(FleetPinCmd); + const { args: params } = await this.parse(FleetPinCmd); const balena = getBalenaSdk(); diff --git a/lib/commands/fleet/purge.ts b/lib/commands/fleet/purge.ts index 1f577e08..5456ea0a 100644 --- a/lib/commands/fleet/purge.ts +++ b/lib/commands/fleet/purge.ts @@ -15,22 +15,12 @@ * limitations under the License. */ -import type { flags } from '@oclif/command'; - import Command from '../../command'; import * as cf from '../../utils/common-flags'; import * as ca from '../../utils/common-args'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { applicationIdInfo } from '../../utils/messages'; -interface FlagsDef { - help: void; -} - -interface ArgsDef { - fleet: string; -} - export default class FleetPurgeCmd extends Command { public static description = stripIndent` Purge data from a fleet. @@ -46,18 +36,20 @@ export default class FleetPurgeCmd extends Command { '$ balena fleet purge myorg/myfleet', ]; - public static args = [ca.fleetRequired]; + public static args = { + fleet: ca.fleetRequired, + }; public static usage = 'fleet purge '; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; public static authenticated = true; public async run() { - const { args: params } = this.parse(FleetPurgeCmd); + const { args: params } = await this.parse(FleetPurgeCmd); const { getApplication } = await import('../../utils/sdk'); diff --git a/lib/commands/fleet/rename.ts b/lib/commands/fleet/rename.ts index 23e0f183..9214d2e6 100644 --- a/lib/commands/fleet/rename.ts +++ b/lib/commands/fleet/rename.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import type { flags } from '@oclif/command'; +import { Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; @@ -23,15 +23,6 @@ import * as ca from '../../utils/common-args'; import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy'; import { applicationIdInfo } from '../../utils/messages'; -interface FlagsDef { - help: void; -} - -interface ArgsDef { - fleet: string; - newName?: string; -} - export default class FleetRenameCmd extends Command { public static description = stripIndent` Rename a fleet. @@ -50,24 +41,23 @@ export default class FleetRenameCmd extends Command { '$ balena fleet rename myorg/oldname NewName', ]; - public static args = [ - ca.fleetRequired, - { - name: 'newName', + public static args = { + fleet: ca.fleetRequired, + newName: Args.string({ description: 'the new name for the fleet', - }, - ]; + }), + }; public static usage = 'fleet rename [newName]'; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; public static authenticated = true; public async run() { - const { args: params } = this.parse(FleetRenameCmd); + const { args: params } = await this.parse(FleetRenameCmd); const { validateApplicationName } = await import('../../utils/validation'); const { ExpectedError } = await import('../../errors'); diff --git a/lib/commands/fleet/restart.ts b/lib/commands/fleet/restart.ts index f4838ccf..f9a8c4b6 100644 --- a/lib/commands/fleet/restart.ts +++ b/lib/commands/fleet/restart.ts @@ -15,22 +15,12 @@ * limitations under the License. */ -import type { flags } from '@oclif/command'; - import Command from '../../command'; import * as cf from '../../utils/common-flags'; import * as ca from '../../utils/common-args'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { applicationIdInfo } from '../../utils/messages'; -interface FlagsDef { - help: void; -} - -interface ArgsDef { - fleet: string; -} - export default class FleetRestartCmd extends Command { public static description = stripIndent` Restart a fleet. @@ -45,18 +35,20 @@ export default class FleetRestartCmd extends Command { '$ balena fleet restart myorg/myfleet', ]; - public static args = [ca.fleetRequired]; + public static args = { + fleet: ca.fleetRequired, + }; public static usage = 'fleet restart '; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; public static authenticated = true; public async run() { - const { args: params } = this.parse(FleetRestartCmd); + const { args: params } = await this.parse(FleetRestartCmd); const { getApplication } = await import('../../utils/sdk'); diff --git a/lib/commands/fleet/rm.ts b/lib/commands/fleet/rm.ts index f47acb02..e75d1c04 100644 --- a/lib/commands/fleet/rm.ts +++ b/lib/commands/fleet/rm.ts @@ -15,23 +15,12 @@ * limitations under the License. */ -import type { flags } from '@oclif/command'; - import Command from '../../command'; import * as cf from '../../utils/common-flags'; import * as ca from '../../utils/common-args'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { applicationIdInfo } from '../../utils/messages'; -interface FlagsDef { - yes: boolean; - help: void; -} - -interface ArgsDef { - fleet: string; -} - export default class FleetRmCmd extends Command { public static description = stripIndent` Remove a fleet. @@ -49,11 +38,13 @@ export default class FleetRmCmd extends Command { '$ balena fleet rm myorg/myfleet', ]; - public static args = [ca.fleetRequired]; + public static args = { + fleet: ca.fleetRequired, + }; public static usage = 'fleet rm '; - public static flags: flags.Input = { + public static flags = { yes: cf.yes, help: cf.help, }; @@ -61,9 +52,7 @@ export default class FleetRmCmd extends Command { public static authenticated = true; public async run() { - const { args: params, flags: options } = this.parse( - FleetRmCmd, - ); + const { args: params, flags: options } = await this.parse(FleetRmCmd); const { confirm } = await import('../../utils/patterns'); const { getApplication } = await import('../../utils/sdk'); diff --git a/lib/commands/fleet/track-latest.ts b/lib/commands/fleet/track-latest.ts index 28048f18..e05a9365 100644 --- a/lib/commands/fleet/track-latest.ts +++ b/lib/commands/fleet/track-latest.ts @@ -15,20 +15,11 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; -import type { IArg } from '@oclif/parser/lib/args'; +import { Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -interface FlagsDef { - help: void; -} - -interface ArgsDef { - slug: string; -} - export default class FleetTrackLatestCmd extends Command { public static description = stripIndent` Make this fleet track the latest release. @@ -40,24 +31,23 @@ export default class FleetTrackLatestCmd extends Command { '$ balena fleet track-latest myfleet', ]; - public static args: Array> = [ - { - name: 'slug', + public static args = { + slug: Args.string({ description: 'the slug of the fleet to make track the latest release', required: true, - }, - ]; + }), + }; public static usage = 'fleet track-latest '; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; public static authenticated = true; public async run() { - const { args: params } = this.parse(FleetTrackLatestCmd); + const { args: params } = await this.parse(FleetTrackLatestCmd); const balena = getBalenaSdk(); diff --git a/lib/commands/fleets.ts b/lib/commands/fleets.ts index 6146009c..a4b0a194 100644 --- a/lib/commands/fleets.ts +++ b/lib/commands/fleets.ts @@ -16,12 +16,10 @@ */ import type * as BalenaSdk from 'balena-sdk'; -import { flags } from '@oclif/command'; import Command from '../command'; import * as cf from '../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../utils/lazy'; -import type { DataSetOutputOptions } from '../framework'; interface ExtendedApplication extends ApplicationWithDeviceTypeSlug { device_count: number; @@ -29,11 +27,6 @@ interface ExtendedApplication extends ApplicationWithDeviceTypeSlug { device_type?: string; } -interface FlagsDef extends DataSetOutputOptions { - help: void; - verbose?: boolean; -} - export default class FleetsCmd extends Command { public static description = stripIndent` List all fleets. @@ -48,7 +41,7 @@ export default class FleetsCmd extends Command { public static usage = 'fleets'; - public static flags: flags.Input = { + public static flags = { ...cf.dataSetOutputFlags, help: cf.help, }; @@ -57,7 +50,7 @@ export default class FleetsCmd extends Command { public static primary = true; public async run() { - const { flags: options } = this.parse(FleetsCmd); + const { flags: options } = await this.parse(FleetsCmd); const balena = getBalenaSdk(); diff --git a/lib/commands/internal/osinit.ts b/lib/commands/internal/osinit.ts index 197202be..8bc12992 100644 --- a/lib/commands/internal/osinit.ts +++ b/lib/commands/internal/osinit.ts @@ -15,6 +15,7 @@ * limitations under the License. */ +import { Args } from '@oclif/core'; import Command from '../../command'; import { stripIndent } from '../../utils/lazy'; import { CommandHelp } from '../../utils/oclif-utils'; @@ -27,12 +28,6 @@ import { CommandHelp } from '../../utils/oclif-utils'; // - https://github.com/balena-io/balena-cli/pull/1455#discussion_r334308357 // - https://github.com/balena-io/balena-cli/pull/1455#discussion_r334308526 -interface ArgsDef { - image: string; - type: string; - config: string; -} - export default class OsinitCmd extends Command { public static description = stripIndent` Do actual init of the device with the preconfigured os image. @@ -41,20 +36,17 @@ export default class OsinitCmd extends Command { Use \`balena os initialize \` instead. `; - public static args = [ - { - name: 'image', + public static args = { + image: Args.string({ required: true, - }, - { - name: 'type', + }), + type: Args.string({ required: true, - }, - { - name: 'config', + }), + config: Args.string({ required: true, - }, - ]; + }), + }; public static usage = ( 'internal osinit ' + @@ -66,7 +58,7 @@ export default class OsinitCmd extends Command { public static offlineCompatible = true; public async run() { - const { args: params } = this.parse<{}, ArgsDef>(OsinitCmd); + const { args: params } = await this.parse(OsinitCmd); const config = JSON.parse(params.config); diff --git a/lib/commands/join.ts b/lib/commands/join.ts index 93d86343..a483bed8 100644 --- a/lib/commands/join.ts +++ b/lib/commands/join.ts @@ -15,23 +15,13 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Args, Flags } from '@oclif/core'; import Command from '../command'; import * as cf from '../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../utils/lazy'; import { applicationIdInfo } from '../utils/messages'; import { parseAsLocalHostnameOrIp } from '../utils/validation'; -interface FlagsDef { - fleet?: string; - pollInterval?: number; - help?: void; -} - -interface ArgsDef { - deviceIpOrHostname?: string; -} - export default class JoinCmd extends Command { public static description = stripIndent` Move a local device to a fleet on another balena server. @@ -63,20 +53,19 @@ export default class JoinCmd extends Command { '$ balena join 192.168.1.25 --fleet MyFleet', ]; - public static args = [ - { - name: 'deviceIpOrHostname', + public static args = { + deviceIpOrHostname: Args.string({ description: 'the IP or hostname of device', parse: parseAsLocalHostnameOrIp, - }, - ]; + }), + }; // Hardcoded to preserve camelcase public static usage = 'join [deviceIpOrHostname]'; - public static flags: flags.Input = { + public static flags = { fleet: cf.fleet, - pollInterval: flags.integer({ + pollInterval: Flags.integer({ description: 'the interval in minutes to check for updates', char: 'i', }), @@ -87,9 +76,7 @@ export default class JoinCmd extends Command { public static primary = true; public async run() { - const { args: params, flags: options } = this.parse( - JoinCmd, - ); + const { args: params, flags: options } = await this.parse(JoinCmd); const promote = await import('../utils/promote'); const sdk = getBalenaSdk(); diff --git a/lib/commands/key/add.ts b/lib/commands/key/add.ts index 34437f9f..797c251b 100644 --- a/lib/commands/key/add.ts +++ b/lib/commands/key/add.ts @@ -15,21 +15,12 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Args } from '@oclif/core'; import Command from '../../command'; import { ExpectedError } from '../../errors'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -interface FlagsDef { - help: void; -} - -interface ArgsDef { - name: string; - path: string; -} - export default class KeyAddCmd extends Command { public static description = stripIndent` Add an SSH key to balenaCloud. @@ -60,21 +51,19 @@ export default class KeyAddCmd extends Command { '$ balena key add Main %userprofile%.sshid_rsa.pub', ]; - public static args = [ - { - name: 'name', + public static args = { + name: Args.string({ description: 'the SSH key name', required: true, - }, - { - name: `path`, + }), + path: Args.string({ description: `the path to the public key file`, - }, - ]; + }), + }; public static usage = 'key add [path]'; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; @@ -83,7 +72,7 @@ export default class KeyAddCmd extends Command { public static readStdin = true; public async run() { - const { args: params } = this.parse(KeyAddCmd); + const { args: params } = await this.parse(KeyAddCmd); let key: string; if (params.path != null) { diff --git a/lib/commands/key/index.ts b/lib/commands/key/index.ts index dcc03e77..895e7451 100644 --- a/lib/commands/key/index.ts +++ b/lib/commands/key/index.ts @@ -15,22 +15,12 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy'; import { parseAsInteger } from '../../utils/validation'; -type IArg = import('@oclif/parser').args.IArg; - -interface FlagsDef { - help: void; -} - -interface ArgsDef { - id: number; -} - export default class KeyCmd extends Command { public static description = stripIndent` Display an SSH key. @@ -40,25 +30,24 @@ export default class KeyCmd extends Command { public static examples = ['$ balena key 17']; - public static args: Array> = [ - { - name: 'id', + public static args = { + id: Args.integer({ description: 'balenaCloud ID for the SSH key', - parse: (x) => parseAsInteger(x, 'id'), + parse: async (x) => parseAsInteger(x, 'id'), required: true, - }, - ]; + }), + }; public static usage = 'key '; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; public static authenticated = true; public async run() { - const { args: params } = this.parse<{}, ArgsDef>(KeyCmd); + const { args: params } = await this.parse(KeyCmd); const key = await getBalenaSdk().models.key.get(params.id); diff --git a/lib/commands/key/rm.ts b/lib/commands/key/rm.ts index 320d9edf..5e0ad100 100644 --- a/lib/commands/key/rm.ts +++ b/lib/commands/key/rm.ts @@ -15,23 +15,12 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { parseAsInteger } from '../../utils/validation'; -type IArg = import('@oclif/parser').args.IArg; - -interface FlagsDef { - yes: boolean; - help: void; -} - -interface ArgsDef { - id: number; -} - export default class KeyRmCmd extends Command { public static description = stripIndent` Remove an SSH key from balenaCloud. @@ -43,18 +32,17 @@ export default class KeyRmCmd extends Command { public static examples = ['$ balena key rm 17', '$ balena key rm 17 --yes']; - public static args: Array> = [ - { - name: 'id', + public static args = { + id: Args.integer({ description: 'balenaCloud ID for the SSH key', - parse: (x) => parseAsInteger(x, 'id'), + parse: async (x) => parseAsInteger(x, 'id'), required: true, - }, - ]; + }), + }; public static usage = 'key rm '; - public static flags: flags.Input = { + public static flags = { yes: cf.yes, help: cf.help, }; @@ -62,9 +50,7 @@ export default class KeyRmCmd extends Command { public static authenticated = true; public async run() { - const { args: params, flags: options } = this.parse( - KeyRmCmd, - ); + const { args: params, flags: options } = await this.parse(KeyRmCmd); const patterns = await import('../../utils/patterns'); diff --git a/lib/commands/keys.ts b/lib/commands/keys.ts index 0fd0fa5b..fa8db893 100644 --- a/lib/commands/keys.ts +++ b/lib/commands/keys.ts @@ -15,15 +15,10 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; import Command from '../command'; import * as cf from '../utils/common-flags'; import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy'; -interface FlagsDef { - help: void; -} - export default class KeysCmd extends Command { public static description = stripIndent` List the SSH keys in balenaCloud. @@ -34,14 +29,14 @@ export default class KeysCmd extends Command { public static usage = 'keys'; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; public static authenticated = true; public async run() { - this.parse(KeysCmd); + await this.parse(KeysCmd); const keys = await getBalenaSdk().models.key.getAll(); diff --git a/lib/commands/leave.ts b/lib/commands/leave.ts index b2bf3e06..46a08c2a 100644 --- a/lib/commands/leave.ts +++ b/lib/commands/leave.ts @@ -15,20 +15,12 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Args } from '@oclif/core'; import Command from '../command'; import * as cf from '../utils/common-flags'; import { stripIndent } from '../utils/lazy'; import { parseAsLocalHostnameOrIp } from '../utils/validation'; -interface FlagsDef { - help?: void; -} - -interface ArgsDef { - deviceIpOrHostname?: string; -} - export default class LeaveCmd extends Command { public static description = stripIndent` Remove a local device from its balena fleet. @@ -51,17 +43,16 @@ export default class LeaveCmd extends Command { '$ balena leave 192.168.1.25', ]; - public static args = [ - { - name: 'deviceIpOrHostname', + public static args = { + deviceIpOrHostname: Args.string({ description: 'the device IP or hostname', parse: parseAsLocalHostnameOrIp, - }, - ]; + }), + }; public static usage = 'leave [deviceIpOrHostname]'; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; @@ -69,7 +60,7 @@ export default class LeaveCmd extends Command { public static primary = true; public async run() { - const { args: params } = this.parse(LeaveCmd); + const { args: params } = await this.parse(LeaveCmd); const promote = await import('../utils/promote'); const logger = await Command.getLogger(); diff --git a/lib/commands/local/configure.ts b/lib/commands/local/configure.ts index 6cb15474..53aa170d 100644 --- a/lib/commands/local/configure.ts +++ b/lib/commands/local/configure.ts @@ -15,20 +15,12 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Args } from '@oclif/core'; import { promisify } from 'util'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { stripIndent } from '../../utils/lazy'; -interface FlagsDef { - help: void; -} - -interface ArgsDef { - target: string; -} - export default class LocalConfigureCmd extends Command { public static description = stripIndent` (Re)configure a balenaOS drive or image. @@ -41,17 +33,16 @@ export default class LocalConfigureCmd extends Command { '$ balena local configure path/to/image.img', ]; - public static args = [ - { - name: 'target', + public static args = { + target: Args.string({ description: 'path of drive or image to configure', required: true, - }, - ]; + }), + }; public static usage = 'local configure '; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; @@ -59,7 +50,7 @@ export default class LocalConfigureCmd extends Command { public static offlineCompatible = true; public async run() { - const { args: params } = this.parse(LocalConfigureCmd); + const { args: params } = await this.parse(LocalConfigureCmd); const reconfix = await import('reconfix'); const { denyMount, safeUmount } = await import('../../utils/umount'); diff --git a/lib/commands/local/flash.ts b/lib/commands/local/flash.ts index cb2f445e..e2301718 100644 --- a/lib/commands/local/flash.ts +++ b/lib/commands/local/flash.ts @@ -15,23 +15,13 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Args } from '@oclif/core'; import type { BlockDevice } from 'etcher-sdk/build/source-destination'; import Command from '../../command'; import { ExpectedError } from '../../errors'; import * as cf from '../../utils/common-flags'; import { getChalk, getVisuals, stripIndent } from '../../utils/lazy'; -interface FlagsDef { - yes: boolean; - drive?: string; - help: void; -} - -interface ArgsDef { - image: string; -} - export default class LocalFlashCmd extends Command { public static description = stripIndent` Flash an image to a drive. @@ -49,17 +39,16 @@ export default class LocalFlashCmd extends Command { '$ balena local flash path/to/balenaos.img --drive /dev/disk2 --yes', ]; - public static args = [ - { - name: 'image', + public static args = { + image: Args.string({ description: 'path to OS image', required: true, - }, - ]; + }), + }; public static usage = 'local flash '; - public static flags: flags.Input = { + public static flags = { drive: cf.drive, yes: cf.yes, help: cf.help, @@ -68,9 +57,7 @@ export default class LocalFlashCmd extends Command { public static offlineCompatible = true; public async run() { - const { args: params, flags: options } = this.parse( - LocalFlashCmd, - ); + const { args: params, flags: options } = await this.parse(LocalFlashCmd); if (process.platform === 'linux') { const { promisify } = await import('util'); diff --git a/lib/commands/login.ts b/lib/commands/login.ts index 4e8ce038..e8c2211c 100644 --- a/lib/commands/login.ts +++ b/lib/commands/login.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags, Args } from '@oclif/core'; import Command from '../command'; import * as cf from '../utils/common-flags'; import { getBalenaSdk, stripIndent, getCliForm } from '../utils/lazy'; @@ -34,10 +34,6 @@ interface FlagsDef { hideExperimentalWarning: boolean; } -interface ArgsDef { - token?: string; -} - export default class LoginCmd extends Command { public static description = stripIndent` Login to balena. @@ -61,37 +57,34 @@ export default class LoginCmd extends Command { '$ balena login --credentials --email johndoe@gmail.com --password secret', ]; - public static args = [ - { - // Capitano allowed -t to be type boolean|string, which oclif does not. - // So -t is now bool, and we check first arg for token content. - name: 'token', + public static args = { + token: Args.string({ hidden: true, - }, - ]; + }), + }; public static usage = 'login'; - public static flags: flags.Input = { - web: flags.boolean({ + public static flags = { + web: Flags.boolean({ default: false, char: 'w', description: 'web-based login', exclusive: ['token', 'credentials'], }), - token: flags.boolean({ + token: Flags.boolean({ default: false, char: 't', description: 'session token or API key', exclusive: ['web', 'credentials'], }), - credentials: flags.boolean({ + credentials: Flags.boolean({ default: false, char: 'c', description: 'credential-based login', exclusive: ['web', 'token'], }), - email: flags.string({ + email: Flags.string({ char: 'e', description: 'email', exclusive: ['user'], @@ -99,24 +92,24 @@ export default class LoginCmd extends Command { }), // Capitano version of this command had a second alias for email, 'u'. // Using an oclif hidden flag to support the same behaviour. - user: flags.string({ + user: Flags.string({ char: 'u', hidden: true, exclusive: ['email'], dependsOn: ['credentials'], }), - password: flags.string({ + password: Flags.string({ char: 'p', description: 'password', dependsOn: ['credentials'], }), - port: flags.integer({ + port: Flags.integer({ char: 'P', description: 'TCP port number of local HTTP login server (--web auth only)', dependsOn: ['web'], }), - hideExperimentalWarning: flags.boolean({ + hideExperimentalWarning: Flags.boolean({ char: 'H', default: false, description: 'Hides warning for experimental features', @@ -127,9 +120,7 @@ export default class LoginCmd extends Command { public static primary = true; public async run() { - const { flags: options, args: params } = this.parse( - LoginCmd, - ); + const { flags: options, args: params } = await this.parse(LoginCmd); const balena = getBalenaSdk(); const messages = await import('../utils/messages'); diff --git a/lib/commands/logout.ts b/lib/commands/logout.ts index 499ae816..89d8c727 100644 --- a/lib/commands/logout.ts +++ b/lib/commands/logout.ts @@ -29,7 +29,7 @@ export default class LogoutCmd extends Command { public static usage = 'logout'; public async run() { - this.parse<{}, {}>(LogoutCmd); + await this.parse(LogoutCmd); await getBalenaSdk().auth.logout(); } } diff --git a/lib/commands/logs.ts b/lib/commands/logs.ts index 1a083ae5..ef0f1204 100644 --- a/lib/commands/logs.ts +++ b/lib/commands/logs.ts @@ -15,24 +15,11 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags, Args } from '@oclif/core'; import Command from '../command'; import * as cf from '../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../utils/lazy'; import { LogMessage } from 'balena-sdk'; -import { IArg } from '@oclif/parser/lib/args'; - -interface FlagsDef { - 'max-retry'?: number; - tail?: boolean; - service?: string[]; - system?: boolean; - help: void; -} - -interface ArgsDef { - device: string; -} const MAX_RETRY = 1000; @@ -67,35 +54,34 @@ export default class LogsCmd extends Command { '$ balena logs 23c73a1.local --system --service my-service', ]; - public static args: Array> = [ - { - name: 'device', + public static args = { + device: Args.string({ description: 'device UUID, IP, or .local address', required: true, - }, - ]; + }), + }; public static usage = 'logs '; - public static flags: flags.Input = { - 'max-retry': flags.integer({ + public static flags = { + 'max-retry': Flags.integer({ description: stripIndent` Maximum number of reconnection attempts on "connection lost" errors (use 0 to disable auto reconnection).`, }), - tail: flags.boolean({ + tail: Flags.boolean({ default: false, description: 'continuously stream output', char: 't', }), - service: flags.string({ + service: Flags.string({ description: stripIndent` Reject logs not originating from this service. This can be used in combination with --system or other --service flags.`, char: 's', multiple: true, }), - system: flags.boolean({ + system: Flags.boolean({ default: false, description: 'Only show system logs. This can be used in combination with --service.', @@ -107,9 +93,7 @@ export default class LogsCmd extends Command { public static primary = true; public async run() { - const { args: params, flags: options } = this.parse( - LogsCmd, - ); + const { args: params, flags: options } = await this.parse(LogsCmd); const balena = getBalenaSdk(); const { serviceIdToName } = await import('../utils/cloud'); diff --git a/lib/commands/note.ts b/lib/commands/note.ts index 14f914cf..8afd2036 100644 --- a/lib/commands/note.ts +++ b/lib/commands/note.ts @@ -15,22 +15,12 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags, Args } from '@oclif/core'; import Command from '../command'; import { ExpectedError } from '../errors'; import * as cf from '../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../utils/lazy'; -interface FlagsDef { - device?: string; // device UUID - dev?: string; // Alias for device. - help: void; -} - -interface ArgsDef { - note: string; -} - export default class NoteCmd extends Command { public static description = stripIndent` Set a device note. @@ -46,18 +36,17 @@ export default class NoteCmd extends Command { '$ cat note.txt | balena note --device 7cf02a6', ]; - public static args = [ - { - name: 'note', + public static args = { + note: Args.string({ description: 'note content', - }, - ]; + }), + }; public static usage = 'note <|note>'; - public static flags: flags.Input = { + public static flags = { device: { exclusive: ['dev'], ...cf.device }, - dev: flags.string({ + dev: Flags.string({ exclusive: ['device'], hidden: true, }), @@ -69,9 +58,7 @@ export default class NoteCmd extends Command { public static readStdin = true; public async run() { - const { args: params, flags: options } = this.parse( - NoteCmd, - ); + const { args: params, flags: options } = await this.parse(NoteCmd); params.note = params.note || this.stdin; diff --git a/lib/commands/orgs.ts b/lib/commands/orgs.ts index e91db61c..b05ffa40 100644 --- a/lib/commands/orgs.ts +++ b/lib/commands/orgs.ts @@ -15,15 +15,10 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; import Command from '../command'; import * as cf from '../utils/common-flags'; import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy'; -interface FlagsDef { - help: void; -} - export default class OrgsCmd extends Command { public static description = stripIndent` List all organizations. @@ -34,14 +29,14 @@ export default class OrgsCmd extends Command { public static usage = 'orgs'; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; public static authenticated = true; public async run() { - this.parse(OrgsCmd); + await this.parse(OrgsCmd); const { getOwnOrganizations } = await import('../utils/sdk'); diff --git a/lib/commands/os/build-config.ts b/lib/commands/os/build-config.ts index 55a51312..0b6d954a 100644 --- a/lib/commands/os/build-config.ts +++ b/lib/commands/os/build-config.ts @@ -15,24 +15,13 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags, Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getCliForm, stripIndent } from '../../utils/lazy'; import * as _ from 'lodash'; import type { DeviceTypeJson } from 'balena-sdk'; -interface FlagsDef { - advanced: boolean; - output: string; - help: void; -} - -interface ArgsDef { - image: string; - 'device-type': string; -} - export default class OsBuildConfigCmd extends Command { public static description = stripIndent` Prepare a configuration file for use by the 'os configure' command. @@ -46,27 +35,25 @@ export default class OsBuildConfigCmd extends Command { '$ balena os configure ../path/rpi3.img --device 7cf02a6 --config rpi3-config.json', ]; - public static args = [ - { - name: 'image', + public static args = { + image: Args.string({ description: 'os image', required: true, - }, - { - name: 'device-type', + }), + 'device-type': Args.string({ description: 'device type', required: true, - }, - ]; + }), + }; public static usage = 'os build-config '; - public static flags: flags.Input = { - advanced: flags.boolean({ + public static flags = { + advanced: Flags.boolean({ description: 'show advanced configuration options', char: 'v', }), - output: flags.string({ + output: Flags.string({ description: 'path to output JSON file', char: 'o', required: true, @@ -77,9 +64,7 @@ export default class OsBuildConfigCmd extends Command { public static authenticated = true; public async run() { - const { args: params, flags: options } = this.parse( - OsBuildConfigCmd, - ); + const { args: params, flags: options } = await this.parse(OsBuildConfigCmd); const { writeFile } = (await import('fs')).promises; diff --git a/lib/commands/os/configure.ts b/lib/commands/os/configure.ts index 01db1a71..3853e18c 100644 --- a/lib/commands/os/configure.ts +++ b/lib/commands/os/configure.ts @@ -15,7 +15,8 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags, Args } from '@oclif/core'; +import type { Interfaces } from '@oclif/core'; import type * as BalenaSdk from 'balena-sdk'; import { promisify } from 'util'; import * as _ from 'lodash'; @@ -31,29 +32,7 @@ import { const CONNECTIONS_FOLDER = '/system-connections'; -interface FlagsDef { - advanced?: boolean; - fleet?: string; - config?: string; - 'config-app-update-poll-interval'?: number; - 'config-network'?: string; - 'config-wifi-key'?: string; - 'config-wifi-ssid'?: string; - dev?: boolean; // balenaOS development variant - secureBoot?: boolean; - device?: string; // device UUID - 'device-type'?: string; - help?: void; - version?: string; - 'system-connection': string[]; - 'initial-device-name'?: string; - 'provisioning-key-name'?: string; - 'provisioning-key-expiry-date'?: string; -} - -interface ArgsDef { - image: string; -} +type FlagsDef = Interfaces.InferredFlags; interface Answers { appUpdatePollInterval: number; // in minutes @@ -111,40 +90,39 @@ export default class OsConfigureCmd extends Command { '$ balena os configure ../path/rpi3.img -f MyFinFleet --device-type raspberrypi3 --config myWifiConfig.json', ]; - public static args = [ - { - name: 'image', + public static args = { + image: Args.string({ required: true, description: 'path to a balenaOS image file, e.g. "rpi3.img"', - }, - ]; + }), + }; public static usage = 'os configure '; - public static flags: flags.Input = { - advanced: flags.boolean({ + public static flags = { + advanced: Flags.boolean({ char: 'v', description: 'ask advanced configuration questions (when in interactive mode)', }), fleet: { ...cf.fleet, exclusive: ['device'] }, - config: flags.string({ + config: Flags.string({ description: 'path to a pre-generated config.json file to be injected in the OS image', exclusive: ['provisioning-key-name', 'provisioning-key-expiry-date'], }), - 'config-app-update-poll-interval': flags.integer({ + 'config-app-update-poll-interval': Flags.integer({ description: 'supervisor cloud polling interval in minutes (e.g. for variable updates)', }), - 'config-network': flags.string({ + 'config-network': Flags.string({ description: 'device network type (non-interactive configuration)', options: ['ethernet', 'wifi'], }), - 'config-wifi-key': flags.string({ + 'config-wifi-key': Flags.string({ description: 'WiFi key (password) (non-interactive configuration)', }), - 'config-wifi-ssid': flags.string({ + 'config-wifi-ssid': Flags.string({ description: 'WiFi SSID (network name) (non-interactive configuration)', }), dev: cf.dev, @@ -157,29 +135,29 @@ export default class OsConfigureCmd extends Command { 'provisioning-key-expiry-date', ], }, - 'device-type': flags.string({ + 'device-type': Flags.string({ description: 'device type slug (e.g. "raspberrypi3") to override the fleet device type', }), - 'initial-device-name': flags.string({ + 'initial-device-name': Flags.string({ description: 'This option will set the device name when the device provisions', }), - version: flags.string({ + version: Flags.string({ description: 'balenaOS version, for example "2.32.0" or "2.44.0+rev1"', }), - 'system-connection': flags.string({ + 'system-connection': Flags.string({ multiple: true, char: 'c', required: false, description: "paths to local files to place into the 'system-connections' directory", }), - 'provisioning-key-name': flags.string({ + 'provisioning-key-name': Flags.string({ description: 'custom key name assigned to generated provisioning api key', exclusive: ['config', 'device'], }), - 'provisioning-key-expiry-date': flags.string({ + 'provisioning-key-expiry-date': Flags.string({ description: 'expiry date assigned to generated provisioning api key (format: YYYY-MM-DD)', exclusive: ['config', 'device'], @@ -190,9 +168,7 @@ export default class OsConfigureCmd extends Command { public static authenticated = true; public async run() { - const { args: params, flags: options } = this.parse( - OsConfigureCmd, - ); + const { args: params, flags: options } = await this.parse(OsConfigureCmd); await validateOptions(options); diff --git a/lib/commands/os/download.ts b/lib/commands/os/download.ts index 322939ac..e7ef1bda 100644 --- a/lib/commands/os/download.ts +++ b/lib/commands/os/download.ts @@ -15,21 +15,11 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags, Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { stripIndent } from '../../utils/lazy'; -interface FlagsDef { - output: string; - version?: string; - help: void; -} - -interface ArgsDef { - type: string; -} - export default class OsDownloadCmd extends Command { public static description = stripIndent` Download an unconfigured OS image. @@ -65,23 +55,22 @@ export default class OsDownloadCmd extends Command { '$ balena os download raspberrypi3 -o ../foo/bar/raspberry-pi.img --version menu-esr', ]; - public static args = [ - { - name: 'type', + public static args = { + type: Args.string({ description: 'the device type', required: true, - }, - ]; + }), + }; public static usage = 'os download '; - public static flags: flags.Input = { - output: flags.string({ + public static flags = { + output: Flags.string({ description: 'output path', char: 'o', required: true, }), - version: flags.string({ + version: Flags.string({ description: stripIndent` version number (ESR or non-ESR versions), or semver range (non-ESR versions only), @@ -96,9 +85,7 @@ export default class OsDownloadCmd extends Command { }; public async run() { - const { args: params, flags: options } = this.parse( - OsDownloadCmd, - ); + const { args: params, flags: options } = await this.parse(OsDownloadCmd); // balenaOS ESR versions require user authentication if (options.version) { diff --git a/lib/commands/os/initialize.ts b/lib/commands/os/initialize.ts index 4e62871a..322c1ab8 100644 --- a/lib/commands/os/initialize.ts +++ b/lib/commands/os/initialize.ts @@ -15,22 +15,11 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getCliForm, stripIndent } from '../../utils/lazy'; -interface FlagsDef { - type: string; - drive?: string; - yes: boolean; - help: void; -} - -interface ArgsDef { - image: string; -} - const INIT_WARNING_MESSAGE = ` Note: Initializing the device may ask for administrative permissions @@ -52,17 +41,16 @@ export default class OsInitializeCmd extends Command { '$ balena os initialize ../path/rpi.img --type raspberry-pi', ]; - public static args = [ - { - name: 'image', + public static args = { + image: Args.string({ description: 'path to OS image', required: true, - }, - ]; + }), + }; public static usage = 'os initialize '; - public static flags: flags.Input = { + public static flags = { type: cf.deviceType, drive: cf.drive, yes: cf.yes, @@ -72,9 +60,7 @@ export default class OsInitializeCmd extends Command { public static authenticated = true; public async run() { - const { args: params, flags: options } = this.parse( - OsInitializeCmd, - ); + const { args: params, flags: options } = await this.parse(OsInitializeCmd); const { getManifest, sudo } = await import('../../utils/helpers'); diff --git a/lib/commands/os/versions.ts b/lib/commands/os/versions.ts index 9130e372..d4f7f5b5 100644 --- a/lib/commands/os/versions.ts +++ b/lib/commands/os/versions.ts @@ -15,20 +15,11 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags, Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { stripIndent } from '../../utils/lazy'; -interface FlagsDef { - esr?: boolean; - help: void; -} - -interface ArgsDef { - type: string; -} - export default class OsVersionsCmd extends Command { public static description = stripIndent` Show available balenaOS versions for the given device type. @@ -42,28 +33,25 @@ export default class OsVersionsCmd extends Command { public static examples = ['$ balena os versions raspberrypi3']; - public static args = [ - { - name: 'type', + public static args = { + type: Args.string({ description: 'device type', required: true, - }, - ]; + }), + }; public static usage = 'os versions '; - public static flags: flags.Input = { + public static flags = { help: cf.help, - esr: flags.boolean({ + esr: Flags.boolean({ description: 'select balenaOS ESR versions', default: false, }), }; public async run() { - const { args: params, flags: options } = this.parse( - OsVersionsCmd, - ); + const { args: params, flags: options } = await this.parse(OsVersionsCmd); const { formatOsVersion, getOsVersions } = await import( '../../utils/cloud' diff --git a/lib/commands/preload.ts b/lib/commands/preload.ts index b9eba01c..39378d40 100644 --- a/lib/commands/preload.ts +++ b/lib/commands/preload.ts @@ -25,11 +25,10 @@ import { stripIndent, } from '../utils/lazy'; import { applicationIdInfo } from '../utils/messages'; -import type { DockerConnectionCliFlags } from '../utils/docker'; import { dockerConnectionCliFlags } from '../utils/docker'; import { parseAsInteger } from '../utils/validation'; -import { flags } from '@oclif/command'; +import { Flags, Args } from '@oclif/core'; import * as _ from 'lodash'; import type { Application, @@ -41,21 +40,6 @@ import type { } from 'balena-sdk'; import type { Preloader } from 'balena-preload'; -interface FlagsDef extends DockerConnectionCliFlags { - fleet?: string; - commit?: string; - 'splash-image'?: string; - 'dont-check-arch': boolean; - 'pin-device-to-release'?: boolean; - 'additional-space'?: number; - 'add-certificate'?: string[]; - help: void; -} - -interface ArgsDef { - image: string; -} - export default class PreloadCmd extends Command { public static description = stripIndent` Preload a release on a disk image (or Edison zip archive). @@ -89,19 +73,18 @@ export default class PreloadCmd extends Command { '$ balena preload balena.img', ]; - public static args = [ - { - name: 'image', + public static args = { + image: Args.string({ description: 'the image file path', required: true, - }, - ]; + }), + }; public static usage = 'preload '; - public static flags: flags.Input = { + public static flags = { fleet: cf.fleet, - commit: flags.string({ + commit: Flags.string({ description: `\ The commit hash of the release to preload. Use "current" to specify the current release (ignored if no appId is given). The current release is usually also the @@ -112,27 +95,27 @@ https://github.com/balena-io-examples/staged-releases\ `, char: 'c', }), - 'splash-image': flags.string({ + 'splash-image': Flags.string({ description: 'path to a png image to replace the splash screen', char: 's', }), - 'dont-check-arch': flags.boolean({ + 'dont-check-arch': Flags.boolean({ default: false, description: 'disable architecture compatibility check between image and fleet', }), - 'pin-device-to-release': flags.boolean({ + 'pin-device-to-release': Flags.boolean({ allowNo: true, description: 'pin the preloaded device to the preloaded release on provision', char: 'p', }), - 'additional-space': flags.integer({ + 'additional-space': Flags.integer({ description: 'expand the image by this amount of bytes instead of automatically estimating the required amount', - parse: (x) => parseAsInteger(x, 'additional-space'), + parse: async (x) => parseAsInteger(x, 'additional-space'), }), - 'add-certificate': flags.string({ + 'add-certificate': Flags.string({ description: `\ Add the given certificate (in PEM format) to /etc/ssl/certs in the preloading container. The file name must end with '.crt' and must not be already contained in the preloader's @@ -144,14 +127,14 @@ Can be repeated to add multiple certificates.\ ...dockerConnectionCliFlags, // Redefining --dockerPort here (defined already in dockerConnectionCliFlags) // without -p alias, to avoid clash with -p alias of pin-device-to-release - dockerPort: flags.integer({ + dockerPort: Flags.integer({ description: 'Docker daemon TCP port number (hint: 2375 for balena devices)', - parse: (p) => parseAsInteger(p, 'dockerPort'), + parse: async (p) => parseAsInteger(p, 'dockerPort'), }), // Not supporting -h for help, because of clash with -h in DockerCliFlags // Revisit this in future release. - help: flags.help({}), + help: Flags.help({}), }; public static authenticated = true; @@ -159,9 +142,7 @@ Can be repeated to add multiple certificates.\ public static primary = true; public async run() { - const { args: params, flags: options } = this.parse( - PreloadCmd, - ); + const { args: params, flags: options } = await this.parse(PreloadCmd); const balena = getBalenaSdk(); const balenaPreload = await import('balena-preload'); diff --git a/lib/commands/push.ts b/lib/commands/push.ts index 862fa69e..e5623dcb 100644 --- a/lib/commands/push.ts +++ b/lib/commands/push.ts @@ -15,7 +15,8 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags, Args } from '@oclif/core'; +import type { Interfaces } from '@oclif/core'; import Command from '../command'; import * as cf from '../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../utils/lazy'; @@ -34,30 +35,7 @@ enum BuildTarget { Device, } -interface ArgsDef { - fleetOrDevice: string; -} - -interface FlagsDef { - source: string; - emulated: boolean; - dockerfile?: string; // DeviceDeployOptions.dockerfilePath (alternative Dockerfile) - nocache: boolean; - pull: boolean; - 'noparent-check': boolean; - 'registry-secrets'?: string; - nolive: boolean; - detached: boolean; - service?: string[]; - system: boolean; - env?: string[]; - 'noconvert-eol': boolean; - 'multi-dockerignore': boolean; - 'release-tag'?: string[]; - draft: boolean; - note?: string; - help: void; -} +type FlagsDef = Interfaces.InferredFlags; export default class PushCmd extends Command { public static description = stripIndent` @@ -112,27 +90,26 @@ export default class PushCmd extends Command { '$ balena push 23c73a1.local --system --service my-service', ]; - public static args = [ - { - name: 'fleetOrDevice', + public static args = { + fleetOrDevice: Args.string({ description: 'fleet name or slug, or local device IP address or ".local" hostname', required: true, parse: lowercaseIfSlug, - }, - ]; + }), + }; public static usage = 'push '; - public static flags: flags.Input = { - source: flags.string({ + public static flags = { + source: Flags.string({ description: stripIndent` Source directory to be sent to balenaCloud or balenaOS device (default: current working dir)`, char: 's', default: '.', }), - emulated: flags.boolean({ + emulated: Flags.boolean({ description: stripIndent` Don't use the faster, native balenaCloud ARM builders; force slower QEMU ARM emulation on Intel x86-64 builders. This flag is sometimes used to investigate @@ -140,11 +117,11 @@ export default class PushCmd extends Command { char: 'e', default: false, }), - dockerfile: flags.string({ + dockerfile: Flags.string({ description: 'Alternative Dockerfile name/path, relative to the source folder', }), - nocache: flags.boolean({ + nocache: Flags.boolean({ description: stripIndent` Don't use cached layers of previously built images for this project. This ensures that the latest base image and packages are pulled. Note that build @@ -155,18 +132,18 @@ export default class PushCmd extends Command { char: 'c', default: false, }), - pull: flags.boolean({ + pull: Flags.boolean({ description: stripIndent` When pushing to a local device, force the base images to be pulled again. Currently this option is ignored when pushing to the balenaCloud builders.`, default: false, }), - 'noparent-check': flags.boolean({ + 'noparent-check': Flags.boolean({ description: stripIndent` Disable project validation check of 'docker-compose.yml' file in parent folder`, default: false, }), - 'registry-secrets': flags.string({ + 'registry-secrets': Flags.string({ description: stripIndent` Path to a local YAML or JSON file containing Docker registry passwords used to pull base images. Note that if registry-secrets are not provided on the @@ -174,7 +151,7 @@ export default class PushCmd extends Command { used (usually $HOME/.balena/secrets.yml|.json)`, char: 'R', }), - nolive: flags.boolean({ + nolive: Flags.boolean({ description: stripIndent` Don't run a live session on this push. The filesystem will not be monitored, and changes will not be synchronized to any running containers. Note that both @@ -182,7 +159,7 @@ export default class PushCmd extends Command { initial build has completed.`, default: false, }), - detached: flags.boolean({ + detached: Flags.boolean({ description: stripIndent` When pushing to the cloud, this option will cause the build to start, then return execution back to the shell, with the status and release ID (if @@ -191,20 +168,20 @@ export default class PushCmd extends Command { char: 'd', default: false, }), - service: flags.string({ + service: Flags.string({ description: stripIndent` Reject logs not originating from this service. This can be used in combination with --system and other --service flags. Only valid when pushing to a local mode device.`, multiple: true, }), - system: flags.boolean({ + system: Flags.boolean({ description: stripIndent` Only show system logs. This can be used in combination with --service. Only valid when pushing to a local mode device.`, default: false, }), - env: flags.string({ + env: Flags.string({ description: stripIndent` When performing a push to device, run the built containers with environment variables provided with this argument. Environment variables can be applied @@ -216,17 +193,17 @@ export default class PushCmd extends Command { `, multiple: true, }), - 'noconvert-eol': flags.boolean({ + 'noconvert-eol': Flags.boolean({ description: `Don't convert line endings from CRLF (Windows format) to LF (Unix format).`, default: false, }), - 'multi-dockerignore': flags.boolean({ + 'multi-dockerignore': Flags.boolean({ description: 'Have each service use its own .dockerignore file. See "balena help push".', char: 'm', default: false, }), - 'release-tag': flags.string({ + 'release-tag': Flags.string({ description: stripIndent` Set release tags if the image build is successful (balenaCloud only). Multiple arguments may be provided, alternating tag keys and values (see examples). @@ -235,7 +212,7 @@ export default class PushCmd extends Command { multiple: true, exclusive: ['detached'], }), - draft: flags.boolean({ + draft: Flags.boolean({ description: stripIndent` Instruct the builder to create the release as a draft. Draft releases are ignored by the 'track latest' release policy but can be used through release pinning. @@ -243,16 +220,14 @@ export default class PushCmd extends Command { as final by default unless this option is given.`, default: false, }), - note: flags.string({ description: 'The notes for this release' }), + note: Flags.string({ description: 'The notes for this release' }), help: cf.help, }; public static primary = true; public async run() { - const { args: params, flags: options } = this.parse( - PushCmd, - ); + const { args: params, flags: options } = await this.parse(PushCmd); const logger = await Command.getLogger(); logger.logDebug(`Using build source directory: ${options.source} `); diff --git a/lib/commands/release/finalize.ts b/lib/commands/release/finalize.ts index 6cd09f7d..2ddb422e 100644 --- a/lib/commands/release/finalize.ts +++ b/lib/commands/release/finalize.ts @@ -15,19 +15,10 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { commitOrIdArg } from '.'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { tryAsInteger } from '../../utils/validation'; - -interface FlagsDef { - help: void; -} - -interface ArgsDef { - commitOrId: string | number; -} export default class ReleaseFinalizeCmd extends Command { public static description = stripIndent` @@ -51,23 +42,21 @@ export default class ReleaseFinalizeCmd extends Command { public static usage = 'release finalize '; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; - public static args = [ - { - name: 'commitOrId', + public static args = { + commitOrId: commitOrIdArg({ description: 'the commit or ID of the release to finalize', required: true, - parse: (commitOrId: string) => tryAsInteger(commitOrId), - }, - ]; + }), + }; public static authenticated = true; public async run() { - const { args: params } = this.parse(ReleaseFinalizeCmd); + const { args: params } = await this.parse(ReleaseFinalizeCmd); const balena = getBalenaSdk(); diff --git a/lib/commands/release/index.ts b/lib/commands/release/index.ts index ae01d0db..5409efe4 100644 --- a/lib/commands/release/index.ts +++ b/lib/commands/release/index.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags, Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy'; @@ -23,14 +23,9 @@ import type * as BalenaSdk from 'balena-sdk'; import jsyaml = require('js-yaml'); import { tryAsInteger } from '../../utils/validation'; -interface FlagsDef { - help: void; - composition?: boolean; -} - -interface ArgsDef { - commitOrId: string | number; -} +export const commitOrIdArg = Args.custom({ + parse: async (commitOrId: string) => tryAsInteger(commitOrId), +}); export default class ReleaseCmd extends Command { public static description = stripIndent` @@ -43,30 +38,26 @@ export default class ReleaseCmd extends Command { public static usage = 'release '; - public static flags: flags.Input = { + public static flags = { help: cf.help, - composition: flags.boolean({ + composition: Flags.boolean({ default: false, char: 'c', description: 'Return the release composition', }), }; - public static args = [ - { - name: 'commitOrId', + public static args = { + commitOrId: commitOrIdArg({ description: 'the commit or ID of the release to get information', required: true, - parse: (commitOrId: string) => tryAsInteger(commitOrId), - }, - ]; + }), + }; public static authenticated = true; public async run() { - const { args: params, flags: options } = this.parse( - ReleaseCmd, - ); + const { args: params, flags: options } = await this.parse(ReleaseCmd); const balena = getBalenaSdk(); if (options.composition) { diff --git a/lib/commands/release/invalidate.ts b/lib/commands/release/invalidate.ts index b57c51ea..f9f3f9b2 100644 --- a/lib/commands/release/invalidate.ts +++ b/lib/commands/release/invalidate.ts @@ -15,19 +15,10 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { commitOrIdArg } from '.'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { tryAsInteger } from '../../utils/validation'; - -interface FlagsDef { - help: void; -} - -interface ArgsDef { - commitOrId: string | number; -} export default class ReleaseInvalidateCmd extends Command { public static description = stripIndent` @@ -46,25 +37,21 @@ export default class ReleaseInvalidateCmd extends Command { public static usage = 'release invalidate '; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; - public static args = [ - { - name: 'commitOrId', + public static args = { + commitOrId: commitOrIdArg({ description: 'the commit or ID of the release to invalidate', required: true, - parse: (commitOrId: string) => tryAsInteger(commitOrId), - }, - ]; + }), + }; public static authenticated = true; public async run() { - const { args: params } = this.parse( - ReleaseInvalidateCmd, - ); + const { args: params } = await this.parse(ReleaseInvalidateCmd); const balena = getBalenaSdk(); diff --git a/lib/commands/release/validate.ts b/lib/commands/release/validate.ts index dc0334ab..85ce4b45 100644 --- a/lib/commands/release/validate.ts +++ b/lib/commands/release/validate.ts @@ -15,19 +15,10 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { commitOrIdArg } from '.'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { tryAsInteger } from '../../utils/validation'; - -interface FlagsDef { - help: void; -} - -interface ArgsDef { - commitOrId: string | number; -} export default class ReleaseValidateCmd extends Command { public static description = stripIndent` @@ -45,23 +36,21 @@ export default class ReleaseValidateCmd extends Command { public static usage = 'release validate '; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; - public static args = [ - { - name: 'commitOrId', + public static args = { + commitOrId: commitOrIdArg({ description: 'the commit or ID of the release to validate', required: true, - parse: (commitOrId: string) => tryAsInteger(commitOrId), - }, - ]; + }), + }; public static authenticated = true; public async run() { - const { args: params } = this.parse(ReleaseValidateCmd); + const { args: params } = await this.parse(ReleaseValidateCmd); const balena = getBalenaSdk(); diff --git a/lib/commands/releases.ts b/lib/commands/releases.ts index 43f375c9..28f7754f 100644 --- a/lib/commands/releases.ts +++ b/lib/commands/releases.ts @@ -15,21 +15,13 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Args } from '@oclif/core'; import Command from '../command'; import * as cf from '../utils/common-flags'; import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy'; import { applicationNameNote } from '../utils/messages'; import type * as BalenaSdk from 'balena-sdk'; -interface FlagsDef { - help: void; -} - -interface ArgsDef { - fleet: string; -} - export default class ReleasesCmd extends Command { public static description = stripIndent` List all releases of a fleet. @@ -42,22 +34,21 @@ export default class ReleasesCmd extends Command { public static usage = 'releases '; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; - public static args = [ - { - name: 'fleet', + public static args = { + fleet: Args.string({ description: 'fleet name or slug (preferred)', required: true, - }, - ]; + }), + }; public static authenticated = true; public async run() { - const { args: params } = this.parse(ReleasesCmd); + const { args: params } = await this.parse(ReleasesCmd); const fields: Array = [ 'id', diff --git a/lib/commands/scan.ts b/lib/commands/scan.ts index b6196421..e18f8781 100644 --- a/lib/commands/scan.ts +++ b/lib/commands/scan.ts @@ -15,18 +15,11 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags } from '@oclif/core'; import Command from '../command'; import * as cf from '../utils/common-flags'; import { getCliUx, stripIndent } from '../utils/lazy'; -interface FlagsDef { - json?: boolean; - verbose: boolean; - timeout?: number; - help: void; -} - export default class ScanCmd extends Command { public static description = stripIndent` Scan for balenaOS devices on your local network. @@ -47,18 +40,18 @@ export default class ScanCmd extends Command { public static usage = 'scan'; - public static flags: flags.Input = { - verbose: flags.boolean({ + public static flags = { + verbose: Flags.boolean({ default: false, char: 'v', description: 'display full info', }), - timeout: flags.integer({ + timeout: Flags.integer({ char: 't', description: 'scan timeout in seconds', }), help: cf.help, - json: flags.boolean({ + json: Flags.boolean({ default: false, char: 'j', description: 'produce JSON output instead of tabular output', @@ -78,7 +71,7 @@ export default class ScanCmd extends Command { const dockerPort = 2375; const dockerTimeout = 2000; - const { flags: options } = this.parse(ScanCmd); + const { flags: options } = await this.parse(ScanCmd); const discoverTimeout = options.timeout != null ? options.timeout * 1000 : undefined; diff --git a/lib/commands/settings.ts b/lib/commands/settings.ts index a105f669..47447728 100644 --- a/lib/commands/settings.ts +++ b/lib/commands/settings.ts @@ -15,15 +15,10 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; import Command from '../command'; import * as cf from '../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../utils/lazy'; -interface FlagsDef { - help: void; -} - export default class SettingsCmd extends Command { public static description = stripIndent` Print current settings. @@ -34,12 +29,12 @@ export default class SettingsCmd extends Command { public static usage = 'settings'; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; public async run() { - this.parse(SettingsCmd); + await this.parse(SettingsCmd); const settings = await getBalenaSdk().settings.getAll(); diff --git a/lib/commands/ssh.ts b/lib/commands/ssh.ts index cdd09468..c59c836a 100644 --- a/lib/commands/ssh.ts +++ b/lib/commands/ssh.ts @@ -15,25 +15,12 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags, Args } from '@oclif/core'; import Command from '../command'; import * as cf from '../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../utils/lazy'; import { parseAsInteger, validateLocalHostnameOrIp } from '../utils/validation'; -interface FlagsDef { - port?: number; - tty: boolean; - verbose: boolean; - noproxy: boolean; - help: void; -} - -interface ArgsDef { - fleetOrDevice: string; - service?: string; -} - export default class SshCmd extends Command { public static description = stripIndent` Open a SSH prompt on a device's host OS or service container. @@ -73,41 +60,39 @@ export default class SshCmd extends Command { '$ echo "uptime; exit;" | balena ssh 192.168.0.1 myService', ]; - public static args = [ - { - name: 'fleetOrDevice', + public static args = { + fleetOrDevice: Args.string({ description: 'fleet name/slug, device uuid, or address of local device', required: true, - }, - { - name: 'service', + }), + service: Args.string({ description: 'service name, if connecting to a container', required: false, - }, - ]; + }), + }; public static usage = 'ssh [service]'; - public static flags: flags.Input = { - port: flags.integer({ + public static flags = { + port: Flags.integer({ description: stripIndent` SSH server port number (default 22222) if the target is an IP address or .local hostname. Otherwise, port number for the balenaCloud gateway (default 22).`, char: 'p', - parse: (p) => parseAsInteger(p, 'port'), + parse: async (p) => parseAsInteger(p, 'port'), }), - tty: flags.boolean({ + tty: Flags.boolean({ default: false, description: 'force pseudo-terminal allocation (bypass TTY autodetection for stdin)', char: 't', }), - verbose: flags.boolean({ + verbose: Flags.boolean({ default: false, description: 'increase verbosity', char: 'v', }), - noproxy: flags.boolean({ + noproxy: Flags.boolean({ default: false, description: 'bypass global proxy configuration for the ssh connection', }), @@ -118,9 +103,7 @@ export default class SshCmd extends Command { public static offlineCompatible = true; public async run() { - const { args: params, flags: options } = this.parse( - SshCmd, - ); + const { args: params, flags: options } = await this.parse(SshCmd); // Local connection if (validateLocalHostnameOrIp(params.fleetOrDevice)) { diff --git a/lib/commands/support.ts b/lib/commands/support.ts index 1f31e560..349ababe 100644 --- a/lib/commands/support.ts +++ b/lib/commands/support.ts @@ -15,24 +15,13 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags, Args } from '@oclif/core'; import Command from '../command'; import { ExpectedError } from '../errors'; import * as cf from '../utils/common-flags'; import { getBalenaSdk, getCliUx, stripIndent } from '../utils/lazy'; import { applicationIdInfo } from '../utils/messages'; -interface FlagsDef { - fleet?: string; - device?: string; - duration?: string; - help: void; -} - -interface ArgsDef { - action: string; -} - export default class SupportCmd extends Command { public static description = stripIndent` Grant or revoke support access for devices or fleets. @@ -56,18 +45,17 @@ export default class SupportCmd extends Command { 'balena support disable -f myorg/myfleet', ]; - public static args = [ - { - name: 'action', + public static args = { + action: Args.string({ description: 'enable|disable support access', options: ['enable', 'disable'], - }, - ]; + }), + }; public static usage = 'support '; - public static flags: flags.Input = { - device: flags.string({ + public static flags = { + device: Flags.string({ description: 'comma-separated list (no spaces) of device UUIDs', char: 'd', }), @@ -76,7 +64,7 @@ export default class SupportCmd extends Command { description: 'comma-separated list (no spaces) of fleet names or slugs (preferred)', }, - duration: flags.string({ + duration: Flags.string({ description: 'length of time to enable support for, in (h)ours or (d)ays, e.g. 12h, 2d', char: 't', @@ -87,9 +75,7 @@ export default class SupportCmd extends Command { public static authenticated = true; public async run() { - const { args: params, flags: options } = this.parse( - SupportCmd, - ); + const { args: params, flags: options } = await this.parse(SupportCmd); const balena = getBalenaSdk(); const ux = getCliUx(); diff --git a/lib/commands/tag/rm.ts b/lib/commands/tag/rm.ts index 9a860148..057418f4 100644 --- a/lib/commands/tag/rm.ts +++ b/lib/commands/tag/rm.ts @@ -15,23 +15,12 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { applicationIdInfo } from '../../utils/messages'; -interface FlagsDef { - fleet?: string; - device?: string; - release?: string; - help: void; -} - -interface ArgsDef { - tagKey: string; -} - export default class TagRmCmd extends Command { public static description = stripIndent` Remove a tag from a fleet, device or release. @@ -49,17 +38,16 @@ export default class TagRmCmd extends Command { '$ balena tag rm myTagKey --release b376b0e544e9429483b656490e5b9443b4349bd6', ]; - public static args = [ - { - name: 'tagKey', + public static args = { + tagKey: Args.string({ description: 'the key string of the tag', required: true, - }, - ]; + }), + }; public static usage = 'tag rm '; - public static flags: flags.Input = { + public static flags = { fleet: { ...cf.fleet, exclusive: ['device', 'release'], @@ -78,9 +66,7 @@ export default class TagRmCmd extends Command { public static authenticated = true; public async run() { - const { args: params, flags: options } = this.parse( - TagRmCmd, - ); + const { args: params, flags: options } = await this.parse(TagRmCmd); const balena = getBalenaSdk(); diff --git a/lib/commands/tag/set.ts b/lib/commands/tag/set.ts index 5a33ef73..db2549ed 100644 --- a/lib/commands/tag/set.ts +++ b/lib/commands/tag/set.ts @@ -15,24 +15,12 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Args } from '@oclif/core'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { getBalenaSdk, stripIndent } from '../../utils/lazy'; import { applicationIdInfo } from '../../utils/messages'; -interface FlagsDef { - fleet?: string; - device?: string; - release?: string; - help: void; -} - -interface ArgsDef { - tagKey: string; - value?: string; -} - export default class TagSetCmd extends Command { public static description = stripIndent` Set a tag on a fleet, device or release. @@ -57,22 +45,20 @@ export default class TagSetCmd extends Command { '$ balena tag set myCompositeTag --release b376b0e544e9429483b656490e5b9443b4349bd6', ]; - public static args = [ - { - name: 'tagKey', + public static args = { + tagKey: Args.string({ description: 'the key string of the tag', required: true, - }, - { - name: 'value', + }), + value: Args.string({ description: 'the optional value associated with the tag', required: false, - }, - ]; + }), + }; public static usage = 'tag set [value]'; - public static flags: flags.Input = { + public static flags = { fleet: { ...cf.fleet, exclusive: ['device', 'release'], @@ -91,9 +77,7 @@ export default class TagSetCmd extends Command { public static authenticated = true; public async run() { - const { args: params, flags: options } = this.parse( - TagSetCmd, - ); + const { args: params, flags: options } = await this.parse(TagSetCmd); const balena = getBalenaSdk(); diff --git a/lib/commands/tags.ts b/lib/commands/tags.ts index 8e9f31e6..62434fe1 100644 --- a/lib/commands/tags.ts +++ b/lib/commands/tags.ts @@ -15,20 +15,12 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; 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'; -interface FlagsDef { - fleet?: string; - device?: string; - release?: string; - help: void; -} - export default class TagsCmd extends Command { public static description = stripIndent` List all tags for a fleet, device or release. @@ -48,7 +40,7 @@ export default class TagsCmd extends Command { public static usage = 'tags'; - public static flags: flags.Input = { + public static flags = { fleet: { ...cf.fleet, exclusive: ['device', 'release'], @@ -67,7 +59,7 @@ export default class TagsCmd extends Command { public static authenticated = true; public async run() { - const { flags: options } = this.parse(TagsCmd); + const { flags: options } = await this.parse(TagsCmd); const balena = getBalenaSdk(); diff --git a/lib/commands/tunnel.ts b/lib/commands/tunnel.ts index 69f04d38..ab8d97eb 100644 --- a/lib/commands/tunnel.ts +++ b/lib/commands/tunnel.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags, Args } from '@oclif/core'; import Command from '../command'; import { NoPortsDefinedError, @@ -28,15 +28,6 @@ import { lowercaseIfSlug } from '../utils/normalization'; import type { Server, Socket } from 'net'; -interface FlagsDef { - port: string[]; - help: void; -} - -interface ArgsDef { - deviceOrFleet: string; -} - export default class TunnelCmd extends Command { public static description = stripIndent` Tunnel local ports to your balenaOS device. @@ -79,19 +70,18 @@ export default class TunnelCmd extends Command { '$ balena tunnel myFleet -p 8080:3000 -p 8081:9000', ]; - public static args = [ - { - name: 'deviceOrFleet', + public static args = { + deviceOrFleet: Args.string({ description: 'device UUID or fleet name/slug', required: true, parse: lowercaseIfSlug, - }, - ]; + }), + }; public static usage = 'tunnel '; - public static flags: flags.Input = { - port: flags.string({ + public static flags = { + port: Flags.string({ description: 'port mapping in the format [:[localIP:]localPort]', char: 'p', @@ -104,9 +94,7 @@ export default class TunnelCmd extends Command { public static authenticated = true; public async run() { - const { args: params, flags: options } = this.parse( - TunnelCmd, - ); + const { args: params, flags: options } = await this.parse(TunnelCmd); const logger = await Command.getLogger(); const sdk = getBalenaSdk(); diff --git a/lib/commands/util/available-drives.ts b/lib/commands/util/available-drives.ts index cf487390..ac71d201 100644 --- a/lib/commands/util/available-drives.ts +++ b/lib/commands/util/available-drives.ts @@ -15,15 +15,10 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { stripIndent, getChalk, getVisuals } from '../../utils/lazy'; -interface FlagsDef { - help: void; -} - export default class UtilAvailableDrivesCmd extends Command { public static description = stripIndent` List available drives. @@ -34,14 +29,14 @@ export default class UtilAvailableDrivesCmd extends Command { public static usage = 'util available-drives'; - public static flags: flags.Input = { + public static flags = { help: cf.help, }; public static offlineCompatible = true; public async run() { - this.parse(UtilAvailableDrivesCmd); + await this.parse(UtilAvailableDrivesCmd); const sdk = await import('etcher-sdk'); diff --git a/lib/commands/version.ts b/lib/commands/version.ts index 6eeb8563..970d362d 100644 --- a/lib/commands/version.ts +++ b/lib/commands/version.ts @@ -15,16 +15,10 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags } from '@oclif/core'; import Command from '../command'; import { stripIndent } from '../utils/lazy'; -interface FlagsDef { - all: boolean; - json: boolean; - help: void; -} - export interface JsonVersions { 'balena-cli': string; 'Node.js': string; @@ -59,24 +53,24 @@ export default class VersionCmd extends Command { public static offlineCompatible = true; - public static flags: flags.Input = { - all: flags.boolean({ + public static flags = { + all: Flags.boolean({ default: false, char: 'a', description: 'include version information for additional components (Node.js)', }), - json: flags.boolean({ + json: Flags.boolean({ default: false, char: 'j', description: 'output version information in JSON format for programmatic use', }), - help: flags.help({ char: 'h' }), + help: Flags.help({ char: 'h' }), }; public async run() { - const { flags: options } = this.parse(VersionCmd); + const { flags: options } = await this.parse(VersionCmd); const versions: JsonVersions = { 'balena-cli': (await import('../../package.json')).version, 'Node.js': diff --git a/lib/commands/whoami.ts b/lib/commands/whoami.ts index 4ca4f8f1..5d96cc7a 100644 --- a/lib/commands/whoami.ts +++ b/lib/commands/whoami.ts @@ -32,7 +32,7 @@ export default class WhoamiCmd extends Command { public static authenticated = true; public async run() { - this.parse<{}, {}>(WhoamiCmd); + await this.parse(WhoamiCmd); const balena = getBalenaSdk(); diff --git a/lib/help.ts b/lib/help.ts index 59203757..e9353450 100644 --- a/lib/help.ts +++ b/lib/help.ts @@ -15,7 +15,6 @@ * limitations under the License. */ import { Help } from '@oclif/core'; -import { HelpFormatter } from '@oclif/core/lib/help/formatter'; import * as indent from 'indent-string'; import { getChalk } from './utils/lazy'; @@ -38,8 +37,6 @@ function getHelpSubject(args: string[]): string | undefined { } export default class BalenaHelp extends Help { - public helpFormatter = new HelpFormatter(this.config); - public static usage: 'help [command]'; public async showHelp(argv: string[]) { @@ -53,15 +50,20 @@ export default class BalenaHelp extends Help { const command = this.config.findCommand(subject); if (command) { - await this.showCommandHelp(command); + await this.showCommandHelp(await command.load()); return; } // If they've typed a topic (e.g. `balena os`) that isn't also a command (e.g. `balena device`) // then list the associated commands. - const topicCommands = this.config.commands.filter((c) => { - return c.id.startsWith(`${subject}:`); - }); + const topicCommands = await Promise.all( + this.config.commands + .filter((c) => { + return c.id.startsWith(`${subject}:`); + }) + .map((topic) => topic.load()), + ); + if (topicCommands.length > 0) { console.log(`${chalk.yellow(subject)} commands include:`); console.log(this.formatCommands(topicCommands)); @@ -188,7 +190,7 @@ See: https://git.io/JRHUW#deprecation-policy`, return ''; } - const body = this.helpFormatter.renderList( + const body = this.renderList( commands .filter((c) => c.usage != null && c.usage !== '') .map((c) => [c.usage, this.formatDescription(c.description)]), diff --git a/lib/preparser.ts b/lib/preparser.ts index bfa3c729..203f388f 100644 --- a/lib/preparser.ts +++ b/lib/preparser.ts @@ -20,6 +20,7 @@ export let unsupportedFlag = false; export interface AppOptions { // Prevent the default behavior of flushing stdout after running a command noFlush?: boolean; + configPath?: string; } export async function preparseArgs(argv: string[]): Promise { diff --git a/lib/utils/common-args.ts b/lib/utils/common-args.ts index 37d78195..74ae9baa 100644 --- a/lib/utils/common-args.ts +++ b/lib/utils/common-args.ts @@ -14,11 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Args } from '@oclif/core'; import { lowercaseIfSlug } from './normalization'; -export const fleetRequired = { - name: 'fleet', +export const fleetRequired = Args.string({ description: 'fleet name or slug (preferred)', required: true, parse: lowercaseIfSlug, -}; +}); diff --git a/lib/utils/common-flags.ts b/lib/utils/common-flags.ts index 818b03b2..0941ed43 100644 --- a/lib/utils/common-flags.ts +++ b/lib/utils/common-flags.ts @@ -15,72 +15,69 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags } from '@oclif/core'; import { stripIndent } from './lazy'; import { lowercaseIfSlug } from './normalization'; -import type { IBooleanFlag } from '@oclif/parser/lib/flags'; -import type { DataOutputOptions, DataSetOutputOptions } from '../framework'; - -export const fleet = flags.string({ +export const fleet = Flags.string({ char: 'f', description: 'fleet name or slug (preferred)', parse: lowercaseIfSlug, }); -export const device = flags.string({ +export const device = Flags.string({ char: 'd', description: 'device UUID', }); -export const help: IBooleanFlag = flags.help({ char: 'h' }); +export const help = Flags.help({ char: 'h' }); -export const quiet: IBooleanFlag = flags.boolean({ +export const quiet = Flags.boolean({ char: 'q', description: 'suppress warning messages', default: false, }); -export const release = flags.string({ +export const release = Flags.string({ char: 'r', description: 'release id', }); -export const service = flags.string({ +export const service = Flags.string({ char: 's', description: 'service name', }); -export const verbose: IBooleanFlag = flags.boolean({ +export const verbose = Flags.boolean({ char: 'v', description: 'produce verbose output', default: false, }); -export const yes: IBooleanFlag = flags.boolean({ +export const yes = Flags.boolean({ char: 'y', description: 'answer "yes" to all questions (non interactive use)', default: false, }); -export const force: IBooleanFlag = flags.boolean({ +export const force = Flags.boolean({ char: 'f', description: 'force action if the update lock is set', default: false, }); -export const dev: IBooleanFlag = flags.boolean({ +export const dev = Flags.boolean({ description: 'Configure balenaOS to operate in development mode', default: false, }); -export const secureBoot: IBooleanFlag = flags.boolean({ +export const secureBoot = Flags.boolean({ description: 'Configure balenaOS installer to opt-in secure boot and disk encryption', default: false, }); -export const drive = flags.string({ +export const drive = Flags.string({ char: 'd', description: stripIndent` the drive to write the image to, eg. \`/dev/sdb\` or \`/dev/mmcblk0\`. @@ -89,30 +86,30 @@ export const drive = flags.string({ `, }); -export const driveOrImg = flags.string({ +export const driveOrImg = Flags.string({ char: 'd', description: 'path to OS image file (e.g. balena.img) or block device (e.g. /dev/disk2)', }); -export const deviceType = flags.string({ +export const deviceType = Flags.string({ description: 'device type (Check available types with `balena devices supported`)', char: 't', required: true, }); -export const json: IBooleanFlag = flags.boolean({ +export const json = Flags.boolean({ char: 'j', description: 'produce JSON output instead of tabular output', default: false, }); -export const dataOutputFlags: flags.Input = { - fields: flags.string({ +export const dataOutputFlags = { + fields: Flags.string({ description: 'only show provided fields (comma-separated)', }), - json: flags.boolean({ + json: Flags.boolean({ char: 'j', exclusive: ['no-truncate'], description: 'output in json format', @@ -120,24 +117,23 @@ export const dataOutputFlags: flags.Input = { }), }; -export const dataSetOutputFlags: flags.Input & - flags.Input = { +export const dataSetOutputFlags = { ...dataOutputFlags, - filter: flags.string({ + filter: Flags.string({ description: 'filter results by substring matching of a given field, eg: --filter field=foo', }), - 'no-header': flags.boolean({ + 'no-header': Flags.boolean({ exclusive: ['json'], description: 'hide table header from output', default: false, }), - 'no-truncate': flags.boolean({ + 'no-truncate': Flags.boolean({ exclusive: ['json'], description: 'do not truncate output to fit screen', default: false, }), - sort: flags.string({ + sort: Flags.string({ description: `field to sort by (prepend '-' for descending order)`, }), }; diff --git a/lib/utils/compose_ts.ts b/lib/utils/compose_ts.ts index 47139ebd..299c857d 100644 --- a/lib/utils/compose_ts.ts +++ b/lib/utils/compose_ts.ts @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags } from '@oclif/core'; import { BalenaSDK } from 'balena-sdk'; import type { TransposeOptions } from '@balena/compose/dist/emulate'; import type * as Dockerode from 'dockerode'; @@ -35,7 +35,6 @@ import type { Pack } from 'tar-stream'; import { ExpectedError } from '../errors'; import { BuiltImage, - ComposeCliFlags, ComposeOpts, ComposeProject, TaggedImage, @@ -1643,39 +1642,39 @@ function truncateString(str: string, len: number): string { return str.slice(0, str.lastIndexOf('\n')); } -export const composeCliFlags: flags.Input = { - emulated: flags.boolean({ +export const composeCliFlags = { + emulated: Flags.boolean({ description: 'Use QEMU for ARM architecture emulation during the image build', char: 'e', }), - dockerfile: flags.string({ + dockerfile: Flags.string({ description: 'Alternative Dockerfile name/path, relative to the source folder', }), - nologs: flags.boolean({ + nologs: Flags.boolean({ description: 'Hide the image build log output (produce less verbose output)', }), - 'multi-dockerignore': flags.boolean({ + 'multi-dockerignore': Flags.boolean({ description: 'Have each service use its own .dockerignore file. See "balena help build".', char: 'm', }), - 'noparent-check': flags.boolean({ + 'noparent-check': Flags.boolean({ description: "Disable project validation check of 'docker-compose.yml' file in parent folder", }), - 'registry-secrets': flags.string({ + 'registry-secrets': Flags.string({ description: 'Path to a YAML or JSON file with passwords for a private Docker registry', char: 'R', }), - 'noconvert-eol': flags.boolean({ + 'noconvert-eol': Flags.boolean({ description: "Don't convert line endings from CRLF (Windows format) to LF (Unix format).", }), - projectName: flags.string({ + projectName: Flags.string({ description: stripIndent`\ Name prefix for locally built images. This is the 'projectName' portion in 'projectName_serviceName:tag'. The default is the directory name.`, diff --git a/lib/utils/docker.ts b/lib/utils/docker.ts index 4242668c..f93ecf03 100644 --- a/lib/utils/docker.ts +++ b/lib/utils/docker.ts @@ -16,7 +16,7 @@ */ import type * as dockerode from 'dockerode'; -import { flags } from '@oclif/command'; +import { Flags } from '@oclif/core'; import { ExpectedError } from '../errors'; import { parseAsInteger } from './validation'; @@ -43,58 +43,58 @@ export interface DockerCliFlags extends DockerConnectionCliFlags { squash: boolean; } -export const dockerConnectionCliFlags: flags.Input = { - docker: flags.string({ +export const dockerConnectionCliFlags = { + docker: Flags.string({ description: 'Path to a local docker socket (e.g. /var/run/docker.sock)', char: 'P', }), - dockerHost: flags.string({ + dockerHost: Flags.string({ description: 'Docker daemon hostname or IP address (dev machine or balena device) ', char: 'h', }), - dockerPort: flags.integer({ + dockerPort: Flags.integer({ description: 'Docker daemon TCP port number (hint: 2375 for balena devices)', char: 'p', - parse: (p) => parseAsInteger(p, 'dockerPort'), + parse: async (p) => parseAsInteger(p, 'dockerPort'), }), - ca: flags.string({ + ca: Flags.string({ description: 'Docker host TLS certificate authority file', }), - cert: flags.string({ + cert: Flags.string({ description: 'Docker host TLS certificate file', }), - key: flags.string({ + key: Flags.string({ description: 'Docker host TLS key file', }), }; -export const dockerCliFlags: flags.Input = { - tag: flags.string({ +export const dockerCliFlags = { + tag: Flags.string({ description: `\ Tag locally built Docker images. This is the 'tag' portion in 'projectName_serviceName:tag'. The default is 'latest'.`, char: 't', }), - buildArg: flags.string({ + buildArg: Flags.string({ description: '[Deprecated] Set a build-time variable (eg. "-B \'ARG=value\'"). Can be specified multiple times.', char: 'B', multiple: true, }), - 'cache-from': flags.string({ + 'cache-from': Flags.string({ description: `\ Comma-separated list (no spaces) of image names for build cache resolution. \ Implements the same feature as the "docker build --cache-from" option.`, }), - nocache: flags.boolean({ + nocache: Flags.boolean({ description: "Don't use docker layer caching when building", }), - pull: flags.boolean({ + pull: Flags.boolean({ description: 'Pull the base images again even if they exist locally', }), - squash: flags.boolean({ + squash: Flags.boolean({ description: 'Squash newly built layers into a single new layer', }), ...dockerConnectionCliFlags, diff --git a/lib/utils/env-common.ts b/lib/utils/env-common.ts index 142e1fa0..194c2cbb 100644 --- a/lib/utils/env-common.ts +++ b/lib/utils/env-common.ts @@ -15,14 +15,12 @@ * limitations under the License. */ -import { flags } from '@oclif/command'; +import { Flags } from '@oclif/core'; import { stripIndent } from './lazy'; import { ExpectedError } from '../errors'; -import type { IBooleanFlag } from '@oclif/parser/lib/flags'; - -export const booleanConfig: IBooleanFlag = flags.boolean({ +export const booleanConfig = Flags.boolean({ char: 'c', description: 'select a configuration variable (may be used together with the --device option)', @@ -30,13 +28,13 @@ export const booleanConfig: IBooleanFlag = flags.boolean({ exclusive: ['service'], }); -export const booleanDevice: IBooleanFlag = flags.boolean({ +export const booleanDevice = Flags.boolean({ char: 'd', description: 'select a device-specific variable instead of a fleet variable', default: false, }); -export const booleanService: IBooleanFlag = flags.boolean({ +export const booleanService = Flags.boolean({ char: 's', description: 'select a service variable (may be used together with the --device option)', diff --git a/lib/utils/helpers.ts b/lib/utils/helpers.ts index 5c3f0361..8d040383 100644 --- a/lib/utils/helpers.ts +++ b/lib/utils/helpers.ts @@ -98,8 +98,9 @@ export async function runCommand(commandArgs: string[]): Promise { ...commandArgs.slice(2), ]; } - const { run } = require('@oclif/command'); - return run(commandArgs); + + const { run } = require('@oclif/core') as typeof import('@oclif/core'); + return run(commandArgs) as Promise; } export async function getManifest( diff --git a/lib/utils/normalization.ts b/lib/utils/normalization.ts index f19b00c5..cf179a29 100644 --- a/lib/utils/normalization.ts +++ b/lib/utils/normalization.ts @@ -78,6 +78,6 @@ export async function disambiguateReleaseParam( /** * Convert to lowercase if looks like slug */ -export function lowercaseIfSlug(s: string) { +export async function lowercaseIfSlug(s: string) { return s.includes('/') ? s.toLowerCase() : s; } diff --git a/lib/utils/oclif-utils.ts b/lib/utils/oclif-utils.ts index a9b05e50..ceac7501 100644 --- a/lib/utils/oclif-utils.ts +++ b/lib/utils/oclif-utils.ts @@ -15,48 +15,29 @@ * limitations under the License. */ -import { Main } from '@oclif/command'; -import type { Command } from '@oclif/core'; - /** * This class is a partial copy-and-paste of * @oclif/plugin-help/command/CommandHelp, which is used to generate oclif's * command help output. */ export class CommandHelp { - constructor(public command: { args?: any[] }) {} + constructor(public command: { args?: { [name: string]: any } }) {} - protected arg(arg: Command.Arg.Any): string { - const name = arg.name.toUpperCase(); - if (arg.required) { + protected arg(name: string, required: boolean): string { + name = name.toUpperCase(); + if (required) { return `${name}`; } return `[${name}]`; } public defaultUsage(): string { - return CommandHelp.compact([ - // this.command.id, - (this.command.args || []) - .filter((a) => !a.hidden) - .map((a) => this.arg(a)) - .join(' '), - ]).join(' '); - } + const argsObject = this.command.args ?? {}; - public static compact(array: Array): T[] { - return array.filter((a): a is T => !!a); - } -} - -export class CustomMain extends Main { - protected _helpOverride(): boolean { - // Disable oclif's default handler for the 'version' command - if (['-v', '--version', 'version'].includes(this.argv[0])) { - return false; - } else { - return super._helpOverride(); - } + return Object.entries(argsObject) + .filter(([_name, { hidden }]) => !hidden) + .map(([name, { required }]) => this.arg(name, required)) + .join(' '); } } diff --git a/lib/utils/validation.ts b/lib/utils/validation.ts index 94f54fcc..891ec39b 100644 --- a/lib/utils/validation.ts +++ b/lib/utils/validation.ts @@ -105,16 +105,12 @@ export function tryAsInteger(input: string): number | string { } } -export function parseAsLocalHostnameOrIp(input: string, paramName?: string) { +export async function parseAsLocalHostnameOrIp(input: string) { if (input && !validateLocalHostnameOrIp(input)) { - const message = - paramName == null - ? 'The parameter must be a local hostname or IP address.' - : `The parameter '${paramName}' must be a local hostname or IP address.`; - - throw new ExpectedError(message); + throw new ExpectedError( + 'The parameter must be a local hostname or IP address.', + ); } - return input; } diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 9ff0b3e0..bba630a2 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -13,7 +13,6 @@ "@balena/compose": "^3.0.2", "@balena/dockerignore": "^1.0.2", "@balena/es-version": "^1.0.1", - "@oclif/command": "^1.8.16", "@oclif/core": "^2.15.0", "@resin.io/valid-email": "^0.1.0", "@sentry/node": "^6.16.1", @@ -102,7 +101,6 @@ "devDependencies": { "@balena/lint": "^6.2.2", "@electron/notarize": "^2.0.0", - "@oclif/parser": "^3.8.6", "@octokit/plugin-throttling": "^3.5.1", "@octokit/rest": "^18.6.7", "@types/archiver": "^5.1.1", @@ -169,7 +167,7 @@ "mocha": "^8.4.0", "mock-require": "^3.0.3", "nock": "^13.2.1", - "oclif": "^3.9.1", + "oclif": "^3.15.0", "parse-link-header": "^2.0.0", "pkg": "^5.8.1", "publish-release": "^1.6.1", @@ -2139,43 +2137,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/@oclif/command": { - "version": "1.8.36", - "resolved": "https://registry.npmjs.org/@oclif/command/-/command-1.8.36.tgz", - "integrity": "sha512-/zACSgaYGtAQRzc7HjzrlIs14FuEYAZrMOEwicRoUnZVyRunG4+t5iSEeQu0Xy2bgbCD0U1SP/EdeNZSTXRwjQ==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dependencies": { - "@oclif/config": "^1.18.2", - "@oclif/errors": "^1.3.6", - "@oclif/help": "^1.0.1", - "@oclif/parser": "^3.8.17", - "debug": "^4.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@oclif/config": "^1" - } - }, - "node_modules/@oclif/config": { - "version": "1.18.16", - "resolved": "https://registry.npmjs.org/@oclif/config/-/config-1.18.16.tgz", - "integrity": "sha512-VskIxVcN22qJzxRUq+raalq6Q3HUde7sokB7/xk5TqRZGEKRVbFeqdQBxDWwQeudiJEgcNiMvIFbMQ43dY37FA==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dependencies": { - "@oclif/errors": "^1.3.6", - "@oclif/parser": "^3.8.16", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-wsl": "^2.1.1", - "tslib": "^2.6.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/@oclif/core": { "version": "2.15.0", "resolved": "https://registry.npmjs.org/@oclif/core/-/core-2.15.0.tgz", @@ -2320,179 +2281,13 @@ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" }, - "node_modules/@oclif/errors": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@oclif/errors/-/errors-1.3.6.tgz", - "integrity": "sha512-fYaU4aDceETd89KXP+3cLyg9EHZsLD3RxF2IU9yxahhBpspWjkWi3Dy3bTgcwZ3V47BgxQaGapzJWDM33XIVDQ==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dependencies": { - "clean-stack": "^3.0.0", - "fs-extra": "^8.1", - "indent-string": "^4.0.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@oclif/errors/node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/@oclif/errors/node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@oclif/errors/node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/@oclif/help": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/@oclif/help/-/help-1.0.15.tgz", - "integrity": "sha512-Yt8UHoetk/XqohYX76DfdrUYLsPKMc5pgkzsZVHDyBSkLiGRzujVaGZdjr32ckVZU9q3a47IjhWxhip7Dz5W/g==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dependencies": { - "@oclif/config": "1.18.16", - "@oclif/errors": "1.3.6", - "chalk": "^4.1.2", - "indent-string": "^4.0.0", - "lodash": "^4.17.21", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "widest-line": "^3.1.0", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@oclif/help/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@oclif/help/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@oclif/help/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@oclif/help/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@oclif/linewrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@oclif/linewrap/-/linewrap-1.0.0.tgz", - "integrity": "sha512-Ups2dShK52xXa8w6iBWLgcjPJWjais6KPJQq3gQ/88AY6BXoTX+MIGFPrWQO1KLMiQfoTpcLnUwloN4brrVUHw==" - }, - "node_modules/@oclif/parser": { - "version": "3.8.17", - "resolved": "https://registry.npmjs.org/@oclif/parser/-/parser-3.8.17.tgz", - "integrity": "sha512-l04iSd0xoh/16TGVpXb81Gg3z7tlQGrEup16BrVLsZBK6SEYpYHRJZnM32BwZrHI97ZSFfuSwVlzoo6HdsaK8A==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dependencies": { - "@oclif/errors": "^1.3.6", - "@oclif/linewrap": "^1.0.0", - "chalk": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@oclif/parser/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@oclif/parser/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@oclif/parser/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@oclif/plugin-help": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-5.2.17.tgz", - "integrity": "sha512-8dhvATZZnkD8uq3etsvbVjjpdxiTqXTPjkMlU8ToQz09DL5BBzYApm65iTHFE0Vn9DPbKcNxX1d8YiF3ilgMOQ==", + "version": "5.2.19", + "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-5.2.19.tgz", + "integrity": "sha512-gf6/dFtzMJ8RA4ovlBCBGJsZsd4jPXhYWJho+Gh6KmA+Ev9LupoExbE0qT+a2uHJyHEvIg4uX/MBW3qdERD/8g==", "dev": true, "dependencies": { - "@oclif/core": "^2.11.8" + "@oclif/core": "^2.15.0" }, "engines": { "node": ">=12.0.0" @@ -14327,9 +14122,9 @@ } }, "node_modules/oclif": { - "version": "3.11.3", - "resolved": "https://registry.npmjs.org/oclif/-/oclif-3.11.3.tgz", - "integrity": "sha512-6bUVTbTflu+IN9UnuIt5S4ba052oqLqsZF6zV2U8bx6ZH+hzgc3aXPTJ5JHU2MbDUg1B9PG5zHAbmvoX7V+16Q==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/oclif/-/oclif-3.15.0.tgz", + "integrity": "sha512-iwIjseO6Zuw1X66bN268yVuT6U7Qfwcp4CP4FZ/fd/a81eqQ6CTSCfeInjn/uDhfC/U01KrbNZPIZCMkjWHzxA==", "dev": true, "dependencies": { "@oclif/core": "^2.11.4", @@ -24758,32 +24553,6 @@ } } }, - "@oclif/command": { - "version": "1.8.36", - "resolved": "https://registry.npmjs.org/@oclif/command/-/command-1.8.36.tgz", - "integrity": "sha512-/zACSgaYGtAQRzc7HjzrlIs14FuEYAZrMOEwicRoUnZVyRunG4+t5iSEeQu0Xy2bgbCD0U1SP/EdeNZSTXRwjQ==", - "requires": { - "@oclif/config": "^1.18.2", - "@oclif/errors": "^1.3.6", - "@oclif/help": "^1.0.1", - "@oclif/parser": "^3.8.17", - "debug": "^4.1.1", - "semver": "^7.5.4" - } - }, - "@oclif/config": { - "version": "1.18.16", - "resolved": "https://registry.npmjs.org/@oclif/config/-/config-1.18.16.tgz", - "integrity": "sha512-VskIxVcN22qJzxRUq+raalq6Q3HUde7sokB7/xk5TqRZGEKRVbFeqdQBxDWwQeudiJEgcNiMvIFbMQ43dY37FA==", - "requires": { - "@oclif/errors": "^1.3.6", - "@oclif/parser": "^3.8.16", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-wsl": "^2.1.1", - "tslib": "^2.6.1" - } - }, "@oclif/core": { "version": "2.15.0", "resolved": "https://registry.npmjs.org/@oclif/core/-/core-2.15.0.tgz", @@ -24890,140 +24659,13 @@ } } }, - "@oclif/errors": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@oclif/errors/-/errors-1.3.6.tgz", - "integrity": "sha512-fYaU4aDceETd89KXP+3cLyg9EHZsLD3RxF2IU9yxahhBpspWjkWi3Dy3bTgcwZ3V47BgxQaGapzJWDM33XIVDQ==", - "requires": { - "clean-stack": "^3.0.0", - "fs-extra": "^8.1", - "indent-string": "^4.0.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" - } - } - }, - "@oclif/help": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/@oclif/help/-/help-1.0.15.tgz", - "integrity": "sha512-Yt8UHoetk/XqohYX76DfdrUYLsPKMc5pgkzsZVHDyBSkLiGRzujVaGZdjr32ckVZU9q3a47IjhWxhip7Dz5W/g==", - "requires": { - "@oclif/config": "1.18.16", - "@oclif/errors": "1.3.6", - "chalk": "^4.1.2", - "indent-string": "^4.0.0", - "lodash": "^4.17.21", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "widest-line": "^3.1.0", - "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "@oclif/linewrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@oclif/linewrap/-/linewrap-1.0.0.tgz", - "integrity": "sha512-Ups2dShK52xXa8w6iBWLgcjPJWjais6KPJQq3gQ/88AY6BXoTX+MIGFPrWQO1KLMiQfoTpcLnUwloN4brrVUHw==" - }, - "@oclif/parser": { - "version": "3.8.17", - "resolved": "https://registry.npmjs.org/@oclif/parser/-/parser-3.8.17.tgz", - "integrity": "sha512-l04iSd0xoh/16TGVpXb81Gg3z7tlQGrEup16BrVLsZBK6SEYpYHRJZnM32BwZrHI97ZSFfuSwVlzoo6HdsaK8A==", - "requires": { - "@oclif/errors": "^1.3.6", - "@oclif/linewrap": "^1.0.0", - "chalk": "^4.1.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "@oclif/plugin-help": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-5.2.17.tgz", - "integrity": "sha512-8dhvATZZnkD8uq3etsvbVjjpdxiTqXTPjkMlU8ToQz09DL5BBzYApm65iTHFE0Vn9DPbKcNxX1d8YiF3ilgMOQ==", + "version": "5.2.19", + "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-5.2.19.tgz", + "integrity": "sha512-gf6/dFtzMJ8RA4ovlBCBGJsZsd4jPXhYWJho+Gh6KmA+Ev9LupoExbE0qT+a2uHJyHEvIg4uX/MBW3qdERD/8g==", "dev": true, "requires": { - "@oclif/core": "^2.11.8" + "@oclif/core": "^2.15.0" } }, "@oclif/plugin-not-found": { @@ -34524,9 +34166,9 @@ } }, "oclif": { - "version": "3.11.3", - "resolved": "https://registry.npmjs.org/oclif/-/oclif-3.11.3.tgz", - "integrity": "sha512-6bUVTbTflu+IN9UnuIt5S4ba052oqLqsZF6zV2U8bx6ZH+hzgc3aXPTJ5JHU2MbDUg1B9PG5zHAbmvoX7V+16Q==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/oclif/-/oclif-3.15.0.tgz", + "integrity": "sha512-iwIjseO6Zuw1X66bN268yVuT6U7Qfwcp4CP4FZ/fd/a81eqQ6CTSCfeInjn/uDhfC/U01KrbNZPIZCMkjWHzxA==", "dev": true, "requires": { "@oclif/core": "^2.11.4", diff --git a/package.json b/package.json index 48da8658..416c560c 100644 --- a/package.json +++ b/package.json @@ -103,6 +103,9 @@ "prerun": "./build/hooks/prerun/track", "command_not_found": "./build/hooks/command-not-found/suggest" }, + "additionalHelpFlags": [ + "help" + ], "macos": { "identifier": "io.balena.cli", "sign": "Developer ID Installer: Balena Ltd (66H43P8FRG)" @@ -111,7 +114,6 @@ "devDependencies": { "@balena/lint": "^6.2.2", "@electron/notarize": "^2.0.0", - "@oclif/parser": "^3.8.6", "@octokit/plugin-throttling": "^3.5.1", "@octokit/rest": "^18.6.7", "@types/archiver": "^5.1.1", @@ -178,6 +180,7 @@ "mocha": "^8.4.0", "mock-require": "^3.0.3", "nock": "^13.2.1", + "oclif": "^3.15.0", "parse-link-header": "^2.0.0", "pkg": "^5.8.1", "publish-release": "^1.6.1", @@ -185,14 +188,12 @@ "simple-git": "^3.14.1", "sinon": "^11.1.2", "ts-node": "^10.4.0", - "oclif": "^3.9.1", "typescript": "^5.1.3" }, "dependencies": { "@balena/compose": "^3.0.2", "@balena/dockerignore": "^1.0.2", "@balena/es-version": "^1.0.1", - "@oclif/command": "^1.8.16", "@oclif/core": "^2.15.0", "@resin.io/valid-email": "^0.1.0", "@sentry/node": "^6.16.1", diff --git a/patches/all/@oclif+config+1.18.16.patch b/patches/all/@oclif+config+1.18.16.patch deleted file mode 100644 index facffe54..00000000 --- a/patches/all/@oclif+config+1.18.16.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/node_modules/@oclif/config/lib/config.js b/node_modules/@oclif/config/lib/config.js -index aba1da9..830f800 100644 ---- a/node_modules/@oclif/config/lib/config.js -+++ b/node_modules/@oclif/config/lib/config.js -@@ -165,7 +165,9 @@ class Config { - debug('runCommand %s %o', id, argv); - const c = this.findCommand(id); - if (!c) { -- await this.runHook('command_not_found', { id }); -+ // argv added to command_not_found hook -+ // We should try to upstream this change -+ await this.runHook('command_not_found', { id, argv }); - throw new errors_1.CLIError(`command ${id} not found`); - } - const command = c.load(); diff --git a/patches/all/@oclif+core+2.15.0.patch b/patches/all/@oclif+core+2.15.0.patch index aa716a62..18973462 100644 --- a/patches/all/@oclif+core+2.15.0.patch +++ b/patches/all/@oclif+core+2.15.0.patch @@ -1,3 +1,16 @@ +diff --git a/node_modules/@oclif/core/lib/cli-ux/list.js b/node_modules/@oclif/core/lib/cli-ux/list.js +index dc6058c..64b2f85 100644 +--- a/node_modules/@oclif/core/lib/cli-ux/list.js ++++ b/node_modules/@oclif/core/lib/cli-ux/list.js +@@ -22,7 +22,7 @@ function renderList(items) { + } + left = left.padEnd(maxLength); + right = linewrap(maxLength + 2, right); +- return `${left} ${right}`; ++ return `${left} : ${right}`; + }); + return lines.join('\n'); + } diff --git a/node_modules/@oclif/core/lib/help/command.js b/node_modules/@oclif/core/lib/help/command.js index 6de139b..3a13197 100644 --- a/node_modules/@oclif/core/lib/help/command.js @@ -47,3 +60,47 @@ index f9ef7cc..a14c67c 100644 const aliases = []; const uniqueSubCommands = subCommands.filter(p => { aliases.push(...p.aliases); +diff --git a/node_modules/@oclif/core/lib/parser/errors.js b/node_modules/@oclif/core/lib/parser/errors.js +index 07ec8e5..a4560ea 100644 +--- a/node_modules/@oclif/core/lib/parser/errors.js ++++ b/node_modules/@oclif/core/lib/parser/errors.js +@@ -10,7 +10,8 @@ var errors_2 = require("../errors"); + Object.defineProperty(exports, "CLIError", { enumerable: true, get: function () { return errors_2.CLIError; } }); + class CLIParseError extends errors_1.CLIError { + constructor(options) { +- options.message += '\nSee more help with --help'; ++ const help = options.command ? `\`${options.command} --help\`` : '--help'; ++ options.message += `\nSee more help with ${help}`; + super(options.message); + this.parse = options.parse; + } +@@ -31,7 +32,8 @@ class InvalidArgsSpecError extends CLIParseError { + exports.InvalidArgsSpecError = InvalidArgsSpecError; + class RequiredArgsError extends CLIParseError { + constructor({ args, parse, flagsWithMultiple }) { +- let message = `Missing ${args.length} required arg${args.length === 1 ? '' : 's'}`; ++ const command = 'balena ' + parse.input.context.id.replace(/:/g, ' '); ++ let message = `Missing ${args.length} required argument${args.length === 1 ? '' : 's'}`; + const namedArgs = args.filter(a => a.name); + if (namedArgs.length > 0) { + const list = (0, list_1.renderList)(namedArgs.map(a => [a.name, a.description])); +@@ -42,16 +44,17 @@ class RequiredArgsError extends CLIParseError { + message += `\n\nNote: ${flags} allow${flagsWithMultiple.length === 1 ? 's' : ''} multiple values. Because of this you need to provide all arguments before providing ${flagsWithMultiple.length === 1 ? 'that flag' : 'those flags'}.`; + message += '\nAlternatively, you can use "--" to signify the end of the flags and the beginning of arguments.'; + } +- super({ parse, message }); ++ super({ parse, message, command }); + this.args = args; + } + } + exports.RequiredArgsError = RequiredArgsError; + class RequiredFlagError extends CLIParseError { + constructor({ flag, parse }) { ++ const command = 'balena ' + parse.input.context.id.replace(/:/g, ' '); + const usage = (0, list_1.renderList)((0, help_1.flagUsages)([flag], { displayRequired: false })); + const message = `Missing required flag:\n${usage}`; +- super({ parse, message }); ++ super({ parse, message, command }); + this.flag = flag; + } + } diff --git a/patches/all/@oclif+parser+3.8.17.patch b/patches/all/@oclif+parser+3.8.17.patch deleted file mode 100644 index a3dd8d91..00000000 --- a/patches/all/@oclif+parser+3.8.17.patch +++ /dev/null @@ -1,55 +0,0 @@ -diff --git a/node_modules/@oclif/parser/lib/errors.js b/node_modules/@oclif/parser/lib/errors.js -index 39936e3..23e3925 100644 ---- a/node_modules/@oclif/parser/lib/errors.js -+++ b/node_modules/@oclif/parser/lib/errors.js -@@ -14,7 +14,8 @@ const m = deps_1.default() - .add('list', () => require('./list')); - class CLIParseError extends errors_1.CLIError { - constructor(options) { -- options.message += '\nSee more help with --help'; -+ const help = options.command ? `\`${options.command} --help\`` : '--help'; -+ options.message += `\nSee more help with ${help}`; - super(options.message); - this.parse = options.parse; - } -@@ -35,22 +36,24 @@ class InvalidArgsSpecError extends CLIParseError { - exports.InvalidArgsSpecError = InvalidArgsSpecError; - class RequiredArgsError extends CLIParseError { - constructor({ args, parse }) { -- let message = `Missing ${args.length} required arg${args.length === 1 ? '' : 's'}`; -+ const command = 'balena ' + parse.input.context.id.replace(/:/g, ' '); -+ let message = `Missing ${args.length} required argument${args.length === 1 ? '' : 's'}`; - const namedArgs = args.filter(a => a.name); - if (namedArgs.length > 0) { - const list = m.list.renderList(namedArgs.map(a => [a.name, a.description])); - message += `:\n${list}`; - } -- super({ parse, message }); -+ super({ parse, message, command }); - this.args = args; - } - } - exports.RequiredArgsError = RequiredArgsError; - class RequiredFlagError extends CLIParseError { - constructor({ flag, parse }) { -+ const command = 'balena ' + parse.input.context.id.replace(/:/g, ' '); - const usage = m.list.renderList(m.help.flagUsages([flag], { displayRequired: false })); - const message = `Missing required flag:\n${usage}`; -- super({ parse, message }); -+ super({ parse, message, command }); - this.flag = flag; - } - } -diff --git a/node_modules/@oclif/parser/lib/list.js b/node_modules/@oclif/parser/lib/list.js -index 9d020b7..6ea9eb9 100644 ---- a/node_modules/@oclif/parser/lib/list.js -+++ b/node_modules/@oclif/parser/lib/list.js -@@ -22,7 +22,7 @@ function renderList(items) { - } - left = left.padEnd(maxLength); - right = linewrap(maxLength + 2, right); -- return `${left} ${right}`; -+ return `${left} : ${right}`; - }); - return lines.join('\n'); - } diff --git a/patches/all/oclif+3.11.3.patch b/patches/all/oclif+3.15.0.patch similarity index 88% rename from patches/all/oclif+3.11.3.patch rename to patches/all/oclif+3.15.0.patch index 1e62bf87..c228ec9f 100644 --- a/patches/all/oclif+3.11.3.patch +++ b/patches/all/oclif+3.15.0.patch @@ -13,10 +13,10 @@ index d06d0b3..c571fe3 100644 const arches = _.uniq(buildConfig.targets .filter(t => t.platform === 'darwin') diff --git a/node_modules/oclif/lib/commands/pack/win.js b/node_modules/oclif/lib/commands/pack/win.js -index 360c34b..ae14bf5 100644 +index c0926bd..a37cd6e 100644 --- a/node_modules/oclif/lib/commands/pack/win.js +++ b/node_modules/oclif/lib/commands/pack/win.js -@@ -59,6 +59,13 @@ InstallDir "\$PROGRAMFILES${arch === 'x64' ? '64' : ''}\\${config.dirname}" +@@ -59,6 +59,12 @@ InstallDir "\$PROGRAMFILES${arch === 'x64' ? '64' : ''}\\${config.dirname}" ${customization} Section "${config.name} CLI \${VERSION}" @@ -26,21 +26,10 @@ index 360c34b..ae14bf5 100644 + ; unable to make script wait for completion (despite using _?) + DetailPrint "Removing files from previous version." + RMDir /r "$INSTDIR\\client" -+ SetOutPath $INSTDIR File /r bin File /r client -@@ -203,7 +210,8 @@ class PackWin extends core_1.Command { - async run() { - await this.checkForNSIS(); - const { flags } = await this.parse(PackWin); -- const buildConfig = await Tarballs.buildConfig(flags.root); -+ const $targets = flags.targets ? flags.targets.split(',') : undefined; -+ const buildConfig = await Tarballs.buildConfig(flags.root, { targets: $targets }); - const { config } = buildConfig; - await Tarballs.build(buildConfig, { platform: 'win32', pack: false, tarball: flags.tarball, parallel: true }); - const arches = buildConfig.targets.filter(t => t.platform === 'win32').map(t => t.arch); -@@ -225,7 +233,8 @@ class PackWin extends core_1.Command { +@@ -226,7 +232,8 @@ class PackWin extends core_1.Command { fs.writeFile(path.join(installerBase, 'bin', `${flags['additional-cli']}`), scripts.sh({ bin: flags['additional-cli'] })), ] : [])); await fs.move(buildConfig.workspace({ platform: 'win32', arch }), path.join(installerBase, 'client')); @@ -50,21 +39,11 @@ index 360c34b..ae14bf5 100644 const templateKey = (0, upload_util_1.templateShortKey)('win32', { bin: config.bin, version: config.version, sha: buildConfig.gitSha, arch }); const o = buildConfig.dist(`win32/${templateKey}`); await fs.move(path.join(installerBase, 'installer.exe'), o); -@@ -263,6 +272,9 @@ PackWin.flags = { - default: '.', - required: true, - }), -+ targets: core_1.Flags.string({ -+ description: 'comma-separated targets to pack (e.g.: win32-x86,win32-x64)' -+ }), - 'additional-cli': core_1.Flags.string({ - description: `an Oclif CLI other than the one listed in config.bin that should be made available to the user - the CLI should already exist in a directory named after the CLI that is the root of the tarball produced by "oclif pack:tarballs"`, diff --git a/node_modules/oclif/lib/tarballs/build.js b/node_modules/oclif/lib/tarballs/build.js -index 384ea4b..41963eb 100644 +index 384ea4b..602daa4 100644 --- a/node_modules/oclif/lib/tarballs/build.js +++ b/node_modules/oclif/lib/tarballs/build.js -@@ -21,8 +21,10 @@ const pack = async (from, to) => { +@@ -21,7 +21,8 @@ const pack = async (from, to) => { await exec(`tar cfJ ${to} ${(path.basename(from))}`, { cwd })); }; async function build(c, options = {}) { @@ -72,11 +51,9 @@ index 384ea4b..41963eb 100644 + const { xz, config, tmp } = c; + console.error(`[debug] oclif c.root="${c.root}" c.workspace()="${c.workspace()}"`); const packCLI = async () => { -+ console.error('[debug] packing cli'); const { stdout } = await exec('npm pack --unsafe-perm', { cwd: c.root }); return path.join(c.root, stdout.trim().split('\n').pop()); - }; -@@ -30,7 +32,8 @@ async function build(c, options = {}) { +@@ -30,7 +31,8 @@ async function build(c, options = {}) { await fs.emptyDir(c.workspace()); const tarballNewLocation = path.join(c.workspace(), path.basename(tarball)); await fs.move(tarball, tarballNewLocation); @@ -86,7 +63,7 @@ index 384ea4b..41963eb 100644 await Promise.all((await fs.promises.readdir(path.join(c.workspace(), 'package'), { withFileTypes: true })) .map(i => fs.move(path.join(c.workspace(), 'package', i.name), path.join(c.workspace(), i.name)))); await Promise.all([ -@@ -38,6 +41,13 @@ async function build(c, options = {}) { +@@ -38,6 +40,13 @@ async function build(c, options = {}) { fs.promises.rm(path.join(c.workspace(), path.basename(tarball)), { recursive: true }), fs.remove(path.join(c.workspace(), 'bin', 'run.cmd')), ]); @@ -100,7 +77,7 @@ index 384ea4b..41963eb 100644 }; const updatePJSON = async () => { const pjsonPath = path.join(c.workspace(), 'package.json'); -@@ -49,35 +59,20 @@ async function build(c, options = {}) { +@@ -49,35 +58,20 @@ async function build(c, options = {}) { await fs.writeJSON(pjsonPath, pjson, { spaces: 2 }); }; const addDependencies = async () => { @@ -149,7 +126,7 @@ index 384ea4b..41963eb 100644 }; const pretarball = async () => { const pjson = await fs.readJSON(path.join(c.workspace(), 'package.json')); -@@ -115,7 +110,8 @@ async function build(c, options = {}) { +@@ -115,7 +109,8 @@ async function build(c, options = {}) { output: path.join(workspace, 'bin', 'node'), platform: target.platform, arch: target.arch, @@ -159,7 +136,7 @@ index 384ea4b..41963eb 100644 }); if (options.pack === false) return; -@@ -158,6 +154,7 @@ async function build(c, options = {}) { +@@ -158,6 +153,7 @@ async function build(c, options = {}) { await fs.writeJSON(manifestFilepath, manifest, { spaces: 2 }); }; (0, log_1.log)(`gathering workspace for ${config.bin} to ${c.workspace()}`); @@ -168,7 +145,7 @@ index 384ea4b..41963eb 100644 await updatePJSON(); await addDependencies(); diff --git a/node_modules/oclif/lib/tarballs/config.js b/node_modules/oclif/lib/tarballs/config.js -index 216759d..f7bfbfe 100644 +index 216759d..cab0e6e 100644 --- a/node_modules/oclif/lib/tarballs/config.js +++ b/node_modules/oclif/lib/tarballs/config.js @@ -25,7 +25,10 @@ async function gitSha(cwd, options = {}) { @@ -176,9 +153,9 @@ index 216759d..f7bfbfe 100644 exports.gitSha = gitSha; async function Tmp(config) { - const tmp = path.join(config.root, 'tmp'); -+ const tmp = process.env.BUILD_TMP -+ ? path.join(process.env.BUILD_TMP, 'oclif') -+ : path.join(config.root, 'tmp'); ++ const tmp = process.env.BUILD_TMP ++ ? path.join(process.env.BUILD_TMP, 'oclif') ++ : path.join(config.root, 'tmp'); + console.error(`[debug] oclif tmp="${tmp}"`); await fs.promises.mkdir(tmp, { recursive: true }); return tmp; diff --git a/tests/helpers.ts b/tests/helpers.ts index 3c2075d7..7a8a9ab3 100644 --- a/tests/helpers.ts +++ b/tests/helpers.ts @@ -106,6 +106,7 @@ async function runCommandInProcess(cmd: string): Promise { try { await balenaCLI.run(preArgs.concat(cmd.split(' ').filter((c) => c)), { + configPath: path.resolve(__dirname, '..'), noFlush: true, }); } finally {