From 0339160a0b625a3e05e6dcdd3c4c642599cbab4e Mon Sep 17 00:00:00 2001
From: Otavio Jacobi <otaviojacobi@gmail.com>
Date: Wed, 4 Sep 2024 13:53:39 -0300
Subject: [PATCH 1/3] Remove not necessary 'import = require' syntax for
 js-yaml

Change-type: patch
---
 npm-shrinkwrap.json           | 18 +++++++++---------
 src/commands/release/index.ts |  4 ++--
 src/utils/compose_ts.ts       |  9 ++++-----
 3 files changed, 15 insertions(+), 16 deletions(-)

diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json
index b5be4abf..482c693f 100644
--- a/npm-shrinkwrap.json
+++ b/npm-shrinkwrap.json
@@ -3894,9 +3894,9 @@
       }
     },
     "node_modules/@types/node": {
-      "version": "20.16.3",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.3.tgz",
-      "integrity": "sha512-/wdGiWRkMOm53gAsSyFMXFZHbVg7C6CbkrzHNpaHoYfsUWPg7m6ZRKtvQjgvQ9i8WT540a3ydRlRQbxjY30XxQ==",
+      "version": "20.16.4",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.4.tgz",
+      "integrity": "sha512-ioyQ1zK9aGEomJ45zz8S8IdzElyxhvP1RVWnPrXDf6wFaUb+kk1tEcVVJkF7RPGM0VWI7cp5U57oCPIn5iN1qg==",
       "dependencies": {
         "undici-types": "~6.19.2"
       }
@@ -5681,9 +5681,9 @@
       }
     },
     "node_modules/balena-sdk/node_modules/@types/node": {
-      "version": "18.19.48",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.48.tgz",
-      "integrity": "sha512-7WevbG4ekUcRQSZzOwxWgi5dZmTak7FaxXDoW7xVxPBmKx1rTzfmRLkeCgJzcbBnOV2dkhAPc8cCeT6agocpjg==",
+      "version": "18.19.49",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.49.tgz",
+      "integrity": "sha512-ALCeIR6n0nQ7j0FUF1ycOhrp6+XutJWqEu/vtdEqXFUQwkBfgUA5cEg3ZNmjWGF/ZYA/FcF9QMkL55Ar0O6UrA==",
       "dependencies": {
         "undici-types": "~5.26.4"
       }
@@ -14324,9 +14324,9 @@
       }
     },
     "node_modules/patch-package/node_modules/yaml": {
-      "version": "2.5.0",
-      "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz",
-      "integrity": "sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==",
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz",
+      "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==",
       "bin": {
         "yaml": "bin.mjs"
       },
diff --git a/src/commands/release/index.ts b/src/commands/release/index.ts
index a73a8d31..a05d02a6 100644
--- a/src/commands/release/index.ts
+++ b/src/commands/release/index.ts
@@ -20,7 +20,7 @@ import Command from '../../command';
 import * as cf from '../../utils/common-flags';
 import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
 import type * as BalenaSdk from 'balena-sdk';
-import jsyaml = require('js-yaml');
+import * as yaml from 'js-yaml';
 import { tryAsInteger } from '../../utils/validation';
 import { jsonInfo } from '../../utils/messages';
 
@@ -82,7 +82,7 @@ export default class ReleaseCmd extends Command {
 			$select: 'composition',
 		});
 
