Start using Prettier

Change-Type: patch
This commit is contained in:
Tim Perry 2018-01-09 16:05:24 +01:00
parent 6c988241eb
commit 83a76f7d6f
21 changed files with 451 additions and 325 deletions

5
.prettierrc Normal file
View File

@ -0,0 +1,5 @@
{
"single-quote": true,
"trailing-comma": "all",
"use-tabs": true
}

View File

@ -7,28 +7,30 @@ const ROOT = path.join(__dirname, '..');
console.log('Building package...\n'); console.log('Building package...\n');
execPkg([ execPkg(['--target', 'host', '--output', 'build-bin/resin', 'package.json'])
'--target', 'host', .then(() =>
'--output', 'build-bin/resin', fs.copy(
'package.json' path.join(ROOT, 'node_modules', 'opn', 'xdg-open'),
]).then(() => fs.copy( path.join(ROOT, 'build-bin', 'xdg-open'),
path.join(ROOT, 'node_modules', 'opn', 'xdg-open'), ),
path.join(ROOT, 'build-bin', 'xdg-open') )
)).then(() => { .then(() => {
return filehound.create() return filehound
.paths(path.join(ROOT, 'node_modules')) .create()
.ext(['node', 'dll']) .paths(path.join(ROOT, 'node_modules'))
.find(); .ext(['node', 'dll'])
}).then((nativeExtensions) => { .find();
console.log(`\nCopying to build-bin:\n${nativeExtensions.join('\n')}`); })
.then(nativeExtensions => {
console.log(`\nCopying to build-bin:\n${nativeExtensions.join('\n')}`);
return nativeExtensions.map((extPath) => { return nativeExtensions.map(extPath => {
return fs.copy( return fs.copy(
extPath, extPath,
extPath.replace( extPath.replace(
path.join(ROOT, 'node_modules'), path.join(ROOT, 'node_modules'),
path.join(ROOT, 'build-bin') path.join(ROOT, 'build-bin'),
) ),
); );
});
}); });
});

View File

