Bump resin-multibuild (2.1.4), docker-progress (3.0.5), resin-lint (3.0.1)

The new resin-multibuild and docker-progress versions widen the range
of errors caught by the 'balena push' and 'balena build' commands.

Change-type: patch
Signed-off-by: Paulo Castro <paulo@balena.io>
This commit is contained in:
Paulo Castro 2019-03-12 22:07:57 +00:00
parent da86d3303f
commit a883948d56
36 changed files with 508 additions and 175 deletions

View File

@ -1,6 +1,22 @@
import * as path from 'path';
import * as fs from 'fs-extra';
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as filehound from 'filehound';
import * as fs from 'fs-extra';
import * as path from 'path';
import { exec as execPkg } from 'pkg';
const ROOT = path.join(__dirname, '..');

View File

@ -1,27 +1,46 @@
import capitanodoc = require('../../capitanodoc');
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as _ from 'lodash';
import * as path from 'path';
import capitanodoc = require('../../capitanodoc');
import { Category, Document } from './doc-types';
import * as markdown from './markdown';
import { Document, Category } from './doc-types';
const result = <Document>{};
result.title = capitanodoc.title;
result.introduction = capitanodoc.introduction;
result.categories = [];
const result: Document = {
title: capitanodoc.title,
introduction: capitanodoc.introduction,
categories: [],
};
for (let commandCategory of capitanodoc.categories) {
const category = <Category>{};
category.title = commandCategory.title;
category.commands = [];
for (const commandCategory of capitanodoc.categories) {
const category: Category = {
title: commandCategory.title,
commands: [],
};
for (let file of commandCategory.files) {
for (const file of commandCategory.files) {
// tslint:disable-next-line:no-var-requires
const actions: any = require(path.join(process.cwd(), file));
if (actions.signature) {
category.commands.push(_.omit(actions, 'action'));
} else {
for (let actionName of Object.keys(actions)) {
for (const actionName of Object.keys(actions)) {
const actionCommand = actions[actionName];
category.commands.push(_.omit(actionCommand, 'action'));
}

View File

@ -1,7 +1,24 @@
import * as _ from 'lodash';
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as ent from 'ent';
import * as _ from 'lodash';
import { Category, Command, Document } from './doc-types';
import * as utils from './utils';
import { Document, Category, Command } from './doc-types';
export function renderCommand(command: Command) {
let result = `## ${ent.encode(command.signature)}\n\n${command.help}\n`;
@ -9,7 +26,7 @@ export function renderCommand(command: Command) {
if (!_.isEmpty(command.options)) {
result += '\n### Options';
for (let option of command.options!) {
for (const option of command.options!) {
result += `\n\n#### ${utils.parseSignature(option)}\n\n${
option.description
}`;
@ -24,7 +41,7 @@ export function renderCommand(command: Command) {
export function renderCategory(category: Category) {
let result = `# ${category.title}\n`;
for (let command of category.commands) {
for (const command of category.commands) {
result += `\n${renderCommand(command)}`;
}
@ -51,10 +68,10 @@ function getAnchor(command: Command) {
export function renderToc(categories: Category[]) {
let result = `# Table of contents\n`;
for (let category of categories) {
for (const category of categories) {
result += `\n- ${category.title}\n\n`;
for (let command of category.commands) {
for (const command of category.commands) {
result += `\t- [${ent.encode(command.signature)}](${getAnchor(
command,
)})\n`;
@ -69,7 +86,7 @@ export function render(doc: Document) {
doc.categories,
)}`;
for (let category of doc.categories) {
for (const category of doc.categories) {
result += `\n${renderCategory(category)}`;
}

View File

@ -1,6 +1,22 @@
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { OptionDefinition } from 'capitano';
import * as _ from 'lodash';
import * as ent from 'ent';
import * as _ from 'lodash';
export function getOptionPrefix(signature: string) {
if (signature.length > 1) {
@ -18,7 +34,7 @@ export function parseSignature(option: OptionDefinition) {
let result = getOptionSignature(option.signature);
if (_.isArray(option.alias)) {
for (let alias of option.alias) {
for (const alias of option.alias) {
result += `, ${getOptionSignature(alias)}`;
}
} else if (_.isString(option.alias)) {

View File

@ -1,10 +1,27 @@
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as archiver from 'archiver';
import * as Promise from 'bluebird';
import * as path from 'path';
import * as os from 'os';
import * as fs from 'fs-extra';
import * as mkdirp from 'mkdirp';
import * as os from 'os';
import * as path from 'path';
import * as publishRelease from 'publish-release';
import * as archiver from 'archiver';
import * as packageJSON from '../package.json';
const publishReleaseAsync = Promise.promisify(publishRelease);
@ -26,12 +43,12 @@ mkdirpAsync(path.dirname(outputFile))
new Promise((resolve, reject) => {
console.log('Zipping build...');
let archive = archiver('zip', {
const archive = archiver('zip', {
zlib: { level: 7 },
});
archive.directory(path.join(ROOT, 'build-bin'), 'balena-cli');
let outputStream = fs.createWriteStream(outputFile);
const outputStream = fs.createWriteStream(outputFile);
outputStream.on('close', resolve);
outputStream.on('error', reject);
@ -48,7 +65,7 @@ mkdirpAsync(path.dirname(outputFile))
console.log('Publishing build...');
return publishReleaseAsync({
token: <string>GITHUB_TOKEN,
token: GITHUB_TOKEN || '',
owner: 'balena-io',
repo: 'balena-cli',
tag: version,

View File

@ -14,13 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { ApplicationVariable, DeviceVariable } from 'balena-sdk';
import { CommandDefinition } from 'capitano';
import * as commandOptions from './command-options';
import { normalizeUuidProp } from '../utils/normalization';
import { DeviceVariable, ApplicationVariable } from 'balena-sdk';
import { stripIndent } from 'common-tags';
import { normalizeUuidProp } from '../utils/normalization';
import * as commandOptions from './command-options';
const getReservedPrefixes = async (): Promise<string[]> => {
const balena = (await import('balena-sdk')).fromSharedOptions();
const settings = await balena.settings.getAll();

View File

@ -17,17 +17,17 @@ limitations under the License.
import { CommandDefinition } from 'capitano';
import chalk from 'chalk';
import { stripIndent } from 'common-tags';
import * as sdk from 'etcher-sdk';
import * as SDK from 'etcher-sdk';
async function getDrive(options: {
drive?: string;
}): Promise<sdk.sourceDestination.BlockDevice> {
}): Promise<SDK.sourceDestination.BlockDevice> {
const sdk = await import('etcher-sdk');
const adapter = new sdk.scanner.adapters.BlockDeviceAdapter(() => false);
const scanner = new sdk.scanner.Scanner([adapter]);
await scanner.start();
let drive: sdk.sourceDestination.BlockDevice;
let drive: SDK.sourceDestination.BlockDevice;
if (options.drive !== undefined) {
const d = scanner.getBy('device', options.drive);
if (d === undefined || !(d instanceof sdk.sourceDestination.BlockDevice)) {
@ -49,7 +49,6 @@ export const flash: CommandDefinition<
> = {
signature: 'local flash <image>',
description: 'Flash an image to a drive',
//root: true,
help: stripIndent`
Use this command to flash a balenaOS image to a drive.
@ -111,7 +110,7 @@ export const flash: CommandDefinition<
// onFail
console.log(chalk.red.bold(error.message));
},
(progress: sdk.multiWrite.MultiDestinationProgress) => {
(progress: SDK.multiWrite.MultiDestinationProgress) => {
// onProgress
progressBars[progress.type].update(progress);
},

View File

@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { BalenaSDK } from 'balena-sdk';
import { CommandDefinition } from 'capitano';
import { stripIndent } from 'common-tags';
import { BalenaSDK } from 'balena-sdk';
import { registrySecretsHelp } from '../utils/messages';

View File

@ -14,11 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import * as Bluebird from 'bluebird';
import * as _ from 'lodash';
import { CommandDefinition } from 'capitano';
import { stripIndent } from 'common-tags';
import * as _ from 'lodash';
import { createServer, Server, Socket } from 'net';
import { isArray } from 'util';
import { Socket, Server, createServer } from 'net';
import { tunnelConnectionToDevice } from '../utils/tunnel';
interface Args {
@ -30,7 +31,7 @@ interface Options {
}
class DeviceIsOfflineError extends Error {
uuid: string;
public uuid: string;
constructor(uuid: string) {
super(`Device '${uuid}' is offline`);
this.uuid = uuid;
@ -68,7 +69,7 @@ export const tunnel: CommandDefinition<Args, Options> = {
# map remote port 22222 to localhost:22222
$ balena tunnel abcde12345 -p 22222
# map remote port 22222 to localhost:222
$ balena tunnel abcde12345 -p 22222:222
@ -146,29 +147,30 @@ export const tunnel: CommandDefinition<Args, Options> = {
}
// grab the groups
// tslint:disable-next-line:prefer-const
let [, remotePort, localAddress, localPort] = regexResult;
if (
!isValidPort(parseInt(localPort)) ||
!isValidPort(parseInt(remotePort))
!isValidPort(parseInt(localPort, undefined)) ||
!isValidPort(parseInt(remotePort, undefined))
) {
throw new InvalidPortMappingError(mapping);
}
// default bind to localAddress
if (localAddress == undefined) {
if (localAddress == null) {
localAddress = 'localhost';
}
// default use same port number locally as remote
if (localPort == undefined) {
if (localPort == null) {
localPort = remotePort;
}
return {
localPort: parseInt(localPort),
localPort: parseInt(localPort, undefined),
localAddress,
remotePort: parseInt(remotePort),
remotePort: parseInt(remotePort, undefined),
};
})
.map(({ localPort, localAddress, remotePort }) => {

View File

@ -14,11 +14,11 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import * as Promise from 'bluebird';
import { stripIndent } from 'common-tags';
import * as _ from 'lodash';
import * as os from 'os';
import * as Raven from 'raven';
import * as Promise from 'bluebird';
import { stripIndent } from 'common-tags';
import * as patterns from './utils/patterns';

View File

@ -1,10 +1,26 @@
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import BalenaSdk = require('balena-sdk');
import Promise = require('bluebird');
import * as Capitano from 'capitano';
import _ = require('lodash');
import Mixpanel = require('mixpanel');
import Raven = require('raven');
import Promise = require('bluebird');
import BalenaSdk = require('balena-sdk');
import packageJSON = require('../package.json');
const getBalenaSdk = _.once(() => BalenaSdk.fromSharedOptions());

View File

@ -333,6 +333,7 @@ tagServiceImages = (docker, images, serviceImages) ->
Promise.map images, (d) ->
serviceImage = serviceImages[d.serviceName]
imageName = serviceImage.is_stored_at__image_location
# coffeelint: disable-next-line=check_scope ("Variable is assigned to but never read")
[ _match, registry, repo, tag = 'latest' ] = /(.*?)\/(.*?)(?::([^/]*))?$/.exec(imageName)
name = "#{registry}/#{repo}"
docker.getImage(d.name).tag({ repo: name, tag, force: true })

View File

@ -16,9 +16,10 @@
*/
import * as Bluebird from 'bluebird';
import * as Stream from 'stream';
import { Composition } from 'resin-compose-parse';
import * as Stream from 'stream';
import { Pack } from 'tar-stream';
import Logger = require('./logger');
interface Image {

View File

@ -14,13 +14,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as _ from 'lodash';
import * as Bluebird from 'bluebird';
import * as tar from 'tar-stream';
import { Readable } from 'stream';
import * as MultiBuild from 'resin-multibuild';
import * as _ from 'lodash';
import { Composition } from 'resin-compose-parse';
import * as MultiBuild from 'resin-multibuild';
import { Readable } from 'stream';
import * as tar from 'tar-stream';
import { DeviceInfo } from './device/api';
import Logger = require('./logger');

View File

@ -13,13 +13,13 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Promise = require('bluebird');
import BalenaSdk = require('balena-sdk');
import Promise = require('bluebird');
import * as semver from 'resin-semver';
const balena = BalenaSdk.fromSharedOptions();
type ImgConfig = {
interface ImgConfig {
applicationName: string;
applicationId: number;
deviceType: string;
@ -46,7 +46,7 @@ type ImgConfig = {
deviceId?: number;
uuid?: string;
registered_at?: number;
};
}
export function generateBaseConfig(
application: BalenaSdk.Application,

View File

@ -1,9 +1,24 @@
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as Bluebird from 'bluebird';
import * as request from 'request';
import * as Stream from 'stream';
import Logger = require('../logger');
import * as ApiErrors from './errors';
export interface DeviceResponse {
@ -145,10 +160,11 @@ export class DeviceAPI {
opts: T,
logger?: Logger,
): Promise<any> {
const Bluebird = await import('bluebird');
const _ = await import('lodash');
type ObjectWithUrl = { url?: string };
interface ObjectWithUrl {
url?: string;
}
if (logger != null) {
let url: string | null = null;

View File

@ -28,15 +28,15 @@ import {
import * as semver from 'resin-semver';
import { Readable } from 'stream';
import Logger = require('../logger');
import { displayBuildLog } from './logs';
import { makeBuildTasks } from '../compose_ts';
import Logger = require('../logger');
import { DeviceInfo } from './api';
import * as LocalPushErrors from './errors';
import { displayBuildLog } from './logs';
// Define the logger here so the debug output
// can be used everywhere
const logger = new Logger();
const globalLogger = new Logger();
export interface DeviceDeployOptions {
source: string;
@ -61,7 +61,7 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
exitWithExpectedError(`Could not access source directory: ${opts.source}`);
}
const api = new DeviceAPI(logger, opts.deviceHost);
const api = new DeviceAPI(globalLogger, opts.deviceHost);
// First check that we can access the device with a ping
try {
@ -88,9 +88,9 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
exitWithExpectedError(versionError);
}
logger.logInfo(`Starting build on device ${opts.deviceHost}`);
globalLogger.logInfo(`Starting build on device ${opts.deviceHost}`);
const project = await loadProject(logger, opts.source, 'local');
const project = await loadProject(globalLogger, opts.source, 'local');
// Attempt to attach to the device's docker daemon
const docker = connectToDocker(
@ -108,11 +108,11 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
tarStream,
docker,
deviceInfo,
logger,
globalLogger,
opts,
);
logger.logDebug('Setting device state...');
globalLogger.logDebug('Setting device state...');
// Now set the target state on the device
const currentTargetState = await api.getTargetState();
@ -121,18 +121,18 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
currentTargetState,
project.composition,
);
logger.logDebug(`Sending target state: ${JSON.stringify(targetState)}`);
globalLogger.logDebug(`Sending target state: ${JSON.stringify(targetState)}`);
await api.setTargetState(targetState);
// Print an empty newline to seperate the build output
// Print an empty newline to separate the build output
// from the device output
console.log();
logger.logInfo('Streaming device logs...');
globalLogger.logInfo('Streaming device logs...');
// Now all we need to do is stream back the logs
const logStream = await api.getLogStream();
await displayDeviceLogs(logStream, logger);
await displayDeviceLogs(logStream, globalLogger);
}
function connectToDocker(host: string, port: number): Docker {
@ -226,7 +226,7 @@ async function assignDockerBuildOpts(
// that we can use all of them for cache
const images = await getDeviceDockerImages(docker);
logger.logDebug(`Using ${images.length} on-device images for cache...`);
globalLogger.logDebug(`Using ${images.length} on-device images for cache...`);
await Bluebird.map(buildTasks, async (task: BuildTask) => {
task.dockerOpts = {

View File

@ -14,13 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import os = require('os');
import Bluebird = require('bluebird');
import _ = require('lodash');
import chalk from 'chalk';
import rindle = require('rindle');
import visuals = require('resin-cli-visuals');
import BalenaSdk = require('balena-sdk');
import Bluebird = require('bluebird');
import chalk from 'chalk';
import _ = require('lodash');
import os = require('os');
import visuals = require('resin-cli-visuals');
import rindle = require('rindle');
import { InitializeEmitter, OperationState } from 'balena-device-init';
@ -29,7 +29,7 @@ const waitStreamAsync = Bluebird.promisify(rindle.wait);
const balena = BalenaSdk.fromSharedOptions();
export function getGroupDefaults(group: {
options: { name: string; default?: string }[];
options: Array<{ name: string; default?: string }>;
}): { [name: string]: string | undefined } {
return _.chain(group)
.get('options')

View File

@ -120,7 +120,7 @@ export class FileIgnorer {
});
return !_.some(ignoreTypes, ({ handle }) => handle.ignores(relFile));
};
}; // tslint:disable-line:semicolon
private addEntry(
pattern: string,

View File

@ -1,3 +1,19 @@
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import chalk from 'chalk';
import _ = require('lodash');
import { EOL as eol } from 'os';
@ -45,31 +61,31 @@ class Logger {
this.formatMessage = logger.formatWithPrefix.bind(logger);
}
logInfo(msg: string) {
public logInfo(msg: string) {
return this.streams.info.write(msg + eol);
}
logDebug(msg: string) {
public logDebug(msg: string) {
return this.streams.debug.write(msg + eol);
}
logSuccess(msg: string) {
public logSuccess(msg: string) {
return this.streams.success.write(msg + eol);
}
logWarn(msg: string) {
public logWarn(msg: string) {
return this.streams.warn.write(msg + eol);
}
logError(msg: string) {
public logError(msg: string) {
return this.streams.error.write(msg + eol);
}
logBuild(msg: string) {
public logBuild(msg: string) {
return this.streams.build.write(msg + eol);
}
logLogs(msg: string) {
public logLogs(msg: string) {
return this.streams.logs.write(msg + eol);
}
}

View File

@ -13,15 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import BalenaSdk = require('balena-sdk');
import Promise = require('bluebird');
import chalk from 'chalk';
import _ = require('lodash');
import _form = require('resin-cli-form');
import _visuals = require('resin-cli-visuals');
import _ = require('lodash');
import Promise = require('bluebird');
import BalenaSdk = require('balena-sdk');
import chalk from 'chalk';
import validation = require('./validation');
import messages = require('./messages');
import validation = require('./validation');
const getBalenaSdk = _.once(() => BalenaSdk.fromSharedOptions());
@ -173,7 +173,9 @@ export function selectOrCreateApplication() {
return balena.models.application
.hasAny()
.then(hasAnyApplications => {
if (!hasAnyApplications) return;
if (!hasAnyApplications) {
return;
}
return balena.models.application.getAll().then(applications => {
const appOptions = _.map<

View File

@ -1,9 +1,24 @@
import { stripIndent } from 'common-tags';
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as BalenaSdk from 'balena-sdk';
import Logger = require('./logger');
import { stripIndent } from 'common-tags';
import { runCommand } from './helpers';
import Logger = require('./logger');
import { exec, execBuffered } from './ssh';
const MIN_BALENAOS_VERSION = 'v2.14.0';
@ -34,7 +49,7 @@ export async function join(
logger.logDebug('Determining application...');
const app = await getOrSelectApplication(sdk, deviceType, appName);
logger.logDebug(`Using application: ${app.app_name} (${app.device_type})`);
if (app.device_type != deviceType) {
if (app.device_type !== deviceType) {
logger.logDebug(`Forcing device type to: ${deviceType}`);
app.device_type = deviceType;
}
@ -206,28 +221,12 @@ async function getOrSelectApplication(
.value();
if (!appName) {
const options = {
$filter: { device_type: { $in: compatibleDeviceTypes } },
};
// No application specified, show a list to select one.
const applications = await sdk.models.application.getAll(options);
if (applications.length === 0) {
const shouldCreateApp = await form.ask({
message:
'You have no applications this device can join.\n' +
'Would you like to create one now?',
type: 'confirm',
default: true,
});
if (shouldCreateApp) {
return createApplication(sdk, deviceType);
}
process.exit(1);
}
return selectAppFromList(applications);
return createOrSelectAppOrExit(
form,
sdk,
compatibleDeviceTypes,
deviceType,
);
}
const options: BalenaSdk.PineOptionsFor<BalenaSdk.Application> = {};
@ -279,6 +278,39 @@ async function getOrSelectApplication(
return selectAppFromList(applications);
}
// TODO: revisit this function's purpose. It was refactored out of
// `getOrSelectApplication` above in order to satisfy some resin-lint v3
// rules, but it looks like there's a fair amount of duplicate logic.
async function createOrSelectAppOrExit(
form: any,
sdk: BalenaSdk.BalenaSDK,
compatibleDeviceTypes: string[],
deviceType: string,
) {
const options = {
$filter: { device_type: { $in: compatibleDeviceTypes } },
};
// No application specified, show a list to select one.
const applications = await sdk.models.application.getAll(options);
if (applications.length === 0) {
const shouldCreateApp = await form.ask({
message:
'You have no applications this device can join.\n' +
'Would you like to create one now?',
type: 'confirm',
default: true,
});
if (shouldCreateApp) {
return createApplication(sdk, deviceType);
}
process.exit(1);
}
return selectAppFromList(applications);
}
async function createApplication(
sdk: BalenaSdk.BalenaSDK,
deviceType: string,
@ -294,7 +326,7 @@ async function createApplication(
}
username = username.toLowerCase();
const appName = await new Promise<string>(async (resolve, reject) => {
const applicationName = await new Promise<string>(async (resolve, reject) => {
while (true) {
try {
const appName = await form.ask({
@ -327,7 +359,7 @@ async function createApplication(
});
return sdk.models.application.create({
name: appName,
name: applicationName,
deviceType,
});
}

View File

@ -13,14 +13,14 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { BalenaSDK } from 'balena-sdk';
import * as Bluebird from 'bluebird';
import * as JSONStream from 'JSONStream';
import * as readline from 'readline';
import * as request from 'request';
import * as Stream from 'stream';
import { BalenaSDK } from 'balena-sdk';
import { Pack } from 'tar-stream';
import { RegistrySecrets } from 'resin-multibuild';
import * as Stream from 'stream';
import { Pack } from 'tar-stream';
import { TypedError } from 'typed-error';
import { exitWithExpectedError } from '../utils/patterns';
@ -278,7 +278,7 @@ function createRemoteBuildRequest(
);
}
} else {
let msgArr = [
const msgArr = [
'Remote builder responded with HTTP error:',
`${response.statusCode} ${response.statusMessage}`,
];

View File

@ -1,5 +1,21 @@
import { spawn } from 'child_process';
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as Bluebird from 'bluebird';
import { spawn } from 'child_process';
import { TypedError } from 'typed-error';
import { getSubShellCommand } from './helpers';
@ -41,7 +57,7 @@ export async function exec(
ps.stdout.pipe(stdout);
}
});
if (exitCode != 0) {
if (exitCode !== 0) {
throw new ExecError(cmd, exitCode);
}
}

View File

@ -1,3 +1,19 @@
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Promise = require('bluebird');
import fs = require('fs');
@ -15,9 +31,9 @@ export function buffer(
}).then(
() =>
new Promise(function(resolve, reject) {
const stream = fs.createReadStream(bufferFile);
const fstream = fs.createReadStream(bufferFile);
stream.on('open', () => resolve(stream)).on('error', reject);
fstream.on('open', () => resolve(fstream)).on('error', reject);
}),
);
}

View File

@ -13,16 +13,16 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import * as Bluebird from 'bluebird';
import { BalenaSDK } from 'balena-sdk';
import * as Bluebird from 'bluebird';
import { Socket } from 'net';
import { TypedError } from 'typed-error';
const PROXY_CONNECT_TIMEOUT_MS = 10000;
class UnableToConnectError extends TypedError {
status: string;
statusCode: string;
public status: string;
public statusCode: string;
constructor(statusCode: string, status: string) {
super(`Unable to connect: ${statusCode} ${status}`);
this.status = status;
@ -31,7 +31,7 @@ class UnableToConnectError extends TypedError {
}
class RemoteSocketNotListening extends TypedError {
port: number;
public port: number;
constructor(port: number) {
super(`Device is not listening on port ${port}`);
}
@ -104,7 +104,7 @@ const openPortThroughProxy = (
const [httpStatus] = data.toString('utf8').split('\r\n');
const [, httpStatusCode, ...httpMessage] = httpStatus.split(' ');
if (parseInt(httpStatusCode) === 200) {
if (parseInt(httpStatusCode, 10) === 200) {
proxyTunnel.setTimeout(0);
resolve(proxyTunnel);
} else {

View File

@ -14,8 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import * as UpdateNotifier from 'update-notifier';
import isRoot = require('is-root');
import * as UpdateNotifier from 'update-notifier';
import packageJSON = require('../../package.json');
// Check for an update once a day. 1 day granularity should be

View File

@ -1,3 +1,19 @@
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import DynamicList = require('inquirer-dynamic-list');
export abstract class CustomDynamicList<T> extends DynamicList {
@ -9,7 +25,7 @@ export abstract class CustomDynamicList<T> extends DynamicList {
protected abstract format(thing: T): string;
refresh(): void {
public refresh(): void {
this.opt.choices.choices = [];
this.opt.choices.realChoices = [];
for (const thing of this.getThings()) {
@ -18,7 +34,7 @@ export abstract class CustomDynamicList<T> extends DynamicList {
this.render();
}
async run(): Promise<T> {
public async run(): Promise<T> {
this.refresh();
return await super.run();
}

View File

@ -66,14 +66,16 @@
"@types/archiver": "2.1.2",
"@types/bluebird": "3.5.21",
"@types/common-tags": "1.4.0",
"@types/dockerode": "2.5.5",
"@types/es6-promise": "0.0.32",
"@types/fs-extra": "5.0.4",
"@types/is-root": "1.0.0",
"@types/lodash": "4.14.103",
"@types/lodash": "4.14.112",
"@types/mkdirp": "0.5.2",
"@types/node": "6.14.2",
"@types/prettyjson": "0.0.28",
"@types/raven": "2.5.1",
"@types/tar-stream": "1.6.0",
"catch-uncommitted": "^1.0.0",
"ent": "^2.2.0",
"filehound": "^1.16.2",
@ -85,10 +87,10 @@
"gulp-shell": "^0.5.2",
"mochainon": "^2.0.0",
"pkg": "^4.3.0-beta.1",
"prettier": "^1.14.2",
"prettier": "^1.15.3",
"publish-release": "^1.3.3",
"require-npm4-to-publish": "^1.0.0",
"resin-lint": "^2.0.1",
"resin-lint": "^3.0.1",
"rewire": "^3.0.2",
"ts-node": "^4.0.1",
"typescript": "2.8.1"
@ -113,7 +115,7 @@
"balena-settings-client": "^4.0.0",
"balena-sync": "^10.0.2",
"bash": "0.0.1",
"bluebird": "^3.3.3",
"bluebird": "^3.5.3",
"body-parser": "^1.14.1",
"capitano": "^1.9.0",
"chalk": "^2.3.0",
@ -123,9 +125,9 @@
"columnify": "^1.5.2",
"common-tags": "^1.7.2",
"denymount": "^2.2.0",
"docker-progress": "^3.0.4",
"docker-progress": "^3.0.5",
"docker-qemu-transpose": "^0.5.3",
"docker-toolbelt": "^3.3.5",
"docker-toolbelt": "^3.3.7",
"dockerode": "^2.5.5",
"dockerode-options": "^0.2.1",
"ejs": "^2.5.7",
@ -157,10 +159,10 @@
"request": "^2.81.0",
"resin-cli-form": "^2.0.1",
"resin-cli-visuals": "^1.4.0",
"resin-compose-parse": "^2.0.0",
"resin-compose-parse": "^2.0.3",
"resin-doodles": "0.0.1",
"resin-image-fs": "^5.0.2",
"resin-multibuild": "^2.1.2",
"resin-multibuild": "^2.1.4",
"resin-release": "^1.2.0",
"resin-semver": "^1.4.0",
"resin-stream-logger": "^0.1.2",

View File

@ -1,3 +1,20 @@
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// These are the DefinitelyTyped typings for JSONStream, but because of this
// mismatch in case of jsonstream and JSONStream, it is necessary to include
// them this way, with an upper case module declaration. They have also
@ -15,22 +32,21 @@ declare module 'JSONStream' {
recurse: boolean;
}
export function parse(pattern: any): NodeJS.ReadWriteStream;
export function parse(patterns: any[]): NodeJS.ReadWriteStream;
export function parse(pattern: any | any[]): NodeJS.ReadWriteStream;
type NewlineOnlyIndicator = false;
/**
* Create a writable stream.
* you may pass in custom open, close, and seperator strings. But, by default,
* JSONStream.stringify() will create an array,
* (with default options open='[\n', sep='\n,\n', close='\n]\n')
*
* If you call JSONStream.stringify(false) the elements will only be separated by a newline.
*/
export function stringify(): NodeJS.ReadWriteStream;
/** If you call JSONStream.stringify(false) the elements will only be seperated by a newline. */
export function stringify(
newlineOnly: NewlineOnlyIndicator,
newlineOnly?: NewlineOnlyIndicator,
): NodeJS.ReadWriteStream;
type NewlineOnlyIndicator = false;
/**
* Create a writable stream.

View File

@ -1,7 +1,23 @@
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare module 'balena-device-init' {
import { DeviceType } from 'balena-sdk';
import * as Promise from 'bluebird';
import { EventEmitter } from 'events';
import { DeviceType } from 'balena-sdk';
interface OperationState {
operation:
@ -55,8 +71,7 @@ declare module 'balena-device-init' {
}
interface InitializeEmitter {
on(event: 'stdout', callback: (msg: string) => void): void;
on(event: 'stderr', callback: (msg: string) => void): void;
on(event: 'stdout' | 'stderr', callback: (msg: string) => void): void;
on(event: 'state', callback: (state: OperationState) => void): void;
on(event: 'burn', callback: (state: BurnProgress) => void): void;
}

View File

@ -1,11 +1,27 @@
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare module 'color-hash' {
interface Hasher {
hex(text: string): string;
}
class ColorHash {
hex(text: string): string;
rgb(text: string): [number, number, number];
public hex(text: string): string;
public rgb(text: string): [number, number, number];
}
export = ColorHash;

View File

@ -1,3 +1,19 @@
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare module 'inquirer-dynamic-list' {
interface Choice {
name: string;
@ -5,7 +21,7 @@ declare module 'inquirer-dynamic-list' {
}
class DynamicList {
opt: {
public opt: {
choices: {
choices: Choice[];
realChoices: Choice[];
@ -17,9 +33,9 @@ declare module 'inquirer-dynamic-list' {
emptyMessage?: string;
choices: Choice[];
});
addChoice(choice: Choice): void;
render(): void;
run(): Promise<any>;
public addChoice(choice: Choice): void;
public render(): void;
public run(): Promise<any>;
}
export = DynamicList;

18
typings/nplugm.d.ts vendored
View File

@ -1,4 +1,20 @@
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare module 'nplugm' {
import Promise = require('bluebird');
export function list(regexp: RegExp): Promise<Array<string>>;
export function list(regexp: RegExp): Promise<string[]>;
}

18
typings/rindle.d.ts vendored
View File

@ -1,3 +1,19 @@
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare module 'rindle' {
export function extract(
stream: NodeJS.ReadableStream,
@ -6,7 +22,7 @@ declare module 'rindle' {
export function wait(
stream: {
on(event: string, callback: Function): void;
on(event: string, callback: (...args: any[]) => void): void;
},
callback: (error: Error, data: string) => void,
): void;

View File

@ -1,4 +1,22 @@
// Based on the official types at https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/update-notifier/index.d.ts
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Based on the official types at:
// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/update-notifier/index.d.ts
// but fixed to handle options correctly
declare module 'update-notifier' {
@ -12,10 +30,10 @@ declare module 'update-notifier' {
class UpdateNotifier {
constructor(settings?: Settings);
update: UpdateInfo;
check(): void;
checkNpm(): void;
notify(customMessage?: NotifyOptions): void;
public update: UpdateInfo;
public check(): void;
public checkNpm(): void;
public notify(customMessage?: NotifyOptions): void;
}
interface Settings {