-		console.log(jsyaml.dump(release.composition));
+		console.log(yaml.dump(release.composition));
 	}
 
 	async showReleaseInfo(
diff --git a/src/utils/compose_ts.ts b/src/utils/compose_ts.ts
index 8cb9ae5b..c1c130a4 100644
--- a/src/utils/compose_ts.ts
+++ b/src/utils/compose_ts.ts
@@ -19,7 +19,7 @@ import type { 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 yaml from 'js-yaml';
 import * as _ from 'lodash';
 import * as path from 'path';
 import type {
@@ -180,7 +180,6 @@ async function mergeDevComposeOverlay(
 		interface ComposeObj {
 			services?: object;
 		}
-		const yaml = await import('js-yaml');
 		const loadObj = (inputStr: string): ComposeObj =>
 			(yaml.load(inputStr) || {}) as ComposeObj;
 		try {
@@ -659,7 +658,7 @@ async function loadBuildMetatada(
 		if (metadataPath.endsWith('json')) {
 			buildMetadata = JSON.parse(rawString);
 		} else {
-			buildMetadata = require('js-yaml').load(rawString);
+			buildMetadata = yaml.load(rawString) as MultiBuild.ParsedBalenaYml;
 		}
 	} catch (err) {
 		throw new ExpectedError(
@@ -944,7 +943,7 @@ async function parseRegistrySecrets(
 		const multiBuild = await import('@balena/compose/dist/multibuild');
 		const registrySecrets =
 			new multiBuild.RegistrySecretValidator().validateRegistrySecrets(
-				isYaml ? require('js-yaml').load(raw) : JSON.parse(raw),
+				isYaml ? yaml.load(raw) : JSON.parse(raw),
 			);
 		multiBuild.addCanonicalDockerHubEntry(registrySecrets);
 		return registrySecrets;
@@ -1494,7 +1493,7 @@ async function getContractContent(
 
 	let asJson;
 	try {
-		asJson = jsyaml.load(fileContentAsString);
+		asJson = yaml.load(fileContentAsString);
 	} catch (err) {
 		throw new ExpectedError(
 			`Error parsing file "${filePath}":\n ${err.message}`,

From 6efd24489ffa10dc616cd0224d4de889b2029709 Mon Sep 17 00:00:00 2001
From: Otavio Jacobi <otaviojacobi@gmail.com>
Date: Wed, 4 Sep 2024 14:33:52 -0300
Subject: [PATCH 2/3] Remove the use of CJS require() on test files

Change-type: patch
---
 tests/auth/server.spec.ts       |  2 +-
 tests/auth/utils.spec.ts        |  6 +++---
 tests/commands/build.spec.ts    |  2 +-
 tests/commands/env/envs.spec.ts |  3 ++-
 tests/commands/ssh.spec.ts      |  2 +-
 tests/deprecation.spec.ts       |  2 +-
 tests/docker-build.ts           | 12 ++++++------
 tests/helpers.ts                |  5 ++---
 tests/nock/builder-mock.ts      |  2 +-
 tests/nock/nock-mock.ts         |  3 +--
 tests/nock/proxy-server.ts      |  3 +--
 tests/utils/device/live.spec.ts |  4 ++--
 12 files changed, 22 insertions(+), 24 deletions(-)

diff --git a/tests/auth/server.spec.ts b/tests/auth/server.spec.ts
index 6a399d1b..2e3245ab 100644
--- a/tests/auth/server.spec.ts
+++ b/tests/auth/server.spec.ts
@@ -16,7 +16,7 @@
  */
 
 import * as chai from 'chai';
-import chaiAsPromised = require('chai-as-promised');
+import * as chaiAsPromised from 'chai-as-promised';
 import * as ejs from 'ejs';
 import * as fs from 'fs';
 import * as path from 'path';
diff --git a/tests/auth/utils.spec.ts b/tests/auth/utils.spec.ts
index ad1e8285..7db48171 100644
--- a/tests/auth/utils.spec.ts
+++ b/tests/auth/utils.spec.ts
@@ -1,15 +1,15 @@
 import * as Bluebird from 'bluebird';
 import { expect } from 'chai';
-import rewire = require('rewire');
 import * as sinon from 'sinon';
 import * as url from 'url';
 import { getBalenaSdk } from '../../build/utils/lazy';
 import tokens from './tokens';
 
-const utils = rewire('../../build/auth/utils');
 const balena = getBalenaSdk();
 
-describe('Utils:', function () {
+describe('Utils:', async function () {
+	const rewire = await import('rewire');
+	const utils = rewire('../../build/auth/utils');
 	describe('.getDashboardLoginURL()', function () {
 		it('should eventually be a valid url', () =>
 			utils
diff --git a/tests/commands/build.spec.ts b/tests/commands/build.spec.ts
index d402f551..ec5400a0 100644
--- a/tests/commands/build.spec.ts
+++ b/tests/commands/build.spec.ts
@@ -259,7 +259,7 @@ describe('balena build', function () {
 		const fsModPath = 'fs';
 		const fsMod = await import(fsModPath);
 		const qemuModPath = '../../build/utils/qemu';
-		const qemuMod = require(qemuModPath);
+		const qemuMod = await import(qemuModPath);
 		const qemuBinPath = await qemuMod.getQemuPath(arch);
 		try {
 			// patch fs.access and fs.stat to pretend that a copy of the Qemu binary
diff --git a/tests/commands/env/envs.spec.ts b/tests/commands/env/envs.spec.ts
index 5c429299..aec9e816 100644
--- a/tests/commands/env/envs.spec.ts
+++ b/tests/commands/env/envs.spec.ts
@@ -20,6 +20,7 @@ import { stripIndent } from '../../../build/utils/lazy';
 
 import { BalenaAPIMock } from '../../nock/balena-api-mock';
 import { runCommand } from '../../helpers';
+import { randomBytes } from 'node:crypto';
 
 describe('balena envs', function () {
 	const appName = 'test';
@@ -32,7 +33,7 @@ describe('balena envs', function () {
 		api.expectGetWhoAmI({ optional: true, persist: true });
 		api.expectGetMixpanel({ optional: true });
 		// Random device UUID used to frustrate _.memoize() in utils/cloud.ts
-		fullUUID = require('crypto').randomBytes(16).toString('hex');
+		fullUUID = randomBytes(16).toString('hex');
 		shortUUID = fullUUID.substring(0, 7);
 	});
 
diff --git a/tests/commands/ssh.spec.ts b/tests/commands/ssh.spec.ts
index 4e67b7e4..f2d05874 100644
--- a/tests/commands/ssh.spec.ts
+++ b/tests/commands/ssh.spec.ts
@@ -16,7 +16,7 @@
  */
 
 import { expect } from 'chai';
-import mock = require('mock-require');
+import * as mock from 'mock-require';
 import type { Server } from 'net';
 import { createServer } from 'net';
 
diff --git a/tests/deprecation.spec.ts b/tests/deprecation.spec.ts
index 1e37a42e..f42b128b 100644
--- a/tests/deprecation.spec.ts
+++ b/tests/deprecation.spec.ts
@@ -18,7 +18,7 @@
 import * as settings from 'balena-settings-client';
 import { getStorage } from 'balena-settings-storage';
 import { expect } from 'chai';
-import mock = require('mock-require');
+import * as mock from 'mock-require';
 import * as semver from 'semver';
 import * as sinon from 'sinon';
 
diff --git a/tests/docker-build.ts b/tests/docker-build.ts
index 20b912dd..84841a4b 100644
--- a/tests/docker-build.ts
+++ b/tests/docker-build.ts
@@ -20,12 +20,12 @@ import * as _ from 'lodash';
 import { promises as fs } from 'fs';
 import * as path from 'path';
 import { PathUtils } from '@balena/compose/dist/multibuild';
-import rewire = require('rewire');
 import * as sinon from 'sinon';
 import { Readable } from 'stream';
 import * as tar from 'tar-stream';
 import { streamToBuffer } from 'tar-utils';
 import { URL } from 'url';
+import { diff } from 'deep-object-diff';
 
 import { makeImageName } from '../build/utils/compose_ts';
 import { stripIndent } from '../build/utils/lazy';
@@ -101,8 +101,6 @@ export async function inspectTarStream(
 	try {
 		expect($expected).to.deep.equal(found);
 	} catch (e) {
-		const { diff } =
-			require('deep-object-diff') as typeof import('deep-object-diff');
 		const diffStr = JSON.stringify(
 			diff($expected, found),
 			(_k, v) => (v === undefined ? 'undefined' : v),
@@ -202,7 +200,7 @@ export async function testDockerBuildStream(o: {
 		}
 	}
 
-	resetDockerignoreCache();
+	await resetDockerignoreCache();
 
 	const { exitCode, out, err } = await runCommand(o.commandLine);
 
@@ -254,7 +252,7 @@ export async function testPushBuildStream(o: {
 			inspectTarStream(buildRequestBody, o.expectedFiles, o.projectPath),
 	});
 
-	resetDockerignoreCache();
+	await resetDockerignoreCache();
 
 	const { out, err } = await runCommand(o.commandLine);
 
@@ -262,7 +260,9 @@ export async function testPushBuildStream(o: {
 	expect(cleanOutput(out, true)).to.include.members(expectedResponseLines);
 }
 
-export function resetDockerignoreCache() {
+export async function resetDockerignoreCache() {
+	const rewire = await import('rewire');
+
 	if (process.env.BALENA_CLI_TEST_TYPE !== 'source') {
 		return;
 	}
diff --git a/tests/helpers.ts b/tests/helpers.ts
index dddf112e..9da54649 100644
--- a/tests/helpers.ts
+++ b/tests/helpers.ts
@@ -19,6 +19,8 @@ import * as _ from 'lodash';
 import * as path from 'path';
 
 import * as packageJSON from '../package.json';
+import { getNodeEngineVersionWarn } from '../build/utils/messages';
+import { warnify } from '../build/utils/messages';
 
 const balenaExe = process.platform === 'win32' ? 'balena.exe' : 'balena';
 const standalonePath = path.resolve(__dirname, '..', 'build-bin', balenaExe);
@@ -41,7 +43,6 @@ function matchesNodeEngineVersionWarn(msg: string) {
 			.map((l) => l.trim())
 			.filter((l) => l);
 
-	const { getNodeEngineVersionWarn } = require('../build/utils/messages');
 	let nodeEngineWarn: string = getNodeEngineVersionWarn(
 		'x.y.z',
 		packageJSON.engines.node,
@@ -179,8 +180,6 @@ async function runCommandInSubprocess(
 					const msg = `
 Error (possibly expected) executing child CLI process "${standalonePath}"
 ${$error}`;
-					const { warnify } =
-						require('../build/utils/messages') as typeof import('../build/utils/messages');
 					console.error(warnify(msg, '[debug] '));
 				}
 				resolve();
diff --git a/tests/nock/builder-mock.ts b/tests/nock/builder-mock.ts
index ffed6304..b0f69d79 100644
--- a/tests/nock/builder-mock.ts
+++ b/tests/nock/builder-mock.ts
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-import Bluebird = require('bluebird');
+import * as Bluebird from 'bluebird';
 import * as path from 'path';
 import * as zlib from 'zlib';
 
diff --git a/tests/nock/nock-mock.ts b/tests/nock/nock-mock.ts
index 04ce0d6d..80cd37b2 100644
--- a/tests/nock/nock-mock.ts
+++ b/tests/nock/nock-mock.ts
@@ -17,6 +17,7 @@
 
 import * as nock from 'nock';
 import * as fs from 'fs';
+import { interceptorServerPort } from './proxy-server';
 
 export interface ScopeOpts {
 	optional?: boolean;
@@ -170,8 +171,6 @@ export class NockMock {
 	}
 
 	protected handleUnexpectedRequest(req: any) {
-		const { interceptorServerPort } =
-			require('./proxy-server') as typeof import('./proxy-server');
 		const o = req.options || {};
 		const u = o.uri || {};
 		const method = req.method;
diff --git a/tests/nock/proxy-server.ts b/tests/nock/proxy-server.ts
index d160e93d..221171d9 100644
--- a/tests/nock/proxy-server.ts
+++ b/tests/nock/proxy-server.ts
@@ -55,6 +55,7 @@
  */
 
 import * as http from 'http';
+import * as httpProxy from 'http-proxy';
 
 const proxyServers: http.Server[] = [];
 
@@ -81,8 +82,6 @@ export async function createProxyServerOnce(): Promise<[number, number]> {
 }
 
 async function createProxyServer(): Promise<[number, number]> {
-	const httpProxy = require('http-proxy') as typeof import('http-proxy');
-
 	const interceptorPort = await createInterceptorServer();
 
 	const proxy = httpProxy.createProxyServer();
diff --git a/tests/utils/device/live.spec.ts b/tests/utils/device/live.spec.ts
index 6ca79bb6..510df946 100644
--- a/tests/utils/device/live.spec.ts
+++ b/tests/utils/device/live.spec.ts
@@ -132,8 +132,8 @@ describeSS('LivepushManager::setupFilesystemWatcher', function () {
 		await setupDockerignoreTestData({ cleanup: true });
 	});
 
-	this.beforeEach(() => {
-		resetDockerignoreCache();
+	this.beforeEach(async () => {
+		await resetDockerignoreCache();
 	});
 
 	describe('for project no-docker-compose/basic', function () {

From facc66e9f97d075610d4383efa92dceb5b4f7acf Mon Sep 17 00:00:00 2001
From: Otavio Jacobi <otaviojacobi@gmail.com>
Date: Wed, 4 Sep 2024 14:47:18 -0300
Subject: [PATCH 3/3] Reduce use of CJS require() on automation files

Change-type: patch
---
 automation/capitanodoc/index.ts |  9 ++++++---
 automation/deploy-bin.ts        | 22 +++++++++-------------
 automation/utils.ts             |  8 ++++++--
 3 files changed, 21 insertions(+), 18 deletions(-)

diff --git a/automation/capitanodoc/index.ts b/automation/capitanodoc/index.ts
index b82f0097..4bb8047a 100644
--- a/automation/capitanodoc/index.ts
+++ b/automation/capitanodoc/index.ts
@@ -39,7 +39,7 @@ export async function renderMarkdown(): Promise<string> {
 		};
 
 		for (const jsFilename of commandCategory.files) {
-			category.commands.push(...importOclifCommands(jsFilename));
+			category.commands.push(...(await importOclifCommands(jsFilename)));
 		}
 		result.categories.push(category);
 	}
@@ -78,7 +78,9 @@ class FakeHelpCommand {
 	};
 }
 
-function importOclifCommands(jsFilename: string): OclifCommand[] {
+async function importOclifCommands(
+	jsFilename: string,
+): Promise<OclifCommand[]> {
 	// TODO: Currently oclif commands with no `usage` overridden will cause
 	//  an error when parsed.  This should be improved so that `usage` does not have
 	//  to be overridden if not necessary.
@@ -86,7 +88,8 @@ function importOclifCommands(jsFilename: string): OclifCommand[] {
 	const command: OclifCommand =
 		jsFilename === 'help'
 			? (new FakeHelpCommand() as unknown as OclifCommand)
-			: (require(path.join(process.cwd(), jsFilename)).default as OclifCommand);
+			: ((await import(path.join(process.cwd(), jsFilename)))
+					.default as OclifCommand);
 
 	return [command];
 }
diff --git a/automation/deploy-bin.ts b/automation/deploy-bin.ts
index 4a44a7da..4a345822 100644
--- a/automation/deploy-bin.ts
+++ b/automation/deploy-bin.ts
@@ -17,19 +17,15 @@
 
 import * as _ from 'lodash';
 import * as semver from 'semver';
+import { Octokit } from '@octokit/rest';
+import { throttling } from '@octokit/plugin-throttling';
 
 const { GITHUB_TOKEN } = process.env;
 
 /** Return a cached Octokit instance, creating a new one as needed. */
 const getOctokit = _.once(function () {
-	const Octokit = (
-		require('@octokit/rest') as typeof import('@octokit/rest')
-	).Octokit.plugin(
-		(
-			require('@octokit/plugin-throttling') as typeof import('@octokit/plugin-throttling')
-		).throttling,
-	);
-	return new Octokit({
+	const OctokitConstructor = Octokit.plugin(throttling);
+	return new OctokitConstructor({
 		auth: GITHUB_TOKEN,
 		throttle: {
 			onRateLimit: (retryAfter: number, options: any) => {
@@ -65,16 +61,16 @@ const getOctokit = _.once(function () {
  * 'pages' is the total number of pages, and 'ordinal' is the ordinal number
  * (3rd, 4th, 5th...) of the first item in the current page.
  */
-function getPageNumbers(
+async function getPageNumbers(
 	response: any,
 	perPageDefault: number,
-): { page: number; pages: number; ordinal: number } {
+): Promise<{ page: number; pages: number; ordinal: number }> {
 	const res = { page: 1, pages: 1, ordinal: 1 };
 	if (!response.headers.link) {
 		return res;
 	}
-	const parse =
-		require('parse-link-header') as typeof import('parse-link-header');
+	const parse = await import('parse-link-header');
+
 	const parsed = parse(response.headers.link);
 	if (parsed == null) {
 		throw new Error(`Failed to parse link header: '${response.headers.link}'`);
@@ -129,7 +125,7 @@ async function updateGitHubReleaseDescriptions(
 			page: thisPage,
 			pages: totalPages,
 			ordinal,
-		} = getPageNumbers(response, perPage);
+		} = await getPageNumbers(response, perPage);
 		let i = 0;
 		for (const cliRelease of response.data) {
 			const prefix = `[#${ordinal + i++} pg ${thisPage}/${totalPages}]`;
diff --git a/automation/utils.ts b/automation/utils.ts
index 77c97b2b..6c21d76c 100644
--- a/automation/utils.ts
+++ b/automation/utils.ts
@@ -17,6 +17,8 @@
 
 import { spawn } from 'child_process';
 import * as path from 'path';
+import * as fs from 'fs';
+import { diffTrimmedLines } from 'diff';
 
 export const ROOT = path.join(__dirname, '..');
 
@@ -64,7 +66,6 @@ export class StdOutTap {
  * https://www.npmjs.com/package/diff
  */
 export function diffLines(str1: string, str2: string): string {
-	const { diffTrimmedLines } = require('diff');
 	const diffObjs = diffTrimmedLines(str1, str2);
 	const prefix = (chunk: string, char: string) =>
 		chunk
@@ -84,7 +85,10 @@ export function diffLines(str1: string, str2: string): string {
 }
 
 export function loadPackageJson() {
-	return require(path.join(ROOT, 'package.json'));
+	const packageJsonPath = path.join(ROOT, 'package.json');
+
+	const packageJson = fs.readFileSync(packageJsonPath, 'utf8');
+	return JSON.parse(packageJson);
 }
 
 /**