Merge pull request #2571 from balena-io/nodejs-14

Update to Node 14
This commit is contained in:
Felipe Lalanne 2023-01-10 08:41:54 -05:00 committed by GitHub
commit bc727521c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 94 additions and 93 deletions

View File

@ -15,8 +15,7 @@ inputs:
default: "accounts+apple@balena.io"
NODE_VERSION:
type: string
# FIXME: (please) https://github.com/balena-io/balena-cli/issues/2165
default: "12.x"
default: "14.x"
VERBOSE:
type: string
default: "true"

View File

@ -12,8 +12,7 @@ inputs:
# --- custom environment
NODE_VERSION:
type: string
# FIXME: (please) https://github.com/balena-io/balena-cli/issues/2165
default: "12.x"
default: "14.x"
VERBOSE:
type: string
default: "true"

View File

@ -78,8 +78,8 @@ If you are a Node.js developer, you may wish to install the balena CLI via [npm]
The npm installation involves building native (platform-specific) binary modules, which require
some development tools to be installed first, as follows.
> **The balena CLI currently requires Node.js version 12 (min 12.8.0).**
> **Versions 13 and later are not yet fully supported.**
> **The balena CLI currently requires Node.js version 14.**
> **Versions 15 and later are not yet fully supported.**
### Install development tools
@ -89,7 +89,7 @@ some development tools to be installed first, as follows.
$ sudo apt-get update && sudo apt-get -y install curl python3 git make g++
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
$ . ~/.bashrc
$ nvm install 12
$ nvm install 14
```
The `curl` command line above uses
@ -106,14 +106,14 @@ recommended.
```sh
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
$ . ~/.bashrc
$ nvm install 12
$ nvm install 14
```
#### **Windows** (not WSL)
Install:
* Node.js v12 from the [Nodejs.org releases page](https://nodejs.org/en/download/releases/).
* Node.js v14 from the [Nodejs.org releases page](https://nodejs.org/en/download/releases/).
* If you'd like the ability to switch between Node.js versions, install
[nvm-windows](https://github.com/coreybutler/nvm-windows#node-version-manager-nvm-for-windows)
instead.

View File

@ -209,9 +209,9 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
globalLogger.logDebug('Fetching device information...');
const deviceInfo = await api.getDeviceInformation();
let buildLogs: Dictionary<string> | undefined;
let imageIds: Dictionary<string[]> | undefined;
if (!opts.nolive) {
buildLogs = {};
imageIds = {};
}
const { awaitInterruptibleTask } = await import('../helpers');
@ -223,7 +223,7 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
deviceInfo,
globalLogger,
opts,
buildLogs,
imageIds,
);
globalLogger.outputDeferredMessages();
@ -265,7 +265,7 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
docker,
logger: globalLogger,
composition: project.composition,
buildLogs: buildLogs!,
imageIds: imageIds!,
deployOpts: opts,
});
promises.push(livepush.init());
@ -312,6 +312,14 @@ function connectToDocker(host: string, port: number): Docker {
});
}
function extractDockerArrowMessage(outputLine: string): string | undefined {
const arrowTest = /^.*\s*-+>\s*(.+)/i;
const match = arrowTest.exec(outputLine);
if (match != null) {
return match[1];
}
}
async function performBuilds(
composition: Composition,
tarStream: Readable,
@ -319,7 +327,7 @@ async function performBuilds(
deviceInfo: DeviceInfo,
logger: Logger,
opts: DeviceDeployOptions,
buildLogs?: Dictionary<string>,
imageIds?: Dictionary<string[]>,
): Promise<BuildTask[]> {
const multibuild = await import('@balena/compose/dist/multibuild');
@ -345,14 +353,29 @@ async function performBuilds(
// If we're passed a build logs object make sure to set it
// up properly
let logHandlers: ((serviceName: string, line: string) => void) | undefined;
if (buildLogs != null) {
const lastArrowMessage: Dictionary<string> = {};
if (imageIds != null) {
for (const task of buildTasks) {
if (!task.external) {
buildLogs[task.serviceName] = '';
imageIds[task.serviceName] = [];
}
}
logHandlers = (serviceName: string, line: string) => {
buildLogs[serviceName] += `${line}\n`;
// If this was a from line, take the last found
// image id and save it
if (
/step \d+(?:\/\d+)?\s*:\s*FROM/i.test(line) &&
lastArrowMessage[serviceName] != null
) {
imageIds[serviceName].push(lastArrowMessage[serviceName]);
} else {
const msg = extractDockerArrowMessage(line);
if (msg != null) {
lastArrowMessage[serviceName] = msg;
}
}
};
}
@ -413,12 +436,26 @@ export async function rebuildSingleTask(
// the logs, so any calller who wants to keep track of
// this should provide the following callback
containerIdCb?: (id: string) => void,
): Promise<string> {
): Promise<string[]> {
const multibuild = await import('@balena/compose/dist/multibuild');
// First we run the build task, to get the new image id
let buildLogs = '';
const stageIds = [] as string[];
let lastArrowMessage: string | undefined;
const logHandler = (_s: string, line: string) => {
buildLogs += `${line}\n`;
// If this was a FROM line, take the last found
// image id and save it as a stage id
if (
/step \d+(?:\/\d+)?\s*:\s*FROM/i.test(line) &&
lastArrowMessage != null
) {
stageIds.push(lastArrowMessage);
} else {
const msg = extractDockerArrowMessage(line);
if (msg != null) {
lastArrowMessage = msg;
}
}
if (containerIdCb != null) {
const match = line.match(/^\s*--->\s*Running\s*in\s*([a-f0-9]*)\s*$/i);
@ -477,7 +514,7 @@ export async function rebuildSingleTask(
]);
}
return buildLogs;
return stageIds;
}
function assignOutputHandlers(

View File

@ -52,7 +52,6 @@ interface MonitoredContainer {
containerId: string;
}
type BuildLogs = Dictionary<string>;
type StageImageIDs = Dictionary<string[]>;
export interface LivepushOpts {
@ -62,7 +61,7 @@ export interface LivepushOpts {
docker: Dockerode;
api: DeviceAPI;
logger: Logger;
buildLogs: BuildLogs;
imageIds: StageImageIDs;
deployOpts: DeviceDeployOptions;
}
@ -97,7 +96,7 @@ export class LivepushManager {
this.api = opts.api;
this.logger = opts.logger;
this.deployOpts = opts.deployOpts;
this.imageIds = LivepushManager.getMultistageImageIDs(opts.buildLogs);
this.imageIds = opts.imageIds;
}
public async init(): Promise<void> {
@ -297,33 +296,6 @@ export class LivepushManager {
return new Dockerfile(content).generateLiveDockerfile();
}
private static getMultistageImageIDs(buildLogs: BuildLogs): StageImageIDs {
const stageIds: StageImageIDs = {};
_.each(buildLogs, (log, serviceName) => {
stageIds[serviceName] = [];
const lines = log.split(/\r?\n/);
let lastArrowMessage: string | undefined;
for (const line of lines) {
// If this was a from line, take the last found
// image id and save it
if (
/step \d+(?:\/\d+)?\s*:\s*FROM/i.test(line) &&
lastArrowMessage != null
) {
stageIds[serviceName].push(lastArrowMessage);
} else {
const msg = LivepushManager.extractDockerArrowMessage(line);
if (msg != null) {
lastArrowMessage = msg;
}
}
}
});
return stageIds;
}
private async awaitDeviceStateSettle(): Promise<void> {
// Cache the state to avoid unnecessary calls
this.lastDeviceStatus = await this.api.getStatus();
@ -405,9 +377,9 @@ export class LivepushManager {
);
}
let buildLog: string;
let stageImages: string[];
try {
buildLog = await rebuildSingleTask(
stageImages = await rebuildSingleTask(
serviceName,
this.docker,
this.logger,
@ -466,17 +438,13 @@ export class LivepushManager {
);
}
const buildLogs: Dictionary<string> = {};
buildLogs[serviceName] = buildLog;
const stageImages = LivepushManager.getMultistageImageIDs(buildLogs);
const dockerfile = new Dockerfile(buildTask.dockerfile!);
instance.livepush = await Livepush.init({
dockerfile,
context: buildTask.context!,
containerId: container.containerId,
stageImages: stageImages[serviceName],
stageImages,
docker: this.docker,
});
this.assignLivepushOutputHandlers(serviceName, instance.livepush);
@ -536,16 +504,6 @@ export class LivepushManager {
});
}
private static extractDockerArrowMessage(
outputLine: string,
): string | undefined {
const arrowTest = /^.*\s*-+>\s*(.+)/i;
const match = arrowTest.exec(outputLine);
if (match != null) {
return match[1];
}
}
private getDockerfilePathFromTask(task: BuildTask): string[] {
switch (task.projectType) {
case 'Standard Dockerfile':

View File

@ -151,23 +151,23 @@ export async function runRemoteCommand({
let exitCode: number | undefined;
let exitSignal: NodeJS.Signals | undefined;
try {
[exitCode, exitSignal] = await new Promise<[number, NodeJS.Signals]>(
(resolve, reject) => {
const ps = spawn(program, args, { stdio })
.on('error', reject)
.on('close', (code, signal) => resolve([code, signal]));
[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]),
);
if (ps.stdin && stdin && typeof stdin !== 'string') {
stdin.pipe(ps.stdin);
}
if (ps.stdout && stdout && typeof stdout !== 'string') {
ps.stdout.pipe(stdout);
}
if (ps.stderr && stderr && typeof stderr !== 'string') {
ps.stderr.pipe(stderr);
}
},
);
if (ps.stdin && stdin && typeof stdin !== 'string') {
stdin.pipe(ps.stdin);
}
if (ps.stdout && stdout && typeof stdout !== 'string') {
ps.stdout.pipe(stdout);
}
if (ps.stderr && stderr && typeof stderr !== 'string') {
ps.stderr.pipe(stderr);
}
});
} catch (error) {
const msg = [
`ssh failed with exit code=${exitCode} signal=${exitSignal}:`,

14
npm-shrinkwrap.json generated
View File

@ -1421,6 +1421,14 @@
"tslint-no-unused-expression-chai": "^0.1.4",
"typescript": "^4.2.4",
"yargs": "^16.2.0"
},
"dependencies": {
"@types/node": {
"version": "12.20.55",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
"integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==",
"dev": true
}
}
},
"@balena/node-beaglebone-usbboot": {
@ -2888,9 +2896,9 @@
}
},
"@types/node": {
"version": "12.20.42",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.42.tgz",
"integrity": "sha512-aI3/oo5DzyiI5R/xAhxxRzfZlWlsbbqdgxfTPkqu/Zt+23GXiJvMCyPJT4+xKSXOnLqoL8jJYMLTwvK2M3a5hw=="
"version": "14.18.36",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.36.tgz",
"integrity": "sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ=="
},
"@types/node-cleanup": {
"version": "2.1.2",

View File

@ -89,7 +89,7 @@
"author": "Balena Inc. (https://balena.io/)",
"license": "Apache-2.0",
"engines": {
"node": ">=12 <16"
"node": ">=14 <16"
},
"husky": {
"hooks": {
@ -145,7 +145,7 @@
"@types/ndjson": "^2.0.1",
"@types/net-keepalive": "^0.4.1",
"@types/nock": "^11.1.0",
"@types/node": "^12.20.42",
"@types/node": "^14.18.36",
"@types/node-cleanup": "^2.1.2",
"@types/parse-link-header": "^1.0.1",
"@types/prettyjson": "^0.0.30",

View File

@ -178,7 +178,7 @@ async function startMockSshServer(): Promise<[Server, number]> {
});
return await new Promise<[Server, number]>((resolve, reject) => {
// TODO: remove 'as any' below. According to @types/node v12.20.42, the
// TODO: remove 'as any' below. According to @types/node v14.18.36, the
// callback type is `() => void`, but our code assumes `(err: Error) => void`
const listener = (server.listen as any)(0, '127.0.0.1', (err: Error) => {
// this callback is called for the 'listening' event

View File

@ -113,7 +113,7 @@ async function createProxyServer(): Promise<[number, number]> {
let proxyPort = 0; // TCP port number, 0 means automatic allocation
await new Promise<void>((resolve, reject) => {
// TODO: remove 'as any' below. According to @types/node v12.20.42, the
// TODO: remove 'as any' below. According to @types/node v14.18.36, the
// callback type is `() => void`, but our code assumes `(err: Error) => void`
const listener = (server.listen as any)(0, '127.0.0.1', (err: Error) => {
if (err) {
@ -197,7 +197,7 @@ async function createInterceptorServer(): Promise<number> {
let interceptorPort = 0;
await new Promise<void>((resolve, reject) => {
// TODO: remove 'as any' below. According to @types/node v12.20.42, the
// TODO: remove 'as any' below. According to @types/node v14.18.36, the
// callback type is `() => void`, but our code assumes `(err: Error) => void`
const listener = (server.listen as any)(0, '127.0.0.1', (err: Error) => {
if (err) {

View File

@ -45,7 +45,7 @@ class MockLivepushManager extends LivepushManager {
docker: {} as import('dockerode'),
api: {} as import('../../../lib/utils/device/api').DeviceAPI,
logger: {} as import('../../../lib/utils/logger'),
buildLogs: {},
imageIds: {},
deployOpts:
{} as import('../../../lib/utils/device/deploy').DeviceDeployOptions,
});