Merge pull request #2895 from balena-io/bump-balena-lint-v9

Update @balena/lint to v9.1.3
This commit is contained in:
flowzone-app[bot] 2024-12-16 17:35:45 +00:00 committed by GitHub
commit 45f482fad1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
67 changed files with 1777 additions and 902 deletions

View File

@ -145,7 +145,7 @@ export async function getCapitanoDoc(): Promise<typeof capitanoDoc> {
throw new Error(`Error parsing section title`);
}
// match[1] has the title, match[2] has the rest
return match && match[2];
return match?.[2];
}),
mdParser.getSectionOfTitle('Installation'),
mdParser.getSectionOfTitle('Choosing a shell (command prompt/terminal)'),

View File

@ -3,7 +3,7 @@ import * as semver from 'semver';
const changeTypes = ['major', 'minor', 'patch'] as const;
const validateChangeType = (maybeChangeType: string = 'minor') => {
const validateChangeType = (maybeChangeType = 'minor') => {
maybeChangeType = maybeChangeType.toLowerCase();
switch (maybeChangeType) {
case 'patch':
@ -136,5 +136,4 @@ async function main() {
}
}
// eslint-disable-next-line @typescript-eslint/no-floating-promises
main();
void main();

View File

@ -19,6 +19,7 @@ import { spawn } from 'child_process';
import * as path from 'path';
import * as fs from 'fs';
import { diffTrimmedLines } from 'diff';
import * as whichMod from 'which';
export const ROOT = path.join(__dirname, '..');
@ -101,7 +102,6 @@ export function loadPackageJson() {
* @returns The program's full path, e.g. 'C:\WINDOWS\System32\OpenSSH\ssh.EXE'
*/
export async function which(program: string): Promise<string> {
const whichMod = await import('which');
let programPath: string;
try {
programPath = await whichMod(program);
@ -132,7 +132,7 @@ export async function whichSpawn(
.on('error', reject)
.on('close', resolve);
} catch (err) {
reject(err);
reject(err as Error);
}
});
} catch (err) {

View File

@ -57,7 +57,10 @@ require('ts-node').register({
project: path.join(rootDir, 'tsconfig.json'),
transpileOnly: true,
});
require('../src/app').run(undefined, { dir: __dirname, development: true });
void require('../src/app').run(undefined, {
dir: __dirname,
development: true,
});
// Modify package.json oclif paths from build/ -> src/, or vice versa
function modifyOclifPaths(revert) {

View File

@ -5,7 +5,7 @@
process.env.UV_THREADPOOL_SIZE = '64';
// Disable oclif registering ts-node
process.env.OCLIF_TS_NODE = 0;
process.env.OCLIF_TS_NODE = '0';
async function run() {
// Use fast-boot to cache require lookups, speeding up startup
@ -18,4 +18,4 @@ async function run() {
await require('../build/app').run(undefined, { dir: __dirname });
}
run();
void run();

32
eslint.config.js Normal file
View File

@ -0,0 +1,32 @@
const { FlatCompat } = require('@eslint/eslintrc');
const compat = new FlatCompat({
baseDirectory: __dirname,
});
module.exports = [
...require('@balena/lint/config/eslint.config'),
...compat.config({
parserOptions: {
project: 'tsconfig.dev.json',
},
ignorePatterns: ['**/generate-completion.js', '**/bin/**/*'],
rules: {
ignoreDefinitionFiles: 0,
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-shadow': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-require-imports': 'off',
'@typescript-eslint/no-unnecessary-type-assertion': 'off',
'@typescript-eslint/prefer-nullish-coalescing': 'warn',
'no-restricted-imports': ['error', {
paths: ['resin-cli-visuals', 'chalk', 'common-tags', 'resin-cli-form'],
}],
'@typescript-eslint/no-unused-vars': ['error', {
argsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
}],
},
}),
];

2145
npm-shrinkwrap.json generated

File diff suppressed because it is too large Load Diff

View File

@ -111,7 +111,7 @@
}
},
"devDependencies": {
"@balena/lint": "^8.0.0",
"@balena/lint": "^9.1.3",
"@electron/notarize": "^2.0.0",
"@types/archiver": "^6.0.2",
"@types/bluebird": "^3.5.36",

View File

@ -50,9 +50,9 @@ export default class RevokeCmd extends Command {
return;
}
await Promise.all(
apiKeyIds.map(
async (id) => await getBalenaSdk().models.apiKey.revoke(Number(id)),
),
apiKeyIds.map(async (id) => {
await getBalenaSdk().models.apiKey.revoke(Number(id));
}),
);
console.log('Successfully revoked the given API keys');
}

View File

@ -91,7 +91,7 @@ export default class DeviceDetectCmd extends Command {
try {
await docker.ping();
return true;
} catch (err) {
} catch {
return false;
}
}),

View File

@ -155,7 +155,7 @@ export default class DeviceInitCmd extends Command {
try {
logger.logDebug(`Process failed, removing device ${device.uuid}`);
await balena.models.device.remove(device.uuid);
} catch (e) {
} catch {
// Ignore removal failures, and throw original error
}
throw e;

View File

@ -135,7 +135,7 @@ export default class DeviceLogsCmd extends Command {
logger.logDebug('Checking we can access device');
try {
await deviceApi.ping();
} catch (e) {
} catch {
const { ExpectedError } = await import('../../errors');
throw new ExpectedError(
`Cannot access device at address ${params.device}. Device may not be in local mode.`,

View File

@ -76,6 +76,6 @@ export default class DeviceRegisterCmd extends Command {
options.deviceType,
);
return result && result.uuid;
return result.uuid;
}
}

View File

@ -82,7 +82,7 @@ export default class DeviceSSHCmd extends Command {
SSH server port number (default 22222) if the target is an IP address or .local
hostname. Otherwise, port number for the balenaCloud gateway (default 22).`,
char: 'p',
parse: async (p) => parseAsInteger(p, 'port'),
parse: (p) => parseAsInteger(p, 'port'),
}),
tty: Flags.boolean({
default: false,
@ -110,13 +110,14 @@ export default class DeviceSSHCmd extends Command {
// Local connection
if (validateLocalHostnameOrIp(params.fleetOrDevice)) {
const { performLocalDeviceSSH } = await import('../../utils/device/ssh');
return await performLocalDeviceSSH({
await performLocalDeviceSSH({
hostname: params.fleetOrDevice,
port: options.port || 'local',
forceTTY: options.tty,
verbose: options.verbose,
service: params.service,
});
return;
}
// Remote connection
@ -132,7 +133,7 @@ export default class DeviceSSHCmd extends Command {
const useProxy = !!proxyConfig && !options.noproxy;
// this will be a tunnelled SSH connection...
await checkNotUsingOfflineMode();
checkNotUsingOfflineMode();
await checkLoggedIn();
const deviceUuid = await getOnlineTargetDeviceUuid(
sdk,

View File

@ -41,7 +41,7 @@ export default class EnvRenameCmd extends Command {
id: Args.integer({
required: true,
description: "variable's numeric database ID",
parse: async (input) => parseAsInteger(input, 'id'),
parse: (input) => parseAsInteger(input, 'id'),
}),
value: Args.string({
required: true,

View File

@ -46,7 +46,7 @@ export default class EnvRmCmd extends Command {
id: Args.integer({
required: true,
description: "variable's numeric database ID",
parse: async (input) => parseAsInteger(input, 'id'),
parse: (input) => parseAsInteger(input, 'id'),
}),
};

View File

@ -252,10 +252,7 @@ export default class LocalConfigureCmd extends Command {
`${this.CONNECTIONS_FOLDER}/resin-sample.ignore`,
{ encoding: 'utf8' },
);
return await writeFileAsync(
`${this.CONNECTIONS_FOLDER}/resin-wifi`,
contents,
);
await writeFileAsync(`${this.CONNECTIONS_FOLDER}/resin-wifi`, contents);
});
} else if (_.includes(files, 'resin-sample')) {
// Legacy mode, to be removed later
@ -269,13 +266,13 @@ export default class LocalConfigureCmd extends Command {
} else {
// In case there's no file at all (shouldn't happen normally, but the file might have been removed)
await imagefs.interact(target, bootPartition, async (_fs) => {
return await promisify(_fs.writeFile)(
await promisify(_fs.writeFile)(
`${this.CONNECTIONS_FOLDER}/resin-wifi`,
this.CONNECTION_FILE,
);
});
}
return await this.getConfigurationSchema(bootPartition, connectionFileName);
return this.getConfigurationSchema(bootPartition, connectionFileName);
}
async removeHostname(schema: any) {

View File

@ -132,7 +132,7 @@ export default class LoginCmd extends Command {
// We can safely assume this won't be undefined as doLogin will throw if this call fails
// We also don't need to worry too much about the amount of calls to whoami
// as these are cached by the SDK
const whoamiResult = (await balena.auth.whoami()) as WhoamiResult;
const whoamiResult = (await balena.auth.whoami())!;
if (whoamiResult.actorType !== 'user' && !options.hideExperimentalWarning) {
console.info(stripIndent`
@ -168,7 +168,7 @@ ${messages.reachingOut}`);
async doLogin(
loginOptions: FlagsDef,
balenaUrl: string = 'balena-cloud.com',
balenaUrl = 'balena-cloud.com',
token?: string,
): Promise<void> {
// Token

View File

@ -292,7 +292,7 @@ export default class OsConfigureCmd extends Command {
for (const { name, content } of files) {
await imagefs.interact(image, bootPartition, async (_fs) => {
return await promisify(_fs.writeFile)(
await promisify(_fs.writeFile)(
path.join(CONNECTIONS_FOLDER, name),
content,
);

View File

@ -109,7 +109,7 @@ https://github.com/balena-io-examples/staged-releases\
'additional-space': Flags.integer({
description:
'expand the image by this amount of bytes instead of automatically estimating the required amount',
parse: async (x) => parseAsInteger(x, 'additional-space'),
parse: (x) => parseAsInteger(x, 'additional-space'),
}),
'add-certificate': Flags.string({
description: `\
@ -126,7 +126,7 @@ Can be repeated to add multiple certificates.\
dockerPort: Flags.integer({
description:
'Docker daemon TCP port number (hint: 2375 for balena devices)',
parse: async (p) => parseAsInteger(p, 'dockerPort'),
parse: (p) => parseAsInteger(p, 'dockerPort'),
}),
};
@ -155,7 +155,7 @@ Can be repeated to add multiple certificates.\
------------------------------------------------------------------------------
`);
}
} catch (error) {
} catch {
throw new ExpectedError(
`The provided image path does not exist: ${params.image}`,
);
@ -192,11 +192,11 @@ Can be repeated to add multiple certificates.\
event.name,
));
if (event.action === 'start') {
return spinner.start();
} else {
console.log();
return spinner.stop();
spinner.start();
return;
}
console.log();
spinner.stop();
};
const commit = this.isCurrentCommit(options.commit || '')

View File

@ -24,7 +24,7 @@ import { tryAsInteger } from '../../utils/validation';
import { jsonInfo } from '../../utils/messages';
export const commitOrIdArg = Args.custom({
parse: async (commitOrId: string) => tryAsInteger(commitOrId),
parse: tryAsInteger,
});
type FlagsDef = Interfaces.InferredFlags<typeof ReleaseCmd.flags>;

View File

@ -34,7 +34,7 @@ export default class SSHKeyCmd extends Command {
public static args = {
id: Args.integer({
description: 'balenaCloud ID for the SSH key',
parse: async (x) => parseAsInteger(x, 'id'),
parse: (x) => parseAsInteger(x, 'id'),
required: true,
}),
};

View File

@ -40,7 +40,7 @@ export default class SSHKeyRmCmd extends Command {
public static args = {
id: Args.integer({
description: 'balenaCloud ID for the SSH key',
parse: async (x) => parseAsInteger(x, 'id'),
parse: (x) => parseAsInteger(x, 'id'),
required: true,
}),
};

View File

@ -69,10 +69,9 @@ export default class VersionCmd extends Command {
const { flags: options } = await this.parse(VersionCmd);
const versions: JsonVersions = {
'balena-cli': (await import('../../../package.json')).version,
'Node.js':
process.version && process.version.startsWith('v')
? process.version.slice(1)
: process.version,
'Node.js': process.version.startsWith('v')
? process.version.slice(1)
: process.version,
};
if (options.json) {
console.log(JSON.stringify(versions, null, 4));

View File

@ -93,7 +93,7 @@ function interpret(error: Error): string {
if (hasCode(error)) {
const errorCodeHandler = messages[error.code];
const message = errorCodeHandler && errorCodeHandler(error);
const message = errorCodeHandler?.(error);
if (message) {
return message;
@ -229,7 +229,7 @@ async function sentryCaptureException(error: Error) {
Sentry.captureException(error);
try {
await Sentry.close(1000);
} catch (e) {
} catch {
if (process.env.DEBUG) {
console.error('[debug] Timeout reporting error to sentry.io');
}

View File

@ -209,12 +209,12 @@ See: https://git.io/JRHUW#deprecation-policy`,
return indent(body, 2);
}
protected formatDescription(desc: string = '') {
protected formatDescription(desc = '') {
const chalk = getChalk();
desc = desc.split('\n')[0];
// Remove any ending .
if (desc[desc.length - 1] === '.') {
if (desc.endsWith('.')) {
desc = desc.substring(0, desc.length - 1);
}
// Lowercase first letter if second char is lowercase, to preserve e.g. 'SSH ...')

View File

@ -103,7 +103,7 @@ const hook: Hook<'prerun'> = async function (options) {
.offlineCompatible ?? DEFAULT_OFFLINE_COMPATIBLE
)
) {
await checkNotUsingOfflineMode();
checkNotUsingOfflineMode();
}
} catch (error) {
this.error(error);

View File

@ -48,7 +48,7 @@ export async function preparseArgs(argv: string[]): Promise<string[]> {
if (
cmdSlice.length > 1 &&
cmdSlice[0] === 'help' &&
cmdSlice[1][0] !== '-'
!cmdSlice[1].startsWith('-')
) {
cmdSlice.shift();
cmdSlice.push('--help');

View File

@ -164,9 +164,8 @@ export async function downloadOSImage(
stream.on('progress', (state: any) => {
if (state != null) {
return bar.update(state);
} else {
return spinner.start();
}
spinner.start();
});
stream.on('end', () => {

View File

@ -386,7 +386,7 @@ export class BuildProgressUI implements Renderer {
.map(function (service) {
const stream = through.obj(function (event, _enc, cb) {
eventHandler(service, event);
return cb();
cb();
});
stream.pipe(tty.stream, { end: false });
return [service, stream];
@ -471,17 +471,20 @@ export class BuildProgressUI implements Renderer {
const { status, progress, error } = serviceToDataMap[service] ?? {};
if (error) {
return `${error}`;
} else if (progress) {
}
if (progress) {
const bar = renderProgressBar(progress, 20);
if (status) {
return `${bar} ${status}`;
}
return `${bar}`;
} else if (status) {
return `${status}`;
} else {
return 'Waiting...';
return bar;
}
if (status) {
return status;
}
return 'Waiting...';
})
.map((data, index) => [services[index], data])
.fromPairs()
@ -552,7 +555,7 @@ export class BuildProgressInline implements Renderer {
.map(function (service) {
const stream = through.obj(function (event, _enc, cb) {
eventHandler(service, event);
return cb();
cb();
});
stream.pipe(outStream, { end: false });
return [service, stream];
@ -606,11 +609,11 @@ export class BuildProgressInline implements Renderer {
const { status, error } = event;
if (error) {
return `${error}`;
} else if (status) {
return `${status}`;
} else {
return 'Waiting...';
}
if (status) {
return status;
}
return 'Waiting...';
})();
const prefix = _.padEnd(getChalk().bold(service), this._prefixWidth);

View File

@ -966,7 +966,7 @@ export async function makeBuildTasks(
deviceInfo: DeviceInfo,
logger: Logger,
projectName: string,
releaseHash: string = 'unavailable',
releaseHash = 'unavailable',
preprocessHook?: (dockerfile: string) => string,
): Promise<MultiBuild.BuildTask[]> {
const multiBuild = await import('@balena/compose/dist/multibuild');
@ -1492,7 +1492,7 @@ export function createRunLoop(tick: (...args: any[]) => void) {
},
end() {
clearInterval(timerId);
return runloop.onEnd();
runloop.onEnd();
},
};
return runloop;
@ -1549,7 +1549,7 @@ function dropEmptyLinesStream() {
if (str.trim()) {
this.push(str);
}
return cb();
cb();
});
}
@ -1570,7 +1570,7 @@ function buildLogCapture(objectMode: boolean, buffer: string[]) {
buffer.push(data);
}
return cb(null, data);
cb(null, data);
});
}
@ -1585,14 +1585,16 @@ function buildProgressAdapter(inline: boolean) {
return through({ objectMode: true }, function (str, _enc, cb) {
if (str == null) {
return cb(null, str);
cb(null, str);
return;
}
if (inline) {
return cb(null, { status: str });
cb(null, { status: str });
return;
}
if (!/^Successfully tagged /.test(str)) {
if (!str.startsWith('Successfully tagged ')) {
const match = stepRegex.exec(str);
if (match) {
step = match[1];
@ -1607,7 +1609,7 @@ function buildProgressAdapter(inline: boolean) {
}
}
return cb(null, { status: str, progress });
cb(null, { status: str, progress });
});
}

View File

@ -81,7 +81,7 @@ export async function generateApplicationConfig(
)) as ImgConfig;
// merge sshKeys to config, when they have been specified
if (options.os && options.os.sshKeys) {
if (options.os?.sshKeys) {
// Create config.os object if it does not exist
config.os = config.os ? config.os : {};
config.os.sshKeys = config.os.sshKeys

View File

@ -86,7 +86,7 @@ const uploadToPromise = (uploadRequest: Request, logger: Logger) =>
obj = JSON.parse(data);
} catch (e) {
logger.logError('Error parsing reply from remote side');
reject(e);
reject(e as Error);
return;
}
@ -232,7 +232,7 @@ export const deployLegacy = async function (
username,
appName,
]);
await uploadLogs(...args);
uploadLogs(...args);
}
return buildId;

View File

@ -74,7 +74,7 @@ export class DeviceAPI {
public constructor(
private logger: Logger,
addr: string,
port: number = 48484,
port = 48484,
) {
this.deviceAddress = `http://${addr}:${port}/`;
}
@ -201,7 +201,7 @@ export class DeviceAPI {
return new Promise((resolve, reject) => {
const req = request.get(url);
req.on('error', reject).on('response', async (res) => {
req.on('error', reject).on('response', (res) => {
if (res.statusCode !== 200) {
reject(
new ApiErrors.DeviceAPIError(
@ -213,7 +213,7 @@ export class DeviceAPI {
try {
res.socket.setKeepAlive(true, 1000);
} catch (error) {
reject(error);
reject(error as Error);
}
resolve(res);
});
@ -238,7 +238,7 @@ export class DeviceAPI {
if (_.isObject(opts) && (opts as ObjectWithUrl).url != null) {
// the `as string` shouldn't be necessary, but the type system
// is getting a little confused
url = (opts as ObjectWithUrl).url as string;
url = (opts as ObjectWithUrl).url!;
} else if (typeof opts === 'string') {
url = opts;
}
@ -252,21 +252,26 @@ export class DeviceAPI {
return await new Promise((resolve, reject) => {
return request(opts, (err, response, body) => {
if (err) {
return reject(err);
reject(err as Error);
return;
}
switch (response.statusCode) {
case 200:
return resolve(body);
case 400:
return reject(
new ApiErrors.BadRequestDeviceAPIError(body.message),
);
case 503:
return reject(
new ApiErrors.ServiceUnavailableAPIError(body.message),
);
default:
return reject(new ApiErrors.DeviceAPIError(body.message));
case 200: {
resolve(body);
return;
}
case 400: {
reject(new ApiErrors.BadRequestDeviceAPIError(body.message));
return;
}
case 503: {
reject(new ApiErrors.ServiceUnavailableAPIError(body.message));
return;
}
default: {
reject(new ApiErrors.DeviceAPIError(body.message));
return;
}
}
});
});

View File

@ -74,11 +74,11 @@ interface ParsedEnvironment {
[serviceName: string]: { [key: string]: string };
}
async function environmentFromInput(
function environmentFromInput(
envs: string[],
serviceNames: string[],
logger: Logger,
): Promise<ParsedEnvironment> {
): ParsedEnvironment {
// A normal environment variable regex, with an added part
// to find a colon followed servicename at the start
const varRegex = /^(?:([^\s:]+):)?([^\s]+?)=(.*)$/;
@ -143,7 +143,7 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
try {
globalLogger.logDebug('Checking we can access device');
await api.ping();
} catch (e) {
} catch {
throw new ExpectedError(stripIndent`
Could not communicate with device supervisor at address ${opts.deviceHost}:${port}.
Device may not have local mode enabled. Check with:
@ -191,10 +191,7 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
});
// Attempt to attach to the device's docker daemon
const docker = connectToDocker(
opts.deviceHost,
opts.devicePort != null ? opts.devicePort : 2375,
);
const docker = connectToDocker(opts.deviceHost, opts.devicePort ?? 2375);
await checkBuildSecretsRequirements(docker, opts.source);
globalLogger.logDebug('Tarring all non-ignored files...');
@ -231,7 +228,7 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
// Print a newline to clearly separate build time and runtime
console.log();
const envs = await environmentFromInput(
const envs = environmentFromInput(
opts.env,
Object.getOwnPropertyNames(project.composition.services),
globalLogger,
@ -388,7 +385,7 @@ async function performBuilds(
);
// Check for failures
await inspectBuildResults(localImages);
inspectBuildResults(localImages);
const imagesToRemove: string[] = [];
@ -497,7 +494,7 @@ export async function rebuildSingleTask(
}
await assignDockerBuildOpts(docker, [task], opts);
await assignOutputHandlers([task], logger, logHandler);
assignOutputHandlers([task], logger, logHandler);
const [localImage] = await multibuild.performBuilds(
[task],
@ -568,7 +565,7 @@ async function assignDockerBuildOpts(
globalLogger.logDebug(`Using ${images.length} on-device images for cache...`);
await Promise.all(
buildTasks.map(async (task: BuildTask) => {
buildTasks.map((task: BuildTask) => {
task.dockerOpts = {
...(task.dockerOpts || {}),
...{
@ -666,7 +663,7 @@ export function generateTargetState(
return targetState;
}
async function inspectBuildResults(images: LocalImage[]): Promise<void> {
function inspectBuildResults(images: LocalImage[]): void {
const failures: LocalPushErrors.BuildFailure[] = [];
_.each(images, (image) => {
@ -679,6 +676,6 @@ async function inspectBuildResults(images: LocalImage[]): Promise<void> {
});
if (failures.length > 0) {
throw new LocalPushErrors.BuildError(failures).toString();
throw new LocalPushErrors.BuildError(failures);
}
}

View File

@ -191,8 +191,8 @@ export class LivepushManager {
);
const eventQueue = this.updateEventsWaiting[$serviceName];
eventQueue.push(changedPath);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.getDebouncedEventHandler($serviceName)();
void this.getDebouncedEventHandler($serviceName)();
};
const monitor = this.setupFilesystemWatcher(
@ -252,7 +252,7 @@ export class LivepushManager {
try {
// sync because chokidar defines a sync interface
stats = fs.lstatSync(filePath);
} catch (err) {
} catch {
// OK: the file may have been deleted. See also:
// https://github.com/paulmillr/chokidar/blob/3.4.3/lib/fsevents-handler.js#L326-L328
// https://github.com/paulmillr/chokidar/blob/3.4.3/lib/nodefs-handler.js#L364
@ -267,15 +267,15 @@ export class LivepushManager {
return dockerignore.ignores(relPath);
},
});
monitor.on('add', (changedPath: string) =>
changedPathHandler(serviceName, changedPath),
);
monitor.on('change', (changedPath: string) =>
changedPathHandler(serviceName, changedPath),
);
monitor.on('unlink', (changedPath: string) =>
changedPathHandler(serviceName, changedPath),
);
monitor.on('add', (changedPath: string) => {
changedPathHandler(serviceName, changedPath);
});
monitor.on('change', (changedPath: string) => {
changedPathHandler(serviceName, changedPath);
});
monitor.on('unlink', (changedPath: string) => {
changedPathHandler(serviceName, changedPath);
});
return monitor;
}

View File

@ -57,7 +57,7 @@ export const dockerConnectionCliFlags = {
description:
'Docker daemon TCP port number (hint: 2375 for balena devices)',
char: 'p',
parse: async (p) => parseAsInteger(p, 'dockerPort'),
parse: (p) => parseAsInteger(p, 'dockerPort'),
}),
ca: Flags.string({
description: 'Docker host TLS certificate authority file',
@ -169,9 +169,7 @@ export async function isBalenaEngine(docker: dockerode): Promise<boolean> {
// version of balenaEngine, but it was at one point (mis)spelt 'balaena':
// https://github.com/balena-os/balena-engine/pull/32/files
const dockerVersion = (await docker.version()) as BalenaEngineVersion;
return !!(
dockerVersion.Engine && dockerVersion.Engine.match(/balena|balaena/)
);
return !!dockerVersion.Engine?.match(/balena|balaena/);
}
export async function getDocker(

View File

@ -84,7 +84,7 @@ export async function readFileWithEolConversion(
}
// Analyse encoding
const encoding = await detectEncoding(fileBuffer);
const encoding = detectEncoding(fileBuffer);
// Skip further processing of non-convertible encodings
if (!CONVERTIBLE_ENCODINGS.includes(encoding)) {
@ -132,10 +132,10 @@ export async function readFileWithEolConversion(
* @param fileBuffer File contents whose encoding should be detected
* @param bytesRead Optional "file size" if smaller than the buffer size
*/
export async function detectEncoding(
export function detectEncoding(
fileBuffer: Buffer,
bytesRead = fileBuffer.length,
): Promise<string> {
): string {
// empty file
if (bytesRead === 0) {
return '';

View File

@ -281,8 +281,7 @@ export function isWindowsComExeShell() {
// neither bash nor sh (e.g. not MSYS, MSYS2, Cygwin, WSL)
process.env.SHELL == null &&
// Windows cmd.exe or PowerShell
process.env.ComSpec != null &&
process.env.ComSpec.endsWith('cmd.exe')
process.env.ComSpec?.endsWith('cmd.exe')
);
}
@ -366,7 +365,7 @@ export function getProxyConfig(): ProxyConfig | undefined {
let url: InstanceType<typeof URL>;
try {
url = new URL(proxyUrl);
} catch (_e) {
} catch {
return;
}
return {
@ -469,7 +468,7 @@ export function pickAndRename<T extends Dictionary<any>>(
let renameFrom = f;
let renameTo = f;
const match = f.match(/(?<from>\S+)\s+=>\s+(?<to>\S+)/);
if (match && match.groups) {
if (match?.groups) {
renameFrom = match.groups.from;
renameTo = match.groups.to;
}

View File

@ -118,7 +118,7 @@ export const isImageFresh = async (deviceType: string, version: string) => {
*/
export const isESR = (version: string) => {
const match = version.match(/^v?(\d+)\.\d+\.\d+/);
const major = parseInt((match && match[1]) || '', 10);
const major = parseInt(match?.[1] || '', 10);
return major >= 2018; // note: (NaN >= 2018) is false
};

View File

@ -39,7 +39,7 @@ export async function disambiguateReleaseParam(
// Accepting short hashes of 7,8,9 chars.
const possibleUuidHashLength = [7, 8, 9, 32, 40, 62].includes(release.length);
const hasLeadingZero = release[0] === '0';
const hasLeadingZero = release.startsWith('0');
const isOnlyNumerical = /^[0-9]+$/.test(release);
// Reject non-numerical values with invalid uuid/hash lengths
@ -78,14 +78,16 @@ export async function disambiguateReleaseParam(
/**
* Convert to lowercase if looks like slug
*/
export async function lowercaseIfSlug(s: string) {
return s.includes('/') ? s.toLowerCase() : s;
export function lowercaseIfSlug(s: string) {
return s.includes('/')
? Promise.resolve(s.toLowerCase())
: Promise.resolve(s);
}
export function normalizeOsVersion(version: string) {
// Note that `version` may also be 'latest', 'recommended', 'default'
if (/^v?\d+\.\d+\.\d+/.test(version)) {
if (version[0] === 'v') {
if (version.startsWith('v')) {
version = version.slice(1);
}
}

View File

@ -25,7 +25,7 @@ export function capitanoizeOclifUsage(
.toLowerCase();
}
export async function getCommandsFromManifest() {
export function getCommandsFromManifest() {
const manifest = require('../../oclif.manifest.json');
if (manifest.commands == null) {

View File

@ -119,7 +119,7 @@ export const checkLoggedInIf = async (doCheck: boolean) => {
*
* @throws {NotAvailableInOfflineModeError}
*/
export const checkNotUsingOfflineMode = async () => {
export const checkNotUsingOfflineMode = () => {
if (process.env.BALENARC_OFFLINE_MODE) {
throw new NotAvailableInOfflineModeError(stripIndent`
This command requires an internet connection, and cannot be used in offline mode.

View File

@ -390,13 +390,12 @@ async function createApplication(
try {
const userInfo = await sdk.auth.getUserInfo();
username = userInfo.username;
} catch (err) {
} catch {
throw new sdk.errors.BalenaNotLoggedIn();
}
// eslint-disable-next-line no-async-promise-executor
const applicationName = await new Promise<string>(async (resolve, reject) => {
// eslint-disable-next-line no-constant-condition
while (true) {
try {
const appName = await getCliForm().ask({
@ -418,11 +417,13 @@ async function createApplication(
'You already have a fleet with that name; please choose another.',
);
continue;
} catch (err) {
return resolve(appName);
} catch {
resolve(appName);
return;
}
} catch (err) {
return reject(err);
reject(err as Error);
return;
}
}
});
@ -452,9 +453,7 @@ async function generateApplicationConfig(
const manifest = await sdk.models.config.getDeviceTypeManifestBySlug(
app.is_for__device_type[0].slug,
);
const opts =
manifest.options &&
manifest.options.filter((opt) => opt.name !== 'network');
const opts = manifest.options?.filter((opt) => opt.name !== 'network');
const override = {
appUpdatePollInterval: options.appUpdatePollInterval,

View File

@ -114,7 +114,7 @@ async function installQemu(arch: string, qemuPath: string) {
stream.resume();
}
} catch (err) {
reject(err);
reject(err as Error);
}
});
request(qemuUrl)

View File

@ -246,7 +246,8 @@ function getBuilderMessageHandler(
console.error(`[debug] handling message: ${JSON.stringify(obj)}`);
}
if (obj.type != null && obj.type === 'metadata') {
return handleBuilderMetadata(obj, build);
handleBuilderMetadata(obj, build);
return;
}
if (obj.message) {
readline.clearLine(process.stdout, 0);
@ -423,10 +424,20 @@ async function getRemoteBuildStream(
stream = buildRequest.pipe(JSONStream.parse('*')) as NodeJS.ReadStream;
}
stream = stream
.once('error', () => uploadSpinner.stop())
.once('close', () => uploadSpinner.stop())
.once('data', () => uploadSpinner.stop())
.once('end', () => uploadSpinner.stop())
.once('finish', () => uploadSpinner.stop());
.once('error', () => {
uploadSpinner.stop();
})
.once('close', () => {
uploadSpinner.stop();
})
.once('data', () => {
uploadSpinner.stop();
})
.once('end', () => {
uploadSpinner.stop();
})
.once('finish', () => {
uploadSpinner.stop();
});
return [buildRequest, stream];
}

View File

@ -73,7 +73,7 @@ export function sshArgsForRemoteCommand({
...['-o', 'LogLevel=ERROR'],
...['-o', 'StrictHostKeyChecking=no'],
...['-o', 'UserKnownHostsFile=/dev/null'],
...(proxyCommand && proxyCommand.length
...(proxyCommand?.length
? ['-o', `ProxyCommand=${proxyCommand.join(' ')}`]
: []),
`${username}@${hostname}`,
@ -155,9 +155,9 @@ export async function runRemoteCommand({
[exitCode, exitSignal] = await new Promise((resolve, reject) => {
const ps = spawn(program, args, { stdio })
.on('error', reject)
.on('close', (code, signal) =>
resolve([code ?? undefined, signal ?? undefined]),
);
.on('close', (code, signal) => {
resolve([code ?? undefined, signal ?? undefined]);
});
if (ps.stdin && stdin && typeof stdin !== 'string') {
stdin.pipe(ps.stdin);
@ -272,7 +272,7 @@ export async function getLocalDeviceCmdStdout(
export const isRootUserGood = _.memoize(async (hostname: string, port) => {
try {
await runRemoteCommand({ cmd: 'exit 0', hostname, port, ...stdioIgnore });
} catch (e) {
} catch {
return false;
}
return true;

View File

@ -29,7 +29,11 @@ export function buffer(
new Promise(function (resolve, reject) {
const fstream = fs.createReadStream(bufferFile);
fstream.on('open', () => resolve(fstream)).on('error', reject);
fstream
.on('open', () => {
resolve(fstream);
})
.on('error', reject);
}),
);
}

View File

@ -105,7 +105,7 @@ async function spawnAndPipe(
});
}
async function windosuExec(
function windosuExec(
escapedArgs: string[],
stderr?: NodeJS.WritableStream,
): Promise<void> {

View File

@ -42,9 +42,9 @@ export = (stream: NodeJS.WriteStream = process.stdout) => {
const showCursor = () => stream.write('\u001B[?25h');
const cursorUp = (rows: number = 0) => stream.write(`\u001B[${rows}A`);
const cursorUp = (rows = 0) => stream.write(`\u001B[${rows}A`);
const cursorDown = (rows: number = 0) => stream.write(`\u001B[${rows}B`);
const cursorDown = (rows = 0) => stream.write(`\u001B[${rows}B`);
const write = (str: string) => stream.write(str);

View File

@ -61,11 +61,11 @@ export const tunnelConnectionToDevice = (
client.pipe(remote);
remote.pipe(client);
remote.on('error', (err) => {
console.error('Remote: ' + err);
console.error(`Remote: ${err}`);
client.end();
});
client.on('error', (err) => {
console.error('Client: ' + err);
console.error(`Client: ${err}`);
remote.end();
});
remote.on('close', () => {

View File

@ -97,24 +97,24 @@ export function parseAsInteger(input: string, paramName?: string) {
throw new ExpectedError(message);
}
return Number(input);
return Promise.resolve(Number(input));
}
export function tryAsInteger(input: string): number | string {
export async function tryAsInteger(input: string): Promise<number | string> {
try {
return parseAsInteger(input);
return await parseAsInteger(input);
} catch {
return input;
}
}
export async function parseAsLocalHostnameOrIp(input: string) {
export function parseAsLocalHostnameOrIp(input: string) {
if (input && !validateLocalHostnameOrIp(input)) {
throw new ExpectedError(
'The parameter must be a local hostname or IP address.',
);
}
return input;
return Promise.resolve(input);
}
export function looksLikeFleetSlug(input: string) {

View File

@ -31,7 +31,7 @@ chai.use(chaiAsPromised);
const { expect } = chai;
async function getPage(name: string): Promise<string> {
function getPage(name: string): string {
const pagePath = path.join(
__dirname,
'..',
@ -88,7 +88,7 @@ describe('Login server:', function () {
expect(body).to.equal(opt.expectedBody);
resolve();
} catch (err) {
reject(err);
reject(err as Error);
}
},
);
@ -143,7 +143,7 @@ describe('Login server:', function () {
it('should eventually be the token', async () => {
await testLogin({
expectedBody: await getPage('success'),
expectedBody: getPage('success'),
expectedStatusCode: 200,
expectedToken: tokens.johndoe.token,
});
@ -162,7 +162,7 @@ describe('Login server:', function () {
it('should be rejected', async () => {
await testLogin({
expectedBody: await getPage('error'),
expectedBody: getPage('error'),
expectedStatusCode: 401,
expectedToken: tokens.johndoe.token,
expectedErrorMsg: 'Invalid token',
@ -171,7 +171,7 @@ describe('Login server:', function () {
it('should be rejected if no token', async () => {
await testLogin({
expectedBody: await getPage('error'),
expectedBody: getPage('error'),
expectedStatusCode: 401,
expectedToken: '',
expectedErrorMsg: 'No token',
@ -180,7 +180,7 @@ describe('Login server:', function () {
it('should be rejected if token is malformed', async () => {
await testLogin({
expectedBody: await getPage('error'),
expectedBody: getPage('error'),
expectedStatusCode: 401,
expectedToken: 'asdf',
expectedErrorMsg: 'Invalid token',

View File

@ -267,15 +267,15 @@ describe('balena build', function () {
...fsMod,
promises: {
...fsMod.promises,
access: async (p: string) =>
access: (p: string) =>
p === qemuBinPath ? undefined : fsMod.promises.access(p),
stat: async (p: string) =>
stat: (p: string) =>
p === qemuBinPath ? { size: 1 } : fsMod.promises.stat(p),
},
});
mock(qemuModPath, {
...qemuMod,
copyQemu: async () => '',
copyQemu: () => '',
});
mock.reRequire('../../build/utils/qemu');
docker.expectGetInfo({ OperatingSystem: 'balenaOS 2.44.0+rev1' });

View File

@ -169,8 +169,12 @@ async function startMockSshServer(): Promise<[Server, number]> {
console.error(`[debug] mock ssh server: ${msg}`);
}
};
c.on('error', (err) => handler(err.message));
c.on('end', () => handler('client disconnected'));
c.on('error', (err) => {
handler(err.message);
});
c.on('end', () => {
handler('client disconnected');
});
c.end();
});
server.on('error', (err) => {

View File

@ -462,7 +462,13 @@ describe('balena push', function () {
tmp.setGracefulCleanup();
const projectPath = await new Promise<string>((resolve, reject) => {
const opts = { template: 'tmp-XXXXXX', unsafeCleanup: true };
tmp.dir(opts, (e, p) => (e ? reject(e) : resolve(p)));
tmp.dir(opts, (e, p) => {
if (e) {
reject(e);
} else {
resolve(p);
}
});
});
console.error(`[debug] Temp project dir: ${projectPath}`);
@ -475,7 +481,7 @@ describe('balena push', function () {
try {
server.listen(socketPath, resolve);
} catch (e) {
reject(e);
reject(e as Error);
}
});
console.error(`[debug] Checking existence of socket at '${socketPath}'`);
@ -505,7 +511,13 @@ describe('balena push', function () {
// Terminate Unix Domain Socket server
await new Promise<void>((resolve, reject) => {
server.close((e) => (e ? reject(e) : resolve()));
server.close((e) => {
if (e) {
reject(e);
} else {
resolve();
}
});
});
expect(await exists(socketPath), 'Socket existence').to.be.false;

View File

@ -26,7 +26,7 @@ describe('balena whoami', function () {
api = new BalenaAPIMock();
});
this.afterEach(async () => {
this.afterEach(() => {
// Check all expected api calls have been made and clean up.
api.done();
});

View File

@ -26,7 +26,6 @@ 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';
import type { BuilderMock } from './nock/builder-mock';
@ -77,13 +76,13 @@ export async function inspectTarStream(
type: header.type,
};
const expected = expectedFiles[header.name];
if (expected && expected.testStream) {
if (expected?.testStream) {
await expected.testStream(header, stream, expected);
} else {
await defaultTestStream(header, stream, expected, projectPath);
}
} catch (err) {
reject(err);
reject(err as Error);
}
next();
},
@ -144,9 +143,8 @@ export async function expectStreamNoCRLF(
_header: tar.Headers,
stream: Readable,
): Promise<void> {
const chai = await import('chai');
const buf = await streamToBuffer(stream);
await chai.expect(buf.includes('\r\n')).to.be.false;
expect(buf.includes('\r\n')).to.be.false;
}
/**
@ -184,12 +182,13 @@ export async function testDockerBuildStream(o: {
o.dockerMock.expectPostBuild({
...o,
checkURI: async (uri: string) => {
checkURI: (uri: string) => {
const url = new URL(uri, 'http://test.net/');
const queryParams = Array.from(url.searchParams.entries());
expect(deepJsonParse(queryParams)).to.have.deep.members(
deepJsonParse(expectedQueryParams),
);
return Promise.resolve();
},
checkBuildRequestBody: (buildRequestBody: string) =>
inspectTarStream(buildRequestBody, expectedFiles, projectPath),
@ -241,12 +240,13 @@ export async function testPushBuildStream(o: {
o.builderMock.expectPostBuild({
...o,
checkURI: async (uri: string) => {
checkURI: (uri: string) => {
const url = new URL(uri, 'http://test.net/');
const queryParams = Array.from(url.searchParams.entries());
expect(deepJsonParse(queryParams)).to.have.deep.members(
deepJsonParse(expectedQueryParams),
);
return Promise.resolve();
},
checkBuildRequestBody: (buildRequestBody) =>
inspectTarStream(buildRequestBody, o.expectedFiles, o.projectPath),

View File

@ -287,15 +287,14 @@ export function monochrome(text: string): string {
*/
export function fillTemplate(
templateString: string,
templateVars: object,
templateVars: Record<string, unknown>,
): string {
const escaped = templateString.replace(/\\/g, '\\\\').replace(/`/g, '\\`');
const resolved = new Function(
...Object.keys(templateVars),
`return \`${escaped}\`;`,
).call(null, ...Object.values(templateVars));
const unescaped = resolved.replace(/\\`/g, '`').replace(/\\\\/g, '\\');
return unescaped;
return templateString.replace(/\$\{(\w+)\}/g, (_, key) => {
if (key in templateVars) {
return String(templateVars[key]);
}
throw new Error(`Missing template variable: ${key}`);
});
}
/**

View File

@ -283,7 +283,7 @@ export class BalenaAPIMock extends NockMock {
this.optGet(/^\/v\d+\/service_environment_variable($|\?)/, opts).reply(
function (uri, _requestBody) {
const match = uri.match(/service_name%20eq%20%27(.+?)%27/);
const serviceName = (match && match[1]) || undefined;
const serviceName = match?.[1] || undefined;
let varArray: any[];
if (serviceName) {
const varObj = appServiceVarsByService[serviceName];
@ -331,7 +331,7 @@ export class BalenaAPIMock extends NockMock {
opts,
).reply(function (uri, _requestBody) {
const match = uri.match(/service_name%20eq%20%27(.+?)%27/);
const serviceName = (match && match[1]) || undefined;
const serviceName = match?.[1] || undefined;
let varArray: any[];
if (serviceName) {
const varObj = deviceServiceVarsByService[serviceName];

View File

@ -37,7 +37,7 @@ export class NockMock {
constructor(
public basePathPattern: string | RegExp,
public allowUnmocked: boolean = false,
public allowUnmocked = false,
) {
if (NockMock.instanceCount === 0) {
if (!nock.isActive()) {

View File

@ -50,7 +50,7 @@ export async function exists(fPath: string) {
try {
await fs.stat(fPath);
return true;
} catch (e) {
} catch {
return false;
}
}

View File

@ -1,5 +1,5 @@
import * as stream from 'stream';
import { AssertionError, expect } from 'chai';
import { expect } from 'chai';
import { stub } from 'sinon';
import * as tmp from 'tmp';
import { delay } from '../../utils';
@ -62,7 +62,7 @@ describe('image-manager', function () {
return stream.on('end', function () {
expect(result).to.equal('Cache image');
return done();
done();
});
});
});
@ -153,9 +153,7 @@ describe('image-manager', function () {
const contents = await fsAsync
.stat(this.image.name + '.inprogress')
.then(function () {
throw new AssertionError(
'Image cache should be deleted on failure',
);
throw new Error('Image cache should be deleted on failure');
})
.catch((err) => {
if (err.code !== 'ENOENT') {
@ -174,7 +172,7 @@ describe('image-manager', function () {
});
});
describe('given a stream with the mime property', async function () {
describe('given a stream with the mime property', function () {
beforeEach(function () {
this.osDownloadStub = stub(balena.models.os, 'download');
const message = 'Lorem ipsum dolor sit amet';
@ -422,7 +420,7 @@ describe('image-manager', function () {
.then(function (stream) {
let result = '';
stream.on('data', (chunk) => (result += chunk));
stream.on('data', (chunk: string) => (result += chunk));
stream.on('end', function () {
expect(result).to.equal('Lorem ipsum dolor sit amet');
@ -554,7 +552,9 @@ describe('image-manager', function () {
mockFs({});
});
afterEach(() => mockFs.restore());
afterEach(() => {
mockFs.restore();
});
it('should keep the cache directory removed', async function () {
const exists = await fsExistsAsync(this.cacheDirectory);

View File

@ -210,31 +210,31 @@ describe('parseAsInteger() function', () => {
);
});
it('should parse integers to number type', () => {
expect(v.parseAsInteger('100')).to.equal(100);
expect(v.parseAsInteger('100')).to.be.a('number');
expect(v.parseAsInteger('0')).to.equal(0);
expect(v.parseAsInteger('0')).to.be.a('number');
it('should parse integers to number type', async () => {
expect(await v.parseAsInteger('100')).to.equal(100);
expect(await v.parseAsInteger('100')).to.be.a('number');
expect(await v.parseAsInteger('0')).to.equal(0);
expect(await v.parseAsInteger('0')).to.be.a('number');
});
});
describe('tryAsInteger() function', () => {
it('should return string with non-numeric characters as string', () => {
expect(v.tryAsInteger('abc')).to.be.a('string');
expect(v.tryAsInteger('1a')).to.be.a('string');
expect(v.tryAsInteger('a1')).to.be.a('string');
expect(v.tryAsInteger('a')).to.be.a('string');
expect(v.tryAsInteger('1.0')).to.be.a('string');
it('should return string with non-numeric characters as string', async () => {
expect(await v.tryAsInteger('abc')).to.be.a('string');
expect(await v.tryAsInteger('1a')).to.be.a('string');
expect(await v.tryAsInteger('a1')).to.be.a('string');
expect(await v.tryAsInteger('a')).to.be.a('string');
expect(await v.tryAsInteger('1.0')).to.be.a('string');
});
it('should return numerical strings with leading zeros as string', () => {
expect(v.tryAsInteger('01')).to.be.a('string');
expect(v.tryAsInteger('001')).to.be.a('string');
it('should return numerical strings with leading zeros as string', async () => {
expect(await v.tryAsInteger('01')).to.be.a('string');
expect(await v.tryAsInteger('001')).to.be.a('string');
});
it('should return numerical strings without leading zeros as number', () => {
expect(v.tryAsInteger('100')).to.be.a('number');
expect(v.tryAsInteger('256')).to.be.a('number');
expect(v.tryAsInteger('0')).to.be.a('number');
it('should return numerical strings without leading zeros as number', async () => {
expect(await v.tryAsInteger('100')).to.be.a('number');
expect(await v.tryAsInteger('256')).to.be.a('number');
expect(await v.tryAsInteger('0')).to.be.a('number');
});
});

View File

@ -9,6 +9,7 @@
"./src/**/*",
"./patches/*",
"./tests/**/*",
"./typings/**/*"
"./typings/**/*",
".mocharc-standalone.js",
]
}

View File

@ -32,7 +32,7 @@ declare module 'JSONStream' {
recurse: boolean;
}
export function parse(pattern: any | any[]): NodeJS.ReadWriteStream;
export function parse(pattern: any): NodeJS.ReadWriteStream;
type NewlineOnlyIndicator = false;

View File

@ -16,7 +16,7 @@
*/
declare module 'balena-device-init' {
import { DeviceTypeJson } from 'balena-sdk';
import type { DeviceTypeJson } from 'balena-sdk';
interface OperationState {
operation:
@ -83,23 +83,23 @@ declare module 'balena-device-init' {
export function configure(
image: string,
manifest: BalenaSdk.DeviceTypeJson.DeviceType.DeviceType,
manifest: DeviceTypeJson.DeviceType,
config: object,
options?: object,
): Promise<InitializeEmitter>;
export function initialize(
image: string,
manifest: BalenaSdk.DeviceTypeJson.DeviceType.DeviceType,
manifest: DeviceTypeJson.DeviceType,
config: object,
): Promise<InitializeEmitter>;
export function getImageOsVersion(
image: string,
manifest: BalenaSdk.DeviceTypeJson.DeviceType.DeviceType,
manifest: DeviceTypeJson.DeviceType,
): Promise<string | null>;
export function getImageManifest(
image: string,
): Promise<BalenaSdk.DeviceTypeJson.DeviceType.DeviceType | null>;
): Promise<DeviceTypeJson.DeviceType | null>;
}