Update @balena/lint to v7

This updates balena lint to the latest version to enable eslint support
and unblock Typescript updates. This is a huge number of changes as the
linting rules are much more strict now, requiring modifiying most files
in the codebase. This commit also bumps the test dependency `rewire` as
that was interfering with the update of balena-lint

Change-type: patch
This commit is contained in:
Felipe Lalanne
2024-02-29 19:00:39 -03:00
parent 8750951521
commit 988a1c9e9a
136 changed files with 7256 additions and 2756 deletions

View File

@ -1,18 +1,18 @@
import * as _ from 'lodash';
import { promises as fs } from 'fs';
import { ImageInspectInfo } from 'dockerode';
import type { ImageInspectInfo } from 'dockerode';
import Network from './network';
import Volume from './volume';
import Service from './service';
import * as imageManager from './images';
import type { Image } from './images';
import {
import type {
CompositionStep,
generateStep,
CompositionStepAction,
} from './composition-steps';
import * as targetStateCache from '../device-state/target-state-cache';
import { generateStep } from './composition-steps';
import type * as targetStateCache from '../device-state/target-state-cache';
import { getNetworkGateway } from '../lib/docker-utils';
import * as constants from '../lib/constants';
import {
@ -22,7 +22,7 @@ import {
import { isNotFoundError } from '../lib/errors';
import * as config from '../config';
import { checkTruthy } from '../lib/validation';
import { ServiceComposeConfig, DeviceMetadata } from './types/service';
import type { ServiceComposeConfig, DeviceMetadata } from './types/service';
import { pathExistsOnRoot } from '../lib/host-utils';
import { isSupervisor } from '../lib/supervisor-metadata';
@ -64,7 +64,10 @@ export class App {
public networks: Network[];
public volumes: Volume[];
public constructor(opts: AppConstructOpts, public isTargetState: boolean) {
public constructor(
opts: AppConstructOpts,
public isTargetState: boolean,
) {
this.appId = opts.appId;
this.appUuid = opts.appUuid;
this.appName = opts.appName;
@ -513,12 +516,18 @@ export class App {
return;
}
const needsDownload = !context.availableImages.some(
(image) =>
image.dockerImageId === target?.config.image ||
imageManager.isSameImage(image, { name: target?.imageName! }),
);
if (needsDownload && context.downloading.includes(target?.imageName!)) {
const needsDownload =
target != null &&
!context.availableImages.some(
(image) =>
image.dockerImageId === target.config.image ||
imageManager.isSameImage(image, { name: target.imageName! }),
);
if (
target != null &&
needsDownload &&
context.downloading.includes(target.imageName!)
) {
// The image needs to be downloaded, and it's currently downloading.
// We simply keep the application loop alive
return generateStep('noop', {});

View File

@ -1,9 +1,10 @@
import * as _ from 'lodash';
import { EventEmitter } from 'events';
import StrictEventEmitter from 'strict-event-emitter-types';
import type StrictEventEmitter from 'strict-event-emitter-types';
import * as config from '../config';
import { transaction, Transaction } from '../db';
import type { Transaction } from '../db';
import { transaction } from '../db';
import * as logger from '../logger';
import LocalModeManager from '../local-mode';
@ -25,9 +26,9 @@ import * as networkManager from './network-manager';
import * as serviceManager from './service-manager';
import * as imageManager from './images';
import * as commitStore from './commit';
import Service from './service';
import Network from './network';
import Volume from './volume';
import type Service from './service';
import type Network from './network';
import type Volume from './volume';
import { generateStep, getExecutors } from './composition-steps';
import type {
@ -45,11 +46,11 @@ type ApplicationManagerEventEmitter = StrictEventEmitter<
{ change: DeviceLegacyReport }
>;
const events: ApplicationManagerEventEmitter = new EventEmitter();
export const on: typeof events['on'] = events.on.bind(events);
export const once: typeof events['once'] = events.once.bind(events);
export const removeListener: typeof events['removeListener'] =
export const on: (typeof events)['on'] = events.on.bind(events);
export const once: (typeof events)['once'] = events.once.bind(events);
export const removeListener: (typeof events)['removeListener'] =
events.removeListener.bind(events);
export const removeAllListeners: typeof events['removeAllListeners'] =
export const removeAllListeners: (typeof events)['removeAllListeners'] =
events.removeAllListeners.bind(events);
const localModeManager = new LocalModeManager();
@ -611,7 +612,7 @@ export async function serviceNameFromId(serviceId: number) {
(svcName) => services[svcName].id === serviceId,
);
if (!!serviceName) {
if (serviceName) {
return serviceName;
}
}
@ -771,7 +772,7 @@ function saveAndRemoveImages(
: availableAndUnused.filter((image) => !deltaSources.includes(image.name));
return imagesToSave
.map((image) => ({ action: 'saveImage', image } as CompositionStep))
.map((image) => ({ action: 'saveImage', image }) as CompositionStep)
.concat(imagesToRemove.map((image) => ({ action: 'removeImage', image })));
}
@ -948,7 +949,7 @@ export async function getState() {
// We cannot report services that do not have an image as the API
// requires passing the image name
.filter(([, img]) => !!img)
.map(([svc, img]) => ({ ...img, ...svc } as ServiceInfo))
.map(([svc, img]) => ({ ...img, ...svc }) as ServiceInfo)
.map((svc, __, serviceList) => {
// If the service is not running it cannot be a handover
if (svc.status !== 'Running') {

View File

@ -4,15 +4,15 @@ import * as config from '../config';
import type { Image } from './images';
import * as images from './images';
import Network from './network';
import Service from './service';
import type Network from './network';
import type Service from './service';
import * as serviceManager from './service-manager';
import Volume from './volume';
import type Volume from './volume';
import { checkTruthy } from '../lib/validation';
import * as networkManager from './network-manager';
import * as volumeManager from './volume-manager';
import { DeviceLegacyReport } from '../types/state';
import type { DeviceLegacyReport } from '../types/state';
import * as commitStore from './commit';
interface BaseCompositionStepArgs {
@ -81,7 +81,7 @@ interface CompositionStepArgs {
saveImage: {
image: Image;
};
cleanup: {};
cleanup: object;
createNetwork: {
target: Network;
};
@ -94,8 +94,8 @@ interface CompositionStepArgs {
removeVolume: {
current: Volume;
};
ensureSupervisorNetwork: {};
noop: {};
ensureSupervisorNetwork: object;
noop: object;
}
export type CompositionStepAction = keyof CompositionStepArgs;

View File

@ -10,7 +10,10 @@ export class InvalidNetworkNameError extends TypedError {
}
export class ResourceRecreationAttemptError extends TypedError {
public constructor(public resource: string, public name: string) {
public constructor(
public resource: string,
public name: string,
) {
super(
`Trying to create ${resource} with name: ${name}, but a ${resource} ` +
'with that name and a different configuration already exists',

View File

@ -1,12 +1,13 @@
import * as Docker from 'dockerode';
import type * as Docker from 'dockerode';
import { EventEmitter } from 'events';
import * as _ from 'lodash';
import StrictEventEmitter from 'strict-event-emitter-types';
import type StrictEventEmitter from 'strict-event-emitter-types';
import * as config from '../config';
import * as db from '../db';
import * as constants from '../lib/constants';
import { DeltaFetchOptions, FetchOptions, docker } from '../lib/docker-utils';
import type { DeltaFetchOptions, FetchOptions } from '../lib/docker-utils';
import { docker } from '../lib/docker-utils';
import * as dockerUtils from '../lib/docker-utils';
import {
DeltaStillProcessingError,
@ -67,11 +68,11 @@ class ImageEventEmitter extends (EventEmitter as new () => StrictEventEmitter<
>) {}
const events = new ImageEventEmitter();
export const on: typeof events['on'] = events.on.bind(events);
export const once: typeof events['once'] = events.once.bind(events);
export const removeListener: typeof events['removeListener'] =
export const on: (typeof events)['on'] = events.on.bind(events);
export const once: (typeof events)['once'] = events.once.bind(events);
export const removeListener: (typeof events)['removeListener'] =
events.removeListener.bind(events);
export const removeAllListeners: typeof events['removeAllListeners'] =
export const removeAllListeners: (typeof events)['removeAllListeners'] =
events.removeAllListeners.bind(events);
const imageFetchFailures: Dictionary<number> = {};
@ -144,14 +145,16 @@ function reportEvent(event: 'start' | 'update' | 'finish', state: Image) {
switch (event) {
case 'start':
return true; // always report change on start
case 'update':
case 'update': {
const [updatedTask, changedAfterUpdate] = currentTask.update(state);
runningTasks[imageName] = updatedTask;
return changedAfterUpdate; // report change only if the task context changed
case 'finish':
}
case 'finish': {
const [, changedAfterFinish] = currentTask.finish();
delete runningTasks[imageName];
return changedAfterFinish; // report change depending on the state of the task
}
}
})();
@ -551,14 +554,14 @@ const inspectByReference = async (imageName: string) => {
filters: { reference: [reference] },
})
.then(([img]) =>
!!img
img
? docker.getImage(img.Id).inspect()
: Promise.reject(
new StatusError(
404,
`Failed to find an image matching ${imageName}`,
),
),
),
);
};
@ -582,14 +585,14 @@ const inspectByDigest = async (imageName: string) => {
// Assume that all db entries will point to the same dockerImageId, so use
// the first one. If this assumption is false, there is a bug with cleanup
.then(([img]) =>
!!img
img
? docker.getImage(img.dockerImageId).inspect()
: Promise.reject(
new StatusError(
404,
`Failed to find an image matching ${imageName}`,
),
),
),
);
};

View File

@ -1,5 +1,5 @@
import * as _ from 'lodash';
import * as dockerode from 'dockerode';
import type * as dockerode from 'dockerode';
import { docker } from '../lib/docker-utils';
import logTypes = require('../lib/log-types');
@ -7,7 +7,7 @@ import * as logger from '../logger';
import log from '../lib/supervisor-console';
import * as ComposeUtils from './utils';
import {
import type {
ComposeNetworkConfig,
NetworkConfig,
NetworkInspectInfo,
@ -22,7 +22,9 @@ export class Network {
public name: string;
public config: NetworkConfig;
private constructor() {}
private constructor() {
/* do not allow instances using `new` */
}
private static deconstructDockerName(
name: string,

View File

@ -15,7 +15,7 @@ export interface PortBindings {
}
export interface DockerPortOptions {
exposedPorts: Dictionary<{}>;
exposedPorts: Dictionary<EmptyObject>;
portBindings: PortBindings;
}
@ -49,7 +49,7 @@ export class PortMap {
this.ports.externalEnd,
);
const exposedPorts: { [key: string]: {} } = {};
const exposedPorts: { [key: string]: EmptyObject } = {};
const portBindings: PortBindings = {};
_.zipWith(internalRange, externalRange, (internal, external) => {

View File

@ -1,6 +1,6 @@
import * as _ from 'lodash';
import { ConfigMap, ServiceComposeConfig } from './types/service';
import type { ConfigMap, ServiceComposeConfig } from './types/service';
import log from '../lib/supervisor-console';

View File

@ -1,10 +1,10 @@
import * as Dockerode from 'dockerode';
import type * as Dockerode from 'dockerode';
import { EventEmitter } from 'events';
import { isLeft } from 'fp-ts/lib/Either';
import * as JSONStream from 'JSONStream';
import * as _ from 'lodash';
import { promises as fs } from 'fs';
import StrictEventEmitter from 'strict-event-emitter-types';
import type StrictEventEmitter from 'strict-event-emitter-types';
import * as config from '../config';
import { docker } from '../lib/docker-utils';
@ -12,15 +12,16 @@ import * as logger from '../logger';
import { PermissiveNumber } from '../config/types';
import * as constants from '../lib/constants';
import type { StatusCodeError } from '../lib/errors';
import {
InternalInconsistencyError,
isNotFoundError,
StatusCodeError,
isStatusError,
} from '../lib/errors';
import * as LogTypes from '../lib/log-types';
import { checkInt, isValidDeviceName } from '../lib/validation';
import { Service, ServiceStatus } from './service';
import type { ServiceStatus } from './service';
import { Service } from './service';
import { serviceNetworksToDockerNetworks } from './utils';
import log from '../lib/supervisor-console';
@ -41,11 +42,11 @@ interface KillOpts {
wait?: boolean;
}
export const on: typeof events['on'] = events.on.bind(events);
export const once: typeof events['once'] = events.once.bind(events);
export const removeListener: typeof events['removeListener'] =
export const on: (typeof events)['on'] = events.on.bind(events);
export const once: (typeof events)['once'] = events.once.bind(events);
export const removeListener: (typeof events)['removeListener'] =
events.removeListener.bind(events);
export const removeAllListeners: typeof events['removeAllListeners'] =
export const removeAllListeners: (typeof events)['removeAllListeners'] =
events.removeAllListeners.bind(events);
// Whether a container has died, indexed by ID
@ -359,7 +360,7 @@ export async function start(service: Service) {
);
}
logger.attach(container.id, { serviceId, imageId });
void logger.attach(container.id, { serviceId, imageId });
if (!alreadyStarted) {
logger.logSystemEvent(LogTypes.startServiceSuccess, { service });
@ -421,7 +422,7 @@ export function listenToEvents() {
`serviceId and imageId not defined for service: ${service.serviceName} in ServiceManager.listenToEvents`,
);
}
logger.attach(data.id, {
void logger.attach(data.id, {
serviceId,
imageId,
});
@ -447,7 +448,7 @@ export function listenToEvents() {
});
};
(async () => {
void (async () => {
try {
await listen();
} catch (e) {
@ -479,7 +480,7 @@ export async function attachToRunning() {
`containerId not defined for service: ${service.serviceName} in ServiceManager.attachToRunning`,
);
}
logger.attach(service.containerId, {
void logger.attach(service.containerId, {
serviceId,
imageId,
});

View File

@ -1,10 +1,11 @@
import { detailedDiff as diff } from 'deep-object-diff';
import * as Dockerode from 'dockerode';
import type * as Dockerode from 'dockerode';
import Duration = require('duration-js');
import * as _ from 'lodash';
import * as path from 'path';
import { DockerPortOptions, PortMap } from './ports';
import type { DockerPortOptions } from './ports';
import { PortMap } from './ports';
import * as ComposeUtils from './utils';
import * as updateLock from '../lib/update-lock';
import { sanitiseComposeConfig } from './sanitise';
@ -14,14 +15,16 @@ import * as conversions from '../lib/conversions';
import { checkInt } from '../lib/validation';
import { InternalInconsistencyError } from '../lib/errors';
import { EnvVarObject } from '../types';
import {
import type { EnvVarObject } from '../types';
import type {
ServiceConfig,
ServiceConfigArrayField,
ServiceComposeConfig,
ConfigMap,
DeviceMetadata,
DockerDevice,
} from './types/service';
import {
ShortMount,
ShortBind,
ShortAnonymousVolume,
@ -108,7 +111,9 @@ export class Service {
'hostname',
].concat(Service.allConfigArrayFields);
private constructor() {}
private constructor() {
/* do not allow instancing a service object with `new` */
}
// The type here is actually ServiceComposeConfig, except that the
// keys must be camelCase'd first
@ -909,11 +914,11 @@ export class Service {
private getBindsMountsAndVolumes(): {
binds: string[];
mounts: Dockerode.MountSettings[];
volumes: { [volName: string]: {} };
volumes: { [volName: string]: EmptyObject };
} {
const binds: string[] = [];
const mounts: Dockerode.MountSettings[] = [];
const volumes: { [volName: string]: {} } = {};
const volumes: { [volName: string]: EmptyObject } = {};
for (const volume of this.config.volumes) {
if (LongDefinition.is(volume)) {
@ -921,7 +926,11 @@ export class Service {
mounts.push(ComposeUtils.serviceMountToDockerMount(volume));
} else {
// Volumes with the string short syntax are acceptable as Docker configs as-is
ShortMount.is(volume) ? binds.push(volume) : (volumes[volume] = {});
if (ShortMount.is(volume)) {
binds.push(volume);
} else {
volumes[volume] = {};
}
}
}

View File

@ -1,4 +1,4 @@
import { NetworkInspectInfo as DockerNetworkInspectInfo } from 'dockerode';
import type { NetworkInspectInfo as DockerNetworkInspectInfo } from 'dockerode';
// TODO: ConfigOnly is part of @types/dockerode@v3.2.0, but that version isn't
// compatible with `resin-docker-build` which is used for `npm run sync`.

View File

@ -1,8 +1,8 @@
import * as Dockerode from 'dockerode';
import type * as Dockerode from 'dockerode';
import * as t from 'io-ts';
import { isAbsolute } from 'path';
import { PortMap } from '../ports';
import type { PortMap } from '../ports';
export interface ComposeHealthcheck {
test: string | string[];

View File

@ -1,6 +1,7 @@
import * as imageManager from './images';
import Service from './service';
import { CompositionStep, generateStep } from './composition-steps';
import type Service from './service';
import type { CompositionStep } from './composition-steps';
import { generateStep } from './composition-steps';
import { InternalInconsistencyError } from '../lib/errors';
import { checkString } from '../lib/validation';

View File

@ -1,12 +1,12 @@
import * as Dockerode from 'dockerode';
import type * as Dockerode from 'dockerode';
import Duration = require('duration-js');
import * as _ from 'lodash';
import { parse as parseCommand } from 'shell-quote';
import * as constants from '../lib/constants';
import { checkTruthy } from '../lib/validation';
import { Service } from './service';
import {
import type { Service } from './service';
import type {
ComposeHealthcheck,
ConfigMap,
DeviceMetadata,
@ -209,7 +209,7 @@ function getNanoseconds(timeStr: string): number {
export function composeHealthcheckToServiceHealthcheck(
healthcheck: ComposeHealthcheck | null | undefined,
): ServiceHealthcheck | {} {
): ServiceHealthcheck | EmptyObject {
if (healthcheck == null) {
return {};
}
@ -351,9 +351,8 @@ export async function addFeaturesFromLabels(
} as LongBind);
if (service.config.environment['DOCKER_HOST'] == null) {
service.config.environment[
'DOCKER_HOST'
] = `unix://${constants.containerDockerSocket}`;
service.config.environment['DOCKER_HOST'] =
`unix://${constants.containerDockerSocket}`;
}
// We keep balena.sock for backwards compatibility
if (constants.dockerSocket !== '/var/run/balena.sock') {

View File

@ -1,6 +1,6 @@
import * as _ from 'lodash';
import * as path from 'path';
import { VolumeInspectInfo } from 'dockerode';
import type { VolumeInspectInfo } from 'dockerode';
import { isNotFoundError, InternalInconsistencyError } from '../lib/errors';
import { safeRename } from '../lib/fs-utils';
@ -10,7 +10,8 @@ import * as LogTypes from '../lib/log-types';
import log from '../lib/supervisor-console';
import * as logger from '../logger';
import { ResourceRecreationAttemptError } from './errors';
import Volume, { VolumeConfig } from './volume';
import type { VolumeConfig } from './volume';
import Volume from './volume';
export interface VolumeNameOpts {
name: string;

View File

@ -1,4 +1,4 @@
import * as Docker from 'dockerode';
import type * as Docker from 'dockerode';
import isEqual = require('lodash/isEqual');
import omitBy = require('lodash/omitBy');
@ -6,7 +6,7 @@ import * as constants from '../lib/constants';
import { docker } from '../lib/docker-utils';
import { InternalInconsistencyError } from '../lib/errors';
import * as LogTypes from '../lib/log-types';
import { LabelObject } from '../types';
import type { LabelObject } from '../types';
import * as logger from '../logger';
import * as ComposeUtils from './utils';