diff --git a/.eslintrc.js b/.eslintrc.cjs similarity index 100% rename from .eslintrc.js rename to .eslintrc.cjs diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c310c863..b5bc0b9f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,14 +8,14 @@ The balena CLI is an open source project and your contribution is welcome! * Clone the `balena-cli` repository (or a [forked repo](https://docs.github.com/en/free-pro-team@latest/github/getting-started-with-github/fork-a-repo), if you are not in the balena team), `cd` to it and run `npm install`. -* Build the CLI with `npm run build` or `npm test`, and execute it with `./bin/balena` +* Build the CLI with `npm run build` or `npm test`, and execute it with `./bin/balena.js` (on a Windows command prompt, you may need to run `node .\bin\balena`). In order to ease development: * `npm run build:fast` skips some of the build steps for interactive testing, or * `npm run test:source` skips testing the standalone zip packages (which is rather slow) -* `./bin/balena-dev` uses `ts-node/register` to transpile on the fly. +* `./bin/balena-dev.js` uses `ts-node/register` to transpile on the fly. Before opening a PR, test your changes with `npm test`. Keep compatibility in mind, as the CLI is meant to run on Linux, macOS and Windows. balena CI will run test code on all three platforms, but @@ -230,7 +230,7 @@ be automatically invalidated if `npm link` is used or if manual modifications ar `node_modules` folder. In this situation: * Manually delete the module cache file (typically `~/.balena/cli-module-cache.json`), or -* Use the `bin/balena-dev` entry point (instead of `bin/balena`) as it does not activate +* Use the `bin/balena-dev.js` entry point (instead of `bin/balena.js`) as it does not activate `fast-boot2`. ## TypeScript and oclif diff --git a/INSTALL-ADVANCED.md b/INSTALL-ADVANCED.md index 9b4f2e02..9cac0c61 100644 --- a/INSTALL-ADVANCED.md +++ b/INSTALL-ADVANCED.md @@ -40,7 +40,7 @@ By default, the CLI is installed to the following folders: OS | Folders --- | --- Windows: | `C:\Program Files\balena-cli\` -macOS: | `/usr/local/lib/balena-cli/`
`/usr/local/bin/balena` +macOS: | `/usr/local/lib/balena-cli/`
`/usr/local/bin/balena.js` ## Standalone Zip Package @@ -65,7 +65,7 @@ macOS: | `/usr/local/lib/balena-cli/`
`/usr/local/bin/balena` > these "compact" Linux distributions, because of the alternative C libraries they ship with. > For these, consider the [NPM Installation](#npm-installation) option. > * Note that moving the `balena` executable out of the extracted `balena-cli` folder on its own -> (e.g. moving it to `/usr/local/bin/balena`) will **not** work, as it depends on the other +> (e.g. moving it to `/usr/local/bin/balena.js`) will **not** work, as it depends on the other > folders and files also present in the `balena-cli` folder. To update the CLI to a new version, download a new release zip file and replace the previous diff --git a/automation/capitanodoc/index.ts b/automation/capitanodoc/index.ts index 0c605cf0..84f62003 100644 --- a/automation/capitanodoc/index.ts +++ b/automation/capitanodoc/index.ts @@ -83,6 +83,7 @@ function importOclifCommands(jsFilename: string): OclifCommand[] { // an error when parsed. This should be improved so that `usage` does not have // to be overridden if not necessary. + // TODO: FIX ME ESM const command: OclifCommand = jsFilename === 'help' ? (new FakeHelpCommand() as unknown as OclifCommand) diff --git a/automation/utils.ts b/automation/utils.ts index 87921024..84b6acb2 100644 --- a/automation/utils.ts +++ b/automation/utils.ts @@ -97,7 +97,7 @@ export function loadPackageJson() { * @returns The program's full path, e.g. 'C:\WINDOWS\System32\OpenSSH\ssh.EXE' */ export async function which(program: string): Promise { - const whichMod = await import('which'); + const { default: whichMod } = await import('which'); let programPath: string; try { programPath = await whichMod(program); diff --git a/bin/balena-dev b/bin/balena-dev.js similarity index 97% rename from bin/balena-dev rename to bin/balena-dev.js index ceaf47c4..9bc7f43c 100755 --- a/bin/balena-dev +++ b/bin/balena-dev.js @@ -2,7 +2,7 @@ // **************************************************************************** // THIS IS FOR DEV PURPOSES ONLY AND WILL NOT BE PART OF THE PUBLISHED PACKAGE -// Before opening a PR you should build and test your changes using bin/balena +// Before opening a PR you should build and test your changes using bin/balena.js // **************************************************************************** // We boost the threadpool size as ext2fs can deadlock with some @@ -57,7 +57,10 @@ require('ts-node').register({ project: path.join(rootDir, 'tsconfig.json'), transpileOnly: true, }); -require('../lib/app').run(undefined, { dir: __dirname, development: true }); +require('../lib/app').run(undefined, { + dir: import.meta.url, + development: true, +}); // Modify package.json oclif paths from build/ -> lib/, or vice versa function modifyOclifPaths(revert) { diff --git a/bin/balena b/bin/balena.js similarity index 67% rename from bin/balena rename to bin/balena.js index 6a27aff0..8927d4c3 100755 --- a/bin/balena +++ b/bin/balena.js @@ -9,13 +9,13 @@ process.env.OCLIF_TS_NODE = 0; async function run() { // Use fast-boot to cache require lookups, speeding up startup - await require('../build/fast-boot').start(); + await (await import('../build/fast-boot.js')).start(); // Set the desired es version for downstream modules that support it - require('@balena/es-version').set('es2018'); + (await import('@balena/es-version')).set('es2018'); // Run the CLI - await require('../build/app').run(undefined, { dir: __dirname }); + await (await import('../build/app.js')).run(undefined, { dir: import.meta.url }); } -run(); +await run(); diff --git a/lib/app.ts b/lib/app.ts index 4794bd51..654d94e8 100644 --- a/lib/app.ts +++ b/lib/app.ts @@ -15,23 +15,23 @@ * limitations under the License. */ -import * as packageJSON from '../package.json'; +import packageJSON from '../package.json' assert {type: 'json'}; import { AppOptions, checkDeletedCommand, preparseArgs, unsupportedFlag, -} from './preparser'; -import { CliSettings } from './utils/bootstrap'; -import { onceAsync } from './utils/lazy'; -import { run as mainRun, settings } from '@oclif/core'; +} from './preparser.js'; +import { CliSettings } from './utils/bootstrap.js'; +import { onceAsync } from './utils/lazy.js'; +import { run as mainRun, flush, settings } from '@oclif/core'; /** * Sentry.io setup * @see https://docs.sentry.io/error-reporting/quickstart/?platform=node */ export const setupSentry = onceAsync(async () => { - const config = await import('./config'); + const config = await import('./config.js'); const Sentry = await import('@sentry/node'); Sentry.init({ autoSessionTracking: false, @@ -51,14 +51,14 @@ export const setupSentry = onceAsync(async () => { async function checkNodeVersion() { const validNodeVersions = packageJSON.engines.node; if (!(await import('semver')).satisfies(process.version, validNodeVersions)) { - const { getNodeEngineVersionWarn } = await import('./utils/messages'); + const { getNodeEngineVersionWarn } = await import('./utils/messages.js'); console.warn(getNodeEngineVersionWarn(process.version, validNodeVersions)); } } /** Setup balena-sdk options that are shared with imported packages */ -function setupBalenaSdkSharedOptions(settings: CliSettings) { - const BalenaSdk = require('balena-sdk') as typeof import('balena-sdk'); +async function setupBalenaSdkSharedOptions(settings: CliSettings) { + const BalenaSdk = await import('balena-sdk'); BalenaSdk.setSharedOptions({ apiUrl: settings.get('apiUrl'), dataDirectory: settings.get('dataDirectory'), @@ -71,8 +71,8 @@ function setupBalenaSdkSharedOptions(settings: CliSettings) { * leak detected. 11 error listeners added. Use emitter.setMaxListeners() to * increase limit */ -export function setMaxListeners(maxListeners: number) { - require('events').EventEmitter.defaultMaxListeners = maxListeners; +export async function setMaxListeners(maxListeners: number) { + (await import('events')).EventEmitter.defaultMaxListeners = maxListeners; } /** Selected CLI initialization steps */ @@ -89,13 +89,13 @@ async function init() { const settings = new CliSettings(); // Proxy setup should be done early on, before loading balena-sdk - await (await import('./utils/proxy')).setupGlobalHttpProxy(settings); + await (await import('./utils/proxy.js')).setupGlobalHttpProxy(settings); - setupBalenaSdkSharedOptions(settings); + await setupBalenaSdkSharedOptions(settings); // check for CLI updates once a day if (!process.env.BALENARC_OFFLINE_MODE) { - (await import('./utils/update')).notify(); + (await import('./utils/update.js')).notify(); } } @@ -106,7 +106,7 @@ async function oclifRun(command: string[], options: AppOptions) { if (unsupportedFlag || process.env.BALENARC_UNSUPPORTED) { deprecationPromise = Promise.resolve(); } else { - const { DeprecationChecker } = await import('./deprecation'); + const { DeprecationChecker } = await import('./deprecation.js'); const deprecationChecker = new DeprecationChecker(packageJSON.version); // warnAndAbortIfDeprecated uses previously cached data only await deprecationChecker.warnAndAbortIfDeprecated(); @@ -137,7 +137,7 @@ async function oclifRun(command: string[], options: AppOptions) { } } if (shouldFlush) { - await import('@oclif/core/flush'); + await 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. @@ -148,20 +148,20 @@ async function oclifRun(command: string[], options: AppOptions) { // the try/catch block above, execution does not get past the // Promise.all() call below, but I don't understand why. if (isEEXIT) { - (await import('./fast-boot')).stop(); + (await import('./fast-boot.js')).stop(); } })(!options.noFlush); - const { trackPromise } = await import('./hooks/prerun/track'); + const { trackPromise } = await import('./hooks/prerun/track.js'); await Promise.all([trackPromise, deprecationPromise, runPromise]); } -/** CLI entrypoint. Called by the `bin/balena` and `bin/balena-dev` scripts. */ +/** CLI entrypoint. Called by the `bin/balena.js` and `bin/balena-dev.js` scripts. */ export async function run(cliArgs = process.argv, options: AppOptions) { try { const { setOfflineModeEnvVars, normalizeEnvVars, pkgExec } = await import( - './utils/bootstrap' + './utils/bootstrap.js' ); setOfflineModeEnvVars(); normalizeEnvVars(); @@ -175,15 +175,15 @@ export async function run(cliArgs = process.argv, options: AppOptions) { await init(); // Look for commands that have been removed and if so, exit with a notice - checkDeletedCommand(cliArgs.slice(2)); + await checkDeletedCommand(cliArgs.slice(2)); const args = await preparseArgs(cliArgs); await oclifRun(args, options); } catch (err) { - await (await import('./errors')).handleError(err); + await (await import('./errors.js')).handleError(err); } finally { try { - (await import('./fast-boot')).stop(); + (await import('./fast-boot.js')).stop(); } catch (e) { if (process.env.DEBUG) { console.error(`[debug] Stopping fast-boot: ${e}`); diff --git a/lib/auth/index.ts b/lib/auth/index.ts index 2089eaad..79779212 100644 --- a/lib/auth/index.ts +++ b/lib/auth/index.ts @@ -14,8 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { getBalenaSdk } from '../utils/lazy'; -import { LoginServer } from './server'; +import { getBalenaSdk } from '../utils/lazy.js'; +import { LoginServer } from './server.js'; /** * @module auth @@ -42,7 +42,7 @@ import { LoginServer } from './server'; * console.log("My session token is: #{sessionToken}") */ export async function login({ host = '127.0.0.1', port = 0 }) { - const utils = await import('./utils'); + const utils = await import('./utils.js'); const loginServer = new LoginServer(); const { @@ -55,10 +55,10 @@ export async function login({ host = '127.0.0.1', port = 0 }) { const loginUrl = await utils.getDashboardLoginURL(callbackUrl); console.info(`Opening web browser for URL:\n${loginUrl}`); - const open = await import('open'); + const { default: open } = await import('open'); await open(loginUrl, { wait: false }); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); const token = await loginServer.awaitForToken(); await balena.auth.loginWithToken(token); loginServer.shutdown(); diff --git a/lib/auth/server.ts b/lib/auth/server.ts index df7880d4..72d5fdef 100644 --- a/lib/auth/server.ts +++ b/lib/auth/server.ts @@ -14,14 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -import * as bodyParser from 'body-parser'; +import bodyParser from 'body-parser'; import { EventEmitter } from 'events'; -import * as express from 'express'; +import express from 'express'; import type { Socket } from 'net'; -import * as path from 'path'; +import path from 'path'; -import * as utils from './utils'; -import { ExpectedError } from '../errors'; +import * as utils from './utils.js'; +import { ExpectedError } from '../errors.js'; export class LoginServer extends EventEmitter { protected expressApp: express.Express; @@ -56,7 +56,7 @@ export class LoginServer extends EventEmitter { ); app.set('view engine', 'ejs'); - app.set('views', path.join(__dirname, 'pages')); + app.set('views', path.join(import.meta.url, 'pages')); this.server = await new Promise((resolve, reject) => { const callback = (err: Error) => { diff --git a/lib/auth/utils.ts b/lib/auth/utils.ts index 7956b55b..b528962f 100644 --- a/lib/auth/utils.ts +++ b/lib/auth/utils.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { getBalenaSdk } from '../utils/lazy'; +import { getBalenaSdk } from '../utils/lazy.js'; /** * Get dashboard CLI login URL @@ -32,7 +32,7 @@ export async function getDashboardLoginURL( const [{ URL }, dashboardUrl] = await Promise.all([ import('url'), - getBalenaSdk().settings.get('dashboardUrl'), + (await getBalenaSdk()).settings.get('dashboardUrl'), ]); return new URL(`/login/cli/${callbackUrl}`, dashboardUrl).href; } @@ -54,7 +54,7 @@ export async function loginIfTokenValid(token?: string): Promise { if (!token) { return false; } - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); let currentToken; try { diff --git a/lib/command.ts b/lib/command.ts index 1b260a35..b2442d2b 100644 --- a/lib/command.ts +++ b/lib/command.ts @@ -19,9 +19,9 @@ import { Command } from '@oclif/core'; import { InsufficientPrivilegesError, NotAvailableInOfflineModeError, -} from './errors'; -import { stripIndent } from './utils/lazy'; -import * as output from './framework/output'; +} from './errors.js'; +import { stripIndent } from './utils/lazy.js'; +import * as output from './framework/output.js'; export default abstract class BalenaCommand extends Command { /** @@ -71,7 +71,7 @@ export default abstract class BalenaCommand extends Command { * - other code needs to execute before check */ protected static async checkElevatedPrivileges() { - const isElevated = await (await import('is-elevated'))(); + const isElevated = await (await import('is-elevated')).default(); if (!isElevated) { throw new InsufficientPrivilegesError( 'You need root/admin privileges to run this command', @@ -94,7 +94,7 @@ export default abstract class BalenaCommand extends Command { * @throws {NotLoggedInError} */ public static async checkLoggedIn() { - await (await import('./utils/patterns')).checkLoggedIn(); + await (await import('./utils/patterns.js')).checkLoggedIn(); } /** @@ -139,14 +139,14 @@ export default abstract class BalenaCommand extends Command { * values from stdin based in configuration, minimising command implementation. */ protected async getStdin() { - this.stdin = await (await import('get-stdin'))(); + this.stdin = await (await import('get-stdin')).default(); } /** * Get a logger instance. */ protected static async getLogger() { - return (await import('./utils/logger')).getLogger(); + return (await import('./utils/logger.js')).default.getLogger(); } protected async init() { diff --git a/lib/commands/api-key/generate.ts b/lib/commands/api-key/generate.ts index c9213b67..00076b47 100644 --- a/lib/commands/api-key/generate.ts +++ b/lib/commands/api-key/generate.ts @@ -16,10 +16,10 @@ */ 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'; +import Command from '../../command.js'; +import { ExpectedError } from '../../errors.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; export default class GenerateCmd extends Command { public static description = stripIndent` @@ -53,7 +53,7 @@ export default class GenerateCmd extends Command { let key; try { - key = await getBalenaSdk().models.apiKey.create(params.name); + key = await (await getBalenaSdk()).models.apiKey.create(params.name); } catch (e) { if (e.name === 'BalenaNotLoggedIn') { throw new ExpectedError(stripIndent` diff --git a/lib/commands/api-key/revoke.ts b/lib/commands/api-key/revoke.ts index 27475f39..00051172 100644 --- a/lib/commands/api-key/revoke.ts +++ b/lib/commands/api-key/revoke.ts @@ -16,9 +16,9 @@ */ import { Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; export default class RevokeCmd extends Command { public static description = stripIndent` @@ -59,7 +59,8 @@ export default class RevokeCmd extends Command { } await Promise.all( apiKeyIds.map( - async (id) => await getBalenaSdk().models.apiKey.revoke(Number(id)), + async (id) => + await (await getBalenaSdk()).models.apiKey.revoke(Number(id)), ), ); console.log('Successfully revoked the given API keys'); diff --git a/lib/commands/api-keys/index.ts b/lib/commands/api-keys/index.ts index be8de21b..32941004 100644 --- a/lib/commands/api-keys/index.ts +++ b/lib/commands/api-keys/index.ts @@ -16,9 +16,9 @@ */ import { Flags } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy.js'; export default class ApiKeysCmd extends Command { public static description = stripIndent` @@ -46,15 +46,16 @@ export default class ApiKeysCmd extends Command { public async run() { const { flags: options } = await this.parse(ApiKeysCmd); - const { getApplication } = await import('../../utils/sdk'); + const { getApplication } = await import('../../utils/sdk.js'); + const balena = await getBalenaSdk(); const actorId = options.fleet ? ( - await getApplication(getBalenaSdk(), options.fleet, { + await getApplication(balena, options.fleet, { $select: 'actor', }) ).actor - : await getBalenaSdk().auth.getActorId(); - const keys = await getBalenaSdk().pine.get({ + : await balena.auth.getActorId(); + const keys = await balena.pine.get({ resource: 'api_key', options: { $select: ['id', 'created_at', 'name', 'description', 'expiry_date'], diff --git a/lib/commands/app/create.ts b/lib/commands/app/create.ts index 5d82e33c..60baabc8 100644 --- a/lib/commands/app/create.ts +++ b/lib/commands/app/create.ts @@ -17,9 +17,9 @@ import { Flags, Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { stripIndent } from '../../utils/lazy.js'; export default class AppCreateCmd extends Command { public static description = stripIndent` @@ -77,7 +77,7 @@ export default class AppCreateCmd extends Command { const { args: params, flags: options } = await this.parse(AppCreateCmd); await ( - await import('../../utils/application-create') + await import('../../utils/application-create.js') ).applicationCreateBase('app', options, params); } } diff --git a/lib/commands/block/create.ts b/lib/commands/block/create.ts index efb5ced7..9372bff1 100644 --- a/lib/commands/block/create.ts +++ b/lib/commands/block/create.ts @@ -17,9 +17,9 @@ import { Flags, Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { stripIndent } from '../../utils/lazy.js'; export default class BlockCreateCmd extends Command { public static description = stripIndent` @@ -77,7 +77,7 @@ export default class BlockCreateCmd extends Command { const { args: params, flags: options } = await this.parse(BlockCreateCmd); await ( - await import('../../utils/application-create') + await import('../../utils/application-create.js') ).applicationCreateBase('block', options, params); } } diff --git a/lib/commands/build/index.ts b/lib/commands/build/index.ts index 44718584..c4669fe7 100644 --- a/lib/commands/build/index.ts +++ b/lib/commands/build/index.ts @@ -16,20 +16,23 @@ */ import { Args, Flags } from '@oclif/core'; -import Command from '../../command'; -import { getBalenaSdk } from '../../utils/lazy'; -import * as cf from '../../utils/common-flags'; -import * as compose from '../../utils/compose'; +import Command from '../../command.js'; +import { getBalenaSdk } from '../../utils/lazy.js'; +import * as cf from '../../utils/common-flags.js'; +import * as compose from '../../utils/compose.js'; import type { ApplicationType, BalenaSDK } from 'balena-sdk'; import { buildArgDeprecation, dockerignoreHelp, registrySecretsHelp, -} from '../../utils/messages'; -import type { ComposeCliFlags, ComposeOpts } from '../../utils/compose-types'; -import { buildProject, composeCliFlags } from '../../utils/compose_ts'; -import type { BuildOpts, DockerCliFlags } from '../../utils/docker'; -import { dockerCliFlags } from '../../utils/docker'; +} from '../../utils/messages.js'; +import type { + ComposeCliFlags, + ComposeOpts, +} from '../../utils/compose-types.js'; +import { buildProject, composeCliFlags } from '../../utils/compose_ts.js'; +import type { BuildOpts, DockerCliFlags } from '../../utils/docker.js'; +import { dockerCliFlags } from '../../utils/docker.js'; // 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 @@ -105,7 +108,7 @@ ${dockerignoreHelp} (await import('events')).defaultMaxListeners = 1000; - const sdk = getBalenaSdk(); + const sdk = await getBalenaSdk(); const logger = await Command.getLogger(); logger.logDebug('Parsing input...'); @@ -148,14 +151,16 @@ ${dockerignoreHelp} (opts.fleet == null && (opts.arch == null || opts.deviceType == null)) || (opts.fleet != null && (opts.arch != null || opts.deviceType != null)) ) { - const { ExpectedError } = await import('../../errors'); + const { ExpectedError } = await import('../../errors.js'); throw new ExpectedError( 'You must specify either a fleet (-f), or the device type (-d) and architecture (-A)', ); } // Validate project directory - const { validateProjectDirectory } = await import('../../utils/compose_ts'); + const { validateProjectDirectory } = await import( + '../../utils/compose_ts.js' + ); const { dockerfilePath, registrySecrets } = await validateProjectDirectory( sdk, { @@ -172,7 +177,7 @@ ${dockerignoreHelp} protected async getAppAndResolveArch(opts: FlagsDef) { if (opts.fleet) { - const { getAppWithArch } = await import('../../utils/helpers'); + const { getAppWithArch } = await import('../../utils/helpers.js'); const app = await getAppWithArch(opts.fleet); opts.arch = app.arch; opts.deviceType = app.is_for__device_type[0].slug; @@ -180,8 +185,11 @@ ${dockerignoreHelp} } } - protected async prepareBuild(options: FlagsDef) { - const { getDocker, generateBuildOpts } = await import('../../utils/docker'); + // TODO: FIX ME ESM + protected async prepareBuild(options: FlagsDef): Promise { + const { getDocker, generateBuildOpts } = await import( + '../../utils/docker.js' + ); const [docker, buildOpts, composeOpts] = await Promise.all([ getDocker(options), generateBuildOpts(options), @@ -209,7 +217,7 @@ ${dockerignoreHelp} */ protected async buildProject( docker: import('dockerode'), - logger: import('../../utils/logger'), + logger: import('../../utils/logger.js').default, composeOpts: ComposeOpts, opts: { app?: { @@ -221,7 +229,7 @@ ${dockerignoreHelp} buildOpts: BuildOpts; }, ) { - const { loadProject } = await import('../../utils/compose_ts'); + const { loadProject } = await import('../../utils/compose_ts.js'); const project = await loadProject( logger, diff --git a/lib/commands/config/generate.ts b/lib/commands/config/generate.ts index 59d77fb8..4615ec74 100644 --- a/lib/commands/config/generate.ts +++ b/lib/commands/config/generate.ts @@ -17,14 +17,14 @@ import { Flags } from '@oclif/core'; import type { Interfaces } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, getCliForm, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, getCliForm, stripIndent } from '../../utils/lazy.js'; import { applicationIdInfo, devModeInfo, secureBootInfo, -} from '../../utils/messages'; +} from '../../utils/messages.js'; import type { BalenaSDK, PineDeferred } from 'balena-sdk'; export default class ConfigGenerateCmd extends Command { @@ -126,7 +126,7 @@ export default class ConfigGenerateCmd extends Command { public static authenticated = true; public async getApplication(balena: BalenaSDK, fleet: string) { - const { getApplication } = await import('../../utils/sdk'); + const { getApplication } = await import('../../utils/sdk.js'); return await getApplication(balena, fleet, { $select: 'slug', $expand: { @@ -137,7 +137,7 @@ export default class ConfigGenerateCmd extends Command { public async run() { const { flags: options } = await this.parse(ConfigGenerateCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); await this.validateOptions(options); @@ -152,7 +152,7 @@ export default class ConfigGenerateCmd extends Command { $expand: { is_of__device_type: { $select: 'slug' } }, }); if (!rawDevice.belongs_to__application) { - const { ExpectedError } = await import('../../errors'); + const { ExpectedError } = await import('../../errors.js'); throw new ExpectedError(stripIndent` Device ${options.device} does not appear to belong to an accessible fleet. Try with a different device, or use '--fleet' instead of '--device'.`); @@ -171,14 +171,14 @@ export default class ConfigGenerateCmd extends Command { // Check compatibility if application and deviceType provided if (options.fleet && options.deviceType) { - const helpers = await import('../../utils/helpers'); + const helpers = await import('../../utils/helpers.js'); if ( !(await helpers.areDeviceTypesCompatible( resourceDeviceType, deviceType, )) ) { - const { ExpectedError } = await import('../../errors'); + const { ExpectedError } = await import('../../errors.js'); throw new ExpectedError( `Device type ${options.deviceType} is incompatible with fleet ${options.fleet}`, ); @@ -189,7 +189,7 @@ export default class ConfigGenerateCmd extends Command { await balena.models.config.getDeviceTypeManifestBySlug(deviceType); const { validateSecureBootOptionAndWarn } = await import( - '../../utils/config' + '../../utils/config.js' ); await validateSecureBootOptionAndWarn( options.secureBoot, @@ -211,7 +211,7 @@ export default class ConfigGenerateCmd extends Command { // Generate config const { generateDeviceConfig, generateApplicationConfig } = await import( - '../../utils/config' + '../../utils/config.js' ); let config; @@ -250,7 +250,7 @@ export default class ConfigGenerateCmd extends Command { protected async validateOptions( options: Interfaces.InferredFlags, ) { - const { ExpectedError } = await import('../../errors'); + const { ExpectedError } = await import('../../errors.js'); if (options.device == null && options.fleet == null) { throw new ExpectedError(this.missingDeviceOrAppMessage); @@ -259,7 +259,7 @@ export default class ConfigGenerateCmd extends Command { if (!options.fleet && options.deviceType) { throw new ExpectedError(this.deviceTypeNotAllowedMessage); } - const { validateDevOptionAndWarn } = await import('../../utils/config'); + const { validateDevOptionAndWarn } = await import('../../utils/config.js'); await validateDevOptionAndWarn(options.dev, options.version); } } diff --git a/lib/commands/config/inject.ts b/lib/commands/config/inject.ts index 6e344f70..05a267ab 100644 --- a/lib/commands/config/inject.ts +++ b/lib/commands/config/inject.ts @@ -16,9 +16,9 @@ */ import { Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getVisuals, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getVisuals, stripIndent } from '../../utils/lazy.js'; export default class ConfigInjectCmd extends Command { public static description = stripIndent` @@ -56,7 +56,7 @@ export default class ConfigInjectCmd extends Command { public async run() { const { args: params, flags: options } = await this.parse(ConfigInjectCmd); - const { safeUmount } = await import('../../utils/umount'); + const { safeUmount } = await import('../../utils/umount.js'); const drive = options.drive || (await getVisuals().drive('Select the device/OS drive')); diff --git a/lib/commands/config/read.ts b/lib/commands/config/read.ts index 333fea87..3acd906d 100644 --- a/lib/commands/config/read.ts +++ b/lib/commands/config/read.ts @@ -15,9 +15,9 @@ * limitations under the License. */ -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getVisuals, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getVisuals, stripIndent } from '../../utils/lazy.js'; export default class ConfigReadCmd extends Command { public static description = stripIndent` @@ -50,7 +50,7 @@ export default class ConfigReadCmd extends Command { public async run() { const { flags: options } = await this.parse(ConfigReadCmd); - const { safeUmount } = await import('../../utils/umount'); + const { safeUmount } = await import('../../utils/umount.js'); const drive = options.drive || (await getVisuals().drive('Select the device drive')); diff --git a/lib/commands/config/reconfigure.ts b/lib/commands/config/reconfigure.ts index be9ab466..7e1d6b4e 100644 --- a/lib/commands/config/reconfigure.ts +++ b/lib/commands/config/reconfigure.ts @@ -16,9 +16,9 @@ */ import { Flags } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getVisuals, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getVisuals, stripIndent } from '../../utils/lazy.js'; export default class ConfigReconfigureCmd extends Command { public static description = stripIndent` @@ -59,7 +59,7 @@ export default class ConfigReconfigureCmd extends Command { public async run() { const { flags: options } = await this.parse(ConfigReconfigureCmd); - const { safeUmount } = await import('../../utils/umount'); + const { safeUmount } = await import('../../utils/umount.js'); const drive = options.drive || (await getVisuals().drive('Select the device drive')); @@ -70,7 +70,7 @@ export default class ConfigReconfigureCmd extends Command { await safeUmount(drive); if (!uuid) { - const { ExpectedError } = await import('../../errors'); + const { ExpectedError } = await import('../../errors.js'); throw new ExpectedError( `Error: UUID not found in 'config.json' file for '${drive}'`, ); @@ -84,7 +84,7 @@ export default class ConfigReconfigureCmd extends Command { configureCommand.push('--advanced'); } - const { runCommand } = await import('../../utils/helpers'); + const { runCommand } = await import('../../utils/helpers.js'); await runCommand(configureCommand); console.info('Done'); diff --git a/lib/commands/config/write.ts b/lib/commands/config/write.ts index e1323f5f..e70b1b4b 100644 --- a/lib/commands/config/write.ts +++ b/lib/commands/config/write.ts @@ -16,10 +16,11 @@ */ import { Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getVisuals, stripIndent } from '../../utils/lazy'; - +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getVisuals, stripIndent } from '../../utils/lazy.js'; +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); 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. @@ -61,7 +62,7 @@ export default class ConfigWriteCmd extends Command { public async run() { const { args: params, flags: options } = await this.parse(ConfigWriteCmd); - const { denyMount, safeUmount } = await import('../../utils/umount'); + const { denyMount, safeUmount } = await import('../../utils/umount.js'); const drive = options.drive || (await getVisuals().drive('Select the device drive')); diff --git a/lib/commands/deploy/index.ts b/lib/commands/deploy/index.ts index c4be73c0..c8346f63 100644 --- a/lib/commands/deploy/index.ts +++ b/lib/commands/deploy/index.ts @@ -14,35 +14,34 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import { Args, Flags } from '@oclif/core'; import type { ImageDescriptor } from '@balena/compose/dist/parse'; -import Command from '../../command'; -import { ExpectedError } from '../../errors'; -import { getBalenaSdk, getChalk, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import { ExpectedError } from '../../errors.js'; +import { getBalenaSdk, getChalk, stripIndent } from '../../utils/lazy.js'; import { dockerignoreHelp, registrySecretsHelp, buildArgDeprecation, -} from '../../utils/messages'; -import * as ca from '../../utils/common-args'; -import * as compose from '../../utils/compose'; +} from '../../utils/messages.js'; +import * as ca from '../../utils/common-args.js'; +import * as compose from '../../utils/compose.js'; import type { BuiltImage, ComposeCliFlags, ComposeOpts, Release as ComposeReleaseInfo, -} from '../../utils/compose-types'; -import type { BuildOpts, DockerCliFlags } from '../../utils/docker'; +} from '../../utils/compose-types.js'; +import type { BuildOpts, DockerCliFlags } from '../../utils/docker.js'; import { applyReleaseTagKeysAndValues, buildProject, composeCliFlags, isBuildConfig, parseReleaseTagKeysAndValues, -} from '../../utils/compose_ts'; -import { dockerCliFlags } from '../../utils/docker'; +} from '../../utils/compose_ts.js'; +import { dockerCliFlags } from '../../utils/docker.js'; import type { ApplicationType, DeviceType, Release } from 'balena-sdk'; interface ApplicationWithArch { @@ -173,9 +172,9 @@ ${dockerignoreHelp} ); } - const sdk = getBalenaSdk(); + const sdk = await getBalenaSdk(); const { getRegistrySecrets, validateProjectDirectory } = await import( - '../../utils/compose_ts' + '../../utils/compose_ts.js' ); const { releaseTagKeys, releaseTagValues } = parseReleaseTagKeysAndValues( @@ -199,10 +198,10 @@ ${dockerignoreHelp} (options as FlagsDef)['registry-secrets'] = registrySecrets; } - const helpers = await import('../../utils/helpers'); + const helpers = await import('../../utils/helpers.js'); const app = await helpers.getAppWithArch(fleet); - const dockerUtils = await import('../../utils/docker'); + const dockerUtils = await import('../../utils/docker.js'); const [docker, buildOpts, composeOpts] = await Promise.all([ dockerUtils.getDocker(options), dockerUtils.generateBuildOpts(options as FlagsDef), @@ -232,7 +231,7 @@ ${dockerignoreHelp} async deployProject( docker: import('dockerode'), - logger: import('../../utils/logger'), + logger: import('../../utils/logger.js').default, composeOpts: ComposeOpts, opts: { app: ApplicationWithArch; // the application instance to deploy to @@ -248,9 +247,9 @@ ${dockerignoreHelp} ) { const _ = await import('lodash'); const doodles = await import('resin-doodles'); - const sdk = getBalenaSdk(); + const sdk = await getBalenaSdk(); const { deployProject: $deployProject, loadProject } = await import( - '../../utils/compose_ts' + '../../utils/compose_ts.js' ); const appType = opts.app.application_type[0]; @@ -321,7 +320,7 @@ ${dockerignoreHelp} builtImagesByService = _.keyBy(builtImages, 'serviceName'); } const images: BuiltImage[] = project.descriptors.map( - (d) => + (d: any) => builtImagesByService[d.serviceName] ?? { serviceName: d.serviceName, name: (isBuildConfig(d.image) ? d.image.tag : d.image) || '', @@ -332,7 +331,7 @@ ${dockerignoreHelp} let release: Release | ComposeReleaseInfo['release']; if (appType.slug === 'legacy-v1' || appType.slug === 'legacy-v2') { - const { deployLegacy } = require('../../utils/deploy-legacy'); + const { deployLegacy } = await import('../../utils/deploy-legacy.js'); const msg = getChalk().yellow( 'Target fleet requires legacy deploy method.', diff --git a/lib/commands/device/deactivate.ts b/lib/commands/device/deactivate.ts index 43afcc8e..63839056 100644 --- a/lib/commands/device/deactivate.ts +++ b/lib/commands/device/deactivate.ts @@ -16,9 +16,9 @@ */ import { Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; export default class DeviceDeactivateCmd extends Command { public static description = stripIndent` @@ -54,8 +54,8 @@ export default class DeviceDeactivateCmd extends Command { const { args: params, flags: options } = await this.parse(DeviceDeactivateCmd); - const balena = getBalenaSdk(); - const patterns = await import('../../utils/patterns'); + const balena = await getBalenaSdk(); + const patterns = await import('../../utils/patterns.js'); const uuid = params.uuid; const deactivationWarning = ` diff --git a/lib/commands/device/identify.ts b/lib/commands/device/identify.ts index 7766a088..56364313 100644 --- a/lib/commands/device/identify.ts +++ b/lib/commands/device/identify.ts @@ -16,10 +16,10 @@ */ 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'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; +import { ExpectedError } from '../../errors.js'; export default class DeviceIdentifyCmd extends Command { public static description = stripIndent` @@ -47,7 +47,7 @@ export default class DeviceIdentifyCmd extends Command { public async run() { const { args: params } = await this.parse(DeviceIdentifyCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); try { await balena.models.device.identify(params.uuid); diff --git a/lib/commands/device/index.ts b/lib/commands/device/index.ts index 7ef83af0..1fe233d3 100644 --- a/lib/commands/device/index.ts +++ b/lib/commands/device/index.ts @@ -16,11 +16,11 @@ */ import { Flags, Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { expandForAppName } from '../../utils/helpers'; -import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy'; -import { jsonInfo } from '../../utils/messages'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { expandForAppName } from '../../utils/helpers.js'; +import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy.js'; +import { jsonInfo } from '../../utils/messages.js'; import type { Application, Release } from 'balena-sdk'; @@ -79,7 +79,7 @@ export default class DeviceCmd extends Command { public async run() { const { args: params, flags: options } = await this.parse(DeviceCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); const device = (await balena.models.device.get( params.uuid, @@ -124,7 +124,7 @@ export default class DeviceCmd extends Command { if (options.view) { const open = await import('open'); const dashboardUrl = balena.models.device.getDashboardUrl(device.uuid); - await open(dashboardUrl, { wait: false }); + await open.default(dashboardUrl, { wait: false }); return; } diff --git a/lib/commands/device/init.ts b/lib/commands/device/init.ts index b60fe073..ca6e57a4 100644 --- a/lib/commands/device/init.ts +++ b/lib/commands/device/init.ts @@ -16,11 +16,11 @@ */ import { Flags } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { applicationIdInfo } from '../../utils/messages'; -import { runCommand } from '../../utils/helpers'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; +import { applicationIdInfo } from '../../utils/messages.js'; +import { runCommand } from '../../utils/helpers.js'; interface FlagsDef { fleet?: string; @@ -113,15 +113,15 @@ export default class DeviceInitCmd extends Command { // Imports const { promisify } = await import('util'); - const rimraf = promisify(await import('rimraf')); + const rimraf = promisify((await import('rimraf')).default); const tmp = await import('tmp'); const tmpNameAsync = promisify(tmp.tmpName); tmp.setGracefulCleanup(); - const { downloadOSImage } = await import('../../utils/cloud'); - const { getApplication } = await import('../../utils/sdk'); + const { downloadOSImage } = await import('../../utils/cloud.js'); + const { getApplication } = await import('../../utils/sdk.js'); const logger = await Command.getLogger(); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); // Get application and const application = options.fleet @@ -133,7 +133,7 @@ export default class DeviceInitCmd extends Command { }, }, }) - : await (await import('../../utils/patterns')).selectApplication(); + : await (await import('../../utils/patterns.js')).selectApplication(); // Register new device const deviceUuid = balena.models.device.generateUniqueKey(); diff --git a/lib/commands/device/local-mode.ts b/lib/commands/device/local-mode.ts index 1d048baf..20600b14 100644 --- a/lib/commands/device/local-mode.ts +++ b/lib/commands/device/local-mode.ts @@ -16,9 +16,9 @@ */ import { Flags, Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; export default class DeviceLocalModeCmd extends Command { public static description = stripIndent` @@ -66,7 +66,7 @@ export default class DeviceLocalModeCmd extends Command { const { args: params, flags: options } = await this.parse(DeviceLocalModeCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); if (options.enable) { await balena.models.device.enableLocalMode(params.uuid); diff --git a/lib/commands/device/move.ts b/lib/commands/device/move.ts index 20e4c9af..cc1ff77e 100644 --- a/lib/commands/device/move.ts +++ b/lib/commands/device/move.ts @@ -22,11 +22,11 @@ import type { PineOptions, PineTypedResult, } from 'balena-sdk'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { ExpectedError } from '../../errors'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { applicationIdInfo } from '../../utils/messages'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { ExpectedError } from '../../errors.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; +import { applicationIdInfo } from '../../utils/messages.js'; export default class DeviceMoveCmd extends Command { public static description = stripIndent` @@ -93,7 +93,7 @@ export default class DeviceMoveCmd extends Command { public async run() { const { args: params, flags: options } = await this.parse(DeviceMoveCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); // Split uuids string into array of uuids const deviceUuids = params.uuid.split(','); @@ -101,7 +101,7 @@ export default class DeviceMoveCmd extends Command { const devices = await this.getDevices(balena, deviceUuids); // Disambiguate application - const { getApplication } = await import('../../utils/sdk'); + const { getApplication } = await import('../../utils/sdk.js'); // Get destination application const application = options.fleet @@ -151,7 +151,7 @@ export default class DeviceMoveCmd extends Command { }) .map((deviceType) => deviceType.id); - const patterns = await import('../../utils/patterns'); + const patterns = await import('../../utils/patterns.js'); try { const application = await patterns.selectApplication( { diff --git a/lib/commands/device/os-update.ts b/lib/commands/device/os-update.ts index 9b66b1b7..c5fe82fa 100644 --- a/lib/commands/device/os-update.ts +++ b/lib/commands/device/os-update.ts @@ -16,11 +16,11 @@ */ 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 Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy.js'; import type { Device } from 'balena-sdk'; -import { ExpectedError } from '../../errors'; +import { ExpectedError } from '../../errors.js'; export default class DeviceOsUpdateCmd extends Command { public static description = stripIndent` @@ -62,7 +62,7 @@ export default class DeviceOsUpdateCmd extends Command { const { args: params, flags: options } = await this.parse(DeviceOsUpdateCmd); - const sdk = getBalenaSdk(); + const sdk = await getBalenaSdk(); // Get device info const { uuid, is_of__device_type, os_version, os_variant } = @@ -119,7 +119,7 @@ export default class DeviceOsUpdateCmd extends Command { }); } - const patterns = await import('../../utils/patterns'); + const patterns = await import('../../utils/patterns.js'); // Confirm and start update await patterns.confirm( options.yes || false, diff --git a/lib/commands/device/pin.ts b/lib/commands/device/pin.ts index 51f582bf..1d7031c1 100644 --- a/lib/commands/device/pin.ts +++ b/lib/commands/device/pin.ts @@ -16,10 +16,10 @@ */ 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'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; +import { getExpandedProp } from '../../utils/pine.js'; export default class DevicePinCmd extends Command { public static description = stripIndent` @@ -55,7 +55,7 @@ export default class DevicePinCmd extends Command { public async run() { const { args: params } = await this.parse(DevicePinCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); const device = await balena.models.device.get(params.uuid, { $expand: { diff --git a/lib/commands/device/public-url.ts b/lib/commands/device/public-url.ts index 51326070..c5a78ea4 100644 --- a/lib/commands/device/public-url.ts +++ b/lib/commands/device/public-url.ts @@ -16,10 +16,10 @@ */ 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'; +import Command from '../../command.js'; +import { ExpectedError } from '../../errors.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; export default class DevicePublicUrlCmd extends Command { public static description = stripIndent` @@ -68,7 +68,7 @@ export default class DevicePublicUrlCmd extends Command { const { args: params, flags: options } = await this.parse(DevicePublicUrlCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); if (options.enable) { // Enable public URL diff --git a/lib/commands/device/purge.ts b/lib/commands/device/purge.ts index a40d00e6..3403cfed 100644 --- a/lib/commands/device/purge.ts +++ b/lib/commands/device/purge.ts @@ -16,9 +16,9 @@ */ import { Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy.js'; export default class DevicePurgeCmd extends Command { public static description = stripIndent` @@ -53,7 +53,7 @@ export default class DevicePurgeCmd extends Command { public async run() { const { args: params } = await this.parse(DevicePurgeCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); const ux = getCliUx(); const deviceUuids = params.uuid.split(','); diff --git a/lib/commands/device/reboot.ts b/lib/commands/device/reboot.ts index daef864b..4641dfb9 100644 --- a/lib/commands/device/reboot.ts +++ b/lib/commands/device/reboot.ts @@ -16,9 +16,9 @@ */ import { Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; export default class DeviceRebootCmd extends Command { public static description = stripIndent` @@ -47,7 +47,7 @@ export default class DeviceRebootCmd extends Command { public async run() { const { args: params, flags: options } = await this.parse(DeviceRebootCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); // The SDK current throws "BalenaDeviceNotFound: Device not found: xxxxx" // when the device is not online, which may be confusing. diff --git a/lib/commands/device/register.ts b/lib/commands/device/register.ts index 7a9d10b5..027e9b7b 100644 --- a/lib/commands/device/register.ts +++ b/lib/commands/device/register.ts @@ -16,11 +16,11 @@ */ 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 Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import * as ca from '../../utils/common-args.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; +import { applicationIdInfo } from '../../utils/messages.js'; export default class DeviceRegisterCmd extends Command { public static description = stripIndent` @@ -64,9 +64,9 @@ export default class DeviceRegisterCmd extends Command { const { args: params, flags: options } = await this.parse(DeviceRegisterCmd); - const { getApplication } = await import('../../utils/sdk'); + const { getApplication } = await import('../../utils/sdk.js'); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); const application = await getApplication(balena, params.fleet, { $select: ['id', 'slug'], diff --git a/lib/commands/device/rename.ts b/lib/commands/device/rename.ts index be6c38f4..ca10f183 100644 --- a/lib/commands/device/rename.ts +++ b/lib/commands/device/rename.ts @@ -16,9 +16,9 @@ */ import { Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy.js'; export default class DeviceRenameCmd extends Command { public static description = stripIndent` @@ -54,7 +54,7 @@ export default class DeviceRenameCmd extends Command { public async run() { const { args: params } = await this.parse(DeviceRenameCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); const newName = params.newName || diff --git a/lib/commands/device/restart.ts b/lib/commands/device/restart.ts index 71f35f7b..c809f66d 100644 --- a/lib/commands/device/restart.ts +++ b/lib/commands/device/restart.ts @@ -16,9 +16,9 @@ */ import { Flags, Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy.js'; import type { BalenaSDK, DeviceWithServiceDetails, @@ -69,7 +69,7 @@ export default class DeviceRestartCmd extends Command { public async run() { const { args: params, flags: options } = await this.parse(DeviceRestartCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); const ux = getCliUx(); const deviceUuids = params.uuid.split(','); @@ -94,8 +94,8 @@ export default class DeviceRestartCmd extends Command { deviceUuid: string, serviceNames: string[], ) { - const { ExpectedError, instanceOf } = await import('../../errors'); - const { getExpandedProp } = await import('../../utils/pine'); + const { ExpectedError, instanceOf } = await import('../../errors.js'); + const { getExpandedProp } = await import('../../utils/pine.js'); // Get device let device: DeviceWithServiceDetails; @@ -161,7 +161,7 @@ export default class DeviceRestartCmd extends Command { // Note: device.restartApplication throws `BalenaDeviceNotFound: Device not found` if device not online. // Need to use device.get first to distinguish between non-existant and offline devices. // Remove this workaround when SDK issue resolved: https://github.com/balena-io/balena-sdk/issues/649 - const { instanceOf, ExpectedError } = await import('../../errors'); + const { instanceOf, ExpectedError } = await import('../../errors.js'); try { const device = await balena.models.device.get(deviceUuid); if (!device.is_online) { diff --git a/lib/commands/device/rm.ts b/lib/commands/device/rm.ts index 6f3cc482..0558bb57 100644 --- a/lib/commands/device/rm.ts +++ b/lib/commands/device/rm.ts @@ -16,9 +16,9 @@ */ import { Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; export default class DeviceRmCmd extends Command { public static description = stripIndent` @@ -55,8 +55,8 @@ export default class DeviceRmCmd extends Command { public async run() { const { args: params, flags: options } = await this.parse(DeviceRmCmd); - const balena = getBalenaSdk(); - const patterns = await import('../../utils/patterns'); + const balena = await getBalenaSdk(); + const patterns = await import('../../utils/patterns.js'); // Confirm const uuids = params.uuid.split(','); diff --git a/lib/commands/device/shutdown.ts b/lib/commands/device/shutdown.ts index 7da13ade..def53aef 100644 --- a/lib/commands/device/shutdown.ts +++ b/lib/commands/device/shutdown.ts @@ -16,10 +16,10 @@ */ 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'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; +import { ExpectedError } from '../../errors.js'; export default class DeviceShutdownCmd extends Command { public static description = stripIndent` @@ -49,7 +49,7 @@ export default class DeviceShutdownCmd extends Command { const { args: params, flags: options } = await this.parse(DeviceShutdownCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); try { await balena.models.device.shutdown(params.uuid, options); diff --git a/lib/commands/device/start-service.ts b/lib/commands/device/start-service.ts index 4637f221..b0163ccb 100644 --- a/lib/commands/device/start-service.ts +++ b/lib/commands/device/start-service.ts @@ -16,9 +16,9 @@ */ import { Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy.js'; import type { BalenaSDK } from 'balena-sdk'; export default class DeviceStartServiceCmd extends Command { @@ -57,7 +57,7 @@ export default class DeviceStartServiceCmd extends Command { public async run() { const { args: params } = await this.parse(DeviceStartServiceCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); const ux = getCliUx(); const deviceUuids = params.uuid.split(','); @@ -78,8 +78,8 @@ export default class DeviceStartServiceCmd extends Command { deviceUuid: string, serviceNames: string[], ) { - const { ExpectedError } = await import('../../errors'); - const { getExpandedProp } = await import('../../utils/pine'); + const { ExpectedError } = await import('../../errors.js'); + const { getExpandedProp } = await import('../../utils/pine.js'); // Get device const device = await balena.models.device.getWithServiceDetails( diff --git a/lib/commands/device/stop-service.ts b/lib/commands/device/stop-service.ts index b7aa53dc..686b3964 100644 --- a/lib/commands/device/stop-service.ts +++ b/lib/commands/device/stop-service.ts @@ -16,9 +16,9 @@ */ import { Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy.js'; import type { BalenaSDK } from 'balena-sdk'; export default class DeviceStopServiceCmd extends Command { @@ -57,7 +57,7 @@ export default class DeviceStopServiceCmd extends Command { public async run() { const { args: params } = await this.parse(DeviceStopServiceCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); const ux = getCliUx(); const deviceUuids = params.uuid.split(','); @@ -78,8 +78,8 @@ export default class DeviceStopServiceCmd extends Command { deviceUuid: string, serviceNames: string[], ) { - const { ExpectedError } = await import('../../errors'); - const { getExpandedProp } = await import('../../utils/pine'); + const { ExpectedError } = await import('../../errors.js'); + const { getExpandedProp } = await import('../../utils/pine.js'); // Get device const device = await balena.models.device.getWithServiceDetails( diff --git a/lib/commands/device/track-fleet.ts b/lib/commands/device/track-fleet.ts index edb164ef..d95c5ec5 100644 --- a/lib/commands/device/track-fleet.ts +++ b/lib/commands/device/track-fleet.ts @@ -16,9 +16,9 @@ */ import { Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; export default class DeviceTrackFleetCmd extends Command { public static description = stripIndent` @@ -46,7 +46,7 @@ export default class DeviceTrackFleetCmd extends Command { public async run() { const { args: params } = await this.parse(DeviceTrackFleetCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); await balena.models.device.trackApplicationRelease(params.uuid); } diff --git a/lib/commands/devices/index.ts b/lib/commands/devices/index.ts index e7196c42..2d5190be 100644 --- a/lib/commands/devices/index.ts +++ b/lib/commands/devices/index.ts @@ -15,11 +15,11 @@ * limitations under the License. */ -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { expandForAppName } from '../../utils/helpers'; -import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy'; -import { applicationIdInfo, jsonInfo } from '../../utils/messages'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { expandForAppName } from '../../utils/helpers.js'; +import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy.js'; +import { applicationIdInfo, jsonInfo } from '../../utils/messages.js'; import type { Device, PineOptions } from 'balena-sdk'; @@ -68,7 +68,7 @@ export default class DevicesCmd extends Command { public async run() { const { flags: options } = await this.parse(DevicesCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); const devicesOptions = { ...devicesSelectFields, ...expandForAppName, @@ -78,7 +78,7 @@ export default class DevicesCmd extends Command { const devices = ( await (async () => { if (options.fleet != null) { - const { getApplication } = await import('../../utils/sdk'); + const { getApplication } = await import('../../utils/sdk.js'); const application = await getApplication(balena, options.fleet, { $select: 'slug', $expand: { @@ -115,7 +115,7 @@ export default class DevicesCmd extends Command { ]; if (options.json) { - const { pickAndRename } = await import('../../utils/helpers'); + const { pickAndRename } = await import('../../utils/helpers.js'); const mapped = devices.map((device) => pickAndRename(device, fields)); console.log(JSON.stringify(mapped, null, 4)); } else { diff --git a/lib/commands/devices/supported.ts b/lib/commands/devices/supported.ts index 93305a41..21c74107 100644 --- a/lib/commands/devices/supported.ts +++ b/lib/commands/devices/supported.ts @@ -16,12 +16,12 @@ */ import { Flags } from '@oclif/core'; import type * as BalenaSdk from 'balena-sdk'; -import * as _ from 'lodash'; -import Command from '../../command'; +import _ from 'lodash'; +import Command from '../../command.js'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy'; -import { CommandHelp } from '../../utils/oclif-utils'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy.js'; +import { CommandHelp } from '../../utils/oclif-utils.js'; export default class DevicesSupportedCmd extends Command { public static description = stripIndent` @@ -65,9 +65,9 @@ export default class DevicesSupportedCmd extends Command { }, }, } satisfies BalenaSdk.PineOptions; - const dts = (await getBalenaSdk().models.deviceType.getAllSupported( - pineOptions, - )) as Array< + const dts = (await ( + await getBalenaSdk() + ).models.deviceType.getAllSupported(pineOptions)) as Array< BalenaSdk.PineTypedResult >; interface DT { diff --git a/lib/commands/env/add.ts b/lib/commands/env/add.ts index 479e03c4..adc346eb 100644 --- a/lib/commands/env/add.ts +++ b/lib/commands/env/add.ts @@ -17,11 +17,11 @@ import { Args } from '@oclif/core'; import type * as BalenaSdk from 'balena-sdk'; -import Command from '../../command'; -import { ExpectedError } from '../../errors'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { applicationIdInfo } from '../../utils/messages'; +import Command from '../../command.js'; +import { ExpectedError } from '../../errors.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; +import { applicationIdInfo } from '../../utils/messages.js'; interface FlagsDef { fleet?: string; @@ -128,7 +128,7 @@ export default class EnvAddCmd extends Command { } } - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); const reservedPrefixes = await getReservedPrefixes(balena); const isConfigVar = reservedPrefixes.some((prefix) => params.name.startsWith(prefix), @@ -185,7 +185,7 @@ async function resolveFleetSlugs( fleetOption: string, ) { const fleetSlugs: string[] = []; - const { getFleetSlug } = await import('../../utils/sdk'); + const { getFleetSlug } = await import('../../utils/sdk.js'); for (const appNameOrSlug of fleetOption.split(',')) { try { fleetSlugs.push(await getFleetSlug(balena, appNameOrSlug)); @@ -222,7 +222,7 @@ async function setServiceVars( } } } else if (options.device) { - const { getDeviceAndAppFromUUID } = await import('../../utils/cloud'); + const { getDeviceAndAppFromUUID } = await import('../../utils/cloud.js'); for (const uuid of options.device.split(',')) { let device; let app; diff --git a/lib/commands/env/rename.ts b/lib/commands/env/rename.ts index 6f9a3b99..e9e384fa 100644 --- a/lib/commands/env/rename.ts +++ b/lib/commands/env/rename.ts @@ -15,12 +15,12 @@ * limitations under the License. */ import { Args } from '@oclif/core'; -import Command from '../../command'; +import Command from '../../command.js'; -import * as cf from '../../utils/common-flags'; -import * as ec from '../../utils/env-common'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { parseAsInteger } from '../../utils/validation'; +import * as cf from '../../utils/common-flags.js'; +import * as ec from '../../utils/env-common.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; +import { parseAsInteger } from '../../utils/validation.js'; export default class EnvRenameCmd extends Command { public static description = stripIndent` @@ -67,7 +67,9 @@ export default class EnvRenameCmd extends Command { await Command.checkLoggedIn(); - await getBalenaSdk().pine.patch({ + await ( + await getBalenaSdk() + ).pine.patch({ resource: ec.getVarResourceName(opt.config, opt.device, opt.service), id: params.id, body: { diff --git a/lib/commands/env/rm.ts b/lib/commands/env/rm.ts index d3589001..d2ad3030 100644 --- a/lib/commands/env/rm.ts +++ b/lib/commands/env/rm.ts @@ -16,11 +16,11 @@ */ import { Flags, Args } from '@oclif/core'; -import Command from '../../command'; +import Command from '../../command.js'; -import * as ec from '../../utils/env-common'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { parseAsInteger } from '../../utils/validation'; +import * as ec from '../../utils/env-common.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; +import { parseAsInteger } from '../../utils/validation.js'; export default class EnvRmCmd extends Command { public static description = stripIndent` @@ -71,13 +71,13 @@ export default class EnvRmCmd extends Command { await Command.checkLoggedIn(); - const { confirm } = await import('../../utils/patterns'); + const { confirm } = await import('../../utils/patterns.js'); await confirm( opt.yes || false, 'Are you sure you want to delete the environment variable?', ); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); await balena.pine.delete({ resource: ec.getVarResourceName(opt.config, opt.device, opt.service), id: params.id, diff --git a/lib/commands/envs/index.ts b/lib/commands/envs/index.ts index 9434b7f9..82efaa56 100644 --- a/lib/commands/envs/index.ts +++ b/lib/commands/envs/index.ts @@ -17,12 +17,12 @@ import { Flags } from '@oclif/core'; import type { Interfaces } from '@oclif/core'; import type * as SDK from 'balena-sdk'; -import * as _ from 'lodash'; -import Command from '../../command'; -import { ExpectedError } from '../../errors'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy'; -import { applicationIdInfo } from '../../utils/messages'; +import _ from 'lodash'; +import Command from '../../command.js'; +import { ExpectedError } from '../../errors.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy.js'; +import { applicationIdInfo } from '../../utils/messages.js'; type FlagsDef = Interfaces.InferredFlags; @@ -121,18 +121,18 @@ export default class EnvsCmd extends Command { throw new ExpectedError('Missing --fleet or --device option'); } - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); let fleetSlug: string | undefined = options.fleet ? await ( - await import('../../utils/sdk') + await import('../../utils/sdk.js') ).getFleetSlug(balena, options.fleet) : undefined; let fullUUID: string | undefined; // as oppposed to the short, 7-char UUID if (options.device) { const { getDeviceAndMaybeAppFromUUID } = await import( - '../../utils/cloud' + '../../utils/cloud.js' ); const [device, app] = await getDeviceAndMaybeAppFromUUID( balena, @@ -186,7 +186,7 @@ export default class EnvsCmd extends Command { } if (options.json) { - const { pickAndRename } = await import('../../utils/helpers'); + const { pickAndRename } = await import('../../utils/helpers.js'); const mapped = varArray.map((o) => pickAndRename(o, fields)); this.log(JSON.stringify(mapped, null, 4)); } else { diff --git a/lib/commands/fleet/create.ts b/lib/commands/fleet/create.ts index 2d10c221..5026df92 100644 --- a/lib/commands/fleet/create.ts +++ b/lib/commands/fleet/create.ts @@ -17,9 +17,9 @@ import { Flags, Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { stripIndent } from '../../utils/lazy.js'; export default class FleetCreateCmd extends Command { public static description = stripIndent` @@ -77,7 +77,7 @@ export default class FleetCreateCmd extends Command { const { args: params, flags: options } = await this.parse(FleetCreateCmd); await ( - await import('../../utils/application-create') + await import('../../utils/application-create.js') ).applicationCreateBase('fleet', options, params); } } diff --git a/lib/commands/fleet/index.ts b/lib/commands/fleet/index.ts index b59c135a..4a35a0d9 100644 --- a/lib/commands/fleet/index.ts +++ b/lib/commands/fleet/index.ts @@ -17,11 +17,11 @@ 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 Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import * as ca from '../../utils/common-args.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; +import { applicationIdInfo } from '../../utils/messages.js'; export default class FleetCmd extends Command { public static description = stripIndent` @@ -58,9 +58,9 @@ export default class FleetCmd extends Command { public async run() { const { args: params, flags: options } = await this.parse(FleetCmd); - const { getApplication } = await import('../../utils/sdk'); + const { getApplication } = await import('../../utils/sdk.js'); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); const application = await getApplication(balena, params.fleet, { $expand: { @@ -70,7 +70,7 @@ export default class FleetCmd extends Command { }); if (options.view) { - const open = await import('open'); + const { default: open } = await import('open'); const dashboardUrl = balena.models.application.getDashboardUrl( application.id, ); diff --git a/lib/commands/fleet/pin.ts b/lib/commands/fleet/pin.ts index 2b9f6cc1..2239fb17 100644 --- a/lib/commands/fleet/pin.ts +++ b/lib/commands/fleet/pin.ts @@ -16,10 +16,10 @@ */ 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'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; +import { getExpandedProp } from '../../utils/pine.js'; export default class FleetPinCmd extends Command { public static description = stripIndent` @@ -55,7 +55,7 @@ export default class FleetPinCmd extends Command { public async run() { const { args: params } = await this.parse(FleetPinCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); const fleet = await balena.models.application.get(params.slug, { $expand: { diff --git a/lib/commands/fleet/purge.ts b/lib/commands/fleet/purge.ts index 5456ea0a..7748ccaa 100644 --- a/lib/commands/fleet/purge.ts +++ b/lib/commands/fleet/purge.ts @@ -15,11 +15,11 @@ * limitations under the License. */ -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import * as ca from '../../utils/common-args'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { applicationIdInfo } from '../../utils/messages'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import * as ca from '../../utils/common-args.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; +import { applicationIdInfo } from '../../utils/messages.js'; export default class FleetPurgeCmd extends Command { public static description = stripIndent` @@ -51,9 +51,9 @@ export default class FleetPurgeCmd extends Command { public async run() { const { args: params } = await this.parse(FleetPurgeCmd); - const { getApplication } = await import('../../utils/sdk'); + const { getApplication } = await import('../../utils/sdk.js'); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); // balena.models.application.purge only accepts a numeric id // so we must first fetch the app to get it's id, diff --git a/lib/commands/fleet/rename.ts b/lib/commands/fleet/rename.ts index 9214d2e6..2722ee43 100644 --- a/lib/commands/fleet/rename.ts +++ b/lib/commands/fleet/rename.ts @@ -17,11 +17,11 @@ import { Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import * as ca from '../../utils/common-args'; -import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy'; -import { applicationIdInfo } from '../../utils/messages'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import * as ca from '../../utils/common-args.js'; +import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy.js'; +import { applicationIdInfo } from '../../utils/messages.js'; export default class FleetRenameCmd extends Command { public static description = stripIndent` @@ -59,13 +59,15 @@ export default class FleetRenameCmd extends Command { public async run() { const { args: params } = await this.parse(FleetRenameCmd); - const { validateApplicationName } = await import('../../utils/validation'); - const { ExpectedError } = await import('../../errors'); + const { validateApplicationName } = await import( + '../../utils/validation.js' + ); + const { ExpectedError } = await import('../../errors.js'); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); // Disambiguate target application (if params.params is a number, it could either be an ID or a numerical name) - const { getApplication } = await import('../../utils/sdk'); + const { getApplication } = await import('../../utils/sdk.js'); const application = await getApplication(balena, params.fleet, { $select: ['id', 'app_name', 'slug'], $expand: { diff --git a/lib/commands/fleet/restart.ts b/lib/commands/fleet/restart.ts index f9a8c4b6..f2e0db1e 100644 --- a/lib/commands/fleet/restart.ts +++ b/lib/commands/fleet/restart.ts @@ -15,11 +15,11 @@ * limitations under the License. */ -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import * as ca from '../../utils/common-args'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { applicationIdInfo } from '../../utils/messages'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import * as ca from '../../utils/common-args.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; +import { applicationIdInfo } from '../../utils/messages.js'; export default class FleetRestartCmd extends Command { public static description = stripIndent` @@ -50,9 +50,9 @@ export default class FleetRestartCmd extends Command { public async run() { const { args: params } = await this.parse(FleetRestartCmd); - const { getApplication } = await import('../../utils/sdk'); + const { getApplication } = await import('../../utils/sdk.js'); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); // Disambiguate application const application = await getApplication(balena, params.fleet, { diff --git a/lib/commands/fleet/rm.ts b/lib/commands/fleet/rm.ts index e75d1c04..3e358861 100644 --- a/lib/commands/fleet/rm.ts +++ b/lib/commands/fleet/rm.ts @@ -15,11 +15,11 @@ * limitations under the License. */ -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import * as ca from '../../utils/common-args'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { applicationIdInfo } from '../../utils/messages'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import * as ca from '../../utils/common-args.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; +import { applicationIdInfo } from '../../utils/messages.js'; export default class FleetRmCmd extends Command { public static description = stripIndent` @@ -54,9 +54,9 @@ export default class FleetRmCmd extends Command { public async run() { const { args: params, flags: options } = await this.parse(FleetRmCmd); - const { confirm } = await import('../../utils/patterns'); - const { getApplication } = await import('../../utils/sdk'); - const balena = getBalenaSdk(); + const { confirm } = await import('../../utils/patterns.js'); + const { getApplication } = await import('../../utils/sdk.js'); + const balena = await getBalenaSdk(); // Confirm await confirm( diff --git a/lib/commands/fleet/track-latest.ts b/lib/commands/fleet/track-latest.ts index e05a9365..6f291056 100644 --- a/lib/commands/fleet/track-latest.ts +++ b/lib/commands/fleet/track-latest.ts @@ -16,9 +16,9 @@ */ import { Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; export default class FleetTrackLatestCmd extends Command { public static description = stripIndent` @@ -49,7 +49,7 @@ export default class FleetTrackLatestCmd extends Command { public async run() { const { args: params } = await this.parse(FleetTrackLatestCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); await balena.models.application.trackLatestRelease(params.slug); } diff --git a/lib/commands/fleets/index.ts b/lib/commands/fleets/index.ts index fe649c18..c561501f 100644 --- a/lib/commands/fleets/index.ts +++ b/lib/commands/fleets/index.ts @@ -17,9 +17,9 @@ import type * as BalenaSdk from 'balena-sdk'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; interface ExtendedApplication extends ApplicationWithDeviceTypeSlug { device_count: number; @@ -52,7 +52,7 @@ export default class FleetsCmd extends Command { public async run() { const { flags: options } = await this.parse(FleetsCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); const pineOptions = { $select: ['id', 'app_name', 'slug'], diff --git a/lib/commands/internal/osinit.ts b/lib/commands/internal/osinit.ts index 8bc12992..73d3a450 100644 --- a/lib/commands/internal/osinit.ts +++ b/lib/commands/internal/osinit.ts @@ -16,9 +16,9 @@ */ import { Args } from '@oclif/core'; -import Command from '../../command'; -import { stripIndent } from '../../utils/lazy'; -import { CommandHelp } from '../../utils/oclif-utils'; +import Command from '../../command.js'; +import { stripIndent } from '../../utils/lazy.js'; +import { CommandHelp } from '../../utils/oclif-utils.js'; // 'Internal' commands are called during the execution of other commands. // `osinit` is called during `os initialize` @@ -63,7 +63,7 @@ export default class OsinitCmd extends Command { const config = JSON.parse(params.config); const { getManifest, osProgressHandler } = await import( - '../../utils/helpers' + '../../utils/helpers.js' ); const manifest = await getManifest(params.image, params.type); diff --git a/lib/commands/join/index.ts b/lib/commands/join/index.ts index 152381b3..b4f693d8 100644 --- a/lib/commands/join/index.ts +++ b/lib/commands/join/index.ts @@ -16,11 +16,11 @@ */ 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'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; +import { applicationIdInfo } from '../../utils/messages.js'; +import { parseAsLocalHostnameOrIp } from '../../utils/validation.js'; export default class JoinCmd extends Command { public static description = stripIndent` @@ -78,8 +78,8 @@ export default class JoinCmd extends Command { public async run() { const { args: params, flags: options } = await this.parse(JoinCmd); - const promote = await import('../../utils/promote'); - const sdk = getBalenaSdk(); + const promote = await import('../../utils/promote.js'); + const sdk = await getBalenaSdk(); const logger = await Command.getLogger(); return promote.join( logger, diff --git a/lib/commands/key/add.ts b/lib/commands/key/add.ts index 797c251b..d61ebe29 100644 --- a/lib/commands/key/add.ts +++ b/lib/commands/key/add.ts @@ -16,10 +16,10 @@ */ 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'; +import Command from '../../command.js'; +import { ExpectedError } from '../../errors.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; export default class KeyAddCmd extends Command { public static description = stripIndent` @@ -84,6 +84,6 @@ export default class KeyAddCmd extends Command { throw new ExpectedError('No public key file or path provided.'); } - await getBalenaSdk().models.key.create(params.name, key); + await (await getBalenaSdk()).models.key.create(params.name, key); } } diff --git a/lib/commands/key/index.ts b/lib/commands/key/index.ts index 895e7451..5df64480 100644 --- a/lib/commands/key/index.ts +++ b/lib/commands/key/index.ts @@ -16,10 +16,10 @@ */ 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'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy.js'; +import { parseAsInteger } from '../../utils/validation.js'; export default class KeyCmd extends Command { public static description = stripIndent` @@ -49,7 +49,7 @@ export default class KeyCmd extends Command { public async run() { const { args: params } = await this.parse(KeyCmd); - const key = await getBalenaSdk().models.key.get(params.id); + const key = await (await getBalenaSdk()).models.key.get(params.id); // Use 'name' instead of 'title' to match dashboard. const displayKey = { diff --git a/lib/commands/key/rm.ts b/lib/commands/key/rm.ts index 5e0ad100..c2df7d46 100644 --- a/lib/commands/key/rm.ts +++ b/lib/commands/key/rm.ts @@ -16,10 +16,10 @@ */ 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'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; +import { parseAsInteger } from '../../utils/validation.js'; export default class KeyRmCmd extends Command { public static description = stripIndent` @@ -52,13 +52,13 @@ export default class KeyRmCmd extends Command { public async run() { const { args: params, flags: options } = await this.parse(KeyRmCmd); - const patterns = await import('../../utils/patterns'); + const patterns = await import('../../utils/patterns.js'); await patterns.confirm( options.yes ?? false, `Are you sure you want to delete key ${params.id}?`, ); - await getBalenaSdk().models.key.remove(params.id); + await (await getBalenaSdk()).models.key.remove(params.id); } } diff --git a/lib/commands/keys/index.ts b/lib/commands/keys/index.ts index ca81f366..15f676b4 100644 --- a/lib/commands/keys/index.ts +++ b/lib/commands/keys/index.ts @@ -15,9 +15,9 @@ * limitations under the License. */ -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy.js'; export default class KeysCmd extends Command { public static description = stripIndent` @@ -38,7 +38,7 @@ export default class KeysCmd extends Command { public async run() { await this.parse(KeysCmd); - const keys = await getBalenaSdk().models.key.getAll(); + const keys = await (await getBalenaSdk()).models.key.getAll(); // Use 'name' instead of 'title' to match dashboard. const displayKeys: Array<{ id: number; name: string }> = keys.map((k) => { diff --git a/lib/commands/leave/index.ts b/lib/commands/leave/index.ts index 2365d007..8db65dd3 100644 --- a/lib/commands/leave/index.ts +++ b/lib/commands/leave/index.ts @@ -16,10 +16,10 @@ */ 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'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { stripIndent } from '../../utils/lazy.js'; +import { parseAsLocalHostnameOrIp } from '../../utils/validation.js'; export default class LeaveCmd extends Command { public static description = stripIndent` @@ -62,7 +62,7 @@ export default class LeaveCmd extends Command { public async run() { const { args: params } = await this.parse(LeaveCmd); - const promote = await import('../../utils/promote'); + const promote = await import('../../utils/promote.js'); const logger = await Command.getLogger(); return promote.leave(logger, params.deviceIpOrHostname); } diff --git a/lib/commands/local/configure.ts b/lib/commands/local/configure.ts index 53aa170d..6f2b84b4 100644 --- a/lib/commands/local/configure.ts +++ b/lib/commands/local/configure.ts @@ -17,9 +17,9 @@ 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'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { stripIndent } from '../../utils/lazy.js'; export default class LocalConfigureCmd extends Command { public static description = stripIndent` @@ -53,8 +53,8 @@ export default class LocalConfigureCmd extends Command { const { args: params } = await this.parse(LocalConfigureCmd); const reconfix = await import('reconfix'); - const { denyMount, safeUmount } = await import('../../utils/umount'); - const Logger = await import('../../utils/logger'); + const { denyMount, safeUmount } = await import('../../utils/umount.js'); + const { default: Logger } = await import('../../utils/logger.js'); const logger = Logger.getLogger(); diff --git a/lib/commands/local/flash.ts b/lib/commands/local/flash.ts index e2301718..f36e68d2 100644 --- a/lib/commands/local/flash.ts +++ b/lib/commands/local/flash.ts @@ -17,10 +17,10 @@ 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'; +import Command from '../../command.js'; +import { ExpectedError } from '../../errors.js'; +import * as cf from '../../utils/common-flags.js'; +import { getChalk, getVisuals, stripIndent } from '../../utils/lazy.js'; export default class LocalFlashCmd extends Command { public static description = stripIndent` @@ -79,7 +79,7 @@ export default class LocalFlashCmd extends Command { const drive = await this.getDrive(options); - const { confirm } = await import('../../utils/patterns'); + const { confirm } = await import('../../utils/patterns.js'); await confirm( options.yes, 'This will erase the selected drive. Are you sure?', diff --git a/lib/commands/login/index.ts b/lib/commands/login/index.ts index 70d6c1c6..f87c788b 100644 --- a/lib/commands/login/index.ts +++ b/lib/commands/login/index.ts @@ -16,10 +16,10 @@ */ import { Flags, Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy'; -import { ExpectedError } from '../../errors'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy.js'; +import { ExpectedError } from '../../errors.js'; import type { WhoamiResult } from 'balena-sdk'; interface FlagsDef { @@ -122,8 +122,8 @@ export default class LoginCmd extends Command { public async run() { const { flags: options, args: params } = await this.parse(LoginCmd); - const balena = getBalenaSdk(); - const messages = await import('../../utils/messages'); + const balena = await getBalenaSdk(); + const messages = await import('../../utils/messages.js'); const balenaUrl = await balena.settings.get('balenaUrl'); // Consolidate user/email options @@ -186,7 +186,7 @@ ${messages.reachingOut}`); type: 'input', }); } - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); await balena.auth.loginWithToken(token!); try { if (!(await balena.auth.whoami())) { @@ -202,20 +202,20 @@ ${messages.reachingOut}`); } // Credentials else if (loginOptions.credentials) { - const patterns = await import('../../utils/patterns'); + const patterns = await import('../../utils/patterns.js'); return patterns.authenticate(loginOptions); } // Web else if (loginOptions.web) { - const auth = await import('../../auth'); + const auth = await import('../../auth/index.js'); await auth.login({ port: loginOptions.port }); return; } else { - const patterns = await import('../../utils/patterns'); + const patterns = await import('../../utils/patterns.js'); // User had not selected login preference, prompt interactively const loginType = await patterns.askLoginType(); if (loginType === 'register') { - const open = await import('open'); + const { default: open } = await import('open'); const signupUrl = `https://dashboard.${balenaUrl}/signup`; await open(signupUrl, { wait: false }); throw new ExpectedError(`Please sign up at ${signupUrl}`); diff --git a/lib/commands/logout/index.ts b/lib/commands/logout/index.ts index 5ea45963..b095c972 100644 --- a/lib/commands/logout/index.ts +++ b/lib/commands/logout/index.ts @@ -15,8 +15,8 @@ * limitations under the License. */ -import Command from '../../command'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; export default class LogoutCmd extends Command { public static description = stripIndent` @@ -30,6 +30,6 @@ export default class LogoutCmd extends Command { public async run() { await this.parse(LogoutCmd); - await getBalenaSdk().auth.logout(); + await (await getBalenaSdk()).auth.logout(); } } diff --git a/lib/commands/logs/index.ts b/lib/commands/logs/index.ts index 4745211a..afacb998 100644 --- a/lib/commands/logs/index.ts +++ b/lib/commands/logs/index.ts @@ -16,9 +16,9 @@ */ import { Flags, Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; import { LogMessage } from 'balena-sdk'; const MAX_RETRY = 1000; @@ -95,15 +95,15 @@ export default class LogsCmd extends Command { public async run() { const { args: params, flags: options } = await this.parse(LogsCmd); - const balena = getBalenaSdk(); - const { serviceIdToName } = await import('../../utils/cloud'); + const balena = await getBalenaSdk(); + const { serviceIdToName } = await import('../../utils/cloud.js'); const { connectAndDisplayDeviceLogs, displayLogObject } = await import( - '../../utils/device/logs' + '../../utils/device/logs.js' ); const { validateIPAddress, validateDotLocalUrl } = await import( - '../../utils/validation' + '../../utils/validation.js' ); - const Logger = await import('../../utils/logger'); + const { default: Logger } = await import('../../utils/logger.js'); const logger = Logger.getLogger(); @@ -132,13 +132,13 @@ export default class LogsCmd extends Command { validateDotLocalUrl(params.device) ) { // Logs from local device - const { DeviceAPI } = await import('../../utils/device/api'); + const { DeviceAPI } = await import('../../utils/device/api.js'); const deviceApi = new DeviceAPI(logger, params.device); logger.logDebug('Checking we can access device'); try { await deviceApi.ping(); } catch (e) { - const { ExpectedError } = await import('../../errors'); + const { ExpectedError } = await import('../../errors.js'); throw new ExpectedError( `Cannot access device at address ${params.device}. Device may not be in local mode.`, ); diff --git a/lib/commands/notes/index.ts b/lib/commands/notes/index.ts index fb3e38fc..ca8ef440 100644 --- a/lib/commands/notes/index.ts +++ b/lib/commands/notes/index.ts @@ -16,10 +16,10 @@ */ 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'; +import Command from '../../command.js'; +import { ExpectedError } from '../../errors.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; export default class NoteCmd extends Command { public static description = stripIndent` @@ -73,7 +73,7 @@ export default class NoteCmd extends Command { throw new ExpectedError('Missing device UUID (--device)'); } - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); return balena.models.device.setNote(options.device, params.note); } diff --git a/lib/commands/orgs/index.ts b/lib/commands/orgs/index.ts index f50604f8..548f67c4 100644 --- a/lib/commands/orgs/index.ts +++ b/lib/commands/orgs/index.ts @@ -15,9 +15,9 @@ * limitations under the License. */ -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy.js'; export default class OrgsCmd extends Command { public static description = stripIndent` @@ -38,10 +38,10 @@ export default class OrgsCmd extends Command { public async run() { await this.parse(OrgsCmd); - const { getOwnOrganizations } = await import('../../utils/sdk'); + const { getOwnOrganizations } = await import('../../utils/sdk.js'); // Get organizations - const organizations = await getOwnOrganizations(getBalenaSdk(), { + const organizations = await getOwnOrganizations(await getBalenaSdk(), { $select: ['name', 'handle'], }); diff --git a/lib/commands/os/build-config.ts b/lib/commands/os/build-config.ts index 0b6d954a..685f2063 100644 --- a/lib/commands/os/build-config.ts +++ b/lib/commands/os/build-config.ts @@ -16,10 +16,10 @@ */ 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 Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getCliForm, stripIndent } from '../../utils/lazy.js'; +import _ from 'lodash'; import type { DeviceTypeJson } from 'balena-sdk'; export default class OsBuildConfigCmd extends Command { @@ -82,7 +82,7 @@ export default class OsBuildConfigCmd extends Command { async buildConfig(image: string, deviceTypeSlug: string, advanced: boolean) { advanced = advanced || false; - const { getManifest } = await import('../../utils/helpers'); + const { getManifest } = await import('../../utils/helpers.js'); const deviceTypeManifest = await getManifest(image, deviceTypeSlug); return this.buildConfigForDeviceType(deviceTypeManifest, advanced); @@ -103,7 +103,7 @@ export default class OsBuildConfigCmd extends Command { }); if (advancedGroup != null) { - const { getGroupDefaults } = await import('../../utils/helpers'); + const { getGroupDefaults } = await import('../../utils/helpers.js'); override = getGroupDefaults(advancedGroup); } } diff --git a/lib/commands/os/configure.ts b/lib/commands/os/configure.ts index 3853e18c..60a20347 100644 --- a/lib/commands/os/configure.ts +++ b/lib/commands/os/configure.ts @@ -19,16 +19,16 @@ import { Flags, Args } from '@oclif/core'; import type { Interfaces } from '@oclif/core'; import type * as BalenaSdk from 'balena-sdk'; import { promisify } from 'util'; -import * as _ from 'lodash'; -import Command from '../../command'; -import { ExpectedError } from '../../errors'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy'; +import _ from 'lodash'; +import Command from '../../command.js'; +import { ExpectedError } from '../../errors.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy.js'; import { applicationIdInfo, devModeInfo, secureBootInfo, -} from '../../utils/messages'; +} from '../../utils/messages.js'; const CONNECTIONS_FOLDER = '/system-connections'; @@ -175,16 +175,16 @@ export default class OsConfigureCmd extends Command { const devInit = await import('balena-device-init'); const { promises: fs } = await import('fs'); const { generateDeviceConfig, generateApplicationConfig } = await import( - '../../utils/config' + '../../utils/config.js' ); - const helpers = await import('../../utils/helpers'); - const { getApplication } = await import('../../utils/sdk'); + const helpers = await import('../../utils/helpers.js'); + const { getApplication } = await import('../../utils/sdk.js'); let app: ApplicationWithDeviceTypeSlug | undefined; let device; let deviceTypeSlug: string; - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); if (options.device) { device = (await balena.models.device.get(options.device, { $expand: { @@ -210,7 +210,7 @@ export default class OsConfigureCmd extends Command { deviceTypeSlug, ); - let configJson: import('../../utils/config').ImgConfig | undefined; + let configJson: import('../../utils/config.js').ImgConfig | undefined; if (options.config) { const rawConfig = await fs.readFile(options.config, 'utf8'); configJson = JSON.parse(rawConfig); @@ -220,11 +220,11 @@ export default class OsConfigureCmd extends Command { options.version || (await getOsVersionFromImage(params.image, deviceTypeManifest, devInit)); - const { validateDevOptionAndWarn } = await import('../../utils/config'); + const { validateDevOptionAndWarn } = await import('../../utils/config.js'); await validateDevOptionAndWarn(options.dev, osVersion); const { validateSecureBootOptionAndWarn } = await import( - '../../utils/config' + '../../utils/config.js' ); await validateSecureBootOptionAndWarn( options.secureBoot, @@ -362,7 +362,7 @@ async function checkDeviceTypeCompatibility( }, ) { if (options['device-type']) { - const helpers = await import('../../utils/helpers'); + const helpers = await import('../../utils/helpers.js'); if ( !(await helpers.areDeviceTypesCompatible( app.is_for__device_type[0].slug, @@ -393,7 +393,7 @@ async function checkDeviceTypeCompatibility( async function askQuestionsForDeviceType( deviceType: BalenaSdk.DeviceTypeJson.DeviceType, options: FlagsDef, - configJson?: import('../../utils/config').ImgConfig, + configJson?: import('../../utils/config.js').ImgConfig, ): Promise { const answerSources: any[] = [ { @@ -416,7 +416,7 @@ async function askQuestionsForDeviceType( isGroup: true, }); if (!_.isEmpty(advancedGroup)) { - const helpers = await import('../../utils/helpers'); + const helpers = await import('../../utils/helpers.js'); answerSources.push(helpers.getGroupDefaults(advancedGroup)); } } diff --git a/lib/commands/os/download.ts b/lib/commands/os/download.ts index e7ef1bda..0c9e0088 100644 --- a/lib/commands/os/download.ts +++ b/lib/commands/os/download.ts @@ -16,9 +16,9 @@ */ import { Flags, Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { stripIndent } from '../../utils/lazy.js'; export default class OsDownloadCmd extends Command { public static description = stripIndent` @@ -95,7 +95,7 @@ export default class OsDownloadCmd extends Command { await OsDownloadCmd.checkLoggedIn(); } catch (e) { const { ExpectedError, NotLoggedInError } = await import( - '../../errors' + '../../errors.js' ); if (e instanceof NotLoggedInError) { throw new ExpectedError(stripIndent` @@ -107,7 +107,7 @@ export default class OsDownloadCmd extends Command { } } - const { downloadOSImage } = await import('../../utils/cloud'); + const { downloadOSImage } = await import('../../utils/cloud.js'); try { await downloadOSImage(params.type, options.output, options.version); diff --git a/lib/commands/os/initialize.ts b/lib/commands/os/initialize.ts index 322c1ab8..801b67c0 100644 --- a/lib/commands/os/initialize.ts +++ b/lib/commands/os/initialize.ts @@ -16,9 +16,9 @@ */ import { Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getCliForm, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getCliForm, stripIndent } from '../../utils/lazy.js'; const INIT_WARNING_MESSAGE = ` @@ -62,7 +62,7 @@ export default class OsInitializeCmd extends Command { public async run() { const { args: params, flags: options } = await this.parse(OsInitializeCmd); - const { getManifest, sudo } = await import('../../utils/helpers'); + const { getManifest, sudo } = await import('../../utils/helpers.js'); console.info(`Initializing device ${INIT_WARNING_MESSAGE}`); @@ -75,13 +75,13 @@ export default class OsInitializeCmd extends Command { }); if (answers.drive != null) { - const { confirm } = await import('../../utils/patterns'); + const { confirm } = await import('../../utils/patterns.js'); await confirm( options.yes, `This will erase ${answers.drive}. Are you sure?`, `Going to erase ${answers.drive}.`, ); - const { safeUmount } = await import('../../utils/umount'); + const { safeUmount } = await import('../../utils/umount.js'); await safeUmount(answers.drive); } @@ -94,7 +94,7 @@ export default class OsInitializeCmd extends Command { ]); if (answers.drive != null) { - const { safeUmount } = await import('../../utils/umount'); + const { safeUmount } = await import('../../utils/umount.js'); await safeUmount(answers.drive); console.info(`You can safely remove ${answers.drive} now`); } diff --git a/lib/commands/os/versions.ts b/lib/commands/os/versions.ts index d4f7f5b5..df3f967d 100644 --- a/lib/commands/os/versions.ts +++ b/lib/commands/os/versions.ts @@ -16,9 +16,9 @@ */ import { Flags, Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { stripIndent } from '../../utils/lazy.js'; export default class OsVersionsCmd extends Command { public static description = stripIndent` @@ -54,7 +54,7 @@ export default class OsVersionsCmd extends Command { const { args: params, flags: options } = await this.parse(OsVersionsCmd); const { formatOsVersion, getOsVersions } = await import( - '../../utils/cloud' + '../../utils/cloud.js' ); const vs = await getOsVersions(params.type, !!options.esr); diff --git a/lib/commands/preload/index.ts b/lib/commands/preload/index.ts index 3ecee597..d5e2721a 100644 --- a/lib/commands/preload/index.ts +++ b/lib/commands/preload/index.ts @@ -15,21 +15,21 @@ * limitations under the License. */ -import Command from '../../command'; -import { ExpectedError } from '../../errors'; -import * as cf from '../../utils/common-flags'; +import Command from '../../command.js'; +import { ExpectedError } from '../../errors.js'; +import * as cf from '../../utils/common-flags.js'; import { getBalenaSdk, getCliForm, getVisuals, stripIndent, -} from '../../utils/lazy'; -import { applicationIdInfo } from '../../utils/messages'; -import { dockerConnectionCliFlags } from '../../utils/docker'; -import { parseAsInteger } from '../../utils/validation'; +} from '../../utils/lazy.js'; +import { applicationIdInfo } from '../../utils/messages.js'; +import { dockerConnectionCliFlags } from '../../utils/docker.js'; +import { parseAsInteger } from '../../utils/validation.js'; import { Flags, Args } from '@oclif/core'; -import * as _ from 'lodash'; +import _ from 'lodash'; import type { Application, BalenaSDK, @@ -39,6 +39,7 @@ import type { Release, } from 'balena-sdk'; import type { Preloader } from 'balena-preload'; +import type EventEmitter from 'events'; export default class PreloadCmd extends Command { public static description = stripIndent` @@ -144,11 +145,11 @@ Can be repeated to add multiple certificates.\ public async run() { const { args: params, flags: options } = await this.parse(PreloadCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); const balenaPreload = await import('balena-preload'); const visuals = getVisuals(); const nodeCleanup = await import('node-cleanup'); - const { instanceOf } = await import('../../errors'); + const { instanceOf } = await import('../../errors.js'); // Check image file exists try { @@ -172,7 +173,7 @@ Can be repeated to add multiple certificates.\ // Load app here, and use app slug from hereon const fleetSlug: string | undefined = options.fleet ? await ( - await import('../../utils/sdk') + await import('../../utils/sdk.js') ).getFleetSlug(balena, options.fleet) : undefined; @@ -229,7 +230,7 @@ Can be repeated to add multiple certificates.\ } // Get a configured dockerode instance - const dockerUtils = await import('../../utils/docker'); + const dockerUtils = await import('../../utils/docker.js'); const docker = await dockerUtils.getDocker(options); const preloader = new balenaPreload.Preloader( undefined, @@ -243,11 +244,11 @@ Can be repeated to add multiple certificates.\ pinDevice ?? false, certificates, additionalSpace, - ); + ) as Preloader & EventEmitter; let gotSignal = false; - nodeCleanup(function (_exitCode, signal) { + nodeCleanup.default(function (_exitCode, signal) { if (signal) { gotSignal = true; nodeCleanup.uninstall(); // don't call cleanup handler again @@ -326,7 +327,7 @@ Can be repeated to add multiple certificates.\ } async getApplicationsWithSuccessfulBuilds(deviceTypeSlug: string) { - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); try { await balena.models.deviceType.get(deviceTypeSlug); @@ -434,7 +435,7 @@ Can be repeated to add multiple certificates.\ commit: string, pinDevice: boolean | undefined, ) { - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); if ( this.isCurrentCommit(commit) || @@ -481,7 +482,7 @@ Would you like to disable automatic updates for this fleet now?\ } async getAppWithReleases(balenaSdk: BalenaSDK, slug: string) { - const { getApplication } = await import('../../utils/sdk'); + const { getApplication } = await import('../../utils/sdk.js'); return await getApplication(balenaSdk, slug, { $expand: this.applicationExpandOptions, diff --git a/lib/commands/push/index.ts b/lib/commands/push/index.ts index a00c0fb4..d67d702d 100644 --- a/lib/commands/push/index.ts +++ b/lib/commands/push/index.ts @@ -17,18 +17,18 @@ import { Flags, Args } from '@oclif/core'; import type { Interfaces } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { dockerignoreHelp, registrySecretsHelp } from '../../utils/messages'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; +import { dockerignoreHelp, registrySecretsHelp } from '../../utils/messages.js'; import type { BalenaSDK } from 'balena-sdk'; -import { ExpectedError, instanceOf } from '../../errors'; +import { ExpectedError, instanceOf } from '../../errors.js'; import { RegistrySecrets } from '@balena/compose/dist/multibuild'; -import { lowercaseIfSlug } from '../../utils/normalization'; +import { lowercaseIfSlug } from '../../utils/normalization.js'; import { applyReleaseTagKeysAndValues, parseReleaseTagKeysAndValues, -} from '../../utils/compose_ts'; +} from '../../utils/compose_ts.js'; enum BuildTarget { Cloud, @@ -232,8 +232,10 @@ export default class PushCmd extends Command { const logger = await Command.getLogger(); logger.logDebug(`Using build source directory: ${options.source} `); - const sdk = getBalenaSdk(); - const { validateProjectDirectory } = await import('../../utils/compose_ts'); + const sdk = await getBalenaSdk(); + const { validateProjectDirectory } = await import( + '../../utils/compose_ts.js' + ); const { dockerfilePath, registrySecrets } = await validateProjectDirectory( sdk, { @@ -276,8 +278,8 @@ export default class PushCmd extends Command { dockerfilePath: string, registrySecrets: RegistrySecrets, ) { - const remote = await import('../../utils/remote-build'); - const { getApplication } = await import('../../utils/sdk'); + const remote = await import('../../utils/remote-build.js'); + const { getApplication } = await import('../../utils/sdk.js'); // Check for invalid options const localOnlyOptions: Array = [ @@ -356,7 +358,7 @@ export default class PushCmd extends Command { 'is only valid when pushing to a fleet', ); - const deviceDeploy = await import('../../utils/device/deploy'); + const deviceDeploy = await import('../../utils/device/deploy.js'); try { await deviceDeploy.deployToDevice({ @@ -376,7 +378,7 @@ export default class PushCmd extends Command { convertEol: !options['noconvert-eol'], }); } catch (e) { - const { BuildError } = await import('../../utils/device/errors'); + const { BuildError } = await import('../../utils/device/errors.js'); if (instanceOf(e, BuildError)) { throw new ExpectedError(e.toString()); } else { @@ -387,7 +389,7 @@ export default class PushCmd extends Command { protected async getBuildTarget(appOrDevice: string): Promise { const { validateLocalHostnameOrIp } = await import( - '../../utils/validation' + '../../utils/validation.js' ); return validateLocalHostnameOrIp(appOrDevice) diff --git a/lib/commands/release/finalize.ts b/lib/commands/release/finalize.ts index 2ddb422e..d12f1a37 100644 --- a/lib/commands/release/finalize.ts +++ b/lib/commands/release/finalize.ts @@ -15,10 +15,10 @@ * limitations under the License. */ -import { commitOrIdArg } from '.'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; +import { commitOrIdArg } from './index.js'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; export default class ReleaseFinalizeCmd extends Command { public static description = stripIndent` @@ -58,7 +58,7 @@ export default class ReleaseFinalizeCmd extends Command { public async run() { const { args: params } = await this.parse(ReleaseFinalizeCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); const release = await balena.models.release.get(params.commitOrId, { $select: ['id', 'is_final'], diff --git a/lib/commands/release/index.ts b/lib/commands/release/index.ts index a73a8d31..7062f8c5 100644 --- a/lib/commands/release/index.ts +++ b/lib/commands/release/index.ts @@ -14,15 +14,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import { Flags, Args, type Interfaces } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy.js'; import type * as BalenaSdk from 'balena-sdk'; -import jsyaml = require('js-yaml'); -import { tryAsInteger } from '../../utils/validation'; -import { jsonInfo } from '../../utils/messages'; +import jsyaml from 'js-yaml'; +import { tryAsInteger } from '../../utils/validation.js'; +import { jsonInfo } from '../../utils/messages.js'; export const commitOrIdArg = Args.custom({ parse: async (commitOrId: string) => tryAsInteger(commitOrId), @@ -66,7 +65,7 @@ export default class ReleaseCmd extends Command { public async run() { const { args: params, flags: options } = await this.parse(ReleaseCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); if (options.composition) { await this.showComposition(params.commitOrId, balena); } else { diff --git a/lib/commands/release/invalidate.ts b/lib/commands/release/invalidate.ts index f9f3f9b2..dee32bf0 100644 --- a/lib/commands/release/invalidate.ts +++ b/lib/commands/release/invalidate.ts @@ -15,10 +15,10 @@ * limitations under the License. */ -import { commitOrIdArg } from '.'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; +import { commitOrIdArg } from './index.js'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; export default class ReleaseInvalidateCmd extends Command { public static description = stripIndent` @@ -53,7 +53,7 @@ export default class ReleaseInvalidateCmd extends Command { public async run() { const { args: params } = await this.parse(ReleaseInvalidateCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); const release = await balena.models.release.get(params.commitOrId, { $select: ['id', 'is_invalidated'], diff --git a/lib/commands/release/validate.ts b/lib/commands/release/validate.ts index 85ce4b45..82e88256 100644 --- a/lib/commands/release/validate.ts +++ b/lib/commands/release/validate.ts @@ -15,10 +15,10 @@ * limitations under the License. */ -import { commitOrIdArg } from '.'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; +import { commitOrIdArg } from './index.js'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; export default class ReleaseValidateCmd extends Command { public static description = stripIndent` @@ -52,7 +52,7 @@ export default class ReleaseValidateCmd extends Command { public async run() { const { args: params } = await this.parse(ReleaseValidateCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); const release = await balena.models.release.get(params.commitOrId, { $select: ['id', 'is_invalidated'], diff --git a/lib/commands/releases/index.ts b/lib/commands/releases/index.ts index 5efc7ce0..c10a1bcf 100644 --- a/lib/commands/releases/index.ts +++ b/lib/commands/releases/index.ts @@ -16,12 +16,12 @@ */ 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 Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy.js'; +import { applicationNameNote } from '../../utils/messages.js'; import type * as BalenaSdk from 'balena-sdk'; -import { jsonInfo } from '../../utils/messages'; +import { jsonInfo } from '../../utils/messages.js'; export default class ReleasesCmd extends Command { public static description = stripIndent` @@ -66,8 +66,8 @@ export default class ReleasesCmd extends Command { 'is_final', ]; - const balena = getBalenaSdk(); - const { getFleetSlug } = await import('../../utils/sdk'); + const balena = await getBalenaSdk(); + const { getFleetSlug } = await import('../../utils/sdk.js'); const releases = await balena.models.release.getAllByApplication( await getFleetSlug(balena, params.fleet), diff --git a/lib/commands/scan/index.ts b/lib/commands/scan/index.ts index fd6fdcc2..0bf15f63 100644 --- a/lib/commands/scan/index.ts +++ b/lib/commands/scan/index.ts @@ -16,9 +16,9 @@ */ import { Flags } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getCliUx, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getCliUx, stripIndent } from '../../utils/lazy.js'; export default class ScanCmd extends Command { public static description = stripIndent` @@ -65,10 +65,10 @@ export default class ScanCmd extends Command { public async run() { const _ = await import('lodash'); const { discoverLocalBalenaOsDevices } = await import( - '../../utils/discover' + '../../utils/discover.js' ); const prettyjson = await import('prettyjson'); - const dockerUtils = await import('../../utils/docker'); + const dockerUtils = await import('../../utils/docker.js'); const dockerPort = 2375; const dockerTimeout = 2000; diff --git a/lib/commands/settings/index.ts b/lib/commands/settings/index.ts index f54c4db0..d672a98a 100644 --- a/lib/commands/settings/index.ts +++ b/lib/commands/settings/index.ts @@ -15,9 +15,9 @@ * limitations under the License. */ -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; export default class SettingsCmd extends Command { public static description = stripIndent` @@ -36,7 +36,7 @@ export default class SettingsCmd extends Command { public async run() { await this.parse(SettingsCmd); - const settings = await getBalenaSdk().settings.getAll(); + const settings = await (await getBalenaSdk()).settings.getAll(); const prettyjson = await import('prettyjson'); console.log(prettyjson.render(settings)); diff --git a/lib/commands/ssh/index.ts b/lib/commands/ssh/index.ts index 477736ff..a061e95e 100644 --- a/lib/commands/ssh/index.ts +++ b/lib/commands/ssh/index.ts @@ -16,13 +16,13 @@ */ import { Flags, Args } from '@oclif/core'; -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; import { parseAsInteger, validateLocalHostnameOrIp, -} from '../../utils/validation'; +} from '../../utils/validation.js'; export default class SshCmd extends Command { public static description = stripIndent` @@ -111,7 +111,9 @@ export default class SshCmd extends Command { // Local connection if (validateLocalHostnameOrIp(params.fleetOrDevice)) { - const { performLocalDeviceSSH } = await import('../../utils/device/ssh'); + const { performLocalDeviceSSH } = await import( + '../../utils/device/ssh.js' + ); return await performLocalDeviceSSH({ hostname: params.fleetOrDevice, port: options.port || 'local', @@ -122,9 +124,11 @@ export default class SshCmd extends Command { } // Remote connection - const { getProxyConfig } = await import('../../utils/helpers'); - const { getOnlineTargetDeviceUuid } = await import('../../utils/patterns'); - const sdk = getBalenaSdk(); + const { getProxyConfig } = await import('../../utils/helpers.js'); + const { getOnlineTargetDeviceUuid } = await import( + '../../utils/patterns.js' + ); + const sdk = await getBalenaSdk(); const proxyConfig = getProxyConfig(); const useProxy = !!proxyConfig && !options.noproxy; @@ -137,7 +141,7 @@ export default class SshCmd extends Command { params.fleetOrDevice, ); - const { which } = await import('../../utils/which'); + const { which } = await import('../../utils/which.js'); const [whichProxytunnel, { username }, proxyUrl] = await Promise.all([ useProxy ? which('proxytunnel', false) : undefined, @@ -189,7 +193,7 @@ export default class SshCmd extends Command { let containerId: string | undefined; if (params.service != null) { const { getContainerIdForService } = await import( - '../../utils/device/ssh' + '../../utils/device/ssh.js' ); containerId = await getContainerIdForService({ deviceUuid, @@ -207,7 +211,7 @@ export default class SshCmd extends Command { } else { accessCommand = `host ${deviceUuid}`; } - const { runRemoteCommand } = await import('../../utils/ssh'); + const { runRemoteCommand } = await import('../../utils/ssh.js'); await runRemoteCommand({ cmd: accessCommand, hostname: `ssh.${proxyUrl}`, diff --git a/lib/commands/support/index.ts b/lib/commands/support/index.ts index 84af6f4c..14521de5 100644 --- a/lib/commands/support/index.ts +++ b/lib/commands/support/index.ts @@ -16,11 +16,11 @@ */ 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'; +import Command from '../../command.js'; +import { ExpectedError } from '../../errors.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy.js'; +import { applicationIdInfo } from '../../utils/messages.js'; export default class SupportCmd extends Command { public static description = stripIndent` @@ -77,7 +77,7 @@ export default class SupportCmd extends Command { public async run() { const { args: params, flags: options } = await this.parse(SupportCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); const ux = getCliUx(); const enabling = params.action === 'enable'; @@ -116,7 +116,7 @@ export default class SupportCmd extends Command { ux.action.stop(); } - const { getFleetSlug } = await import('../../utils/sdk'); + const { getFleetSlug } = await import('../../utils/sdk.js'); // Process applications for (const appName of appNames) { diff --git a/lib/commands/tag/rm.ts b/lib/commands/tag/rm.ts index 057418f4..d0e6222e 100644 --- a/lib/commands/tag/rm.ts +++ b/lib/commands/tag/rm.ts @@ -16,10 +16,10 @@ */ 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'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; +import { applicationIdInfo } from '../../utils/messages.js'; export default class TagRmCmd extends Command { public static description = stripIndent` @@ -68,16 +68,16 @@ export default class TagRmCmd extends Command { public async run() { const { args: params, flags: options } = await this.parse(TagRmCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); // Check user has specified one of application/device/release if (!options.fleet && !options.device && !options.release) { - const { ExpectedError } = await import('../../errors'); + const { ExpectedError } = await import('../../errors.js'); throw new ExpectedError(TagRmCmd.missingResourceMessage); } if (options.fleet) { - const { getFleetSlug } = await import('../../utils/sdk'); + const { getFleetSlug } = await import('../../utils/sdk.js'); return balena.models.application.tags.remove( await getFleetSlug(balena, options.fleet), params.tagKey, @@ -88,7 +88,7 @@ export default class TagRmCmd extends Command { } if (options.release) { const { disambiguateReleaseParam } = await import( - '../../utils/normalization' + '../../utils/normalization.js' ); const releaseParam = await disambiguateReleaseParam( balena, diff --git a/lib/commands/tag/set.ts b/lib/commands/tag/set.ts index d77e4190..6156be86 100644 --- a/lib/commands/tag/set.ts +++ b/lib/commands/tag/set.ts @@ -16,10 +16,10 @@ */ 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'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; +import { applicationIdInfo } from '../../utils/messages.js'; export default class TagSetCmd extends Command { public static description = stripIndent` @@ -81,18 +81,18 @@ export default class TagSetCmd extends Command { public async run() { const { args: params, flags: options } = await this.parse(TagSetCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); // Check user has specified one of application/device/release if (!options.fleet && !options.device && !options.release) { - const { ExpectedError } = await import('../../errors'); + const { ExpectedError } = await import('../../errors.js'); throw new ExpectedError(TagSetCmd.missingResourceMessage); } params.value ??= ''; if (options.fleet) { - const { getFleetSlug } = await import('../../utils/sdk'); + const { getFleetSlug } = await import('../../utils/sdk.js'); return balena.models.application.tags.set( await getFleetSlug(balena, options.fleet), params.tagKey, @@ -108,7 +108,7 @@ export default class TagSetCmd extends Command { } if (options.release) { const { disambiguateReleaseParam } = await import( - '../../utils/normalization' + '../../utils/normalization.js' ); const releaseParam = await disambiguateReleaseParam( balena, diff --git a/lib/commands/tags/index.ts b/lib/commands/tags/index.ts index 9653c396..e444d00b 100644 --- a/lib/commands/tags/index.ts +++ b/lib/commands/tags/index.ts @@ -15,11 +15,11 @@ * limitations under the License. */ -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'; +import Command from '../../command.js'; +import { ExpectedError } from '../../errors.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy.js'; +import { applicationIdInfo } from '../../utils/messages.js'; export default class TagsCmd extends Command { public static description = stripIndent` @@ -61,7 +61,7 @@ export default class TagsCmd extends Command { public async run() { const { flags: options } = await this.parse(TagsCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); // Check user has specified one of application/device/release if (!options.fleet && !options.device && !options.release) { @@ -71,7 +71,7 @@ export default class TagsCmd extends Command { let tags; if (options.fleet) { - const { getFleetSlug } = await import('../../utils/sdk'); + const { getFleetSlug } = await import('../../utils/sdk.js'); tags = await balena.models.application.tags.getAllByApplication( await getFleetSlug(balena, options.fleet), ); @@ -81,7 +81,7 @@ export default class TagsCmd extends Command { } if (options.release) { const { disambiguateReleaseParam } = await import( - '../../utils/normalization' + '../../utils/normalization.js' ); const releaseParam = await disambiguateReleaseParam( balena, diff --git a/lib/commands/tunnel/index.ts b/lib/commands/tunnel/index.ts index 91be2fe6..480487a1 100644 --- a/lib/commands/tunnel/index.ts +++ b/lib/commands/tunnel/index.ts @@ -16,15 +16,15 @@ */ import { Flags, Args } from '@oclif/core'; -import Command from '../../command'; +import Command from '../../command.js'; import { NoPortsDefinedError, InvalidPortMappingError, ExpectedError, -} from '../../errors'; -import * as cf from '../../utils/common-flags'; -import { getBalenaSdk, stripIndent } from '../../utils/lazy'; -import { lowercaseIfSlug } from '../../utils/normalization'; +} from '../../errors.js'; +import * as cf from '../../utils/common-flags.js'; +import { getBalenaSdk, stripIndent } from '../../utils/lazy.js'; +import { lowercaseIfSlug } from '../../utils/normalization.js'; import type { Server, Socket } from 'net'; @@ -97,7 +97,7 @@ export default class TunnelCmd extends Command { const { args: params, flags: options } = await this.parse(TunnelCmd); const logger = await Command.getLogger(); - const sdk = getBalenaSdk(); + const sdk = await getBalenaSdk(); const logConnection = ( fromHost: string, @@ -122,7 +122,9 @@ export default class TunnelCmd extends Command { } // Ascertain device uuid - const { getOnlineTargetDeviceUuid } = await import('../../utils/patterns'); + const { getOnlineTargetDeviceUuid } = await import( + '../../utils/patterns.js' + ); const uuid = await getOnlineTargetDeviceUuid(sdk, params.deviceOrFleet); logger.logInfo(`Opening a tunnel to ${uuid}...`); @@ -134,7 +136,7 @@ export default class TunnelCmd extends Command { .map(async ({ localPort, localAddress, remotePort }) => { try { const { tunnelConnectionToDevice } = await import( - '../../utils/tunnel' + '../../utils/tunnel.js' ); const handler = await tunnelConnectionToDevice(uuid, remotePort, sdk); diff --git a/lib/commands/util/available-drives.ts b/lib/commands/util/available-drives.ts index ac71d201..8705dd77 100644 --- a/lib/commands/util/available-drives.ts +++ b/lib/commands/util/available-drives.ts @@ -15,9 +15,9 @@ * limitations under the License. */ -import Command from '../../command'; -import * as cf from '../../utils/common-flags'; -import { stripIndent, getChalk, getVisuals } from '../../utils/lazy'; +import Command from '../../command.js'; +import * as cf from '../../utils/common-flags.js'; +import { stripIndent, getChalk, getVisuals } from '../../utils/lazy.js'; export default class UtilAvailableDrivesCmd extends Command { public static description = stripIndent` diff --git a/lib/commands/version/index.ts b/lib/commands/version/index.ts index 9c59a8cc..dd3cd19f 100644 --- a/lib/commands/version/index.ts +++ b/lib/commands/version/index.ts @@ -16,8 +16,8 @@ */ import { Flags } from '@oclif/core'; -import Command from '../../command'; -import { stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import { stripIndent } from '../../utils/lazy.js'; export interface JsonVersions { 'balena-cli': string; @@ -71,8 +71,9 @@ export default class VersionCmd extends Command { public async run() { const { flags: options } = await this.parse(VersionCmd); + const { default: packageJson } = await import('../../../package.json'); const versions: JsonVersions = { - 'balena-cli': (await import('../../../package.json')).version, + 'balena-cli': packageJson.version, 'Node.js': process.version && process.version.startsWith('v') ? process.version.slice(1) diff --git a/lib/commands/whoami/index.ts b/lib/commands/whoami/index.ts index adc81acd..c1987120 100644 --- a/lib/commands/whoami/index.ts +++ b/lib/commands/whoami/index.ts @@ -15,8 +15,8 @@ * limitations under the License. */ -import Command from '../../command'; -import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy'; +import Command from '../../command.js'; +import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy.js'; export default class WhoamiCmd extends Command { public static description = stripIndent` @@ -34,7 +34,7 @@ export default class WhoamiCmd extends Command { public async run() { await this.parse(WhoamiCmd); - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); const [whoamiResult, url] = await Promise.all([ balena.auth.whoami(), diff --git a/lib/deprecation.ts b/lib/deprecation.ts index 7a8762bb..5d56eb55 100644 --- a/lib/deprecation.ts +++ b/lib/deprecation.ts @@ -14,7 +14,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); import type { BalenaSettingsStorage } from 'balena-settings-storage'; export interface ReleaseTimestampsByVersion { @@ -106,7 +107,7 @@ export class DeprecationChecker { const url = this.getNpmUrl(version); let response: import('got').Response> | undefined; try { - response = await got(url, { + response = await got.default(url, { responseType: 'json', retry: 0, timeout: 4000, @@ -198,17 +199,16 @@ or release date not available`); const nextMajorDate = new Date(nextMajorDateStr).getTime(); const daysElapsed = Math.trunc((this.now - nextMajorDate) / this.msInDay); if (daysElapsed > this.expiryDays) { - const { ExpectedError } = await import('./errors'); + const { ExpectedError } = await import('./errors.js'); throw new ExpectedError(this.getExpiryMsg(daysElapsed)); } else if (daysElapsed > this.deprecationDays && process.stderr.isTTY) { - console.error(this.getDeprecationMsg(daysElapsed)); + console.error(await this.getDeprecationMsg(daysElapsed)); } } /** Separate function for the benefit of code testing */ - getDeprecationMsg(daysElapsed: number) { - const { warnify } = - require('./utils/messages') as typeof import('./utils/messages'); + async getDeprecationMsg(daysElapsed: number) { + const { warnify } = await import('./utils/messages.js'); return warnify(`\ CLI version ${this.nextMajorVersion} was released ${daysElapsed} days ago: please upgrade. This version of the balena CLI (${this.currentVersion}) will exit with an error diff --git a/lib/errors.ts b/lib/errors.ts index 157bc318..f7c862bd 100644 --- a/lib/errors.ts +++ b/lib/errors.ts @@ -15,12 +15,12 @@ limitations under the License. */ import type { BalenaError } from 'balena-errors'; -import * as _ from 'lodash'; +import _ from 'lodash'; import * as os from 'os'; import { TypedError } from 'typed-error'; -import { getChalk, stripIndent } from './utils/lazy'; -import { getHelp } from './utils/messages'; -import { CliSettings } from './utils/bootstrap'; +import { getChalk, stripIndent } from './utils/lazy.js'; +import { getHelp } from './utils/messages.js'; +import { CliSettings } from './utils/bootstrap.js'; export class ExpectedError extends TypedError { public code?: string; diff --git a/lib/events.ts b/lib/events.ts index fd0f74f6..0dd2bd38 100644 --- a/lib/events.ts +++ b/lib/events.ts @@ -15,8 +15,8 @@ * limitations under the License. */ -import * as packageJSON from '../package.json'; -import { stripIndent } from './utils/lazy'; +import packageJSON from '../package.json' assert {type: 'json'}; +import { stripIndent } from './utils/lazy.js'; /** * Track balena CLI usage events (product improvement analytics). @@ -44,7 +44,7 @@ export async function trackCommand(commandSignature: string) { scope.setExtra('command', commandSignature); }); } - const { getCachedUsername } = await import('./utils/bootstrap'); + const { getCachedUsername } = await import('./utils/bootstrap.js'); let username: string | undefined; try { username = (await getCachedUsername())?.username; @@ -99,7 +99,7 @@ async function sendEvent(balenaUrl: string, event: string, username?: string) { const url = `https://data.${balenaUrl}/amplitude/2/httpapi`; try { - await got.post(url, { + await got.default.post(url, { json: trackData, retry: 0, timeout: { diff --git a/lib/fast-boot.ts b/lib/fast-boot.ts index bc842340..3d7b5f99 100644 --- a/lib/fast-boot.ts +++ b/lib/fast-boot.ts @@ -20,13 +20,14 @@ * we have permissions over the cache file before even attempting to load * fast boot. * DON'T IMPORT BALENA-CLI MODULES HERE, as this module is loaded directly - * from `bin/balena`, before the CLI's entrypoint in `lib/app.ts`. + * from `bin/balena.js`, before the CLI's entrypoint in `lib/app.ts`. */ import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; - +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); const stat = process.pkg ? fs.statSync : fs.promises.stat; let fastBootStarted = false; @@ -66,15 +67,15 @@ async function $start() { // such as `/usr[/local]/lib/balena-cli`, while being executed by // a regular user account. const cacheFile = path.join(dataDir, 'cli-module-cache.json'); - const root = path.join(__dirname, '..'); - const [, pJson, pStat, nStat] = await Promise.all([ + const root = path.join(import.meta.url, '..'); + const [, pStat, nStat] = await Promise.all([ ensureCanWrite(dataDir, cacheFile), - import('../package.json'), stat(path.join(root, 'package.json'), { bigint: true }), stat(path.join(root, 'npm-shrinkwrap.json'), { bigint: true }), ]); // Include timestamps to account for dev-time changes to node_modules - const cacheKiller = `${pJson.version}-${pStat.mtimeMs}-${nStat.mtimeMs}`; + const pJson = await import('../package.json'); + const cacheKiller = `${pJson.default.version}-${pStat.mtimeMs}-${nStat.mtimeMs}`; require('fast-boot2').start({ cacheFile, cacheKiller, diff --git a/lib/framework/index.ts b/lib/framework/index.ts index 24097f7b..65281aca 100644 --- a/lib/framework/index.ts +++ b/lib/framework/index.ts @@ -14,6 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -import type { DataOutputOptions, DataSetOutputOptions } from './output'; +import type { DataOutputOptions, DataSetOutputOptions } from './output.js'; export { DataOutputOptions, DataSetOutputOptions }; diff --git a/lib/framework/output.ts b/lib/framework/output.ts index e60c6850..c8adb6f2 100644 --- a/lib/framework/output.ts +++ b/lib/framework/output.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { getCliUx, getChalk } from '../utils/lazy'; +import { getCliUx, getChalk } from '../utils/lazy.js'; export interface DataOutputOptions { fields?: string; diff --git a/lib/help.ts b/lib/help.ts index e9644fee..124cc1a6 100644 --- a/lib/help.ts +++ b/lib/help.ts @@ -15,8 +15,8 @@ * limitations under the License. */ import { Help } from '@oclif/core'; -import * as indent from 'indent-string'; -import { getChalk } from './utils/lazy'; +import indent from 'indent-string'; +import { getChalk } from './utils/lazy.js'; // Partially overrides standard implementation of help plugin // https://github.com/oclif/plugin-help/blob/master/src/index.ts @@ -44,7 +44,7 @@ export default class BalenaHelp extends Help { const subject = getHelpSubject(argv); if (!subject) { const verbose = argv.includes('-v') || argv.includes('--verbose'); - console.log(this.getCustomRootHelp(verbose)); + console.log(await this.getCustomRootHelp(verbose)); return; } @@ -83,7 +83,7 @@ export default class BalenaHelp extends Help { console.log(`command ${chalk.cyan.bold(subject)} not found`); } - getCustomRootHelp(showAllCommands: boolean): string { + async getCustomRootHelp(showAllCommands: boolean): Promise { const { bold, cyan } = getChalk(); let commands = this.config.commands; @@ -147,8 +147,9 @@ See: https://git.io/JRHUW#deprecation-policy`, ]; globalOps[0][0] = globalOps[0][0].padEnd(usageLength); - const { deprecationPolicyNote, reachingOut } = - require('./utils/messages') as typeof import('./utils/messages'); + const { deprecationPolicyNote, reachingOut } = await import( + './utils/messages.js' + ); return [ bold('USAGE'), diff --git a/lib/hooks/command-not-found/suggest.ts b/lib/hooks/command-not-found/suggest.ts index 834dc003..952bde9f 100644 --- a/lib/hooks/command-not-found/suggest.ts +++ b/lib/hooks/command-not-found/suggest.ts @@ -16,7 +16,7 @@ */ import type { Hook, Interfaces } from '@oclif/core'; -import { getChalk } from '../../utils/lazy'; +import { getChalk } from '../../utils/lazy.js'; /* A modified version of the command-not-found plugin logic, diff --git a/lib/hooks/prerun/track.ts b/lib/hooks/prerun/track.ts index c57061aa..434ff534 100644 --- a/lib/hooks/prerun/track.ts +++ b/lib/hooks/prerun/track.ts @@ -33,7 +33,7 @@ export const trackPromise = new Promise((resolve) => { * literally so: 'NAME' and 'VALUE' are NOT replaced with actual values. */ const hook: Hook<'prerun'> = async function (options) { - const events = await import('../../events'); + const events = await import('../../events.js'); const usage: string | string[] | undefined = options.Command.usage; const cmdSignature = usage == null ? '*' : typeof usage === 'string' ? usage : usage.join(' '); diff --git a/lib/preparser.ts b/lib/preparser.ts index b09418a8..5fefe6d5 100644 --- a/lib/preparser.ts +++ b/lib/preparser.ts @@ -68,7 +68,7 @@ export async function preparseArgs(argv: string[]): Promise { process.env.BLUEBIRD_LONG_STACK_TRACES = '1'; } - const Logger = await import('./utils/logger'); + const { default: Logger } = await import('./utils/logger.js'); Logger.command = cmdSlice[0]; let args = cmdSlice; @@ -104,8 +104,8 @@ function extractBooleanFlag(argv: string[], flag: string): boolean { * Check whether the command line refers to a command that has been deprecated * and removed and, if so, exit with an informative error message. */ -export function checkDeletedCommand(argvSlice: string[]): void { - const { ExpectedError } = require('./errors') as typeof import('./errors'); +export async function checkDeletedCommand(argvSlice: string[]): Promise { + const { ExpectedError } = await import('./errors.js'); if (argvSlice[0] === 'help') { argvSlice = argvSlice.slice(1); @@ -157,7 +157,7 @@ Please use "balena ${alternative}" instead.`); // Check if this is a space separated 'topic command' style command subcommand (e.g. `end add`) // by comparing with oclif style colon-separated subcommand list (e.g. `env:add`) export async function isSubcommand(args: string[]) { - const { getCommandIdsFromManifest } = await import('./utils/oclif-utils'); + const { getCommandIdsFromManifest } = await import('./utils/oclif-utils.js'); const commandIds = await getCommandIdsFromManifest(); return commandIds.includes(`${args[0] || ''}:${args[1] || ''}`); } diff --git a/lib/utils/application-create.ts b/lib/utils/application-create.ts index 8a7e9a48..de336a8a 100644 --- a/lib/utils/application-create.ts +++ b/lib/utils/application-create.ts @@ -1,5 +1,5 @@ -import { ExpectedError } from '../errors'; -import { getBalenaSdk } from './lazy'; +import { ExpectedError } from '../errors.js'; +import { getBalenaSdk } from './lazy.js'; export interface FlagsDef { organization?: string; @@ -18,16 +18,18 @@ export async function applicationCreateBase( ) { // Ascertain device type const deviceType = - options.type || (await (await import('./patterns')).selectDeviceType()); + options.type || (await (await import('./patterns.js')).selectDeviceType()); // Ascertain organization const organization = options.organization?.toLowerCase() || - (await (await import('./patterns')).getAndSelectOrganization()); + (await (await import('./patterns.js')).getAndSelectOrganization()); // Create application try { - const application = await getBalenaSdk().models.application.create({ + const application = await ( + await getBalenaSdk() + ).models.application.create({ name: params.name, deviceType, organization, diff --git a/lib/utils/bootstrap.ts b/lib/utils/bootstrap.ts index bcf5c587..addde949 100644 --- a/lib/utils/bootstrap.ts +++ b/lib/utils/bootstrap.ts @@ -22,6 +22,9 @@ * like Sentry error reporting, preparser, oclif parser and the like. */ +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); + export class CliSettings { public readonly settings: any; constructor() { @@ -139,7 +142,7 @@ export async function getCachedUsername(): Promise { return cachedUsername; } const [{ getBalenaSdk }, { getStorage }, settings] = await Promise.all([ - import('./lazy'), + import('./lazy.js'), import('balena-settings-storage'), import('balena-settings-client'), ]); @@ -167,7 +170,7 @@ export async function getCachedUsername(): Promise { // ignore } try { - const { username } = await getBalenaSdk().auth.getUserInfo(); + const { username } = await (await getBalenaSdk()).auth.getUserInfo(); if (username) { cachedUsername = { token, username }; await storage.set('cachedUsername', cachedUsername); diff --git a/lib/utils/cloud.ts b/lib/utils/cloud.ts index 590852c8..e7622cff 100644 --- a/lib/utils/cloud.ts +++ b/lib/utils/cloud.ts @@ -16,10 +16,10 @@ */ import type * as SDK from 'balena-sdk'; -import * as _ from 'lodash'; -import { getBalenaSdk, getCliForm, getVisuals, stripIndent } from './lazy'; +import _ from 'lodash'; +import { getBalenaSdk, getCliForm, getVisuals, stripIndent } from './lazy.js'; -import { ExpectedError } from '../errors'; +import { ExpectedError } from '../errors.js'; export const serviceIdToName = _.memoize( async ( @@ -238,7 +238,7 @@ export async function getOsVersions( deviceType: string, esr: boolean, ): Promise { - const sdk = getBalenaSdk(); + const sdk = await getBalenaSdk(); let slug = deviceType; let versions: SDK.OsVersion[] = await sdk.models.os.getAvailableOsVersions(slug); diff --git a/lib/utils/common-args.ts b/lib/utils/common-args.ts index 74ae9baa..040541eb 100644 --- a/lib/utils/common-args.ts +++ b/lib/utils/common-args.ts @@ -15,7 +15,7 @@ * limitations under the License. */ import { Args } from '@oclif/core'; -import { lowercaseIfSlug } from './normalization'; +import { lowercaseIfSlug } from './normalization.js'; export const fleetRequired = Args.string({ description: 'fleet name or slug (preferred)', diff --git a/lib/utils/common-flags.ts b/lib/utils/common-flags.ts index 0941ed43..9c90ae1d 100644 --- a/lib/utils/common-flags.ts +++ b/lib/utils/common-flags.ts @@ -16,8 +16,8 @@ */ import { Flags } from '@oclif/core'; -import { stripIndent } from './lazy'; -import { lowercaseIfSlug } from './normalization'; +import { stripIndent } from './lazy.js'; +import { lowercaseIfSlug } from './normalization.js'; export const fleet = Flags.string({ char: 'f', diff --git a/lib/utils/compose-types.d.ts b/lib/utils/compose-types.d.ts index 1d8924a2..982f8a41 100644 --- a/lib/utils/compose-types.d.ts +++ b/lib/utils/compose-types.d.ts @@ -18,7 +18,7 @@ import type { ImageModel, ReleaseModel, -} from '@balena/compose/dist/release/models'; +} from '@balena/compose/dist/release/models.js'; import type { Composition, ImageDescriptor } from '@balena/compose/dist/parse'; import type { Pack } from 'tar-stream'; diff --git a/lib/utils/compose.ts b/lib/utils/compose.ts index c9c39496..bd7be12e 100644 --- a/lib/utils/compose.ts +++ b/lib/utils/compose.ts @@ -15,9 +15,12 @@ * limitations under the License. */ -import type { Renderer } from './compose_ts'; +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); + +import type { Renderer } from './compose_ts.js'; import type * as SDK from 'balena-sdk'; -import type Dockerode = require('dockerode'); +import type Dockerode from 'dockerode'; import * as path from 'path'; import type { Composition, ImageDescriptor } from '@balena/compose/dist/parse'; import type { @@ -26,12 +29,12 @@ import type { ComposeProject, Release, TaggedImage, -} from './compose-types'; -import { getChalk } from './lazy'; -import Logger = require('./logger'); +} from './compose-types.js'; +import { getChalk } from './lazy.js'; +import type Logger from './logger.js'; import { ProgressCallback } from 'docker-progress'; -export function generateOpts(options: { +export async function generateOpts(options: { source?: string; projectName?: string; nologs: boolean; @@ -40,7 +43,7 @@ export function generateOpts(options: { 'multi-dockerignore': boolean; 'noparent-check': boolean; }): Promise { - const { promises: fs } = require('fs') as typeof import('fs'); + const { promises: fs } = await import('fs'); return fs.realpath(options.source || '.').then((projectPath) => ({ projectName: options.projectName, projectPath, @@ -56,19 +59,19 @@ export function generateOpts(options: { * - composePath: the *absolute* path to the directory containing the compose file * - composeStr: the contents of the compose file, as a string */ -export function createProject( +export async function createProject( composePath: string, composeStr: string, projectName = '', imageTag = '', -): ComposeProject { - const yml = require('js-yaml') as typeof import('js-yaml'); - const compose = - require('@balena/compose/dist/parse') as typeof import('@balena/compose/dist/parse'); +): Promise { + const yml = await import('js-yaml'); + const compose = await import('@balena/compose/dist/parse'); // both methods below may throw. const rawComposition = yml.load(composeStr); const composition = compose.normalize(rawComposition); + const { makeImageName } = await import('./compose_ts.js'); projectName ||= path.basename(composePath); @@ -80,8 +83,6 @@ export function createProject( descr.image.context != null && descr.image.tag == null ) { - const { makeImageName } = - require('./compose_ts') as typeof import('./compose_ts'); descr.image.tag = makeImageName(projectName, descr.serviceName, imageTag); } return descr; @@ -104,10 +105,9 @@ export const createRelease = async function ( semver?: string, contract?: string, ): Promise { - const _ = require('lodash') as typeof import('lodash'); - const crypto = require('crypto') as typeof import('crypto'); - const releaseMod = - require('@balena/compose/dist/release') as typeof import('@balena/compose/dist/release'); + const _ = await import('lodash'); + const crypto = await import('crypto'); + const releaseMod = await import('@balena/compose/dist/release'); const client = releaseMod.createClient({ apiEndpoint, auth }); @@ -271,7 +271,7 @@ const renderProgressBar = function (percentage: number, stepCount: number) { }; export const pushProgressRenderer = function ( - tty: ReturnType, + tty: ReturnType, prefix: string, ): ProgressCallback & { end: () => void } { const fn: ProgressCallback & { end: () => void } = function (e) { @@ -305,14 +305,14 @@ export class BuildProgressUI implements Renderer { private _spinner; private _runloop: | undefined - | ReturnType; + | ReturnType; // these are to handle window wrapping private _maxLineWidth: undefined | number; private _lineWidths: number[] = []; constructor( - tty: ReturnType, + tty: ReturnType, descriptors: ImageDescriptor[], ) { this._handleEvent = this._handleEvent.bind(this); @@ -353,7 +353,7 @@ export class BuildProgressUI implements Renderer { this._ended = false; this._cancelled = false; this._spinner = ( - require('./compose_ts') as typeof import('./compose_ts') + require('./compose_ts.js') as typeof import('./compose_ts.js') ).createSpinner(); this.streams = streams; @@ -372,12 +372,12 @@ export class BuildProgressUI implements Renderer { this.streams[service].write({ status: 'Preparing...' }); }); this._runloop = ( - require('./compose_ts') as typeof import('./compose_ts') + require('./compose_ts.js') as typeof import('./compose_ts.js') ).createRunLoop(this._display); this._startTime = Date.now(); } - end(summary?: Dictionary) { + async end(summary?: Dictionary) { if (this._ended) { return; } @@ -386,14 +386,14 @@ export class BuildProgressUI implements Renderer { this._runloop = undefined; this._clear(); - this._renderStatus(true); + await this._renderStatus(true); this._renderSummary(summary ?? this._getServiceSummary()); this._tty.showCursor(); } - _display() { + async _display() { this._clear(); - this._renderStatus(); + await this._renderStatus(); this._renderSummary(this._getServiceSummary()); this._tty.cursorUp(this._services.length + 1); // for status line } @@ -431,11 +431,9 @@ export class BuildProgressUI implements Renderer { .value(); } - _renderStatus(end = false) { - const moment = require('moment') as typeof import('moment'); - ( - require('moment-duration-format') as typeof import('moment-duration-format') - )(moment); + async _renderStatus(end = false) { + const moment = await import('moment'); + (await import('moment-duration-format')).default(moment); this._tty.clearLine(); this._tty.write(this._prefix); @@ -529,11 +527,9 @@ export class BuildProgressInline implements Renderer { this._startTime = Date.now(); } - end(summary?: Dictionary) { - const moment = require('moment') as typeof import('moment'); - ( - require('moment-duration-format') as typeof import('moment-duration-format') - )(moment); + async end(summary?: Dictionary) { + const moment = await import('moment'); + (await import('moment-duration-format')).default(moment); if (this._ended) { return; diff --git a/lib/utils/compose_ts.ts b/lib/utils/compose_ts.ts index 202d85f1..7b09a000 100644 --- a/lib/utils/compose_ts.ts +++ b/lib/utils/compose_ts.ts @@ -14,13 +14,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); import { Flags } from '@oclif/core'; import { BalenaSDK } from 'balena-sdk'; import type { TransposeOptions } from '@balena/compose/dist/emulate'; import type * as Dockerode from 'dockerode'; import { promises as fs } from 'fs'; -import jsyaml = require('js-yaml'); -import * as _ from 'lodash'; +import jsyaml from 'js-yaml'; +import _ from 'lodash'; import * as path from 'path'; import type { BuildConfig, @@ -31,18 +33,18 @@ import type * as MultiBuild from '@balena/compose/dist/multibuild'; import * as semver from 'semver'; import type { Duplex, Readable } from 'stream'; import type { Pack } from 'tar-stream'; -import { ExpectedError } from '../errors'; +import { ExpectedError } from '../errors.js'; import { BuiltImage, ComposeOpts, ComposeProject, TaggedImage, TarDirectoryOptions, -} from './compose-types'; -import type { DeviceInfo } from './device/api'; -import { getBalenaSdk, getChalk, stripIndent } from './lazy'; -import Logger = require('./logger'); -import { exists } from './which'; +} from './compose-types.js'; +import type { DeviceInfo } from './device/api.js'; +import { getBalenaSdk, getChalk, stripIndent } from './lazy.js'; +import Logger from './logger.js'; +import { exists } from './which.js'; const allowedContractTypes = ['sw.application', 'sw.block']; @@ -117,7 +119,7 @@ export async function loadProject( imageTag?: string, ): Promise { const compose = await import('@balena/compose/dist/parse'); - const { createProject } = await import('./compose'); + const { createProject } = await import('./compose.js'); let composeName: string; let composeStr: string; @@ -236,7 +238,7 @@ interface BuildTaskPlus extends MultiBuild.BuildTask { export interface Renderer { start: () => void; - end: (buildSummaryByService?: Dictionary) => void; + end: (buildSummaryByService?: Dictionary) => Promise; streams: Dictionary; } @@ -249,7 +251,7 @@ export interface BuildProjectOpts { arch: string; deviceType: string; emulated: boolean; - buildOpts: import('./docker').BuildOpts; + buildOpts: import('./docker.js').BuildOpts; inlineLogs?: boolean; convertEol: boolean; dockerfilePath?: string; @@ -265,7 +267,7 @@ export async function buildProject( const renderer = await startRenderer({ imageDescriptors, ...opts }); let buildSummaryByService: Dictionary | undefined; try { - const { awaitInterruptibleTask } = await import('./helpers'); + const { awaitInterruptibleTask } = await import('./helpers.js'); const [images, summaryMsgByService] = await awaitInterruptibleTask( $buildProject, imageDescriptors, @@ -275,7 +277,7 @@ export async function buildProject( buildSummaryByService = summaryMsgByService; return images; } finally { - renderer.end(buildSummaryByService); + await renderer.end(buildSummaryByService); } } @@ -330,7 +332,7 @@ async function $buildProject( logger.logDebug('Prepared tasks; building...'); - const { BALENA_ENGINE_TMP_PATH } = await import('../config'); + const { BALENA_ENGINE_TMP_PATH } = await import('../config.js'); const builder = await import('@balena/compose/dist/multibuild'); const builtImages = await builder.performBuilds( @@ -358,13 +360,13 @@ async function startRenderer({ }): Promise { let renderer: Renderer; if (inlineLogs) { - renderer = new (await import('./compose')).BuildProgressInline( + renderer = new (await import('./compose.js')).BuildProgressInline( logger.streams['build'], imageDescriptors, ); } else { - const tty = (await import('./tty'))(process.stdout); - renderer = new (await import('./compose')).BuildProgressUI( + const tty = (await import('./tty.js')).default(process.stdout); + renderer = new (await import('./compose.js')).BuildProgressUI( tty, imageDescriptors, ); @@ -388,7 +390,7 @@ async function installQemuIfNeeded({ logger: Logger; projectPath: string; }): Promise { - const qemu = await import('./qemu'); + const qemu = await import('./qemu.js'); const needsQemu = await qemu.installQemuIfNeeded( emulated, logger, @@ -431,7 +433,7 @@ function setTaskAttributes({ projectName, }: { tasks: BuildTaskPlus[]; - buildOpts: import('./docker').BuildOpts; + buildOpts: import('./docker.js').BuildOpts; imageDescriptorsByServiceName: Dictionary; projectName: string; }) { @@ -471,8 +473,8 @@ async function qemuTransposeBuildStream({ dockerfilePath?: string; projectPath: string; }): Promise { - const qemu = await import('./qemu'); - const binPath = qemu.qemuPathInContext( + const qemu = await import('./qemu.js'); + const binPath = await qemu.qemuPathInContext( path.join(projectPath, task.context ?? ''), ); if (task.buildStream == null) { @@ -681,7 +683,7 @@ export async function getServiceDirsFromComposition( sourceDir: string, composition?: Composition, ): Promise> { - const { createProject } = await import('./compose'); + const { createProject } = await import('./compose.js'); const serviceDirs: Dictionary = {}; if (!composition) { const [, composeStr] = await resolveProject( @@ -690,7 +692,7 @@ export async function getServiceDirsFromComposition( true, ); if (composeStr) { - composition = createProject(sourceDir, composeStr).composition; + composition = (await createProject(sourceDir, composeStr)).composition; } } if (composition?.services) { @@ -757,13 +759,13 @@ export async function tarDirectory( preFinalizeCallback, }: TarDirectoryOptions, ): Promise { - const { filterFilesWithDockerignore } = await import('./ignore'); + const { filterFilesWithDockerignore } = await import('./ignore.js'); const { toPosixPath } = (await import('@balena/compose/dist/multibuild')) .PathUtils; let readFile: (file: string) => Promise; if (process.platform === 'win32') { - const { readFileWithEolConversion } = require('./eol-conversion'); + const { readFileWithEolConversion } = await import('./eol-conversion.js'); readFile = (file) => readFileWithEolConversion(file, convertEol); } else { readFile = fs.readFile; @@ -773,7 +775,11 @@ export async function tarDirectory( const serviceDirs = await getServiceDirsFromComposition(dir, composition); const { filteredFileList, dockerignoreFiles } = await filterFilesWithDockerignore(dir, multiDockerignore, serviceDirs); - printDockerignoreWarn(dockerignoreFiles, serviceDirs, multiDockerignore); + await printDockerignoreWarn( + dockerignoreFiles, + serviceDirs, + multiDockerignore, + ); for (const fileStats of filteredFileList) { pack.entry( { @@ -799,18 +805,18 @@ export async function tarDirectory( * @param serviceDirsByService Map of service names to service subdirectories * @param multiDockerignore Whether --multi-dockerignore (-m) was provided */ -function printDockerignoreWarn( - dockerignoreFiles: Array, +async function printDockerignoreWarn( + dockerignoreFiles: Array, serviceDirsByService: Dictionary, multiDockerignore: boolean, ) { - let rootDockerignore: import('./ignore').FileStats | undefined; + let rootDockerignore: import('./ignore.js').FileStats | undefined; const logger = Logger.getLogger(); const relPrefix = '.' + path.sep; const serviceDirs = Object.values(serviceDirsByService || {}); // compute a list of unused .dockerignore files const unusedFiles = dockerignoreFiles.filter( - (dockerignoreStats: import('./ignore').FileStats) => { + (dockerignoreStats: import('./ignore.js').FileStats) => { let dirname = path.dirname(dockerignoreStats.relPath); dirname = dirname.startsWith(relPrefix) ? dirname.slice(2) : dirname; const isProjectRootDir = !dirname || dirname === '.'; @@ -873,7 +879,7 @@ function printDockerignoreWarn( } } if (msg.length) { - const { warnify } = require('./messages') as typeof import('./messages'); + const { warnify } = await import('./messages.js'); logFunc.call(logger, ' \n' + warnify(msg.join('\n'), '')); } } @@ -891,7 +897,7 @@ export async function checkBuildSecretsRequirements( ) { const [metaObj, metaFilename] = await loadBuildMetatada(sourceDir); if (metaObj && !_.isEmpty(metaObj['build-secrets'])) { - const dockerUtils = await import('./docker'); + const dockerUtils = await import('./docker.js'); const isBalenaEngine = await dockerUtils.isBalenaEngine(docker); if (!isBalenaEngine) { throw new ExpectedError(stripIndent` @@ -1222,8 +1228,8 @@ async function getTokenForPreviousRepos( taggedImages: TaggedImage[], ): Promise { logger.logDebug('Authorizing push...'); - const { authorizePush, getPreviousRepos } = await import('./compose'); - const sdk = getBalenaSdk(); + const { authorizePush, getPreviousRepos } = await import('./compose.js'); + const sdk = await getBalenaSdk(); const previousRepos = await getPreviousRepos(sdk, logger, appId); const token = await authorizePush( @@ -1241,14 +1247,14 @@ async function pushAndUpdateServiceImages( token: string, images: TaggedImage[], afterEach: ( - serviceImage: import('@balena/compose/dist/release/models').ImageModel, + serviceImage: import('@balena/compose/dist/release/models.js').ImageModel, props: object, ) => Promise, ) { const { DockerProgress } = await import('docker-progress'); - const { retry } = await import('./helpers'); - const { pushProgressRenderer } = await import('./compose'); - const tty = (await import('./tty'))(process.stdout); + const { retry } = await import('./helpers.js'); + const { pushProgressRenderer } = await import('./compose.js'); + const tty = (await import('./tty.js')).default(process.stdout); const opts = { authconfig: { registrytoken: token } }; const progress = new DockerProgress({ docker }); const renderer = pushProgressRenderer( @@ -1363,10 +1369,10 @@ export async function deployProject( skipLogUpload: boolean, projectPath: string, isDraft: boolean, -): Promise { +): Promise { const releaseMod = await import('@balena/compose/dist/release'); - const { createRelease, tagServiceImages } = await import('./compose'); - const tty = (await import('./tty'))(process.stdout); + const { createRelease, tagServiceImages } = await import('./compose.js'); + const tty = (await import('./tty.js')).default(process.stdout); const prefix = getChalk().cyan('[Info]') + ' '; const spinner = createSpinner(); @@ -1401,7 +1407,7 @@ export async function deployProject( logger.logDebug('Tagging images...'); const taggedImages = await tagServiceImages(docker, images, serviceImages); try { - const { awaitInterruptibleTask } = await import('./helpers'); + const { awaitInterruptibleTask } = await import('./helpers.js'); // awaitInterruptibleTask throws SIGINTError on CTRL-C, // causing the release status to be set to 'failed' await awaitInterruptibleTask(async () => { @@ -1448,7 +1454,7 @@ export function createSpinner() { } async function runSpinner( - tty: ReturnType, + tty: ReturnType, spinner: () => string, msg: string, fn: () => Promise, diff --git a/lib/utils/config.ts b/lib/utils/config.ts index fef0b0b7..cbfe15c6 100644 --- a/lib/utils/config.ts +++ b/lib/utils/config.ts @@ -15,7 +15,7 @@ limitations under the License. */ import type * as BalenaSdk from 'balena-sdk'; import * as semver from 'balena-semver'; -import { getBalenaSdk, stripIndent } from './lazy'; +import { getBalenaSdk, stripIndent } from './lazy.js'; export interface ImgConfig { applicationName: string; @@ -75,10 +75,9 @@ export async function generateApplicationConfig( appUpdatePollInterval: options.appUpdatePollInterval || 10, }; - const config = (await getBalenaSdk().models.os.getConfig( - application.slug, - options, - )) as ImgConfig; + const config = (await ( + await getBalenaSdk() + ).models.os.getConfig(application.slug, options)) as ImgConfig; // merge sshKeys to config, when they have been specified if (options.os && options.os.sshKeys) { @@ -98,14 +97,14 @@ export async function generateApplicationConfig( return config; } -export function generateDeviceConfig( +export async function generateDeviceConfig( device: DeviceWithDeviceType & { belongs_to__application: BalenaSdk.PineDeferred; }, deviceApiKey: string | true | undefined, options: { version: string }, ) { - const sdk = getBalenaSdk(); + const sdk = await getBalenaSdk(); return sdk.models.application .get(device.belongs_to__application.__id) .then(async (application) => { @@ -155,20 +154,20 @@ export function generateDeviceConfig( export async function validateDevOptionAndWarn( dev?: boolean, version?: string, - logger?: import('./logger'), + logger?: import('./logger.js').default, ) { if (!dev) { return; } if (version && /\bprod\b/.test(version)) { - const { ExpectedError } = await import('../errors'); + const { ExpectedError } = await import('../errors.js'); throw new ExpectedError( `Error: The '--dev' option conflicts with production balenaOS version '${version}'`, ); } if (!logger) { - const Logger = await import('./logger'); - logger = Logger.getLogger(); + const Logger = await import('./logger.js'); + logger = Logger.default.getLogger(); } logger.logInfo(stripIndent` The '--dev' option is being used to configure a balenaOS image in development mode. @@ -187,19 +186,19 @@ export async function validateSecureBootOptionAndWarn( secureBoot?: boolean, slug?: string, version?: string, - logger?: import('./logger'), + logger?: import('./logger.js').default, ) { if (!secureBoot) { return; } - const { ExpectedError } = await import('../errors'); + const { ExpectedError } = await import('../errors.js'); if (!version) { throw new ExpectedError(`Error: No version provided`); } if (!slug) { throw new ExpectedError(`Error: No device type provided`); } - const sdk = getBalenaSdk(); + const sdk = await getBalenaSdk(); const [osRelease] = await sdk.models.os.getAllOsVersions(slug, { $select: 'contract', $filter: { raw_version: `${version.replace(/^v/, '')}` }, @@ -215,8 +214,8 @@ export async function validateSecureBootOptionAndWarn( }) ) { if (!logger) { - const Logger = await import('./logger'); - logger = Logger.getLogger(); + const Logger = await import('./logger.js'); + logger = Logger.default.getLogger(); } logger.logInfo(stripIndent` The '--secureBoot' option is being used to configure a balenaOS installer image diff --git a/lib/utils/deploy-legacy.ts b/lib/utils/deploy-legacy.ts index 074e9cf2..60e52735 100644 --- a/lib/utils/deploy-legacy.ts +++ b/lib/utils/deploy-legacy.ts @@ -15,29 +15,29 @@ * limitations under the License. */ -import { getVisuals } from './lazy'; +import { getVisuals } from './lazy.js'; import { promisify } from 'util'; import type * as Dockerode from 'dockerode'; -import type Logger = require('./logger'); +import type Logger from './logger.js'; import type { Request } from 'request'; -const getBuilderPushEndpoint = function ( +const getBuilderPushEndpoint = async function ( baseUrl: string, owner: string, app: string, ) { - const querystring = require('querystring') as typeof import('querystring'); + const querystring = await import('querystring'); const args = querystring.stringify({ owner, app }); return `https://builder.${baseUrl}/v1/push?${args}`; }; -const getBuilderLogPushEndpoint = function ( +const getBuilderLogPushEndpoint = async function ( baseUrl: string, buildId: number, owner: string, app: string, ) { - const querystring = require('querystring') as typeof import('querystring'); + const querystring = await import('querystring'); const args = querystring.stringify({ owner, app, buildId }); return `https://builder.${baseUrl}/v1/pushLogs?${args}`; }; @@ -47,12 +47,12 @@ const getBuilderLogPushEndpoint = function ( * @param {string} imageId * @param {string} bufferFile */ -const bufferImage = function ( +const bufferImage = async function ( docker: Dockerode, imageId: string, bufferFile: string, ): Promise { - const streamUtils = require('./streams') as typeof import('./streams'); + const streamUtils = await import('./streams.js'); const image = docker.getImage(imageId); const sizePromise = image.inspect().then((img) => img.Size); @@ -109,7 +109,7 @@ const uploadToPromise = (uploadRequest: Request, logger: Logger) => /** * @returns {Promise<{ buildId: number }>} */ -const uploadImage = function ( +const uploadImage = async function ( imageStream: NodeJS.ReadableStream & { length: number }, token: string, username: string, @@ -117,10 +117,9 @@ const uploadImage = function ( appName: string, logger: Logger, ): Promise<{ buildId: number }> { - const request = require('request') as typeof import('request'); - const progressStream = - require('progress-stream') as typeof import('progress-stream'); - const zlib = require('zlib') as typeof import('zlib'); + const request = await import('request'); + const { default: progressStream } = await import('progress-stream'); + const zlib = await import('zlib'); // Need to strip off the newline const progressMessage = logger @@ -142,7 +141,7 @@ const uploadImage = function ( ); const uploadRequest = request.post({ - url: getBuilderPushEndpoint(url, username, appName), + url: await getBuilderPushEndpoint(url, username, appName), headers: { 'Content-Encoding': 'gzip', }, @@ -159,7 +158,7 @@ const uploadImage = function ( return uploadToPromise(uploadRequest, logger); }; -const uploadLogs = function ( +const uploadLogs = async function ( logs: string, token: string, url: string, @@ -167,10 +166,10 @@ const uploadLogs = function ( username: string, appName: string, ) { - const request = require('request') as typeof import('request'); + const request = await import('request'); return request.post({ json: true, - url: getBuilderLogPushEndpoint(url, buildId, username, appName), + url: await getBuilderLogPushEndpoint(url, buildId, username, appName), auth: { bearer: token, }, @@ -196,7 +195,7 @@ export const deployLegacy = async function ( shouldUploadLogs: boolean; }, ): Promise { - const tmp = require('tmp') as typeof import('tmp'); + const tmp = await import('tmp'); const tmpNameAsync = promisify(tmp.tmpName); // Ensure the tmp files gets deleted @@ -208,6 +207,7 @@ export const deployLegacy = async function ( const bufferFile = await tmpNameAsync(); logger.logInfo('Initializing deploy...'); + const fs = await import('fs'); const { buildId } = await bufferImage(docker, imageName, bufferFile) .then((stream) => uploadImage(stream, token, username, url, appName, logger), @@ -217,9 +217,7 @@ export const deployLegacy = async function ( // has occured before any data was written) this call will throw an // ugly error, just suppress it - (require('fs') as typeof import('fs')).promises - .unlink(bufferFile) - .catch(() => undefined), + fs.promises.unlink(bufferFile).catch(() => undefined), ); if (shouldUploadLogs) { diff --git a/lib/utils/device/api.ts b/lib/utils/device/api.ts index bff6ffbf..0e10e1c0 100644 --- a/lib/utils/device/api.ts +++ b/lib/utils/device/api.ts @@ -14,15 +14,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import * as _ from 'lodash'; +import _ from 'lodash'; import type { NodeJSSocketWithFileDescriptor } from 'net-keepalive'; import * as os from 'os'; -import * as request from 'request'; +import request from 'request'; import type * as Stream from 'stream'; -import { retry } from '../helpers'; -import Logger = require('../logger'); -import * as ApiErrors from './errors'; +import { retry } from '../helpers.js'; +import type Logger from '../logger.js'; +import * as ApiErrors from './errors.js'; export interface DeviceResponse { [key: string]: any; diff --git a/lib/utils/device/deploy.ts b/lib/utils/device/deploy.ts index 6717735b..ad8a0e3b 100644 --- a/lib/utils/device/deploy.ts +++ b/lib/utils/device/deploy.ts @@ -14,10 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); import * as semver from 'balena-semver'; -import * as Docker from 'dockerode'; -import * as _ from 'lodash'; +import Docker from 'dockerode'; +import _ from 'lodash'; import { Composition } from '@balena/compose/dist/parse'; import { BuildTask, @@ -27,21 +29,21 @@ import { } from '@balena/compose/dist/multibuild'; import type { Readable } from 'stream'; -import { BALENA_ENGINE_TMP_PATH } from '../../config'; -import { ExpectedError } from '../../errors'; +import { BALENA_ENGINE_TMP_PATH } from '../../config.js'; +import { ExpectedError } from '../../errors.js'; import { checkBuildSecretsRequirements, loadProject, makeBuildTasks, tarDirectory, makeImageName, -} from '../compose_ts'; -import Logger = require('../logger'); -import { DeviceAPI, DeviceInfo } from './api'; -import * as LocalPushErrors from './errors'; -import LivepushManager from './live'; -import { displayBuildLog } from './logs'; -import { stripIndent } from '../lazy'; +} from '../compose_ts.js'; +import Logger from '../logger.js'; +import { DeviceAPI, DeviceInfo } from './api.js'; +import * as LocalPushErrors from './errors.js'; +import LivepushManager from './live.js'; +import { displayBuildLog } from './logs.js'; +import { stripIndent } from '../lazy.js'; const LOCAL_APPNAME = 'localapp'; const LOCAL_RELEASEHASH = '10ca12e1ea5e'; @@ -214,7 +216,7 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise { imageIds = {}; } - const { awaitInterruptibleTask } = await import('../helpers'); + const { awaitInterruptibleTask } = await import('../helpers.js'); const buildTasks = await awaitInterruptibleTask( performBuilds, project.composition, @@ -294,7 +296,7 @@ async function streamDeviceLogs( return; } globalLogger.logInfo('Streaming device logs...'); - const { connectAndDisplayDeviceLogs } = await import('./logs'); + const { connectAndDisplayDeviceLogs } = await import('./logs.js'); return connectAndDisplayDeviceLogs({ deviceApi, logger: globalLogger, diff --git a/lib/utils/device/errors.ts b/lib/utils/device/errors.ts index 5455a803..5f3668a4 100644 --- a/lib/utils/device/errors.ts +++ b/lib/utils/device/errors.ts @@ -14,9 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import * as _ from 'lodash'; +import _ from 'lodash'; -import { ExpectedError } from '../../errors'; +import { ExpectedError } from '../../errors.js'; export interface BuildFailure { error: Error; diff --git a/lib/utils/device/live.ts b/lib/utils/device/live.ts index 6bc83cc9..029d274c 100644 --- a/lib/utils/device/live.ts +++ b/lib/utils/device/live.ts @@ -19,25 +19,25 @@ import * as chokidar from 'chokidar'; import type * as Dockerode from 'dockerode'; import * as fs from 'fs'; import Livepush, { ContainerNotRunningError } from 'livepush'; -import * as _ from 'lodash'; +import _ from 'lodash'; import * as path from 'path'; import type { Composition } from '@balena/compose/dist/parse'; import type { BuildTask } from '@balena/compose/dist/multibuild'; -import { instanceOf } from '../../errors'; -import Logger = require('../logger'); +import { instanceOf } from '../../errors.js'; +import type Logger from '../logger.js'; import { Dockerfile } from 'livepush'; -import type DeviceAPI from './api'; -import type { DeviceInfo, Status } from './api'; +import type DeviceAPI from './api.js'; +import type { DeviceInfo, Status } from './api.js'; import { DeviceDeployOptions, generateTargetState, rebuildSingleTask, -} from './deploy'; -import { BuildError } from './errors'; -import { getServiceColourFn } from './logs'; -import { delay } from '../helpers'; +} from './deploy.js'; +import { BuildError } from './errors.js'; +import { getServiceColourFn } from './logs.js'; +import { delay } from '../helpers.js'; // How often do we want to check the device state // engine has settled (delay in ms) @@ -47,7 +47,7 @@ const LIVEPUSH_DEBOUNCE_TIMEOUT = 2000; interface MonitoredContainer { context: string; - livepush: Livepush; + livepush: Livepush.default; monitor: chokidar.FSWatcher; containerId: string; } @@ -111,8 +111,8 @@ export class LivepushManager { this.logger.logLivepush('Device state settled'); // Prepare dockerignore data for file watcher - const { getDockerignoreByService } = await import('../ignore'); - const { getServiceDirsFromComposition } = await import('../compose_ts'); + const { getDockerignoreByService } = await import('../ignore.js'); + const { getServiceDirsFromComposition } = await import('../compose_ts.js'); const rootContext = path.resolve(this.buildContext); const serviceDirsByService = await getServiceDirsFromComposition( this.deployOpts.source, @@ -170,7 +170,7 @@ export class LivepushManager { // and also converts forward slashes to backslashes on Windows. const context = path.resolve(rootContext, service.build.context); - const livepush = await Livepush.init({ + const livepush = await Livepush.default.init({ dockerfile, context, containerId: container.containerId, @@ -441,7 +441,7 @@ export class LivepushManager { const dockerfile = new Dockerfile(buildTask.dockerfile!); - instance.livepush = await Livepush.init({ + instance.livepush = await Livepush.default.init({ dockerfile, context: buildTask.context!, containerId: container.containerId, @@ -476,7 +476,7 @@ export class LivepushManager { private assignLivepushOutputHandlers( serviceName: string, - livepush: Livepush, + livepush: Livepush.default, ) { const msgString = (msg: string) => `[${getServiceColourFn(serviceName)(serviceName)}] ${msg}`; diff --git a/lib/utils/device/logs.ts b/lib/utils/device/logs.ts index 267ba1f4..d08693da 100644 --- a/lib/utils/device/logs.ts +++ b/lib/utils/device/logs.ts @@ -14,13 +14,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import ColorHash = require('color-hash'); -import * as _ from 'lodash'; +import ColorHash from 'color-hash'; +import _ from 'lodash'; import type { Readable } from 'stream'; -import { getChalk } from '../lazy'; -import Logger = require('../logger'); -import { ExpectedError, SIGINTError } from '../../errors'; +import { getChalk } from '../lazy.js'; +import type Logger from '../logger.js'; +import { ExpectedError, SIGINTError } from '../../errors.js'; class DeviceConnectionLostError extends ExpectedError { public static defaultMsg = 'Connection to device lost'; @@ -62,7 +62,7 @@ async function displayDeviceLogs( system: boolean, filterServices?: string[], ): Promise { - const { addSIGINTHandler } = await import('../helpers'); + const { addSIGINTHandler } = await import('../helpers.js'); const { parse: ndjsonParse } = await import('ndjson'); let gotSignal = false; const handleSignal = () => { @@ -113,7 +113,7 @@ export async function connectAndDisplayDeviceLogs({ filterServices, maxAttempts = 3, }: { - deviceApi: import('./api').DeviceAPI; + deviceApi: import('./api.js').DeviceAPI; logger: Logger; system: boolean; filterServices?: string[]; @@ -125,7 +125,7 @@ export async function connectAndDisplayDeviceLogs({ return displayDeviceLogs(logStream, logger, system, filterServices); } - const { retry } = await import('../../utils/helpers'); + const { retry } = await import('../../utils/helpers.js'); try { await retry({ func: connectAndDisplay, diff --git a/lib/utils/device/ssh.ts b/lib/utils/device/ssh.ts index 0a7ee732..31e7abfb 100644 --- a/lib/utils/device/ssh.ts +++ b/lib/utils/device/ssh.ts @@ -14,22 +14,22 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { ExpectedError } from '../../errors'; -import { stripIndent } from '../lazy'; +import { ExpectedError } from '../../errors.js'; +import { stripIndent } from '../lazy.js'; import { findBestUsernameForDevice, getRemoteCommandOutput, runRemoteCommand, SshRemoteCommandOpts, -} from '../ssh'; +} from '../ssh.js'; export interface DeviceSSHOpts extends SshRemoteCommandOpts { forceTTY?: boolean; service?: string; } -const deviceContainerEngineBinary = `$(if [ -f /usr/bin/balena ]; then echo "balena"; else echo "docker"; fi)`; +const deviceContainerEngineBinary = `$(if [ -f /usr/bin/balena.js ]; then echo "balena"; else echo "docker"; fi)`; /** * List the running containers on the device over ssh, and return the full diff --git a/lib/utils/docker.ts b/lib/utils/docker.ts index f93ecf03..fb21ca11 100644 --- a/lib/utils/docker.ts +++ b/lib/utils/docker.ts @@ -14,12 +14,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); import type * as dockerode from 'dockerode'; import { Flags } from '@oclif/core'; -import { ExpectedError } from '../errors'; -import { parseAsInteger } from './validation'; +import { ExpectedError } from '../errors.js'; +import { parseAsInteger } from './validation.js'; interface BalenaEngineVersion extends dockerode.DockerVersion { Engine?: string; @@ -186,7 +187,7 @@ export async function getDocker( export async function createClient( opts: dockerode.DockerOptions, ): Promise { - const Docker = await import('dockerode'); + const { default: Docker } = await import('dockerode'); return new Docker(opts); } diff --git a/lib/utils/env-common.ts b/lib/utils/env-common.ts index 194c2cbb..aedefac8 100644 --- a/lib/utils/env-common.ts +++ b/lib/utils/env-common.ts @@ -16,9 +16,9 @@ */ import { Flags } from '@oclif/core'; -import { stripIndent } from './lazy'; +import { stripIndent } from './lazy.js'; -import { ExpectedError } from '../errors'; +import { ExpectedError } from '../errors.js'; export const booleanConfig = Flags.boolean({ char: 'c', diff --git a/lib/utils/eol-conversion.ts b/lib/utils/eol-conversion.ts index 2d015c6b..ec76fcdd 100644 --- a/lib/utils/eol-conversion.ts +++ b/lib/utils/eol-conversion.ts @@ -16,7 +16,7 @@ */ import { promises as fs } from 'fs'; -import Logger = require('./logger'); +import Logger from './logger.js'; const globalLogger = Logger.getLogger(); diff --git a/lib/utils/helpers.ts b/lib/utils/helpers.ts index 8d040383..d30c66a7 100644 --- a/lib/utils/helpers.ts +++ b/lib/utils/helpers.ts @@ -13,14 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ - +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); import type { InitializeEmitter, OperationState } from 'balena-device-init'; import type * as BalenaSdk from 'balena-sdk'; -import * as _ from 'lodash'; +import _ from 'lodash'; import { promisify } from 'util'; -import { getBalenaSdk, getChalk, getVisuals } from './lazy'; +import { getBalenaSdk, getChalk, getVisuals } from './lazy.js'; export function getGroupDefaults(group: { options: Array<{ name: string; default: string | number }>; @@ -77,7 +78,7 @@ export async function sudo( isCLIcmd, }: { stderr?: NodeJS.WritableStream; msg?: string; isCLIcmd?: boolean } = {}, ) { - const { executeWithPrivileges } = await import('./sudo'); + const { executeWithPrivileges } = await import('./sudo.js'); if (process.platform !== 'win32') { console.log( @@ -90,8 +91,7 @@ export async function sudo( } export async function runCommand(commandArgs: string[]): Promise { - const { isSubcommand } = - require('../preparser') as typeof import('../preparser'); + const { isSubcommand } = await import('../preparser.js'); if (await isSubcommand(commandArgs)) { commandArgs = [ commandArgs[0] + ':' + commandArgs[1], @@ -99,7 +99,7 @@ export async function runCommand(commandArgs: string[]): Promise { ]; } - const { run } = require('@oclif/core') as typeof import('@oclif/core'); + const { run } = await import('@oclif/core'); return run(commandArgs) as Promise; } @@ -108,14 +108,14 @@ export async function getManifest( deviceType: string, ): Promise { const init = await import('balena-device-init'); - const sdk = getBalenaSdk(); + const sdk = await getBalenaSdk(); const manifest = await init.getImageManifest(image); if ( manifest != null && manifest.slug !== deviceType && manifest.slug !== (await sdk.models.deviceType.get(deviceType)).slug ) { - const { ExpectedError } = await import('../errors'); + const { ExpectedError } = await import('../errors.js'); throw new ExpectedError( `The device type of the provided OS image ${manifest.slug}, does not match the expected device type ${deviceType}`, ); @@ -133,7 +133,7 @@ export const areDeviceTypesCompatible = async ( if (appDeviceTypeSlug === osDeviceTypeSlug) { return true; } - const sdk = getBalenaSdk(); + const sdk = await getBalenaSdk(); const pineOptions = { $select: 'is_of__cpu_architecture', $expand: { @@ -182,8 +182,8 @@ export async function osProgressHandler(step: InitializeEmitter) { } export async function getAppWithArch(applicationName: string) { - const { getApplication } = await import('./sdk'); - const balena = getBalenaSdk(); + const { getApplication } = await import('./sdk.js'); + const balena = await getBalenaSdk(); const app = await getApplication(balena, applicationName, { $expand: { application_type: { @@ -239,7 +239,7 @@ export async function retry({ backoffScaler?: number; maxSingleDelayMs?: number; }): Promise { - const { SIGINTError } = await import('../errors'); + const { SIGINTError } = await import('../errors.js'); let delayMs = initialDelayMs; for (let count = 0; count < maxAttempts - 1; count++) { const lastAttemptMs = Date.now(); @@ -486,7 +486,7 @@ export async function awaitInterruptibleTask< const sigintPromise = new Promise((_resolve, reject) => { sigintHandler = () => { const { SIGINTError } = - require('../errors') as typeof import('../errors'); + require('../errors.js') as typeof import('../errors.js'); reject(new SIGINTError('Task aborted on SIGINT signal')); }; addSIGINTHandler(sigintHandler); diff --git a/lib/utils/ignore.ts b/lib/utils/ignore.ts index 42275d63..f7e2b69c 100644 --- a/lib/utils/ignore.ts +++ b/lib/utils/ignore.ts @@ -14,13 +14,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import * as _ from 'lodash'; +import _ from 'lodash'; import { promises as fs, Stats } from 'fs'; import * as path from 'path'; import type { Ignore } from '@balena/dockerignore'; -import { ExpectedError } from '../errors'; +import { ExpectedError } from '../errors.js'; export interface FileStats { filePath: string; @@ -102,7 +102,8 @@ export async function getDockerIgnoreInstance( directory: string, ): Promise { const dockerIgnoreStr = await readDockerIgnoreFile(directory); - const $dockerIgnore = (await import('@balena/dockerignore')).default; + const { default: $dockerIgnore } = (await import('@balena/dockerignore')) + .default; const ig = $dockerIgnore({ ignorecase: false }); ig.add(['**/.git']); diff --git a/lib/utils/lazy.ts b/lib/utils/lazy.ts index c6d0a984..283dc0f2 100644 --- a/lib/utils/lazy.ts +++ b/lib/utils/lazy.ts @@ -15,8 +15,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ - -import type * as BalenaSdk from 'balena-sdk'; +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); import type { Chalk } from 'chalk'; import type * as visuals from 'resin-cli-visuals'; import type * as CliForm from 'resin-cli-form'; @@ -43,8 +43,8 @@ export const onceAsync = (fn: () => Promise) => { }; }; -export const getBalenaSdk = once(() => - (require('balena-sdk') as typeof BalenaSdk).fromSharedOptions(), +export const getBalenaSdk = once(async () => + (await import('balena-sdk')).fromSharedOptions(), ); export const getVisuals = once( diff --git a/lib/utils/logger.ts b/lib/utils/logger.ts index 7aa69314..24c97947 100644 --- a/lib/utils/logger.ts +++ b/lib/utils/logger.ts @@ -14,10 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import _ = require('lodash'); +import _ from 'lodash'; import { EOL as eol } from 'os'; import { StreamLogger } from 'resin-stream-logger'; -import { getChalk } from './lazy'; +import { getChalk } from './lazy.js'; enum Level { BUILD = 'build', @@ -175,4 +175,4 @@ class Logger { } } -export = Logger; +export default Logger; diff --git a/lib/utils/normalization.ts b/lib/utils/normalization.ts index cf179a29..7073d557 100644 --- a/lib/utils/normalization.ts +++ b/lib/utils/normalization.ts @@ -16,7 +16,7 @@ */ import type { BalenaSDK } from 'balena-sdk'; -import { ExpectedError } from '../errors'; +import { ExpectedError } from '../errors.js'; /** * Takes a string which may represent one of: diff --git a/lib/utils/oclif-utils.ts b/lib/utils/oclif-utils.ts index ceac7501..8579a760 100644 --- a/lib/utils/oclif-utils.ts +++ b/lib/utils/oclif-utils.ts @@ -20,6 +20,8 @@ * @oclif/plugin-help/command/CommandHelp, which is used to generate oclif's * command help output. */ +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); export class CommandHelp { constructor(public command: { args?: { [name: string]: any } }) {} diff --git a/lib/utils/patterns.ts b/lib/utils/patterns.ts index ebae0aa6..a9ec0bbf 100644 --- a/lib/utils/patterns.ts +++ b/lib/utils/patterns.ts @@ -24,13 +24,13 @@ import type { PineTypedResult, } from 'balena-sdk'; -import { instanceOf, NotLoggedInError, ExpectedError } from '../errors'; -import { getBalenaSdk, getVisuals, stripIndent, getCliForm } from './lazy'; -import validation = require('./validation'); -import { delay } from './helpers'; +import { instanceOf, NotLoggedInError, ExpectedError } from '../errors.js'; +import { getBalenaSdk, getVisuals, stripIndent, getCliForm } from './lazy.js'; +import * as validation from './validation.js'; +import { delay } from './helpers.js'; -export function authenticate(options: object): Promise { - const balena = getBalenaSdk(); +export async function authenticate(options: object): Promise { + const balena = await getBalenaSdk(); return getCliForm() .run( [ @@ -81,7 +81,7 @@ export function authenticate(options: object): Promise { * Note: `NotLoggedInError` is an `ExpectedError`. */ export async function checkLoggedIn(): Promise { - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); if (!(await balena.auth.isLoggedIn())) { throw new NotLoggedInError(stripIndent` Login required: use the “balena login” command to log in. @@ -116,7 +116,7 @@ export function askLoginType() { } export async function selectDeviceType() { - const sdk = getBalenaSdk(); + const sdk = await getBalenaSdk(); let deviceTypes = await sdk.models.deviceType.getAllSupported(); if (deviceTypes.length === 0) { // Without this open-balena users would get an empty list @@ -184,7 +184,7 @@ export async function selectApplication( | ((app: SelectApplicationResult) => boolean), errorOnEmptySelection = false, ) { - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); let apps = (await balena.models.application.getAllDirectlyAccessible({ ...selectApplicationPineOptions, ...(filter != null && typeof filter === 'object' && { $filter: filter }), @@ -215,7 +215,9 @@ export async function selectOrganization( organizations?: Array>, ) { // Use either provided orgs (if e.g. already loaded) or load from cloud - organizations ??= await getBalenaSdk().models.organization.getAll({ + organizations ??= await ( + await getBalenaSdk() + ).models.organization.getAll({ $select: ['name', 'handle'], }); return getCliForm().ask({ @@ -229,8 +231,8 @@ export async function selectOrganization( } export async function getAndSelectOrganization() { - const { getOwnOrganizations } = await import('./sdk'); - const organizations = await getOwnOrganizations(getBalenaSdk(), { + const { getOwnOrganizations } = await import('./sdk.js'); + const organizations = await getOwnOrganizations(await getBalenaSdk(), { $select: ['name', 'handle'], }); @@ -250,7 +252,7 @@ export async function awaitDeviceOsUpdate( uuid: string, targetOsVersion: string, ) { - const balena = getBalenaSdk(); + const balena = await getBalenaSdk(); const deviceName = await balena.models.device.getName(uuid); const visuals = getVisuals(); @@ -304,7 +306,7 @@ export async function getOnlineTargetDeviceUuid( sdk: BalenaSDK, fleetOrDevice: string, ) { - const logger = (await import('../utils/logger')).getLogger(); + const logger = (await import('../utils/logger.js')).default.getLogger(); // If looks like UUID, probably device if (validation.validateUuid(fleetOrDevice)) { @@ -337,7 +339,7 @@ export async function getOnlineTargetDeviceUuid( const application = await (async () => { try { logger.logDebug(`Fetching fleet ${fleetOrDevice}`); - const { getApplication } = await import('./sdk'); + const { getApplication } = await import('./sdk.js'); return await getApplication(sdk, fleetOrDevice, { $select: ['id', 'slug'], $expand: { @@ -383,6 +385,7 @@ export function selectFromList( message: string, choices: Array, ): Promise { + // @ts-expect-error promise return getCliForm().ask({ message, type: 'list', diff --git a/lib/utils/promote.ts b/lib/utils/promote.ts index 805dc259..cbd4150a 100644 --- a/lib/utils/promote.ts +++ b/lib/utils/promote.ts @@ -16,11 +16,11 @@ */ import type * as BalenaSdk from 'balena-sdk'; -import { ExpectedError, printErrorMessage } from '../errors'; -import { getVisuals, stripIndent, getCliForm } from './lazy'; -import Logger = require('./logger'); -import { confirm } from './patterns'; -import { getLocalDeviceCmdStdout, getDeviceOsRelease } from './ssh'; +import { ExpectedError, printErrorMessage } from '../errors.js'; +import { getVisuals, stripIndent, getCliForm } from './lazy.js'; +import Logger from './logger.js'; +import { confirm } from './patterns.js'; +import { getLocalDeviceCmdStdout, getDeviceOsRelease } from './ssh.js'; const MIN_BALENAOS_VERSION = 'v2.14.0'; @@ -167,7 +167,7 @@ const dockerPort = 2375; const dockerTimeout = 2000; async function selectLocalBalenaOsDevice(timeout = 4000): Promise { - const { discoverLocalBalenaOsDevices } = await import('../utils/discover'); + const { discoverLocalBalenaOsDevices } = await import('../utils/discover.js'); const { SpinnerPromise } = getVisuals(); const devices = await new SpinnerPromise({ promise: discoverLocalBalenaOsDevices(timeout), @@ -185,7 +185,7 @@ async function selectLocalBalenaOsDevice(timeout = 4000): Promise { } try { - const docker = new Docker({ + const docker = new Docker.default({ host: address, port: dockerPort, timeout: dockerTimeout, @@ -231,7 +231,7 @@ async function selectAppFromList( applications: ApplicationWithDeviceTypeSlug[], ): Promise { const _ = await import('lodash'); - const { selectFromList } = await import('../utils/patterns'); + const { selectFromList } = await import('../utils/patterns.js'); // Present a list to the user which shows the fully qualified fleet // name (user/fleetname) and allows them to select. @@ -384,7 +384,7 @@ async function createApplication( deviceType: string, name?: string, ): Promise { - const validation = await import('./validation'); + const validation = await import('./validation.js'); let username: string; try { @@ -447,7 +447,7 @@ async function generateApplicationConfig( appUpdatePollInterval?: number; }, ) { - const { generateApplicationConfig: configGen } = await import('./config'); + const { generateApplicationConfig: configGen } = await import('./config.js'); const manifest = await sdk.models.config.getDeviceTypeManifestBySlug( app.is_for__device_type[0].slug, diff --git a/lib/utils/proxy.ts b/lib/utils/proxy.ts index ebc7aa72..5f49ec49 100644 --- a/lib/utils/proxy.ts +++ b/lib/utils/proxy.ts @@ -18,7 +18,7 @@ import type { Options as GlobalTunnelNgConfig } from 'global-tunnel-ng'; export type { GlobalTunnelNgConfig }; -import { CliSettings } from './bootstrap'; +import { CliSettings } from './bootstrap.js'; type ProxyConfig = string | GlobalTunnelNgConfig; diff --git a/lib/utils/qemu.ts b/lib/utils/qemu.ts index a9e379dc..5e1a2824 100644 --- a/lib/utils/qemu.ts +++ b/lib/utils/qemu.ts @@ -17,23 +17,23 @@ import type * as Dockerode from 'dockerode'; -import { ExpectedError } from '../errors'; -import { getBalenaSdk, stripIndent } from './lazy'; -import Logger = require('./logger'); +import { ExpectedError } from '../errors.js'; +import { getBalenaSdk, stripIndent } from './lazy.js'; +import type Logger from './logger.js'; export const QEMU_VERSION = 'v7.0.0+balena1'; export const QEMU_BIN_NAME = 'qemu-execve'; -export function qemuPathInContext(context: string) { - const path = require('path') as typeof import('path'); +export async function qemuPathInContext(context: string) { + const path = await import('path'); const binDir = path.join(context, '.balena'); const binPath = path.join(binDir, QEMU_BIN_NAME); return path.relative(context, binPath); } -export function copyQemu(context: string, arch: string) { - const path = require('path') as typeof import('path'); - const fs = require('fs') as typeof import('fs'); +export async function copyQemu(context: string, arch: string) { + const path = await import('path'); + const fs = await import('fs'); // Create a hidden directory in the build context, containing qemu const binDir = path.join(context, '.balena'); const binPath = path.join(binDir, QEMU_BIN_NAME); @@ -65,11 +65,11 @@ export function copyQemu(context: string, arch: string) { .then(() => path.relative(context, binPath)); } -export const getQemuPath = function (balenaArch: string) { +export const getQemuPath = async function (balenaArch: string) { const qemuArch = balenaArchToQemuArch(balenaArch); - const balena = getBalenaSdk(); - const path = require('path') as typeof import('path'); - const { promises: fs } = require('fs') as typeof import('fs'); + const balena = await getBalenaSdk(); + const path = await import('path'); + const { promises: fs } = await import('fs'); return balena.settings.get('binDirectory').then((binDir) => fs @@ -117,7 +117,8 @@ async function installQemu(arch: string, qemuPath: string) { reject(err); } }); - request(qemuUrl) + request + .default(qemuUrl) .on('error', reject) .pipe(zlib.createGunzip()) .on('error', reject) diff --git a/lib/utils/remote-build.ts b/lib/utils/remote-build.ts index 3ed2271f..08fe743a 100644 --- a/lib/utils/remote-build.ts +++ b/lib/utils/remote-build.ts @@ -13,19 +13,22 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); + import type { BalenaSDK } from 'balena-sdk'; import * as JSONStream from 'JSONStream'; import * as readline from 'readline'; import * as request from 'request'; import { RegistrySecrets } from '@balena/compose/dist/multibuild'; import type * as Stream from 'stream'; -import streamToPromise = require('stream-to-promise'); +import streamToPromise from 'stream-to-promise'; import type { Pack } from 'tar-stream'; -import { ExpectedError, SIGINTError } from '../errors'; -import { tarDirectory } from './compose_ts'; -import { getVisuals, stripIndent } from './lazy'; -import Logger = require('./logger'); +import { ExpectedError, SIGINTError } from '../errors.js'; +import { tarDirectory } from './compose_ts.js'; +import { getVisuals, stripIndent } from './lazy.js'; +import Logger from './logger.js'; const globalLogger = Logger.getLogger(); @@ -126,7 +129,7 @@ export async function startRemoteBuild( } }; - const { addSIGINTHandler } = await import('./helpers'); + const { addSIGINTHandler } = await import('./helpers.js'); addSIGINTHandler(sigintHandler); try { diff --git a/lib/utils/sdk.ts b/lib/utils/sdk.ts index 0df612df..4e95833f 100644 --- a/lib/utils/sdk.ts +++ b/lib/utils/sdk.ts @@ -42,7 +42,7 @@ export async function getApplication( nameOrSlugOrId: string | number, options?: PineOptions, ): Promise { - const { looksLikeFleetSlug } = await import('./validation'); + const { looksLikeFleetSlug } = await import('./validation.js'); const whoamiResult = await sdk.auth.whoami(); const isDeviceActor = whoamiResult?.actorType === 'device'; @@ -97,7 +97,7 @@ export async function getFleetSlug( sdk: BalenaSDK, nameOrSlug: string, ): Promise { - const { looksLikeFleetSlug } = await import('./validation'); + const { looksLikeFleetSlug } = await import('./validation.js'); if (!looksLikeFleetSlug(nameOrSlug)) { // Not a slug: must be an app name. // TODO: revisit this logic when we add support for fleet UUIDs. diff --git a/lib/utils/ssh.ts b/lib/utils/ssh.ts index 830f63d4..13939b95 100644 --- a/lib/utils/ssh.ts +++ b/lib/utils/ssh.ts @@ -15,9 +15,9 @@ * limitations under the License. */ import { spawn, StdioOptions } from 'child_process'; -import * as _ from 'lodash'; +import _ from 'lodash'; -import { ExpectedError } from '../errors'; +import { ExpectedError } from '../errors.js'; export class SshPermissionDeniedError extends ExpectedError {} @@ -126,7 +126,7 @@ export async function runRemoteCommand({ } else { ignoreStdin = false; } - const { which } = await import('./which'); + const { which } = await import('./which.js'); const program = await which('ssh'); const args = sshArgsForRemoteCommand({ cmd, @@ -139,7 +139,7 @@ export async function runRemoteCommand({ }); if (process.env.DEBUG) { - const logger = (await import('./logger')).getLogger(); + const logger = (await import('./logger.js')).default.getLogger(); logger.logDebug(`Executing [${program},${args}]`); } @@ -295,11 +295,11 @@ export const findBestUsernameForDevice = _.memoize( if (await isRootUserGood(hostname, port)) { username = 'root'; } else { - const { getCachedUsername } = await import('./bootstrap'); + const { getCachedUsername } = await import('./bootstrap.js'); username = (await getCachedUsername())?.username; } if (!username) { - const { stripIndent } = await import('./lazy'); + const { stripIndent } = await import('./lazy.js'); throw new ExpectedError(stripIndent` SSH authentication failed for 'root@${hostname}'. Please login with 'balena login' for alternative authentication.`); diff --git a/lib/utils/streams.ts b/lib/utils/streams.ts index 23f51e88..e8459e66 100644 --- a/lib/utils/streams.ts +++ b/lib/utils/streams.ts @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import fs = require('fs'); +import fs from 'fs'; export function buffer( stream: NodeJS.ReadableStream, diff --git a/lib/utils/sudo.ts b/lib/utils/sudo.ts index 9cf7de9d..1ec540e8 100644 --- a/lib/utils/sudo.ts +++ b/lib/utils/sudo.ts @@ -14,9 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); import { ChildProcess, spawn, SpawnOptions } from 'child_process'; -import { stripIndent } from './lazy'; +import { stripIndent } from './lazy.js'; /** * Execute a child process with admin / superuser privileges, prompting the user for @@ -42,8 +44,8 @@ export async function executeWithPrivileges( isCLIcmd = true, ): Promise { // whether the CLI is already running with admin / super user privileges - const isElevated = await (await import('is-elevated'))(); - const { shellEscape } = await import('./helpers'); + const isElevated = await (await import('is-elevated')).default(); + const { shellEscape } = await import('./helpers.js'); const opts: SpawnOptions = { env: process.env, stdio: ['inherit', 'inherit', stderr ? 'pipe' : 'inherit'], diff --git a/lib/utils/tty.ts b/lib/utils/tty.ts index 7c786956..a6e29cde 100644 --- a/lib/utils/tty.ts +++ b/lib/utils/tty.ts @@ -14,6 +14,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); const windowSize: { width?: number; height?: number } = {}; @@ -25,7 +27,7 @@ const updateWindowSize = () => { process.stdout.on('resize', updateWindowSize); -export = (stream: NodeJS.WriteStream = process.stdout) => { +const tty = (stream: NodeJS.WriteStream = process.stdout) => { // make sure we get initial metrics updateWindowSize(); @@ -73,3 +75,5 @@ export = (stream: NodeJS.WriteStream = process.stdout) => { deleteToEnd, }; }; + +export default tty; diff --git a/lib/utils/tunnel.ts b/lib/utils/tunnel.ts index 5e37863a..5c409187 100644 --- a/lib/utils/tunnel.ts +++ b/lib/utils/tunnel.ts @@ -17,7 +17,7 @@ import type { BalenaSDK } from 'balena-sdk'; import { Socket } from 'net'; import * as tls from 'tls'; import { TypedError } from 'typed-error'; -import { ExpectedError } from '../errors'; +import { ExpectedError } from '../errors.js'; const PROXY_CONNECT_TIMEOUT_MS = 10000; diff --git a/lib/utils/umount.ts b/lib/utils/umount.ts index 335a9536..37ab045e 100644 --- a/lib/utils/umount.ts +++ b/lib/utils/umount.ts @@ -39,7 +39,7 @@ export async function umount(device: string): Promise { if (process.platform === 'win32') { return; } - const { sanitizePath, whichBin } = await import('./which'); + const { sanitizePath, whichBin } = await import('./which.js'); // sanitize user's input (regular expression attacks ?) device = sanitizePath(device); const cmd: string[] = []; @@ -48,7 +48,7 @@ export async function umount(device: string): Promise { cmd.push('/usr/sbin/diskutil', 'unmountDisk', 'force', device); } else { // Linux - const glob = promisify(await import('glob')); + const glob = promisify((await import('glob')).default); // '?*' expands a base device path like '/dev/sdb' to an array of paths // like '/dev/sdb1', '/dev/sdb2', ..., '/dev/sdb11', ... (partitions) // that exist for balenaOS images and are needed as arguments to 'umount' @@ -75,7 +75,7 @@ export async function umount(device: string): Promise { } return; } - const { ExpectedError } = await import('../errors'); + const { ExpectedError } = await import('../errors.js'); throw new ExpectedError(msg.join('\n')); } } @@ -92,7 +92,7 @@ export async function isMounted(device: string): Promise { if (!device) { return false; } - const { whichBin } = await import('./which'); + const { whichBin } = await import('./which.js'); const mountCmd = await whichBin('mount'); let stdout = ''; let stderr = ''; @@ -101,7 +101,7 @@ export async function isMounted(device: string): Promise { stdout = proc.stdout; stderr = proc.stderr; } catch (err) { - const { ExpectedError } = await import('../errors'); + const { ExpectedError } = await import('../errors.js'); throw new ExpectedError( `Error executing "${mountCmd}":\n${stderr}\n${err.message}`, ); @@ -131,7 +131,7 @@ export async function denyMount( handler: () => any, opts: { autoMountOnSuccess?: boolean; executablePath?: string } = {}, ) { - const denymount = promisify(await import('denymount')); + const denymount = promisify((await import('denymount')).default); if (process.pkg) { // when running in a standalone pkg install, the 'denymount' // executable is placed on the same folder as process.execPath diff --git a/lib/utils/update.ts b/lib/utils/update.ts index caf1ca2e..4aa12f52 100644 --- a/lib/utils/update.ts +++ b/lib/utils/update.ts @@ -13,11 +13,13 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); -import isRoot = require('is-root'); -import * as UpdateNotifier from 'update-notifier'; +import isRoot from 'is-root'; +import UpdateNotifier from 'update-notifier'; -import packageJSON = require('../../package.json'); +import packageJSON from '../../package.json' assert {type: 'json'};; // Check for an update at most once a day. 1 day granularity should be // enough, rather than every run. Note because we show the information diff --git a/lib/utils/validation.ts b/lib/utils/validation.ts index 891ec39b..a86267a2 100644 --- a/lib/utils/validation.ts +++ b/lib/utils/validation.ts @@ -14,8 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import validEmail = require('@resin.io/valid-email'); -import { ExpectedError } from '../errors'; +import validEmail from '@resin.io/valid-email'; +import { ExpectedError } from '../errors.js'; const APPNAME_REGEX = new RegExp(/^[a-zA-Z0-9_-]+$/); // An regex to detect an IP address, from https://www.regular-expressions.info/ip.html diff --git a/lib/utils/version.ts b/lib/utils/version.ts index 739b3803..4f307438 100644 --- a/lib/utils/version.ts +++ b/lib/utils/version.ts @@ -16,7 +16,7 @@ */ import * as semver from 'semver'; -import { version } from '../../package.json'; +import { version } from '../../package.json' assert {type: 'json'};; export function isVersionGTE(v: string): boolean { return semver.gte(process.env.BALENA_CLI_VERSION_OVERRIDE || version, v); diff --git a/lib/utils/which.ts b/lib/utils/which.ts index d35b72bb..679e4163 100644 --- a/lib/utils/which.ts +++ b/lib/utils/which.ts @@ -17,6 +17,8 @@ import { promises as fs, constants } from 'fs'; import * as path from 'path'; +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); export const { F_OK, R_OK, W_OK, X_OK } = constants; @@ -76,14 +78,14 @@ export async function which( program: string, rejectOnMissing = true, ): Promise { - const whichMod = await import('which'); + const { default: whichMod } = await import('which'); let programPath: string; try { programPath = await whichMod(program); } catch (err) { if (err.code === 'ENOENT') { if (rejectOnMissing) { - const { ExpectedError } = await import('../errors'); + const { ExpectedError } = await import('../errors.js'); throw new ExpectedError( `'${program}' program not found. Is it installed?`, ); diff --git a/package.json b/package.json index d535376c..4bdd1bbb 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "type": "git", "url": "git@github.com:balena-io/balena-cli.git" }, + "type": "module", "preferGlobal": true, "files": [ "bin/.fast-boot.json", @@ -21,7 +22,7 @@ "oclif.manifest.json" ], "bin": { - "balena": "./bin/balena" + "balena": "./bin/balena.js" }, "pkg": { "scripts": [ @@ -71,7 +72,7 @@ "ci": "npm run test && npm run catch-uncommitted", "lint": "npm run lint-tsconfig && npm run lint-other", "lint-tsconfig": "balena-lint -e ts -e js -t tsconfig.dev.json --fix automation/ lib/ tests/ typings/", - "lint-other": "balena-lint -e ts -e js --fix bin/balena bin/balena-dev completion/ .mocharc.js .mocharc-standalone.js", + "lint-other": "balena-lint -e ts -e js --fix bin/balena.js bin/balena-dev.js completion/ .mocharc.js .mocharc-standalone.js", "update": "ts-node --transpile-only ./automation/update-module.ts", "prepare": "echo {} > bin/.fast-boot.json", "prepublishOnly": "npm run build" diff --git a/patches/all/oclif+4.0.3.patch b/patches/all/oclif+4.0.3.patch index 212aa9da..ea092977 100644 --- a/patches/all/oclif+4.0.3.patch +++ b/patches/all/oclif+4.0.3.patch @@ -68,8 +68,8 @@ index 9ced03f..4040aee 100644 (0, promises_1.rm)(path.join(c.workspace(), path.basename(tarball)), { recursive: true }), (0, fs_extra_1.remove)(path.join(c.workspace(), 'bin', 'run.cmd')), ]); -+ // rename the original balena-cli ./bin/balena entry point for oclif compatibility -+ await fs.move(path.join(c.workspace(), 'bin', 'balena'), path.join(c.workspace(), 'bin', 'run')); ++ // rename the original balena-cli ./bin/balena.js entry point for oclif compatibility ++ await fs.move(path.join(c.workspace(), 'bin', 'balena.js'), path.join(c.workspace(), 'bin', 'run.js')); + // The oclif installers are a production installation, while the source + // `bin` folder may contain a `.fast-boot.json` file of a dev installation. + // This has previously led to issues preventing the CLI from starting, so diff --git a/patches/apply-patches.js b/patches/apply-patches.js index 26c1118a..fbe02e23 100644 --- a/patches/apply-patches.js +++ b/patches/apply-patches.js @@ -15,11 +15,11 @@ * limitations under the License. */ -const { execFile } = require('child_process'); -const fs = require('fs'); -const path = require('path'); +import { execFile } from 'child_process'; +import fs from 'fs'; +import path from 'path'; -const { promisify } = require('util'); +import { promisify } from 'util'; const execFileAsync = promisify(execFile); const patchesDir = 'patches'; @@ -80,4 +80,4 @@ async function run() { } } -run(); +await run(); diff --git a/tests/auth/utils.spec.ts b/tests/auth/utils.spec.ts index ad1e8285..c71b05e1 100644 --- a/tests/auth/utils.spec.ts +++ b/tests/auth/utils.spec.ts @@ -7,7 +7,7 @@ import { getBalenaSdk } from '../../build/utils/lazy'; import tokens from './tokens'; const utils = rewire('../../build/auth/utils'); -const balena = getBalenaSdk(); +const balena = await getBalenaSdk(); describe('Utils:', function () { describe('.getDashboardLoginURL()', function () { diff --git a/tests/deprecation.spec.ts b/tests/deprecation.spec.ts index dcaf09b2..8efe0367 100644 --- a/tests/deprecation.spec.ts +++ b/tests/deprecation.spec.ts @@ -29,7 +29,7 @@ import { } from '../build/deprecation'; import { BalenaAPIMock } from './nock/balena-api-mock'; import { NpmMock } from './nock/npm-mock'; -import { runCommand, TestOutput } from './helpers'; +import { runCommand, TestOutput } from './helpers.js'; // "itSS" means "it() Skip Standalone" const itSS = process.env.BALENA_CLI_TEST_TYPE === 'standalone' ? it.skip : it; diff --git a/tests/docker-build.ts b/tests/docker-build.ts index bbe7c3e0..7f767797 100644 --- a/tests/docker-build.ts +++ b/tests/docker-build.ts @@ -36,7 +36,7 @@ import { deepJsonParse, deepTemplateReplace, runCommand, -} from './helpers'; +} from './helpers.js'; import { ExpectedTarStreamFile, ExpectedTarStreamFiles, diff --git a/tsconfig.json b/tsconfig.json index bfe183cb..d347434b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "declaration": true, - "module": "commonjs", + "module": "NodeNext", "target": "es2018", "outDir": "build", "strict": true, diff --git a/typings/resin-cli-form/index.d.ts b/typings/resin-cli-form/index.d.ts index 625e4cb8..e83e9fef 100644 --- a/typings/resin-cli-form/index.d.ts +++ b/typings/resin-cli-form/index.d.ts @@ -16,7 +16,7 @@ */ declare module 'resin-cli-form' { - import Bluebird = require('bluebird'); + import Bluebird from 'bluebird'; export type TypeOrPromiseLike = T | PromiseLike;