@ -4,13 +4,13 @@ import * as path from 'path';
import * as markdown from './markdown'; import * as markdown from './markdown';
import { Document, Category } from './doc-types'; import { Document, Category } from './doc-types';
const result = <Document> {}; const result = <Document>{};
result.title = capitanodoc.title; result.title = capitanodoc.title;
result.introduction = capitanodoc.introduction; result.introduction = capitanodoc.introduction;
result.categories = []; result.categories = [];
for (let commandCategory of capitanodoc.categories) { for (let commandCategory of capitanodoc.categories) {
const category = <Category> {}; const category = <Category>{};
category.title = commandCategory.title; category.title = commandCategory.title;
category.commands = []; category.commands = [];

View File

@ -10,7 +10,9 @@ export function renderCommand(command: Command) {
result += '\n### Options'; result += '\n### Options';
for (let option of command.options!) { for (let option of command.options!) {
result += `\n\n#### ${utils.parseSignature(option)}\n\n${option.description}`; result += `\n\n#### ${utils.parseSignature(option)}\n\n${
option.description
}`;
} }
result += '\n'; result += '\n';
@ -30,27 +32,31 @@ export function renderCategory(category: Category) {
} }
function getAnchor(command: Command) { function getAnchor(command: Command) {
return '#' + command.signature return (
.replace(/\s/g,'-') '#' +
.replace(/</g, '60-') command.signature
.replace(/>/g, '-62-') .replace(/\s/g, '-')
.replace(/\[/g, '') .replace(/</g, '60-')
.replace(/\]/g, '-') .replace(/>/g, '-62-')
.replace(/--/g, '-') .replace(/\[/g, '')
.replace(/\.\.\./g, '') .replace(/\]/g, '-')
.replace(/\|/g, '') .replace(/--/g, '-')
.toLowerCase(); .replace(/\.\.\./g, '')
.replace(/\|/g, '')
.toLowerCase()
);
} }
export function renderToc(categories: Category[]) { export function renderToc(categories: Category[]) {
let result = `# Table of contents\n`; let result = `# Table of contents\n`;
for (let category of categories) { for (let category of categories) {
result += `\n- ${category.title}\n\n`; result += `\n- ${category.title}\n\n`;
for (let command of category.commands) { for (let command of category.commands) {
result += `\t- [${ent.encode(command.signature)}](${getAnchor(command)})\n`; result += `\t- [${ent.encode(command.signature)}](${getAnchor(
command,
)})\n`;
} }
} }
@ -58,7 +64,9 @@ export function renderToc(categories: Category[]) {
} }
export function render(doc: Document) { export function render(doc: Document) {
let result = `# ${doc.title}\n\n${doc.introduction}\n\n${renderToc(doc.categories)}`; let result = `# ${doc.title}\n\n${doc.introduction}\n\n${renderToc(
doc.categories,
)}`;
for (let category of doc.categories) { for (let category of doc.categories) {
result += `\n${renderCategory(category)}`; result += `\n${renderCategory(category)}`;

View File

@ -29,7 +29,10 @@ declare module 'publish-release' {
html_url: string; html_url: string;
} }
let publishRelease: (args: PublishOptions, callback: (e: Error, release: Release) => void) => void; let publishRelease: (
args: PublishOptions,
callback: (e: Error, release: Release) => void,
) => void;
export = publishRelease; export = publishRelease;
} }

View File

@ -14,43 +14,54 @@ const { GITHUB_TOKEN } = process.env;
const ROOT = path.join(__dirname, '..'); const ROOT = path.join(__dirname, '..');
const version = 'v' + packageJSON.version; const version = 'v' + packageJSON.version;
const outputFile = path.join(ROOT, 'build-zip', `resin-cli-${version}-${os.platform()}-${os.arch()}.zip`); const outputFile = path.join(
ROOT,
'build-zip',
`resin-cli-${version}-${os.platform()}-${os.arch()}.zip`,
);
mkdirpAsync(path.dirname(outputFile)).then(() => new Promise((resolve, reject) => { mkdirpAsync(path.dirname(outputFile))
console.log('Zipping build...'); .then(
() =>
new Promise((resolve, reject) => {
console.log('Zipping build...');
let archive = archiver('zip', { let archive = archiver('zip', {
zlib: { level: 7 }, zlib: { level: 7 },
});
archive.directory(path.join(ROOT, 'build-bin'), 'resin-cli');
let outputStream = fs.createWriteStream(outputFile);
outputStream.on('close', resolve);
outputStream.on('error', reject);
archive.on('error', reject);
archive.on('warning', console.warn);
archive.pipe(outputStream);
archive.finalize();
}),
)
.then(() => {
console.log('Build zipped');
console.log('Publishing build...');
return publishReleaseAsync({
token: <string>GITHUB_TOKEN,
owner: 'resin-io',
repo: 'resin-cli',
tag: version,
name: `Resin-CLI ${version}`,
reuseRelease: true,
assets: [outputFile],
});
})
.then(release => {
console.log(`Release ${version} successful: ${release.html_url}`);
})
.catch(err => {
console.error('Release failed');
console.error(err);
process.exit(1);
}); });
archive.directory(path.join(ROOT, 'build-bin'), 'resin-cli');
let outputStream = fs.createWriteStream(outputFile);
outputStream.on('close', resolve);
outputStream.on('error', reject);
archive.on('error', reject);
archive.on('warning', console.warn);
archive.pipe(outputStream);
archive.finalize();
})).then(() => {
console.log('Build zipped');
console.log('Publishing build...');
return publishReleaseAsync({
token: <string> GITHUB_TOKEN,
owner: 'resin-io',
repo: 'resin-cli',
tag: version,
name: `Resin-CLI ${version}`,
reuseRelease: true,
assets: [outputFile],
});
}).then((release) => {
console.log(`Release ${version} successful: ${release.html_url}`);
}).catch((err) => {
console.error('Release failed');
console.error(err);
process.exit(1);
});

View File

@ -30,7 +30,8 @@ Examples:
const resin = (await import('resin-sdk')).fromSharedOptions(); const resin = (await import('resin-sdk')).fromSharedOptions();
const prettyjson = await import('prettyjson'); const prettyjson = await import('prettyjson');
return resin.settings.getAll() return resin.settings
.getAll()
.then(prettyjson.render) .then(prettyjson.render)
.then(console.log) .then(console.log)
.nodeify(done); .nodeify(done);

View File

@ -1 +1,2 @@
export const sentryDsn = 'https://56d2a46124614b01b0f4086897e96110:6e175465accc41b595a96947155f61fb@sentry.io/149239'; export const sentryDsn =
'https://56d2a46124614b01b0f4086897e96110:6e175465accc41b595a96947155f61fb@sentry.io/149239';

View File

@ -19,11 +19,16 @@ import patterns = require('./utils/patterns');
import Raven = require('raven'); import Raven = require('raven');
import Promise = require('bluebird'); import Promise = require('bluebird');
const captureException = Promise.promisify<string, Error>(Raven.captureException, { context: Raven }); const captureException = Promise.promisify<string, Error>(
Raven.captureException,
{ context: Raven },
);
exports.handle = function(error: any) { exports.handle = function(error: any) {
let message = errors.interpret(error); let message = errors.interpret(error);
if ((message == null)) { return; } if (message == null) {
return;
}
if (process.env.DEBUG) { if (process.env.DEBUG) {
message = error.stack; message = error.stack;
@ -32,8 +37,9 @@ exports.handle = function(error: any) {
patterns.printErrorMessage(message); patterns.printErrorMessage(message);
return captureException(error) return captureException(error)
.timeout(1000) .timeout(1000)
.catch(function() { .catch(function() {
// Ignore any errors (from error logging, or timeouts) // Ignore any errors (from error logging, or timeouts)
}).finally(() => process.exit(error.exitCode || 1)); })
.finally(() => process.exit(error.exitCode || 1));
}; };

View File

@ -9,34 +9,40 @@ import packageJSON = require('../package.json');
const resin = ResinSdk.fromSharedOptions(); const resin = ResinSdk.fromSharedOptions();
const getMatchCommandAsync = Promise.promisify(Capitano.state.getMatchCommand); const getMatchCommandAsync = Promise.promisify(Capitano.state.getMatchCommand);
const getMixpanel = _.memoize<any>(() => resin.models.config.getAll().get('mixpanelToken').then(Mixpanel.init)); const getMixpanel = _.memoize<any>(() =>
resin.models.config
.getAll()
.get('mixpanelToken')
.then(Mixpanel.init),
);
export function trackCommand(capitanoCli: Capitano.Cli) { export function trackCommand(capitanoCli: Capitano.Cli) {
return Promise.props({ return Promise.props({
resinUrl: resin.settings.get('resinUrl'), resinUrl: resin.settings.get('resinUrl'),
username: resin.auth.whoami().catchReturn(undefined), username: resin.auth.whoami().catchReturn(undefined),
mixpanel: getMixpanel(), mixpanel: getMixpanel(),
}).then(({ username, resinUrl, mixpanel }) => {
return getMatchCommandAsync(capitanoCli.command).then((command) => {
Raven.mergeContext({
user: {
id: username,
username,
},
});
return mixpanel.track(`[CLI] ${command.signature.toString()}`, {
distinct_id: username,
argv: process.argv.join(' '),
version: packageJSON.version,
node: process.version,
arch: process.arch,
resinUrl,
platform: process.platform,
command: capitanoCli,
});
});
}) })
.timeout(100) .then(({ username, resinUrl, mixpanel }) => {
.catchReturn(undefined); return getMatchCommandAsync(capitanoCli.command).then(command => {
Raven.mergeContext({
user: {
id: username,
username,
},
});
return mixpanel.track(`[CLI] ${command.signature.toString()}`, {
distinct_id: username,
argv: process.argv.join(' '),
version: packageJSON.version,
node: process.version,
arch: process.arch,
resinUrl,
platform: process.platform,
command: capitanoCli,
});
});
})
.timeout(100)
.catchReturn(undefined);
} }

View File

@ -20,7 +20,10 @@ import deviceConfig = require('resin-device-config');
const resin = ResinSdk.fromSharedOptions(); const resin = ResinSdk.fromSharedOptions();
export function generateBaseConfig(application: ResinSdk.Application, options: {}) { export function generateBaseConfig(
application: ResinSdk.Application,
options: {},
) {
options = _.mapValues(options, function(value, key) { options = _.mapValues(options, function(value, key) {
if (key === 'appUpdatePollInterval') { if (key === 'appUpdatePollInterval') {
return value * 60 * 1000; return value * 60 * 1000;
@ -37,30 +40,37 @@ export function generateBaseConfig(application: ResinSdk.Application, options: {
registryUrl: resin.settings.get('registryUrl'), registryUrl: resin.settings.get('registryUrl'),
deltaUrl: resin.settings.get('deltaUrl'), deltaUrl: resin.settings.get('deltaUrl'),
apiConfig: resin.models.config.getAll(), apiConfig: resin.models.config.getAll(),
}).then((results) => { }).then(results => {
return deviceConfig.generate({ return deviceConfig.generate(
application, {
user: { application,
id: results.userId, user: {
username: results.username, id: results.userId,
username: results.username,
},
endpoints: {
api: results.apiUrl,
vpn: results.vpnUrl,
registry: results.registryUrl,
delta: results.deltaUrl,
},
pubnub: results.apiConfig.pubnub,
mixpanel: {
token: results.apiConfig.mixpanelToken,
},
}, },
endpoints: { options,
api: results.apiUrl, );
vpn: results.vpnUrl,
registry: results.registryUrl,
delta: results.deltaUrl,
},
pubnub: results.apiConfig.pubnub,
mixpanel: {
token: results.apiConfig.mixpanelToken,
},
}, options);
}); });
} }
export function generateApplicationConfig(application: ResinSdk.Application, options: {}) { export function generateApplicationConfig(
return generateBaseConfig(application, options) application: ResinSdk.Application,
.tap(config => addApplicationKey(config, application.id)); options: {},
) {
return generateBaseConfig(application, options).tap(config =>
addApplicationKey(config, application.id),
);
} }
export function generateDeviceConfig( export function generateDeviceConfig(
@ -68,41 +78,43 @@ export function generateDeviceConfig(
deviceApiKey: string | null, deviceApiKey: string | null,
options: {}, options: {},
) { ) {
return resin.models.application.get(device.application_name) return resin.models.application
.then(application => { .get(device.application_name)
return generateBaseConfig(application, options) .then(application => {
.tap((config) => { return generateBaseConfig(application, options).tap(config => {
// Device API keys are only safe for ResinOS 2.0.3+. We could somehow obtain // Device API keys are only safe for ResinOS 2.0.3+. We could somehow obtain
// the expected version for this config and generate one when we know it's safe, // the expected version for this config and generate one when we know it's safe,
// but instead for now we fall back to app keys unless the user has explicitly opted in. // but instead for now we fall back to app keys unless the user has explicitly opted in.
if (deviceApiKey) { if (deviceApiKey) {
return addDeviceKey(config, device.uuid, deviceApiKey); return addDeviceKey(config, device.uuid, deviceApiKey);
} else { } else {
return addApplicationKey(config, application.id); return addApplicationKey(config, application.id);
} }
}); });
}).then((config) => { })
// Associate a device, to prevent the supervisor .then(config => {
// from creating another one on its own. // Associate a device, to prevent the supervisor
config.registered_at = Math.floor(Date.now() / 1000); // from creating another one on its own.
config.deviceId = device.id; config.registered_at = Math.floor(Date.now() / 1000);
config.uuid = device.uuid; config.deviceId = device.id;
config.uuid = device.uuid;
return config; return config;
}); });
} }
function addApplicationKey(config: any, applicationNameOrId: string | number) { function addApplicationKey(config: any, applicationNameOrId: string | number) {
return resin.models.application.generateApiKey(applicationNameOrId) return resin.models.application
.tap((apiKey) => { .generateApiKey(applicationNameOrId)
config.apiKey = apiKey; .tap(apiKey => {
}); config.apiKey = apiKey;
});
} }
function addDeviceKey(config: any, uuid: string, customDeviceApiKey: string) { function addDeviceKey(config: any, uuid: string, customDeviceApiKey: string) {
return Promise.try(() => { return Promise.try(() => {
return customDeviceApiKey || resin.models.device.generateDeviceKey(uuid); return customDeviceApiKey || resin.models.device.generateDeviceKey(uuid);
}).tap((deviceApiKey) => { }).tap(deviceApiKey => {
config.deviceApiKey = deviceApiKey; config.deviceApiKey = deviceApiKey;
}); });
} }

View File

@ -32,25 +32,31 @@ const presidentExecuteAsync = Promise.promisify(execute);
const resin = ResinSdk.fromSharedOptions(); const resin = ResinSdk.fromSharedOptions();
export function getGroupDefaults( export function getGroupDefaults(group: {
group: { options: { name: string, default?: string }[] }, options: { name: string; default?: string }[];
): { [name: string]: string | undefined } { }): { [name: string]: string | undefined } {
return _.chain(group) return _.chain(group)
.get('options') .get('options')
.map((question) => [ question.name, question.default ]) .map(question => [question.name, question.default])
.fromPairs() .fromPairs()
.value(); .value();
} }
export function stateToString(state: OperationState) { export function stateToString(state: OperationState) {
const percentage = _.padStart(`${state.percentage}`, 3, '0'); const percentage = _.padStart(`${state.percentage}`, 3, '0');
const result = `${chalk.blue(percentage + '%')} ${chalk.cyan(state.operation.command)}`; const result = `${chalk.blue(percentage + '%')} ${chalk.cyan(
state.operation.command,
)}`;
switch (state.operation.command) { switch (state.operation.command) {
case 'copy': case 'copy':
return `${result} ${state.operation.from.path} -> ${state.operation.to.path}`; return `${result} ${state.operation.from.path} -> ${
state.operation.to.path
}`;
case 'replace': case 'replace':
return `${result} ${state.operation.file.path}, ${state.operation.copy} -> ${state.operation.replace}`; return `${result} ${state.operation.file.path}, ${
state.operation.copy
} -> ${state.operation.replace}`;
case 'run-script': case 'run-script':
return `${result} ${state.operation.script}`; return `${result} ${state.operation.script}`;
default: default:
@ -68,20 +74,24 @@ export function sudo(command: string[]) {
return presidentExecuteAsync(command); return presidentExecuteAsync(command);
} }
export function getManifest(image: string, deviceType: string): Promise<ResinSdk.DeviceType> { export function getManifest(
image: string,
deviceType: string,
): Promise<ResinSdk.DeviceType> {
// Attempt to read manifest from the first // Attempt to read manifest from the first
// partition, but fallback to the API if // partition, but fallback to the API if
// we encounter any errors along the way. // we encounter any errors along the way.
return imagefs.read({ return imagefs
image, .read({
partition: { image,
primary: 1, partition: {
}, primary: 1,
path: '/device-type.json', },
}) path: '/device-type.json',
.then(extractStreamAsync) })
.then(JSON.parse) .then(extractStreamAsync)
.catch(() => resin.models.device.getManifestBySlug(deviceType)); .then(JSON.parse)
.catch(() => resin.models.device.getManifestBySlug(deviceType));
} }
export function osProgressHandler(step: InitializeEmitter) { export function osProgressHandler(step: InitializeEmitter) {
@ -89,7 +99,9 @@ export function osProgressHandler(step: InitializeEmitter) {
step.on('stderr', process.stderr.write.bind(process.stderr)); step.on('stderr', process.stderr.write.bind(process.stderr));
step.on('state', function(state) { step.on('state', function(state) {
if (state.operation.command === 'burn') { return; } if (state.operation.command === 'burn') {
return;
}
console.log(exports.stateToString(state)); console.log(exports.stateToString(state));
}); });
@ -103,11 +115,13 @@ export function osProgressHandler(step: InitializeEmitter) {
return waitStreamAsync(step); return waitStreamAsync(step);
} }
export function getArchAndDeviceType(applicationName: string): Promise<{ arch: string, device_type: string }> { export function getArchAndDeviceType(
applicationName: string,
): Promise<{ arch: string; device_type: string }> {
return Promise.join( return Promise.join(
getApplication(applicationName), getApplication(applicationName),
resin.models.config.getDeviceTypes(), resin.models.config.getDeviceTypes(),
function (app, deviceTypes) { function(app, deviceTypes) {
const config = _.find(deviceTypes, { slug: app.device_type }); const config = _.find(deviceTypes, { slug: app.device_type });
if (!config) { if (!config) {
@ -139,12 +153,12 @@ export function getSubShellCommand(command: string) {
if (os.platform() === 'win32') { if (os.platform() === 'win32') {
return { return {
program: 'cmd.exe', program: 'cmd.exe',
args: [ '/s', '/c', command ], args: ['/s', '/c', command],
}; };
} else { } else {
return { return {
program: '/bin/sh', program: '/bin/sh',
args: [ '-c', command ], args: ['-c', command],
}; };
} }
} }

View File

@ -26,36 +26,49 @@ import messages = require('./messages');
const resin = ResinSdk.fromSharedOptions(); const resin = ResinSdk.fromSharedOptions();
export function authenticate(options: {}): Promise<void> { export function authenticate(options: {}): Promise<void> {
return form.run([{ return form
message: 'Email:', .run(
name: 'email', [
type: 'input', {
validate: validation.validateEmail, message: 'Email:',
}, { name: 'email',
message: 'Password:', type: 'input',
name: 'password', validate: validation.validateEmail,
type: 'password', },
}], { override: options }) {
.then(resin.auth.login) message: 'Password:',
.then(resin.auth.twoFactor.isPassed) name: 'password',
.then((isTwoFactorAuthPassed: boolean) => { type: 'password',
if (isTwoFactorAuthPassed) { return; } },
],
{ override: options },
)
.then(resin.auth.login)
.then(resin.auth.twoFactor.isPassed)
.then((isTwoFactorAuthPassed: boolean) => {
if (isTwoFactorAuthPassed) {
return;
}
return form.ask({ return form
message: 'Two factor auth challenge:', .ask({
name: 'code', message: 'Two factor auth challenge:',
type: 'input', name: 'code',
}) type: 'input',
.then(resin.auth.twoFactor.challenge) })
.catch((error: any) => { .then(resin.auth.twoFactor.challenge)
return resin.auth.logout().then(() => { .catch((error: any) => {
if ((error.name === 'ResinRequestError') && (error.statusCode === 401)) { return resin.auth.logout().then(() => {
throw new Error('Invalid two factor authentication code'); if (
} error.name === 'ResinRequestError' &&
throw error; error.statusCode === 401
}); ) {
throw new Error('Invalid two factor authentication code');
}
throw error;
});
});
}); });
});
} }
export function askLoginType() { export function askLoginType() {
@ -63,25 +76,29 @@ export function askLoginType() {
message: 'How would you like to login?', message: 'How would you like to login?',
name: 'loginType', name: 'loginType',
type: 'list', type: 'list',
choices: [{ choices: [
name: 'Web authorization (recommended)', {
value: 'web', name: 'Web authorization (recommended)',
}, { value: 'web',
name: 'Credentials', },
value: 'credentials', {
}, { name: 'Credentials',
name: 'Authentication token', value: 'credentials',
value: 'token', },
}, { {
name: 'I don\'t have a Resin account!', name: 'Authentication token',
value: 'register', value: 'token',
}], },
{
name: "I don't have a Resin account!",
value: 'register',
},
],
}); });
} }
export function selectDeviceType() { export function selectDeviceType() {
return resin.models.device.getSupportedDeviceTypes() return resin.models.device.getSupportedDeviceTypes().then(deviceTypes => {
.then(deviceTypes => {
return form.ask({ return form.ask({
message: 'Device Type', message: 'Device Type',
type: 'list', type: 'list',
@ -90,10 +107,16 @@ export function selectDeviceType() {
}); });
} }
export function confirm(yesOption: string, message: string, yesMessage: string) { export function confirm(
return Promise.try(function () { yesOption: string,
message: string,
yesMessage: string,
) {
return Promise.try(function() {
if (yesOption) { if (yesOption) {
if (yesMessage) { console.log(yesMessage); } if (yesMessage) {
console.log(yesMessage);
}
return true; return true;
} }
@ -109,73 +132,79 @@ export function confirm(yesOption: string, message: string, yesMessage: string)
}); });
} }
export function selectApplication(filter: (app: ResinSdk.Application) => boolean) { export function selectApplication(
resin.models.application.hasAny().then(function(hasAnyApplications) { filter: (app: ResinSdk.Application) => boolean,
if (!hasAnyApplications) { ) {
throw new Error('You don\'t have any applications'); resin.models.application
} .hasAny()
.then(function(hasAnyApplications) {
return resin.models.application.getAll(); if (!hasAnyApplications) {
}) throw new Error("You don't have any applications");
.filter(filter || _.constant(true)) }
.then(applications => {
return form.ask({
message: 'Select an application',
type: 'list',
choices: _.map(applications, application =>
({
name: `${application.app_name} (${application.device_type})`,
value: application.app_name,
}),
)});
});
}
export function selectOrCreateApplication() {
return resin.models.application.hasAny().then((hasAnyApplications) => {
if (!hasAnyApplications) return;
return resin.models.application.getAll().then((applications) => {
const appOptions = _.map<
ResinSdk.Application,
{ name: string, value: string | null }
>(applications, application => ({
name: `${application.app_name} (${application.device_type})`,
value: application.app_name,
}));
appOptions.unshift({
name: 'Create a new application',
value: null,
});
return resin.models.application.getAll();
})
.filter(filter || _.constant(true))
.then(applications => {
return form.ask({ return form.ask({
message: 'Select an application', message: 'Select an application',
type: 'list', type: 'list',
choices: appOptions, choices: _.map(applications, application => ({
name: `${application.app_name} (${application.device_type})`,
value: application.app_name,
})),
}); });
}); });
}).then((application) => { }
if (application) {
return application;
}
return form.ask({ export function selectOrCreateApplication() {
message: 'Choose a Name for your new application', return resin.models.application
type: 'input', .hasAny()
validate: validation.validateApplicationName, .then(hasAnyApplications => {
if (!hasAnyApplications) return;
return resin.models.application.getAll().then(applications => {
const appOptions = _.map<
ResinSdk.Application,
{ name: string; value: string | null }
>(applications, application => ({
name: `${application.app_name} (${application.device_type})`,
value: application.app_name,
}));
appOptions.unshift({
name: 'Create a new application',
value: null,
});
return form.ask({
message: 'Select an application',
type: 'list',
choices: appOptions,
});
});
})
.then(application => {
if (application) {
return application;
}
return form.ask({
message: 'Choose a Name for your new application',
type: 'input',
validate: validation.validateApplicationName,
});
}); });
});
} }
export function awaitDevice(uuid: string) { export function awaitDevice(uuid: string) {
return resin.models.device.getName(uuid) return resin.models.device.getName(uuid).then(deviceName => {
.then((deviceName) => { const spinner = new visuals.Spinner(
const spinner = new visuals.Spinner(`Waiting for ${deviceName} to come online`); `Waiting for ${deviceName} to come online`,
);
const poll = (): Promise<void> => { const poll = (): Promise<void> => {
return resin.models.device.isOnline(uuid) return resin.models.device.isOnline(uuid).then(function(isOnline) {
.then(function(isOnline) {
if (isOnline) { if (isOnline) {
spinner.stop(); spinner.stop();
console.info(`The device **${deviceName}** is online!`); console.info(`The device **${deviceName}** is online!`);
@ -196,27 +225,28 @@ export function awaitDevice(uuid: string) {
} }
export function inferOrSelectDevice(preferredUuid: string) { export function inferOrSelectDevice(preferredUuid: string) {
return resin.models.device.getAll() return resin.models.device
.filter<ResinSdk.Device>((device) => device.is_online) .getAll()
.then((onlineDevices) => { .filter<ResinSdk.Device>(device => device.is_online)
if (_.isEmpty(onlineDevices)) { .then(onlineDevices => {
throw new Error('You don\'t have any devices online'); if (_.isEmpty(onlineDevices)) {
} throw new Error("You don't have any devices online");
}
const defaultUuid = _.map(onlineDevices, 'uuid').includes(preferredUuid) ? const defaultUuid = _.map(onlineDevices, 'uuid').includes(preferredUuid)
preferredUuid : ? preferredUuid
onlineDevices[0].uuid; : onlineDevices[0].uuid;
return form.ask({ return form.ask({
message: 'Select a device', message: 'Select a device',
type: 'list', type: 'list',
default: defaultUuid, default: defaultUuid,
choices: _.map(onlineDevices, device => ({ choices: _.map(onlineDevices, device => ({
name: `${device.name || 'Untitled'} (${device.uuid.slice(0, 7)})`, name: `${device.name || 'Untitled'} (${device.uuid.slice(0, 7)})`,
value: device.uuid, value: device.uuid,
}), })),
)}); });
}); });
} }
export function printErrorMessage(message: string) { export function printErrorMessage(message: string) {

View File

@ -20,14 +20,17 @@ import capitano = require('capitano');
import patterns = require('./patterns'); import patterns = require('./patterns');
export function register(regex: RegExp): Promise<void> { export function register(regex: RegExp): Promise<void> {
return nplugm.list(regex).map(async function(plugin: any) { return nplugm
const command = await import(plugin); .list(regex)
command.plugin = true; .map(async function(plugin: any) {
if (!_.isArray(command)) { const command = await import(plugin);
return capitano.command(command); command.plugin = true;
} if (!_.isArray(command)) {
return _.each(command, capitano.command); return capitano.command(command);
}).catch((error: Error) => { }
return patterns.printErrorMessage(error.message); return _.each(command, capitano.command);
}); })
.catch((error: Error) => {
return patterns.printErrorMessage(error.message);
});
} }

View File

@ -1,4 +1,7 @@
export async function buffer(stream: NodeJS.ReadableStream, bufferFile: string) { export async function buffer(
stream: NodeJS.ReadableStream,
bufferFile: string,
) {
const Promise = await import('bluebird'); const Promise = await import('bluebird');
const fs = await import('fs'); const fs = await import('fs');
@ -6,14 +9,15 @@ export async function buffer(stream: NodeJS.ReadableStream, bufferFile: string)
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
stream stream
.on('error', reject) .on('error', reject)
.on('end', resolve) .on('end', resolve)
.pipe(fileWriteStream); .pipe(fileWriteStream);
}).then(() => new Promise(function(resolve, reject) { }).then(
const stream = fs.createReadStream(bufferFile); () =>
new Promise(function(resolve, reject) {
const stream = fs.createReadStream(bufferFile);
stream stream.on('open', () => resolve(stream)).on('error', reject);
.on('open', () => resolve(stream)) }),
.on('error', reject); );
}));
} }

View File

@ -42,6 +42,8 @@ export function notify() {
notifier.notify({ defer: false }); notifier.notify({ defer: false });
if (notifier.update != null) { if (notifier.update != null) {
console.log('Notice that you might need administrator privileges depending on your setup\n'); console.log(
'Notice that you might need administrator privileges depending on your setup\n',
);
} }
} }

View File

@ -31,7 +31,7 @@
"scripts": { "scripts": {
"prebuild": "rimraf build/ build-bin/ build-zip/", "prebuild": "rimraf build/ build-bin/ build-zip/",
"build": "npm run build:src && npm run build:bin", "build": "npm run build:src && npm run build:bin",
"build:src": "npm run lint && gulp build && tsc && npm run build:doc", "build:src": "npm run prettify && npm run lint && gulp build && tsc && npm run build:doc",
"build:doc": "mkdirp doc/ && ts-node automation/capitanodoc/index.ts > doc/cli.markdown", "build:doc": "mkdirp doc/ && ts-node automation/capitanodoc/index.ts > doc/cli.markdown",
"build:bin": "ts-node --type-check -P automation automation/build-bin.ts", "build:bin": "ts-node --type-check -P automation automation/build-bin.ts",
"release": "npm run build && ts-node --type-check -P automation automation/deploy-bin.ts", "release": "npm run build && ts-node --type-check -P automation automation/deploy-bin.ts",
@ -39,6 +39,7 @@
"test": "gulp test", "test": "gulp test",
"ci": "npm run test && catch-uncommitted", "ci": "npm run test && catch-uncommitted",
"watch": "gulp watch", "watch": "gulp watch",
"prettify": "prettier --write \"{lib,tests,automation,typings}/**/*.ts\"",
"lint": "resin-lint lib/ tests/ && resin-lint --typescript automation/ lib/ typings/ tests/", "lint": "resin-lint lib/ tests/ && resin-lint --typescript automation/ lib/ typings/ tests/",
"prepublish": "require-npm4-to-publish", "prepublish": "require-npm4-to-publish",
"prepublishOnly": "npm run build" "prepublishOnly": "npm run build"
@ -71,6 +72,7 @@
"gulp-shell": "^0.5.2", "gulp-shell": "^0.5.2",
"mochainon": "^2.0.0", "mochainon": "^2.0.0",
"pkg": "^4.3.0-beta.1", "pkg": "^4.3.0-beta.1",
"prettier": "^1.9.2",
"publish-release": "^1.3.3", "publish-release": "^1.3.3",
"require-npm4-to-publish": "^1.0.0", "require-npm4-to-publish": "^1.0.0",
"resin-lint": "^1.5.0", "resin-lint": "^1.5.0",
@ -147,4 +149,4 @@
"optionalDependencies": { "optionalDependencies": {
"removedrive": "^1.0.0" "removedrive": "^1.0.0"
} }
} }

View File

@ -48,6 +48,9 @@ declare module 'capitano' {
export function command(command: CommandDefinition): void; export function command(command: CommandDefinition): void;
export const state: { export const state: {
getMatchCommand: (signature: string, callback: (e: Error, cmd: Command) => void) => void getMatchCommand: (
signature: string,
callback: (e: Error, cmd: Command) => void,
) => void;
}; };
} }

View File

@ -1,3 +1,6 @@
declare module 'president' { declare module 'president' {
export function execute(command: string[], callback: (err: Error) => void): void; export function execute(
command: string[],
callback: (err: Error) => void,
): void;
} }

View File

@ -3,7 +3,11 @@ declare module 'resin-device-init' {
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
interface OperationState { interface OperationState {
operation: CopyOperation | ReplaceOperation | RunScriptOperation | BurnOperation; operation:
| CopyOperation
| ReplaceOperation
| RunScriptOperation
| BurnOperation;
percentage: number; percentage: number;
} }
@ -56,5 +60,9 @@ declare module 'resin-device-init' {
on(event: 'burn', callback: (state: BurnProgress) => void): void; on(event: 'burn', callback: (state: BurnProgress) => void): void;
} }
export function initialize(image: string, deviceType: string, config: {}): Promise<InitializeEmitter>; export function initialize(
image: string,
deviceType: string,
config: {},
): Promise<InitializeEmitter>;
} }

View File

@ -4,7 +4,9 @@
declare module 'update-notifier' { declare module 'update-notifier' {
export = UpdateNotifier; export = UpdateNotifier;
function UpdateNotifier(settings?: UpdateNotifier.Settings): UpdateNotifier.UpdateNotifier; function UpdateNotifier(
settings?: UpdateNotifier.Settings,
): UpdateNotifier.UpdateNotifier;
namespace UpdateNotifier { namespace UpdateNotifier {
class UpdateNotifier { class UpdateNotifier {