mirror of
https://github.com/balena-io/balena-cli.git
synced 2025-04-08 11:54:12 +00:00
Auto-merge for PR #743 via VersionBot
Start seriously converting the CLI to TypeScript
This commit is contained in:
commit
29145dfc2d
@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file
|
||||
automatically by Versionist. DO NOT EDIT THIS FILE MANUALLY!
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## v6.12.3 - 2018-01-09
|
||||
|
||||
* Lint TypeScript and CoffeeScript with resin-lint #743 [Tim Perry]
|
||||
* Move documentation generation to TypeScript #743 [Tim Perry]
|
||||
* Convert most of utils to TypeScript #743 [Tim Perry]
|
||||
|
||||
## v6.12.2 - 2018-01-09
|
||||
|
||||
* Convert windows paths to posix when passing to tar #748 [Andrew Shirley]
|
||||
|
14
automation/custom-types.d.ts
vendored
14
automation/custom-types.d.ts
vendored
@ -16,13 +16,13 @@ declare module 'filehound' {
|
||||
|
||||
declare module 'publish-release' {
|
||||
interface PublishOptions {
|
||||
token: string,
|
||||
owner: string,
|
||||
repo: string,
|
||||
tag: string,
|
||||
name: string,
|
||||
reuseRelease?: boolean
|
||||
assets: string[]
|
||||
token: string;
|
||||
owner: string;
|
||||
repo: string;
|
||||
tag: string;
|
||||
name: string;
|
||||
reuseRelease?: boolean;
|
||||
assets: string[];
|
||||
}
|
||||
|
||||
interface Release {
|
||||
|
@ -5,6 +5,7 @@ import * as fs from 'fs-extra';
|
||||
import * as mkdirp from 'mkdirp';
|
||||
import * as publishRelease from 'publish-release';
|
||||
import * as archiver from 'archiver';
|
||||
import * as packageJSON from '../package.json';
|
||||
|
||||
const publishReleaseAsync = Promise.promisify(publishRelease);
|
||||
const mkdirpAsync = Promise.promisify<string | null, string>(mkdirp);
|
||||
@ -12,14 +13,14 @@ const mkdirpAsync = Promise.promisify<string | null, string>(mkdirp);
|
||||
const { GITHUB_TOKEN } = process.env;
|
||||
const ROOT = path.join(__dirname, '..');
|
||||
|
||||
const version = 'v' + require('../package.json').version;
|
||||
const version = 'v' + packageJSON.version;
|
||||
const outputFile = path.join(ROOT, 'build-zip', `resin-cli-${version}-${os.platform()}-${os.arch()}.zip`);
|
||||
|
||||
mkdirpAsync(path.dirname(outputFile)).then(() => new Promise((resolve, reject) => {
|
||||
console.log('Zipping build...');
|
||||
|
||||
let archive = archiver('zip', {
|
||||
zlib: { level: 7 }
|
||||
zlib: { level: 7 },
|
||||
});
|
||||
archive.directory(path.join(ROOT, 'build-bin'), 'resin-cli');
|
||||
|
||||
@ -44,7 +45,7 @@ mkdirpAsync(path.dirname(outputFile)).then(() => new Promise((resolve, reject) =
|
||||
tag: version,
|
||||
name: `Resin-CLI ${version}`,
|
||||
reuseRelease: true,
|
||||
assets: [outputFile]
|
||||
assets: [outputFile],
|
||||
});
|
||||
}).then((release) => {
|
||||
console.log(`Release ${version} successful: ${release.html_url}`);
|
||||
|
@ -10,6 +10,7 @@
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": [
|
||||
"./**/*.ts"
|
||||
"./**/*.ts",
|
||||
"../typings/*.d.ts"
|
||||
]
|
||||
}
|
||||
|
114
capitanodoc.ts
Normal file
114
capitanodoc.ts
Normal file
@ -0,0 +1,114 @@
|
||||
export = {
|
||||
title: 'Resin CLI Documentation',
|
||||
introduction: `\
|
||||
This tool allows you to interact with the resin.io api from the comfort of your command line.
|
||||
|
||||
Please make sure your system meets the requirements as specified in the [README](https://github.com/resin-io/resin-cli).
|
||||
|
||||
To get started download the CLI from npm.
|
||||
|
||||
$ npm install resin-cli -g
|
||||
|
||||
Then authenticate yourself:
|
||||
|
||||
$ resin login
|
||||
|
||||
Now you have access to all the commands referenced below.
|
||||
|
||||
## Proxy support
|
||||
|
||||
The CLI does support HTTP(S) proxies.
|
||||
|
||||
You can configure the proxy using several methods (in order of their precedence):
|
||||
|
||||
* set the \`RESINRC_PROXY\` environment variable in the URL format (with protocol, host, port, and optionally the basic auth),
|
||||
* use the [resin config file](https://www.npmjs.com/package/resin-settings-client#documentation) (project-specific or user-level)
|
||||
and set the \`proxy\` setting. This can be:
|
||||
* a string in the URL format,
|
||||
* or an object following [this format](https://www.npmjs.com/package/global-tunnel-ng#options), which allows more control,
|
||||
* or set the conventional \`https_proxy\` / \`HTTPS_PROXY\` / \`http_proxy\` / \`HTTP_PROXY\`
|
||||
environment variable (in the same standard URL format).\
|
||||
`,
|
||||
|
||||
categories: [
|
||||
{
|
||||
title: 'Application',
|
||||
files: [ 'build/actions/app.js' ]
|
||||
},
|
||||
{
|
||||
title: 'Authentication',
|
||||
files: [ 'build/actions/auth.js' ]
|
||||
},
|
||||
{
|
||||
title: 'Device',
|
||||
files: [ 'build/actions/device.js' ]
|
||||
},
|
||||
{
|
||||
title: 'Environment Variables',
|
||||
files: [ 'build/actions/environment-variables.js' ]
|
||||
},
|
||||
{
|
||||
title: 'Help',
|
||||
files: [ 'build/actions/help.js' ]
|
||||
},
|
||||
{
|
||||
title: 'Information',
|
||||
files: [ 'build/actions/info.js' ]
|
||||
},
|
||||
{
|
||||
title: 'Keys',
|
||||
files: [ 'build/actions/keys.js' ]
|
||||
},
|
||||
{
|
||||
title: 'Logs',
|
||||
files: [ 'build/actions/logs.js' ]
|
||||
},
|
||||
{
|
||||
title: 'Sync',
|
||||
files: [ 'build/actions/sync.js' ]
|
||||
},
|
||||
{
|
||||
title: 'SSH',
|
||||
files: [ 'build/actions/ssh.js' ]
|
||||
},
|
||||
{
|
||||
title: 'Notes',
|
||||
files: [ 'build/actions/notes.js' ]
|
||||
},
|
||||
{
|
||||
title: 'OS',
|
||||
files: [ 'build/actions/os.js' ]
|
||||
},
|
||||
{
|
||||
title: 'Config',
|
||||
files: [ 'build/actions/config.js' ]
|
||||
},
|
||||
{
|
||||
title: 'Preload',
|
||||
files: [ 'build/actions/preload.js' ]
|
||||
},
|
||||
{
|
||||
title: 'Settings',
|
||||
files: [ 'build/actions/settings.js' ]
|
||||
},
|
||||
{
|
||||
title: 'Wizard',
|
||||
files: [ 'build/actions/wizard.js' ]
|
||||
},
|
||||
{
|
||||
title: 'Local',
|
||||
files: [ 'build/actions/local/index.js' ]
|
||||
},
|
||||
{
|
||||
title: 'Deploy',
|
||||
files: [
|
||||
'build/actions/build.js',
|
||||
'build/actions/deploy.js'
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Utilities',
|
||||
files: [ 'build/actions/util.js' ]
|
||||
},
|
||||
]
|
||||
};
|
@ -254,7 +254,7 @@ web-based login
|
||||
|
||||
credential-based login
|
||||
|
||||
#### --email, --e,u, --e,u <email>
|
||||
#### --email, -e, -u <email>
|
||||
|
||||
email
|
||||
|
||||
@ -310,7 +310,7 @@ Examples:
|
||||
|
||||
### Options
|
||||
|
||||
#### --application, --a,app, --a,app <application>
|
||||
#### --application, -a, --app <application>
|
||||
|
||||
application name
|
||||
|
||||
@ -464,7 +464,7 @@ Examples:
|
||||
|
||||
### Options
|
||||
|
||||
#### --application, --a,app, --a,app <application>
|
||||
#### --application, -a, --app <application>
|
||||
|
||||
application name
|
||||
|
||||
@ -482,7 +482,7 @@ Examples:
|
||||
|
||||
### Options
|
||||
|
||||
#### --application, --a,app, --a,app <application>
|
||||
#### --application, -a, --app <application>
|
||||
|
||||
application name
|
||||
|
||||
@ -529,7 +529,7 @@ Example:
|
||||
|
||||
### Options
|
||||
|
||||
#### --application, --a,app, --a,app <application>
|
||||
#### --application, -a, --app <application>
|
||||
|
||||
application name
|
||||
|
||||
@ -589,7 +589,7 @@ Examples:
|
||||
|
||||
### Options
|
||||
|
||||
#### --application, --a,app, --a,app <application>
|
||||
#### --application, -a, --app <application>
|
||||
|
||||
application name
|
||||
|
||||
@ -845,7 +845,7 @@ Examples:
|
||||
|
||||
### Options
|
||||
|
||||
#### --device, --d,dev, --d,dev <device>
|
||||
#### --device, -d, --dev <device>
|
||||
|
||||
device uuid
|
||||
|
||||
@ -937,7 +937,7 @@ Examples:
|
||||
|
||||
show advanced configuration options
|
||||
|
||||
#### --application, --a,app, --a,app <application>
|
||||
#### --application, -a, --app <application>
|
||||
|
||||
application name
|
||||
|
||||
@ -1081,7 +1081,7 @@ Examples:
|
||||
|
||||
### Options
|
||||
|
||||
#### --application, --a,app, --a,app <application>
|
||||
#### --application, -a, --app <application>
|
||||
|
||||
application name
|
||||
|
||||
|
14
extras/capitanodoc/doc-types.d.ts
vendored
Normal file
14
extras/capitanodoc/doc-types.d.ts
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
import { CommandDefinition } from 'capitano';
|
||||
|
||||
export interface Document {
|
||||
title: string;
|
||||
introduction: string;
|
||||
categories: Category[];
|
||||
}
|
||||
|
||||
export interface Category {
|
||||
title: string;
|
||||
commands: CommandDefinition[];
|
||||
}
|
||||
|
||||
export { CommandDefinition as Command };
|
@ -1,46 +0,0 @@
|
||||
_ = require('lodash')
|
||||
path = require('path')
|
||||
capitanodoc = require('../../capitanodoc')
|
||||
markdown = require('./markdown')
|
||||
|
||||
result = {}
|
||||
result.title = capitanodoc.title
|
||||
result.introduction = capitanodoc.introduction
|
||||
result.categories = []
|
||||
|
||||
for commandCategory in capitanodoc.categories
|
||||
category = {}
|
||||
category.title = commandCategory.title
|
||||
category.commands = []
|
||||
|
||||
for file in commandCategory.files
|
||||
actions = require(path.join(process.cwd(), file))
|
||||
|
||||
if actions.signature?
|
||||
category.commands.push(_.omit(actions, 'action'))
|
||||
else
|
||||
for actionName, actionCommand of actions
|
||||
category.commands.push(_.omit(actionCommand, 'action'))
|
||||
|
||||
result.categories.push(category)
|
||||
|
||||
result.toc = _.cloneDeep(result.categories)
|
||||
result.toc = _.map result.toc, (category) ->
|
||||
category.commands = _.map category.commands, (command) ->
|
||||
return {
|
||||
signature: command.signature
|
||||
anchor: '#' + command.signature
|
||||
.replace(/\s/g,'-')
|
||||
.replace(/</g, '60-')
|
||||
.replace(/>/g, '-62-')
|
||||
.replace(/\[/g, '')
|
||||
.replace(/\]/g, '-')
|
||||
.replace(/--/g, '-')
|
||||
.replace(/\.\.\./g, '')
|
||||
.replace(/\|/g, '')
|
||||
.toLowerCase()
|
||||
}
|
||||
|
||||
return category
|
||||
|
||||
console.log(markdown.display(result))
|
34
extras/capitanodoc/index.ts
Normal file
34
extras/capitanodoc/index.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import capitanodoc = require('../../capitanodoc');
|
||||
import * as _ from 'lodash';
|
||||
import * as path from 'path';
|
||||
import * as markdown from './markdown';
|
||||
import { Document, Category } from './doc-types';
|
||||
|
||||
const result = <Document> {};
|
||||
result.title = capitanodoc.title;
|
||||
result.introduction = capitanodoc.introduction;
|
||||
result.categories = [];
|
||||
|
||||
for (let commandCategory of capitanodoc.categories) {
|
||||
const category = <Category> {};
|
||||
category.title = commandCategory.title;
|
||||
category.commands = [];
|
||||
|
||||
for (let 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)) {
|
||||
const actionCommand = actions[actionName];
|
||||
category.commands.push(_.omit(actionCommand, 'action'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.categories.push(category);
|
||||
}
|
||||
|
||||
console.log(markdown.render(result));
|
@ -1,66 +0,0 @@
|
||||
_ = require('lodash')
|
||||
ent = require('ent')
|
||||
utils = require('./utils')
|
||||
|
||||
exports.command = (command) ->
|
||||
result = """
|
||||
## #{ent.encode(command.signature)}
|
||||
|
||||
#{command.help}\n
|
||||
"""
|
||||
|
||||
if not _.isEmpty(command.options)
|
||||
result += '\n### Options'
|
||||
|
||||
for option in command.options
|
||||
result += """
|
||||
\n\n#### #{utils.parseSignature(option)}
|
||||
|
||||
#{option.description}
|
||||
"""
|
||||
|
||||
result += '\n'
|
||||
|
||||
return result
|
||||
|
||||
exports.category = (category) ->
|
||||
result = """
|
||||
# #{category.title}\n
|
||||
"""
|
||||
|
||||
for command in category.commands
|
||||
result += '\n' + exports.command(command)
|
||||
|
||||
return result
|
||||
|
||||
exports.toc = (toc) ->
|
||||
result = '''
|
||||
# Table of contents\n
|
||||
'''
|
||||
|
||||
for category in toc
|
||||
|
||||
result += """
|
||||
\n- #{category.title}\n\n
|
||||
"""
|
||||
|
||||
for command in category.commands
|
||||
result += """
|
||||
\t- [#{ent.encode(command.signature)}](#{command.anchor})\n
|
||||
"""
|
||||
|
||||
return result
|
||||
|
||||
exports.display = (doc) ->
|
||||
result = """
|
||||
# #{doc.title}
|
||||
|
||||
#{doc.introduction}
|
||||
|
||||
#{exports.toc(doc.toc)}
|
||||
"""
|
||||
|
||||
for category in doc.categories
|
||||
result += '\n' + exports.category(category)
|
||||
|
||||
return result
|
68
extras/capitanodoc/markdown.ts
Normal file
68
extras/capitanodoc/markdown.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import * as _ from 'lodash';
|
||||
import * as ent from 'ent';
|
||||
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`;
|
||||
|
||||
if (!_.isEmpty(command.options)) {
|
||||
result += '\n### Options';
|
||||
|
||||
for (let option of command.options!) {
|
||||
result += `\n\n#### ${utils.parseSignature(option)}\n\n${option.description}`;
|
||||
}
|
||||
|
||||
result += '\n';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function renderCategory(category: Category) {
|
||||
let result = `# ${category.title}\n`;
|
||||
|
||||
for (let command of category.commands) {
|
||||
result += `\n${renderCommand(command)}`;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getAnchor(command: Command) {
|
||||
return '#' + command.signature
|
||||
.replace(/\s/g,'-')
|
||||
.replace(/</g, '60-')
|
||||
.replace(/>/g, '-62-')
|
||||
.replace(/\[/g, '')
|
||||
.replace(/\]/g, '-')
|
||||
.replace(/--/g, '-')
|
||||
.replace(/\.\.\./g, '')
|
||||
.replace(/\|/g, '')
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
export function renderToc(categories: Category[]) {
|
||||
let result = `# Table of contents\n`;
|
||||
|
||||
for (let category of categories) {
|
||||
|
||||
result += `\n- ${category.title}\n\n`;
|
||||
|
||||
for (let command of category.commands) {
|
||||
result += `\t- [${ent.encode(command.signature)}](${getAnchor(command)})\n`;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function render(doc: Document) {
|
||||
let result = `# ${doc.title}\n\n${doc.introduction}\n\n${renderToc(doc.categories)}`;
|
||||
|
||||
for (let category of doc.categories) {
|
||||
result += `\n${renderCategory(category)}`;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
_ = require('lodash')
|
||||
ent = require('ent')
|
||||
|
||||
exports.getOptionPrefix = (signature) ->
|
||||
if signature.length > 1
|
||||
return '--'
|
||||
else
|
||||
return '-'
|
||||
|
||||
exports.getOptionSignature = (signature) ->
|
||||
return "#{exports.getOptionPrefix(signature)}#{signature}"
|
||||
|
||||
exports.parseSignature = (option) ->
|
||||
result = exports.getOptionSignature(option.signature)
|
||||
|
||||
if not _.isEmpty(option.alias)
|
||||
if _.isString(option.alias)
|
||||
result += ", #{exports.getOptionSignature(option.alias)}"
|
||||
else
|
||||
for alias in option.alias
|
||||
result += ", #{exports.getOptionSignature(option.alias)}"
|
||||
|
||||
if option.parameter?
|
||||
result += " <#{option.parameter}>"
|
||||
|
||||
return ent.encode(result)
|
33
extras/capitanodoc/utils.ts
Normal file
33
extras/capitanodoc/utils.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { OptionDefinition } from 'capitano';
|
||||
import * as _ from 'lodash';
|
||||
import * as ent from 'ent';
|
||||
|
||||
export function getOptionPrefix(signature: string) {
|
||||
if (signature.length > 1) {
|
||||
return '--';
|
||||
} else {
|
||||
return '-';
|
||||
}
|
||||
}
|
||||
|
||||
export function getOptionSignature(signature: string) {
|
||||
return `${getOptionPrefix(signature)}${signature}`;
|
||||
}
|
||||
|
||||
export function parseSignature(option: OptionDefinition) {
|
||||
let result = getOptionSignature(option.signature);
|
||||
|
||||
if (_.isArray(option.alias)) {
|
||||
for (let alias of option.alias) {
|
||||
result += `, ${getOptionSignature(alias)}`;
|
||||
}
|
||||
} else if (_.isString(option.alias)) {
|
||||
result += `, ${getOptionSignature(option.alias)}`;
|
||||
}
|
||||
|
||||
if (option.parameter) {
|
||||
result += ` <${option.parameter}>`;
|
||||
}
|
||||
|
||||
return ent.encode(result);
|
||||
}
|
17
extras/tsconfig.json
Normal file
17
extras/tsconfig.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"outDir": "build",
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"preserveConstEnums": true,
|
||||
"removeComments": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": [
|
||||
"../typings/*.d.ts",
|
||||
"./**/*.ts"
|
||||
]
|
||||
}
|
@ -1,15 +1,12 @@
|
||||
path = require('path')
|
||||
gulp = require('gulp')
|
||||
coffee = require('gulp-coffee')
|
||||
coffeelint = require('gulp-coffeelint')
|
||||
inlinesource = require('gulp-inline-source')
|
||||
mocha = require('gulp-mocha')
|
||||
shell = require('gulp-shell')
|
||||
packageJSON = require('./package.json')
|
||||
|
||||
OPTIONS =
|
||||
config:
|
||||
coffeelint: path.join(__dirname, 'coffeelint.json')
|
||||
files:
|
||||
coffee: [ 'lib/**/*.coffee', 'gulpfile.coffee' ]
|
||||
app: 'lib/**/*.coffee'
|
||||
@ -23,18 +20,11 @@ gulp.task 'pages', ->
|
||||
.pipe(inlinesource())
|
||||
.pipe(gulp.dest('build/auth/pages'))
|
||||
|
||||
gulp.task 'coffee', [ 'lint' ], ->
|
||||
gulp.task 'coffee', ->
|
||||
gulp.src(OPTIONS.files.app)
|
||||
.pipe(coffee(bare: true, header: true))
|
||||
.pipe(gulp.dest(OPTIONS.directories.build))
|
||||
|
||||
gulp.task 'lint', ->
|
||||
gulp.src(OPTIONS.files.coffee)
|
||||
.pipe(coffeelint({
|
||||
optFile: OPTIONS.config.coffeelint
|
||||
}))
|
||||
.pipe(coffeelint.reporter())
|
||||
|
||||
gulp.task 'test', ->
|
||||
gulp.src(OPTIONS.files.tests, read: false)
|
||||
.pipe(mocha({
|
||||
|
@ -8,7 +8,7 @@ getBundleInfo = Promise.method (options) ->
|
||||
|
||||
if options.application?
|
||||
# An application was provided
|
||||
return helpers.getAppInfo(options.application)
|
||||
return helpers.getArchAndDeviceType(options.application)
|
||||
.then (app) ->
|
||||
return [app.arch, app.device_type]
|
||||
else if options.arch? and options.deviceType?
|
||||
|
@ -271,7 +271,6 @@ exports.generate =
|
||||
Promise = require('bluebird')
|
||||
writeFileAsync = Promise.promisify(require('fs').writeFile)
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
_ = require('lodash')
|
||||
form = require('resin-cli-form')
|
||||
deviceConfig = require('resin-device-config')
|
||||
prettyjson = require('prettyjson')
|
||||
|
@ -40,7 +40,7 @@ showPushProgress = (message) ->
|
||||
getBundleInfo = (options) ->
|
||||
helpers = require('../utils/helpers')
|
||||
|
||||
helpers.getAppInfo(options.appName)
|
||||
helpers.getArchAndDeviceType(options.appName)
|
||||
.then (app) ->
|
||||
[app.arch, app.device_type]
|
||||
|
||||
|
@ -401,7 +401,6 @@ exports.init =
|
||||
tmp.setGracefulCleanup()
|
||||
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
helpers = require('../utils/helpers')
|
||||
patterns = require('../utils/patterns')
|
||||
|
||||
Promise.try ->
|
||||
|
@ -1,4 +1,4 @@
|
||||
###
|
||||
/*
|
||||
Copyright 2016-2017 Resin.io
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -12,15 +12,19 @@ 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.
|
||||
###
|
||||
*/
|
||||
|
||||
exports.version =
|
||||
signature: 'version'
|
||||
description: 'output the version number'
|
||||
help: '''
|
||||
Display the Resin CLI version.
|
||||
'''
|
||||
action: (params, options, done) ->
|
||||
packageJSON = require('../../package.json')
|
||||
console.log(packageJSON.version)
|
||||
return done()
|
||||
import { CommandDefinition } from 'capitano';
|
||||
|
||||
export const version: CommandDefinition = {
|
||||
signature: 'version',
|
||||
description: 'output the version number',
|
||||
help: `\
|
||||
Display the Resin CLI version.\
|
||||
`,
|
||||
async action(_params, _options, done) {
|
||||
const packageJSON = await import('../../package.json');
|
||||
console.log(packageJSON.version);
|
||||
return done();
|
||||
},
|
||||
};
|
@ -44,7 +44,6 @@ module.exports =
|
||||
permission: 'user'
|
||||
primary: true
|
||||
action: (params, options, done) ->
|
||||
_ = require('lodash')
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
moment = require('moment')
|
||||
|
||||
|
@ -63,7 +63,7 @@ selectApplicationCommit = (builds) ->
|
||||
|
||||
if builds.length == 0
|
||||
expectedError('This application has no successful builds.')
|
||||
DEFAULT_CHOICE = {'name': LATEST, 'value': LATEST}
|
||||
DEFAULT_CHOICE = { 'name': LATEST, 'value': LATEST }
|
||||
choices = [ DEFAULT_CHOICE ].concat builds.map (build) ->
|
||||
name: "#{build.push_timestamp} - #{build.commit_hash}"
|
||||
value: build.commit_hash
|
||||
@ -150,8 +150,6 @@ module.exports =
|
||||
_ = require('lodash')
|
||||
Promise = require('bluebird')
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
streamToPromise = require('stream-to-promise')
|
||||
form = require('resin-cli-form')
|
||||
preload = require('resin-preload')
|
||||
errors = require('resin-errors')
|
||||
visuals = require('resin-cli-visuals')
|
||||
|
@ -1,4 +1,4 @@
|
||||
###
|
||||
/*
|
||||
Copyright 2016-2017 Resin.io
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -12,23 +12,27 @@ 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.
|
||||
###
|
||||
*/
|
||||
|
||||
exports.list =
|
||||
signature: 'settings'
|
||||
description: 'print current settings'
|
||||
help: '''
|
||||
Use this command to display detected settings
|
||||
import { CommandDefinition } from 'capitano';
|
||||
|
||||
Examples:
|
||||
export const list: CommandDefinition = {
|
||||
signature: 'settings',
|
||||
description: 'print current settings',
|
||||
help: `\
|
||||
Use this command to display detected settings
|
||||
|
||||
$ resin settings
|
||||
'''
|
||||
action: (params, options, done) ->
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
prettyjson = require('prettyjson')
|
||||
Examples:
|
||||
|
||||
resin.settings.getAll()
|
||||
$ resin settings\
|
||||
`,
|
||||
async action(_params, _options, done) {
|
||||
const resin = (await import('resin-sdk')).fromSharedOptions();
|
||||
const prettyjson = await import('prettyjson');
|
||||
|
||||
return resin.settings.getAll()
|
||||
.then(prettyjson.render)
|
||||
.then(console.log)
|
||||
.nodeify(done)
|
||||
.nodeify(done);
|
||||
},
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
###
|
||||
/*
|
||||
Copyright 2016-2017 Resin.io
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -12,6 +12,7 @@ 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.
|
||||
###
|
||||
*/
|
||||
|
||||
module.exports = require('resin-sync').capitano('resin-cli')
|
||||
import * as ResinSync from 'resin-sync';
|
||||
export = ResinSync.capitano('resin-cli');
|
@ -56,7 +56,6 @@ globalTunnel.initialize(proxy)
|
||||
# TODO: make this a feature of capitano https://github.com/resin-io/capitano/issues/48
|
||||
global.PROXY_CONFIG = globalTunnel.proxyConfig
|
||||
|
||||
_ = require('lodash')
|
||||
Promise = require('bluebird')
|
||||
capitano = require('capitano')
|
||||
capitanoExecuteAsync = Promise.promisify(capitano.execute)
|
||||
|
@ -1 +1 @@
|
||||
exports.sentryDsn = 'https://56d2a46124614b01b0f4086897e96110:6e175465accc41b595a96947155f61fb@sentry.io/149239'
|
||||
export const sentryDsn = 'https://56d2a46124614b01b0f4086897e96110:6e175465accc41b595a96947155f61fb@sentry.io/149239';
|
||||
|
@ -1,38 +0,0 @@
|
||||
###
|
||||
Copyright 2016-2017 Resin.io
|
||||
|
||||
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.
|
||||
###
|
||||
|
||||
chalk = require('chalk')
|
||||
errors = require('resin-cli-errors')
|
||||
patterns = require('./utils/patterns')
|
||||
Raven = require('raven')
|
||||
Promise = require('bluebird')
|
||||
|
||||
captureException = Promise.promisify(Raven.captureException.bind(Raven))
|
||||
|
||||
exports.handle = (error) ->
|
||||
message = errors.interpret(error)
|
||||
return if not message?
|
||||
|
||||
if process.env.DEBUG
|
||||
message = error.stack
|
||||
|
||||
patterns.printErrorMessage(message)
|
||||
|
||||
captureException(error)
|
||||
.timeout(1000)
|
||||
.catch(-> # Ignore any errors (from error logging, or timeouts)
|
||||
).finally ->
|
||||
process.exit(error.exitCode or 1)
|
39
lib/errors.ts
Normal file
39
lib/errors.ts
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright 2016-2017 Resin.io
|
||||
|
||||
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 errors = require('resin-cli-errors');
|
||||
import patterns = require('./utils/patterns');
|
||||
import Raven = require('raven');
|
||||
import Promise = require('bluebird');
|
||||
|
||||
const captureException = Promise.promisify<string, Error>(Raven.captureException, { context: Raven });
|
||||
|
||||
exports.handle = function(error: any) {
|
||||
let message = errors.interpret(error);
|
||||
if ((message == null)) { return; }
|
||||
|
||||
if (process.env.DEBUG) {
|
||||
message = error.stack;
|
||||
}
|
||||
|
||||
patterns.printErrorMessage(message);
|
||||
|
||||
return captureException(error)
|
||||
.timeout(1000)
|
||||
.catch(function() {
|
||||
// Ignore any errors (from error logging, or timeouts)
|
||||
}).finally(() => process.exit(error.exitCode || 1));
|
||||
};
|
@ -1,34 +0,0 @@
|
||||
_ = require('lodash')
|
||||
Mixpanel = require('mixpanel')
|
||||
Raven = require('raven')
|
||||
Promise = require('bluebird')
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
packageJSON = require('../package.json')
|
||||
|
||||
exports.getLoggerInstance = _.memoize ->
|
||||
return resin.models.config.getMixpanelToken().then(Mixpanel.init)
|
||||
|
||||
exports.trackCommand = (capitanoCommand) ->
|
||||
capitanoStateGetMatchCommandAsync = Promise.promisify(require('capitano').state.getMatchCommand)
|
||||
|
||||
return Promise.props
|
||||
resinUrl: resin.settings.get('resinUrl')
|
||||
username: resin.auth.whoami().catchReturn(undefined)
|
||||
mixpanel: exports.getLoggerInstance()
|
||||
.then ({ username, resinUrl, mixpanel }) ->
|
||||
return capitanoStateGetMatchCommandAsync(capitanoCommand.command).then (command) ->
|
||||
Raven.mergeContext(user: {
|
||||
id: username,
|
||||
username
|
||||
})
|
||||
mixpanel.track "[CLI] #{command.signature.toString()}",
|
||||
distinct_id: username
|
||||
argv: process.argv.join(' ')
|
||||
version: packageJSON.version
|
||||
node: process.version
|
||||
arch: process.arch
|
||||
resinUrl: resinUrl
|
||||
platform: process.platform
|
||||
command: capitanoCommand
|
||||
.timeout(100)
|
||||
.catchReturn()
|
42
lib/events.ts
Normal file
42
lib/events.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import * as Capitano from 'capitano';
|
||||
|
||||
import _ = require('lodash');
|
||||
import Mixpanel = require('mixpanel');
|
||||
import Raven = require('raven');
|
||||
import Promise = require('bluebird');
|
||||
import ResinSdk = require('resin-sdk');
|
||||
import packageJSON = require('../package.json');
|
||||
|
||||
const resin = ResinSdk.fromSharedOptions();
|
||||
const getMatchCommandAsync = Promise.promisify(Capitano.state.getMatchCommand);
|
||||
const getMixpanel = _.memoize<any>(() => resin.models.config.getAll().get('mixpanelToken').then(Mixpanel.init));
|
||||
|
||||
export function trackCommand(capitanoCli: Capitano.Cli) {
|
||||
return Promise.props({
|
||||
resinUrl: resin.settings.get('resinUrl'),
|
||||
username: resin.auth.whoami().catchReturn(undefined),
|
||||
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)
|
||||
.catchReturn(undefined);
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
###
|
||||
Copyright 2016-2017 Resin.io
|
||||
|
||||
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.
|
||||
###
|
||||
|
||||
exports.generateBaseConfig = (application, options) ->
|
||||
Promise = require('bluebird')
|
||||
_ = require('lodash')
|
||||
deviceConfig = require('resin-device-config')
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
|
||||
options = _.mapValues options, (value, key) ->
|
||||
if key == 'appUpdatePollInterval'
|
||||
value * 60 * 1000
|
||||
else
|
||||
value
|
||||
|
||||
Promise.props
|
||||
userId: resin.auth.getUserId()
|
||||
username: resin.auth.whoami()
|
||||
apiUrl: resin.settings.get('apiUrl')
|
||||
vpnUrl: resin.settings.get('vpnUrl')
|
||||
registryUrl: resin.settings.get('registryUrl')
|
||||
deltaUrl: resin.settings.get('deltaUrl')
|
||||
pubNubKeys: resin.models.config.getPubNubKeys()
|
||||
mixpanelToken: resin.models.config.getMixpanelToken()
|
||||
.then (results) ->
|
||||
deviceConfig.generate
|
||||
application: application
|
||||
user:
|
||||
id: results.userId
|
||||
username: results.username
|
||||
endpoints:
|
||||
api: results.apiUrl
|
||||
vpn: results.vpnUrl
|
||||
registry: results.registryUrl
|
||||
delta: results.deltaUrl
|
||||
pubnub: results.pubNubKeys
|
||||
mixpanel:
|
||||
token: results.mixpanelToken
|
||||
, options
|
||||
|
||||
exports.generateApplicationConfig = (application, options) ->
|
||||
exports.generateBaseConfig(application, options)
|
||||
.tap (config) ->
|
||||
authenticateWithApplicationKey(config, application.id)
|
||||
|
||||
exports.generateDeviceConfig = (device, deviceApiKey, options) ->
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
|
||||
resin.models.application.get(device.application_name)
|
||||
.then (application) ->
|
||||
exports.generateBaseConfig(application, options)
|
||||
.tap (config) ->
|
||||
# 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,
|
||||
# but instead for now we fall back to app keys unless the user has explicitly opted in.
|
||||
if deviceApiKey?
|
||||
authenticateWithDeviceKey(config, device.uuid, deviceApiKey)
|
||||
else
|
||||
authenticateWithApplicationKey(config, application.id)
|
||||
.then (config) ->
|
||||
# Associate a device, to prevent the supervisor
|
||||
# from creating another one on its own.
|
||||
config.registered_at = Math.floor(Date.now() / 1000)
|
||||
config.deviceId = device.id
|
||||
config.uuid = device.uuid
|
||||
|
||||
return config
|
||||
|
||||
authenticateWithApplicationKey = (config, applicationNameOrId) ->
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
resin.models.application.generateApiKey(applicationNameOrId)
|
||||
.then (apiKey) ->
|
||||
config.apiKey = apiKey
|
||||
return config
|
||||
|
||||
authenticateWithDeviceKey = (config, uuid, customDeviceApiKey) ->
|
||||
Promise = require('bluebird')
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
|
||||
Promise.try ->
|
||||
customDeviceApiKey || resin.models.device.generateDeviceKey(uuid)
|
||||
.then (deviceApiKey) ->
|
||||
config.deviceApiKey = deviceApiKey
|
||||
return config
|
108
lib/utils/config.ts
Normal file
108
lib/utils/config.ts
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
Copyright 2016-2017 Resin.io
|
||||
|
||||
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 ResinSdk = require('resin-sdk');
|
||||
import _ = require('lodash');
|
||||
import deviceConfig = require('resin-device-config');
|
||||
|
||||
const resin = ResinSdk.fromSharedOptions();
|
||||
|
||||
export function generateBaseConfig(application: ResinSdk.Application, options: {}) {
|
||||
options = _.mapValues(options, function(value, key) {
|
||||
if (key === 'appUpdatePollInterval') {
|
||||
return value * 60 * 1000;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.props({
|
||||
userId: resin.auth.getUserId(),
|
||||
username: resin.auth.whoami(),
|
||||
apiUrl: resin.settings.get('apiUrl'),
|
||||
vpnUrl: resin.settings.get('vpnUrl'),
|
||||
registryUrl: resin.settings.get('registryUrl'),
|
||||
deltaUrl: resin.settings.get('deltaUrl'),
|
||||
apiConfig: resin.models.config.getAll(),
|
||||
}).then((results) => {
|
||||
return deviceConfig.generate({
|
||||
application,
|
||||
user: {
|
||||
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,
|
||||
},
|
||||
}, options);
|
||||
});
|
||||
}
|
||||
|
||||
export function generateApplicationConfig(application: ResinSdk.Application, options: {}) {
|
||||
return generateBaseConfig(application, options)
|
||||
.tap(config => addApplicationKey(config, application.id));
|
||||
}
|
||||
|
||||
export function generateDeviceConfig(
|
||||
device: ResinSdk.Device & { application_name: string },
|
||||
deviceApiKey: string | null,
|
||||
options: {},
|
||||
) {
|
||||
return resin.models.application.get(device.application_name)
|
||||
.then(application => {
|
||||
return generateBaseConfig(application, options)
|
||||
.tap((config) => {
|
||||
// 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,
|
||||
// but instead for now we fall back to app keys unless the user has explicitly opted in.
|
||||
if (deviceApiKey) {
|
||||
return addDeviceKey(config, device.uuid, deviceApiKey);
|
||||
} else {
|
||||
return addApplicationKey(config, application.id);
|
||||
}
|
||||
});
|
||||
}).then((config) => {
|
||||
// Associate a device, to prevent the supervisor
|
||||
// from creating another one on its own.
|
||||
config.registered_at = Math.floor(Date.now() / 1000);
|
||||
config.deviceId = device.id;
|
||||
config.uuid = device.uuid;
|
||||
|
||||
return config;
|
||||
});
|
||||
}
|
||||
|
||||
function addApplicationKey(config: any, applicationNameOrId: string | number) {
|
||||
return resin.models.application.generateApiKey(applicationNameOrId)
|
||||
.tap((apiKey) => {
|
||||
config.apiKey = apiKey;
|
||||
});
|
||||
}
|
||||
|
||||
function addDeviceKey(config: any, uuid: string, customDeviceApiKey: string) {
|
||||
return Promise.try(() => {
|
||||
return customDeviceApiKey || resin.models.device.generateDeviceKey(uuid);
|
||||
}).tap((deviceApiKey) => {
|
||||
config.deviceApiKey = deviceApiKey;
|
||||
});
|
||||
}
|
@ -1,135 +0,0 @@
|
||||
###
|
||||
Copyright 2016-2017 Resin.io
|
||||
|
||||
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.
|
||||
###
|
||||
|
||||
Promise = require('bluebird')
|
||||
|
||||
exports.getGroupDefaults = (group) ->
|
||||
_ = require('lodash')
|
||||
|
||||
return _.chain(group)
|
||||
.get('options')
|
||||
.map (question) ->
|
||||
return [ question.name, question.default ]
|
||||
.fromPairs()
|
||||
.value()
|
||||
|
||||
exports.stateToString = (state) ->
|
||||
_str = require('underscore.string')
|
||||
chalk = require('chalk')
|
||||
|
||||
percentage = _str.lpad(state.percentage, 3, '0') + '%'
|
||||
result = "#{chalk.blue(percentage)} #{chalk.cyan(state.operation.command)}"
|
||||
|
||||
switch state.operation.command
|
||||
when 'copy'
|
||||
return "#{result} #{state.operation.from.path} -> #{state.operation.to.path}"
|
||||
when 'replace'
|
||||
return "#{result} #{state.operation.file.path}, #{state.operation.copy} -> #{state.operation.replace}"
|
||||
when 'run-script'
|
||||
return "#{result} #{state.operation.script}"
|
||||
else
|
||||
throw new Error("Unsupported operation: #{state.operation.type}")
|
||||
|
||||
exports.sudo = (command) ->
|
||||
_ = require('lodash')
|
||||
os = require('os')
|
||||
|
||||
if os.platform() isnt 'win32'
|
||||
console.log('If asked please type your computer password to continue')
|
||||
|
||||
command = _.union(_.take(process.argv, 2), command)
|
||||
presidentExecuteAsync = Promise.promisify(require('president').execute)
|
||||
return presidentExecuteAsync(command)
|
||||
|
||||
exports.getManifest = (image, deviceType) ->
|
||||
rindle = require('rindle')
|
||||
imagefs = require('resin-image-fs')
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
|
||||
# Attempt to read manifest from the first
|
||||
# partition, but fallback to the API if
|
||||
# we encounter any errors along the way.
|
||||
imagefs.read
|
||||
image: image
|
||||
partition:
|
||||
primary: 1
|
||||
path: '/device-type.json'
|
||||
.then(rindle.extractAsync)
|
||||
.then(JSON.parse)
|
||||
.catch ->
|
||||
resin.models.device.getManifestBySlug(deviceType)
|
||||
|
||||
exports.osProgressHandler = (step) ->
|
||||
rindle = require('rindle')
|
||||
visuals = require('resin-cli-visuals')
|
||||
|
||||
step.on('stdout', process.stdout.write.bind(process.stdout))
|
||||
step.on('stderr', process.stderr.write.bind(process.stderr))
|
||||
|
||||
step.on 'state', (state) ->
|
||||
return if state.operation.command is 'burn'
|
||||
console.log(exports.stateToString(state))
|
||||
|
||||
progressBars =
|
||||
write: new visuals.Progress('Writing Device OS')
|
||||
check: new visuals.Progress('Validating Device OS')
|
||||
|
||||
step.on 'burn', (state) ->
|
||||
progressBars[state.type].update(state)
|
||||
|
||||
return rindle.wait(step)
|
||||
|
||||
exports.getAppInfo = (application) ->
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
_ = require('lodash')
|
||||
Promise.join(
|
||||
getApplication(application),
|
||||
resin.models.config.getDeviceTypes(),
|
||||
(app, config) ->
|
||||
config = _.find(config, 'slug': app.device_type)
|
||||
if !config?
|
||||
throw new Error('Could not read application information!')
|
||||
app.arch = config.arch
|
||||
return app
|
||||
)
|
||||
|
||||
getApplication = (application) ->
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
|
||||
# Check for an app of the form `user/application`, and send
|
||||
# this off to a special handler (before importing any modules)
|
||||
if (match = /(\w+)\/(\w+)/.exec(application))
|
||||
return resin.models.application.getAppWithOwner(match[2], match[1])
|
||||
|
||||
return resin.models.application.get(application)
|
||||
|
||||
# A function to reliably execute a command
|
||||
# in all supported operating systems, including
|
||||
# different Windows environments like `cmd.exe`
|
||||
# and `Cygwin`.
|
||||
exports.getSubShellCommand = (command) ->
|
||||
os = require('os')
|
||||
|
||||
if os.platform() is 'win32'
|
||||
return {
|
||||
program: 'cmd.exe'
|
||||
args: [ '/s', '/c', command ]
|
||||
}
|
||||
else
|
||||
return {
|
||||
program: '/bin/sh'
|
||||
args: [ '-c', command ]
|
||||
}
|
150
lib/utils/helpers.ts
Normal file
150
lib/utils/helpers.ts
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
Copyright 2016-2017 Resin.io
|
||||
|
||||
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 os = require('os');
|
||||
import Promise = require('bluebird');
|
||||
import _ = require('lodash');
|
||||
import chalk from 'chalk';
|
||||
import rindle = require('rindle');
|
||||
import imagefs = require('resin-image-fs');
|
||||
import visuals = require('resin-cli-visuals');
|
||||
import ResinSdk = require('resin-sdk');
|
||||
|
||||
import { execute } from 'president';
|
||||
import { InitializeEmitter, OperationState } from 'resin-device-init';
|
||||
|
||||
const extractStreamAsync = Promise.promisify(rindle.extract);
|
||||
const waitStreamAsync = Promise.promisify(rindle.wait);
|
||||
const presidentExecuteAsync = Promise.promisify(execute);
|
||||
|
||||
const resin = ResinSdk.fromSharedOptions();
|
||||
|
||||
export function getGroupDefaults(
|
||||
group: { options: { name: string, default?: string }[] },
|
||||
): { [name: string]: string | undefined } {
|
||||
return _.chain(group)
|
||||
.get('options')
|
||||
.map((question) => [ question.name, question.default ])
|
||||
.fromPairs()
|
||||
.value();
|
||||
}
|
||||
|
||||
export function stateToString(state: OperationState) {
|
||||
const percentage = _.padStart(`${state.percentage}`, 3, '0');
|
||||
const result = `${chalk.blue(percentage + '%')} ${chalk.cyan(state.operation.command)}`;
|
||||
|
||||
switch (state.operation.command) {
|
||||
case 'copy':
|
||||
return `${result} ${state.operation.from.path} -> ${state.operation.to.path}`;
|
||||
case 'replace':
|
||||
return `${result} ${state.operation.file.path}, ${state.operation.copy} -> ${state.operation.replace}`;
|
||||
case 'run-script':
|
||||
return `${result} ${state.operation.script}`;
|
||||
default:
|
||||
throw new Error(`Unsupported operation: ${state.operation.command}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function sudo(command: string[]) {
|
||||
if (os.platform() !== 'win32') {
|
||||
console.log('If asked please type your computer password to continue');
|
||||
}
|
||||
|
||||
command = _.union(_.take(process.argv, 2), command);
|
||||
|
||||
return presidentExecuteAsync(command);
|
||||
}
|
||||
|
||||
export function getManifest(image: string, deviceType: string): Promise<ResinSdk.DeviceType> {
|
||||
// Attempt to read manifest from the first
|
||||
// partition, but fallback to the API if
|
||||
// we encounter any errors along the way.
|
||||
return imagefs.read({
|
||||
image,
|
||||
partition: {
|
||||
primary: 1,
|
||||
},
|
||||
path: '/device-type.json',
|
||||
})
|
||||
.then(extractStreamAsync)
|
||||
.then(JSON.parse)
|
||||
.catch(() => resin.models.device.getManifestBySlug(deviceType));
|
||||
}
|
||||
|
||||
export function osProgressHandler(step: InitializeEmitter) {
|
||||
step.on('stdout', process.stdout.write.bind(process.stdout));
|
||||
step.on('stderr', process.stderr.write.bind(process.stderr));
|
||||
|
||||
step.on('state', function(state) {
|
||||
if (state.operation.command === 'burn') { return; }
|
||||
console.log(exports.stateToString(state));
|
||||
});
|
||||
|
||||
const progressBars = {
|
||||
write: new visuals.Progress('Writing Device OS'),
|
||||
check: new visuals.Progress('Validating Device OS'),
|
||||
};
|
||||
|
||||
step.on('burn', state => progressBars[state.type].update(state));
|
||||
|
||||
return waitStreamAsync(step);
|
||||
}
|
||||
|
||||
export function getArchAndDeviceType(applicationName: string): Promise<{ arch: string, device_type: string }> {
|
||||
return Promise.join(
|
||||
getApplication(applicationName),
|
||||
resin.models.config.getDeviceTypes(),
|
||||
function (app, deviceTypes) {
|
||||
const config = _.find(deviceTypes, { slug: app.device_type });
|
||||
|
||||
if (!config) {
|
||||
throw new Error('Could not read application information!');
|
||||
}
|
||||
|
||||
return { device_type: app.device_type, arch: config.arch };
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function getApplication(applicationName: string) {
|
||||
// Check for an app of the form `user/application`, and send
|
||||
// that off to a special handler (before importing any modules)
|
||||
const match = /(\w+)\/(\w+)/.exec(applicationName);
|
||||
|
||||
if (match) {
|
||||
return resin.models.application.getAppByOwner(match[2], match[1]);
|
||||
}
|
||||
|
||||
return resin.models.application.get(applicationName);
|
||||
}
|
||||
|
||||
// A function to reliably execute a command
|
||||
// in all supported operating systems, including
|
||||
// different Windows environments like `cmd.exe`
|
||||
// and `Cygwin`.
|
||||
export function getSubShellCommand(command: string) {
|
||||
if (os.platform() === 'win32') {
|
||||
return {
|
||||
program: 'cmd.exe',
|
||||
args: [ '/s', '/c', command ],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
program: '/bin/sh',
|
||||
args: [ '-c', command ],
|
||||
};
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
eol = require('os').EOL
|
||||
|
||||
module.exports = class Logger
|
||||
constructor: ->
|
||||
{ StreamLogger } = require('resin-stream-logger')
|
||||
colors = require('colors')
|
||||
_ = require('lodash')
|
||||
|
||||
logger = new StreamLogger()
|
||||
logger.addPrefix('build', colors.blue('[Build]'))
|
||||
logger.addPrefix('info', colors.cyan('[Info]'))
|
||||
logger.addPrefix('debug', colors.magenta('[Debug]'))
|
||||
logger.addPrefix('success', colors.green('[Success]'))
|
||||
logger.addPrefix('warn', colors.yellow('[Warn]'))
|
||||
logger.addPrefix('error', colors.red('[Error]'))
|
||||
|
||||
@streams =
|
||||
build: logger.createLogStream('build'),
|
||||
info: logger.createLogStream('info'),
|
||||
debug: logger.createLogStream('debug'),
|
||||
success: logger.createLogStream('success'),
|
||||
warn: logger.createLogStream('warn'),
|
||||
error: logger.createLogStream('error')
|
||||
|
||||
_.mapKeys @streams, (stream, key) ->
|
||||
if key isnt 'debug'
|
||||
stream.pipe(process.stdout)
|
||||
else
|
||||
stream.pipe(process.stdout) if process.env.DEBUG?
|
||||
|
||||
@formatMessage = logger.formatWithPrefix.bind(logger)
|
||||
|
||||
logInfo: (msg) ->
|
||||
@streams.info.write(msg + eol)
|
||||
|
||||
logDebug: (msg) ->
|
||||
@streams.debug.write(msg + eol)
|
||||
|
||||
logSuccess: (msg) ->
|
||||
@streams.success.write(msg + eol)
|
||||
|
||||
logWarn: (msg) ->
|
||||
@streams.warn.write(msg + eol)
|
||||
|
||||
logError: (msg) ->
|
||||
@streams.error.write(msg + eol)
|
64
lib/utils/logger.ts
Normal file
64
lib/utils/logger.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import { EOL as eol } from 'os';
|
||||
import _ = require('lodash');
|
||||
import chalk from 'chalk';
|
||||
import { StreamLogger } from 'resin-stream-logger';
|
||||
|
||||
export class Logger {
|
||||
public streams: {
|
||||
build: NodeJS.ReadWriteStream;
|
||||
info: NodeJS.ReadWriteStream;
|
||||
debug: NodeJS.ReadWriteStream;
|
||||
success: NodeJS.ReadWriteStream;
|
||||
warn: NodeJS.ReadWriteStream;
|
||||
error: NodeJS.ReadWriteStream;
|
||||
};
|
||||
|
||||
public formatMessage: (name: string, message: string) => string;
|
||||
|
||||
constructor() {
|
||||
const logger = new StreamLogger();
|
||||
logger.addPrefix('build', chalk.blue('[Build]'));
|
||||
logger.addPrefix('info', chalk.cyan('[Info]'));
|
||||
logger.addPrefix('debug', chalk.magenta('[Debug]'));
|
||||
logger.addPrefix('success', chalk.green('[Success]'));
|
||||
logger.addPrefix('warn', chalk.yellow('[Warn]'));
|
||||
logger.addPrefix('error', chalk.red('[Error]'));
|
||||
|
||||
this.streams = {
|
||||
build: logger.createLogStream('build'),
|
||||
info: logger.createLogStream('info'),
|
||||
debug: logger.createLogStream('debug'),
|
||||
success: logger.createLogStream('success'),
|
||||
warn: logger.createLogStream('warn'),
|
||||
error: logger.createLogStream('error'),
|
||||
};
|
||||
|
||||
_.forEach(this.streams, function(stream, key) {
|
||||
if (key !== 'debug' || process.env.DEBUG) {
|
||||
stream.pipe(process.stdout);
|
||||
}
|
||||
});
|
||||
|
||||
this.formatMessage = logger.formatWithPrefix.bind(logger);
|
||||
}
|
||||
|
||||
logInfo(msg: string) {
|
||||
return this.streams.info.write(msg + eol);
|
||||
}
|
||||
|
||||
logDebug(msg: string) {
|
||||
return this.streams.debug.write(msg + eol);
|
||||
}
|
||||
|
||||
logSuccess(msg: string) {
|
||||
return this.streams.success.write(msg + eol);
|
||||
}
|
||||
|
||||
logWarn(msg: string) {
|
||||
return this.streams.warn.write(msg + eol);
|
||||
}
|
||||
|
||||
logError(msg: string) {
|
||||
return this.streams.error.write(msg + eol);
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
exports.reachingOut = '''
|
||||
If you need help, or just want to say hi, don't hesitate in reaching out at:
|
||||
|
||||
GitHub: https://github.com/resin-io/resin-cli/issues/new
|
||||
Forums: https://forums.resin.io
|
||||
'''
|
||||
|
||||
exports.getHelp = '''
|
||||
If you need help, don't hesitate in contacting us at:
|
||||
|
||||
GitHub: https://github.com/resin-io/resin-cli/issues/new
|
||||
Forums: https://forums.resin.io
|
||||
'''
|
||||
|
||||
exports.resinAsciiArt = '''
|
||||
______ _ _
|
||||
| ___ \\ (_) (_)
|
||||
| |_/ /___ ___ _ _ __ _ ___
|
||||
| // _ \\/ __| | '_ \\ | |/ _ \\
|
||||
| |\\ \\ __/\\__ \\ | | | |_| | (_) |
|
||||
\\_| \\_\\___||___/_|_| |_(_)_|\\___/
|
||||
'''
|
22
lib/utils/messages.ts
Normal file
22
lib/utils/messages.ts
Normal file
@ -0,0 +1,22 @@
|
||||
export const reachingOut = `\
|
||||
If you need help, or just want to say hi, don't hesitate in reaching out at:
|
||||
|
||||
GitHub: https://github.com/resin-io/resin-cli/issues/new
|
||||
Forums: https://forums.resin.io\
|
||||
`;
|
||||
|
||||
export const getHelp = `\
|
||||
If you need help, don't hesitate in contacting us at:
|
||||
|
||||
GitHub: https://github.com/resin-io/resin-cli/issues/new
|
||||
Forums: https://forums.resin.io\
|
||||
`;
|
||||
|
||||
export const resinAsciiArt = `\
|
||||
______ _ _
|
||||
| ___ \\ (_) (_)
|
||||
| |_/ /___ ___ _ _ __ _ ___
|
||||
| // _ \\/ __| | '_ \\ | |/ _ \\
|
||||
| |\\ \\ __/\\__ \\ | | | |_| | (_) |
|
||||
\\_| \\_\\___||___/_|_| |_(_)_|\\___/\
|
||||
`;
|
@ -1,181 +0,0 @@
|
||||
###
|
||||
Copyright 2016-2017 Resin.io
|
||||
|
||||
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.
|
||||
###
|
||||
|
||||
_ = require('lodash')
|
||||
Promise = require('bluebird')
|
||||
form = require('resin-cli-form')
|
||||
visuals = require('resin-cli-visuals')
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
chalk = require('chalk')
|
||||
validation = require('./validation')
|
||||
messages = require('./messages')
|
||||
|
||||
exports.authenticate = (options) ->
|
||||
return form.run [
|
||||
message: 'Email:'
|
||||
name: 'email'
|
||||
type: 'input'
|
||||
validate: validation.validateEmail
|
||||
,
|
||||
message: 'Password:'
|
||||
name: 'password'
|
||||
type: 'password'
|
||||
],
|
||||
override: options
|
||||
.then(resin.auth.login)
|
||||
.then(resin.auth.twoFactor.isPassed)
|
||||
.then (isTwoFactorAuthPassed) ->
|
||||
return if isTwoFactorAuthPassed
|
||||
return form.ask
|
||||
message: 'Two factor auth challenge:'
|
||||
name: 'code'
|
||||
type: 'input'
|
||||
.then(resin.auth.twoFactor.challenge)
|
||||
.catch (error) ->
|
||||
resin.auth.logout().then ->
|
||||
if error.name is 'ResinRequestError' and error.statusCode is 401
|
||||
throw new Error('Invalid two factor authentication code')
|
||||
throw error
|
||||
|
||||
exports.askLoginType = ->
|
||||
return form.ask
|
||||
message: 'How would you like to login?'
|
||||
name: 'loginType'
|
||||
type: 'list'
|
||||
choices: [
|
||||
name: 'Web authorization (recommended)'
|
||||
value: 'web'
|
||||
,
|
||||
name: 'Credentials'
|
||||
value: 'credentials'
|
||||
,
|
||||
name: 'Authentication token'
|
||||
value: 'token'
|
||||
,
|
||||
name: 'I don\'t have a Resin account!'
|
||||
value: 'register'
|
||||
]
|
||||
|
||||
exports.selectDeviceType = ->
|
||||
resin.models.device.getSupportedDeviceTypes().then (deviceTypes) ->
|
||||
return form.ask
|
||||
message: 'Device Type'
|
||||
type: 'list'
|
||||
choices: deviceTypes
|
||||
|
||||
exports.confirm = (yesOption, message, yesMessage) ->
|
||||
Promise.try ->
|
||||
if yesOption
|
||||
console.log(yesMessage) if yesMessage
|
||||
return true
|
||||
return form.ask
|
||||
message: message
|
||||
type: 'confirm'
|
||||
default: false
|
||||
.then (confirmed) ->
|
||||
if not confirmed
|
||||
throw new Error('Aborted')
|
||||
|
||||
exports.selectApplication = (filter) ->
|
||||
resin.models.application.hasAny().then (hasAnyApplications) ->
|
||||
if not hasAnyApplications
|
||||
throw new Error('You don\'t have any applications')
|
||||
|
||||
return resin.models.application.getAll()
|
||||
.filter(filter or _.constant(true))
|
||||
.then (applications) ->
|
||||
return form.ask
|
||||
message: 'Select an application'
|
||||
type: 'list'
|
||||
choices: _.map applications, (application) ->
|
||||
return {
|
||||
name: "#{application.app_name} (#{application.device_type})"
|
||||
value: application.app_name
|
||||
}
|
||||
|
||||
exports.selectOrCreateApplication = ->
|
||||
resin.models.application.hasAny().then (hasAnyApplications) ->
|
||||
return if not hasAnyApplications
|
||||
resin.models.application.getAll().then (applications) ->
|
||||
applications = _.map applications, (application) ->
|
||||
return {
|
||||
name: "#{application.app_name} (#{application.device_type})"
|
||||
value: application.app_name
|
||||
}
|
||||
|
||||
applications.unshift
|
||||
name: 'Create a new application'
|
||||
value: null
|
||||
|
||||
return form.ask
|
||||
message: 'Select an application'
|
||||
type: 'list'
|
||||
choices: applications
|
||||
.then (application) ->
|
||||
return application if application?
|
||||
form.ask
|
||||
message: 'Choose a Name for your new application'
|
||||
type: 'input'
|
||||
validate: validation.validateApplicationName
|
||||
|
||||
exports.awaitDevice = (uuid) ->
|
||||
resin.models.device.getName(uuid).then (deviceName) ->
|
||||
spinner = new visuals.Spinner("Waiting for #{deviceName} to come online")
|
||||
|
||||
poll = ->
|
||||
resin.models.device.isOnline(uuid).then (isOnline) ->
|
||||
if isOnline
|
||||
spinner.stop()
|
||||
console.info("The device **#{deviceName}** is online!")
|
||||
return
|
||||
else
|
||||
|
||||
# Spinner implementation is smart enough to
|
||||
# not start again if it was already started
|
||||
spinner.start()
|
||||
|
||||
return Promise.delay(3000).then(poll)
|
||||
|
||||
console.info("Waiting for #{deviceName} to connect to resin...")
|
||||
poll().return(uuid)
|
||||
|
||||
exports.inferOrSelectDevice = (preferredUuid) ->
|
||||
resin.models.device.getAll()
|
||||
.filter (device) ->
|
||||
device.is_online
|
||||
.then (onlineDevices) ->
|
||||
if _.isEmpty(onlineDevices)
|
||||
throw new Error('You don\'t have any devices online')
|
||||
|
||||
return form.ask
|
||||
message: 'Select a device'
|
||||
type: 'list'
|
||||
default: if preferredUuid in _.map(onlineDevices, 'uuid') then preferredUuid else onlineDevices[0].uuid
|
||||
choices: _.map onlineDevices, (device) ->
|
||||
return {
|
||||
name: "#{device.name or 'Untitled'} (#{device.uuid.slice(0, 7)})"
|
||||
value: device.uuid
|
||||
}
|
||||
|
||||
exports.printErrorMessage = (message) ->
|
||||
console.error(chalk.red(message))
|
||||
console.error(chalk.red("\n#{messages.getHelp}\n"))
|
||||
|
||||
exports.expectedError = (message) ->
|
||||
if message instanceof Error
|
||||
message = message.message
|
||||
exports.printErrorMessage(message)
|
||||
process.exit(1)
|
234
lib/utils/patterns.ts
Normal file
234
lib/utils/patterns.ts
Normal file
@ -0,0 +1,234 @@
|
||||
/*
|
||||
Copyright 2016-2017 Resin.io
|
||||
|
||||
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 _ = require('lodash');
|
||||
import Promise = require('bluebird');
|
||||
import form = require('resin-cli-form');
|
||||
import visuals = require('resin-cli-visuals');
|
||||
import ResinSdk = require('resin-sdk');
|
||||
import chalk from 'chalk';
|
||||
import validation = require('./validation');
|
||||
import messages = require('./messages');
|
||||
|
||||
const resin = ResinSdk.fromSharedOptions();
|
||||
|
||||
export function authenticate(options: {}): Promise<void> {
|
||||
return form.run([{
|
||||
message: 'Email:',
|
||||
name: 'email',
|
||||
type: 'input',
|
||||
validate: validation.validateEmail,
|
||||
}, {
|
||||
message: 'Password:',
|
||||
name: 'password',
|
||||
type: 'password',
|
||||
}], { override: options })
|
||||
.then(resin.auth.login)
|
||||
.then(resin.auth.twoFactor.isPassed)
|
||||
.then((isTwoFactorAuthPassed: boolean) => {
|
||||
if (isTwoFactorAuthPassed) { return; }
|
||||
|
||||
return form.ask({
|
||||
message: 'Two factor auth challenge:',
|
||||
name: 'code',
|
||||
type: 'input',
|
||||
})
|
||||
.then(resin.auth.twoFactor.challenge)
|
||||
.catch((error: any) => {
|
||||
return resin.auth.logout().then(() => {
|
||||
if ((error.name === 'ResinRequestError') && (error.statusCode === 401)) {
|
||||
throw new Error('Invalid two factor authentication code');
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function askLoginType() {
|
||||
return form.ask({
|
||||
message: 'How would you like to login?',
|
||||
name: 'loginType',
|
||||
type: 'list',
|
||||
choices: [{
|
||||
name: 'Web authorization (recommended)',
|
||||
value: 'web',
|
||||
}, {
|
||||
name: 'Credentials',
|
||||
value: 'credentials',
|
||||
}, {
|
||||
name: 'Authentication token',
|
||||
value: 'token',
|
||||
}, {
|
||||
name: 'I don\'t have a Resin account!',
|
||||
value: 'register',
|
||||
}],
|
||||
});
|
||||
}
|
||||
|
||||
export function selectDeviceType() {
|
||||
return resin.models.device.getSupportedDeviceTypes()
|
||||
.then(deviceTypes => {
|
||||
return form.ask({
|
||||
message: 'Device Type',
|
||||
type: 'list',
|
||||
choices: deviceTypes,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function confirm(yesOption: string, message: string, yesMessage: string) {
|
||||
return Promise.try(function () {
|
||||
if (yesOption) {
|
||||
if (yesMessage) { console.log(yesMessage); }
|
||||
return true;
|
||||
}
|
||||
|
||||
return form.ask({
|
||||
message,
|
||||
type: 'confirm',
|
||||
default: false,
|
||||
});
|
||||
}).then(function(confirmed) {
|
||||
if (!confirmed) {
|
||||
throw new Error('Aborted');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function selectApplication(filter: (app: ResinSdk.Application) => boolean) {
|
||||
resin.models.application.hasAny().then(function(hasAnyApplications) {
|
||||
if (!hasAnyApplications) {
|
||||
throw new Error('You don\'t have any applications');
|
||||
}
|
||||
|
||||
return resin.models.application.getAll();
|
||||
})
|
||||
.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 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) {
|
||||
return resin.models.device.getName(uuid)
|
||||
.then((deviceName) => {
|
||||
const spinner = new visuals.Spinner(`Waiting for ${deviceName} to come online`);
|
||||
|
||||
const poll = (): Promise<void> => {
|
||||
return resin.models.device.isOnline(uuid)
|
||||
.then(function(isOnline) {
|
||||
if (isOnline) {
|
||||
spinner.stop();
|
||||
console.info(`The device **${deviceName}** is online!`);
|
||||
return;
|
||||
} else {
|
||||
// Spinner implementation is smart enough to
|
||||
// not start again if it was already started
|
||||
spinner.start();
|
||||
|
||||
return Promise.delay(3000).then(poll);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
console.info(`Waiting for ${deviceName} to connect to resin...`);
|
||||
return poll().return(uuid);
|
||||
});
|
||||
}
|
||||
|
||||
export function inferOrSelectDevice(preferredUuid: string) {
|
||||
return resin.models.device.getAll()
|
||||
.filter<ResinSdk.Device>((device) => device.is_online)
|
||||
.then((onlineDevices) => {
|
||||
if (_.isEmpty(onlineDevices)) {
|
||||
throw new Error('You don\'t have any devices online');
|
||||
}
|
||||
|
||||
const defaultUuid = _.map(onlineDevices, 'uuid').includes(preferredUuid) ?
|
||||
preferredUuid :
|
||||
onlineDevices[0].uuid;
|
||||
|
||||
return form.ask({
|
||||
message: 'Select a device',
|
||||
type: 'list',
|
||||
default: defaultUuid,
|
||||
choices: _.map(onlineDevices, device => ({
|
||||
name: `${device.name || 'Untitled'} (${device.uuid.slice(0, 7)})`,
|
||||
value: device.uuid,
|
||||
}),
|
||||
)});
|
||||
});
|
||||
}
|
||||
|
||||
export function printErrorMessage(message: string) {
|
||||
console.error(chalk.red(message));
|
||||
console.error(chalk.red(`\n${messages.getHelp}\n`));
|
||||
}
|
||||
|
||||
export function expectedError(message: string | Error) {
|
||||
if (message instanceof Error) {
|
||||
({ message } = message);
|
||||
}
|
||||
|
||||
printErrorMessage(message);
|
||||
process.exit(1);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
###
|
||||
/*
|
||||
Copyright 2016-2017 Resin.io
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -12,18 +12,22 @@ 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.
|
||||
###
|
||||
*/
|
||||
|
||||
nplugm = require('nplugm')
|
||||
_ = require('lodash')
|
||||
capitano = require('capitano')
|
||||
patterns = require('./patterns')
|
||||
import nplugm = require('nplugm');
|
||||
import _ = require('lodash');
|
||||
import capitano = require('capitano');
|
||||
import patterns = require('./patterns');
|
||||
|
||||
exports.register = (regex) ->
|
||||
nplugm.list(regex).map (plugin) ->
|
||||
command = require(plugin)
|
||||
command.plugin = true
|
||||
return capitano.command(command) if not _.isArray(command)
|
||||
return _.each(command, capitano.command)
|
||||
.catch (error) ->
|
||||
patterns.printErrorMessage(error.message)
|
||||
export function register(regex: RegExp): Promise<void> {
|
||||
return nplugm.list(regex).map(async function(plugin: any) {
|
||||
const command = await import(plugin);
|
||||
command.plugin = true;
|
||||
if (!_.isArray(command)) {
|
||||
return capitano.command(command);
|
||||
}
|
||||
return _.each(command, capitano.command);
|
||||
}).catch((error: Error) => {
|
||||
return patterns.printErrorMessage(error.message);
|
||||
});
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
exports.buffer = (stream, bufferFile) ->
|
||||
Promise = require('bluebird')
|
||||
fs = require('fs')
|
||||
|
||||
fileWriteStream = fs.createWriteStream(bufferFile)
|
||||
|
||||
new Promise (resolve, reject) ->
|
||||
stream
|
||||
.on('error', reject)
|
||||
.on('end', resolve)
|
||||
.pipe(fileWriteStream)
|
||||
.then ->
|
||||
new Promise (resolve, reject) ->
|
||||
fs.createReadStream(bufferFile)
|
||||
.on 'open', ->
|
||||
resolve(this)
|
||||
.on('error', reject)
|
19
lib/utils/streams.ts
Normal file
19
lib/utils/streams.ts
Normal file
@ -0,0 +1,19 @@
|
||||
export async function buffer(stream: NodeJS.ReadableStream, bufferFile: string) {
|
||||
const Promise = await import('bluebird');
|
||||
const fs = await import('fs');
|
||||
|
||||
const fileWriteStream = fs.createWriteStream(bufferFile);
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
stream
|
||||
.on('error', reject)
|
||||
.on('end', resolve)
|
||||
.pipe(fileWriteStream);
|
||||
}).then(() => new Promise(function(resolve, reject) {
|
||||
const stream = fs.createReadStream(bufferFile);
|
||||
|
||||
stream
|
||||
.on('open', () => resolve(stream))
|
||||
.on('error', reject);
|
||||
}));
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
###
|
||||
Copyright 2016-2017 Resin.io
|
||||
|
||||
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.
|
||||
###
|
||||
|
||||
updateNotifier = require('update-notifier')
|
||||
isRoot = require('is-root')
|
||||
packageJSON = require('../../package.json')
|
||||
|
||||
# Check for an update once a day. 1 day granularity should be
|
||||
# enough, rather than every run.
|
||||
resinUpdateInterval = 1000 * 60 * 60 * 24 * 1
|
||||
|
||||
# `update-notifier` creates files to make the next
|
||||
# running time ask for updated, however this can lead
|
||||
# to ugly EPERM issues if those files are created as root.
|
||||
if not isRoot()
|
||||
notifier = updateNotifier
|
||||
pkg: packageJSON
|
||||
updateCheckInterval: resinUpdateInterval
|
||||
|
||||
exports.hasAvailableUpdate = ->
|
||||
return notifier?
|
||||
|
||||
exports.notify = ->
|
||||
return if not exports.hasAvailableUpdate()
|
||||
notifier.notify(defer: false)
|
||||
if notifier.update?
|
||||
console.log('Notice that you might need administrator privileges depending on your setup\n')
|
47
lib/utils/update.ts
Normal file
47
lib/utils/update.ts
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright 2016-2017 Resin.io
|
||||
|
||||
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 UpdateNotifier from 'update-notifier';
|
||||
import isRoot = require('is-root');
|
||||
import packageJSON = require('../../package.json');
|
||||
|
||||
// Check for an update once a day. 1 day granularity should be
|
||||
// enough, rather than every run.
|
||||
const resinUpdateInterval = 1000 * 60 * 60 * 24 * 1;
|
||||
|
||||
let notifier: UpdateNotifier.UpdateNotifier;
|
||||
|
||||
// `update-notifier` creates files to make the next
|
||||
// running time ask for updated, however this can lead
|
||||
// to ugly EPERM issues if those files are created as root.
|
||||
if (!isRoot()) {
|
||||
notifier = UpdateNotifier({
|
||||
pkg: packageJSON,
|
||||
updateCheckInterval: resinUpdateInterval,
|
||||
});
|
||||
}
|
||||
|
||||
export function notify() {
|
||||
if (!notifier) {
|
||||
return;
|
||||
}
|
||||
|
||||
notifier.notify({ defer: false });
|
||||
|
||||
if (notifier.update != null) {
|
||||
console.log('Notice that you might need administrator privileges depending on your setup\n');
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
###
|
||||
/*
|
||||
Copyright 2016-2017 Resin.io
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -12,24 +12,30 @@ 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.
|
||||
###
|
||||
*/
|
||||
|
||||
validEmail = require('@resin.io/valid-email')
|
||||
import validEmail = require('@resin.io/valid-email');
|
||||
|
||||
exports.validateEmail = (input) ->
|
||||
if not validEmail(input)
|
||||
return 'Email is not valid'
|
||||
export function validateEmail(input: string) {
|
||||
if (!validEmail(input)) {
|
||||
return 'Email is not valid';
|
||||
}
|
||||
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
|
||||
exports.validatePassword = (input) ->
|
||||
if input.length < 8
|
||||
return 'Password should be 8 characters long'
|
||||
export function validatePassword(input: string) {
|
||||
if (input.length < 8) {
|
||||
return 'Password should be 8 characters long';
|
||||
}
|
||||
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
|
||||
exports.validateApplicationName = (input) ->
|
||||
if input.length < 4
|
||||
return 'The application name should be at least 4 characters'
|
||||
export function validateApplicationName(input: string) {
|
||||
if (input.length < 4) {
|
||||
return 'The application name should be at least 4 characters';
|
||||
}
|
||||
|
||||
return true
|
||||
return true;
|
||||
}
|
20
package.json
20
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "resin-cli",
|
||||
"version": "6.12.2",
|
||||
"version": "6.12.3",
|
||||
"description": "The official resin.io CLI tool",
|
||||
"main": "./build/actions/index.js",
|
||||
"homepage": "https://github.com/resin-io/resin-cli",
|
||||
@ -31,15 +31,15 @@
|
||||
"scripts": {
|
||||
"prebuild": "rimraf build/ build-bin/ build-zip/",
|
||||
"build": "npm run build:src && npm run build:bin",
|
||||
"build:src": "gulp build && tsc && npm run doc",
|
||||
"build:src": "npm run lint && gulp build && tsc && npm run build:doc",
|
||||
"build:doc": "mkdirp doc/ && ts-node extras/capitanodoc/index.ts > doc/cli.markdown",
|
||||
"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",
|
||||
"pretest": "npm run build",
|
||||
"test": "gulp test",
|
||||
"ci": "npm run test && catch-uncommitted",
|
||||
"doc": "mkdirp doc/ && coffee extras/capitanodoc/index.coffee > doc/cli.markdown",
|
||||
"watch": "gulp watch",
|
||||
"lint": "gulp lint",
|
||||
"lint": "resin-lint lib/ tests/ && resin-lint --typescript automation/ extras/ lib/ typings/ tests/",
|
||||
"prepublish": "require-npm4-to-publish",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
@ -54,15 +54,18 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/archiver": "^2.0.1",
|
||||
"@types/bluebird": "^3.5.19",
|
||||
"@types/fs-extra": "^5.0.0",
|
||||
"@types/is-root": "^1.0.0",
|
||||
"@types/mkdirp": "^0.5.2",
|
||||
"@types/prettyjson": "0.0.28",
|
||||
"@types/raven": "^2.1.2",
|
||||
"catch-uncommitted": "^1.0.0",
|
||||
"ent": "^2.2.0",
|
||||
"filehound": "^1.16.2",
|
||||
"fs-extra": "^5.0.0",
|
||||
"gulp": "^3.9.0",
|
||||
"gulp-coffee": "^2.2.0",
|
||||
"gulp-coffeelint": "^0.6.0",
|
||||
"gulp-inline-source": "^2.1.0",
|
||||
"gulp-mocha": "^2.0.0",
|
||||
"gulp-shell": "^0.5.2",
|
||||
@ -70,8 +73,9 @@
|
||||
"pkg": "^4.3.0-beta.1",
|
||||
"publish-release": "^1.3.3",
|
||||
"require-npm4-to-publish": "^1.0.0",
|
||||
"resin-lint": "^1.5.0",
|
||||
"ts-node": "^4.0.1",
|
||||
"typescript": "^2.6.1"
|
||||
"typescript": "2.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@resin.io/valid-email": "^0.1.0",
|
||||
@ -82,7 +86,7 @@
|
||||
"bluebird": "^3.3.3",
|
||||
"body-parser": "^1.14.1",
|
||||
"capitano": "^1.7.0",
|
||||
"chalk": "^1.1.3",
|
||||
"chalk": "^2.3.0",
|
||||
"coffee-script": "^1.12.6",
|
||||
"columnify": "^1.5.2",
|
||||
"denymount": "^2.2.0",
|
||||
@ -129,7 +133,7 @@
|
||||
"resin-sdk": "^7.0.0",
|
||||
"resin-sdk-preconfigured": "^6.9.0",
|
||||
"resin-settings-client": "^3.6.1",
|
||||
"resin-stream-logger": "^0.0.4",
|
||||
"resin-stream-logger": "^0.1.0",
|
||||
"resin-sync": "^9.2.3",
|
||||
"rimraf": "^2.4.3",
|
||||
"rindle": "^1.0.0",
|
||||
|
@ -8,9 +8,20 @@
|
||||
"noUnusedParameters": true,
|
||||
"preserveConstEnums": true,
|
||||
"removeComments": true,
|
||||
"sourceMap": true
|
||||
"sourceMap": true,
|
||||
"lib": [
|
||||
// es5 defaults:
|
||||
"dom",
|
||||
"es5",
|
||||
"scripthost",
|
||||
// some specific es6 bits we're sure are safe:
|
||||
"es2015.collection",
|
||||
"es2015.iterable",
|
||||
"es2016.array.include"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"./typings/*.d.ts",
|
||||
"./lib/**/*.ts"
|
||||
]
|
||||
}
|
||||
|
1
typings/@resin-valid-email.d.ts
vendored
Normal file
1
typings/@resin-valid-email.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
declare module '@resin.io/valid-email';
|
53
typings/capitano.d.ts
vendored
Normal file
53
typings/capitano.d.ts
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
declare module 'capitano' {
|
||||
export function parse(argv: string[]): Cli;
|
||||
|
||||
export interface Cli {
|
||||
command: string;
|
||||
options: {};
|
||||
global: {};
|
||||
}
|
||||
|
||||
export interface OptionDefinition {
|
||||
signature: string;
|
||||
description: string;
|
||||
parameter?: string;
|
||||
boolean?: boolean;
|
||||
alias?: string | string[];
|
||||
}
|
||||
|
||||
export interface CommandDefinition<P = {}, O = {}> {
|
||||
signature: string;
|
||||
description: string;
|
||||
help: string;
|
||||
options?: OptionDefinition[];
|
||||
permission?: 'user';
|
||||
action(params: P, options: O, done: () => void): void;
|
||||
}
|
||||
|
||||
export interface Command {
|
||||
signature: Signature;
|
||||
options: Option[];
|
||||
isWildcard(): boolean;
|
||||
}
|
||||
|
||||
export interface Signature {
|
||||
hasParameters(): boolean;
|
||||
hasVariadicParameters(): boolean;
|
||||
isWildcard(): boolean;
|
||||
allowsStdin(): boolean;
|
||||
}
|
||||
|
||||
export interface Option {
|
||||
signature: Signature;
|
||||
alias: string | string[];
|
||||
boolean: boolean;
|
||||
parameter: string;
|
||||
required: boolean | string;
|
||||
}
|
||||
|
||||
export function command(command: CommandDefinition): void;
|
||||
|
||||
export const state: {
|
||||
getMatchCommand: (signature: string, callback: (e: Error, cmd: Command) => void) => void
|
||||
};
|
||||
}
|
1
typings/mixpanel.d.ts
vendored
Normal file
1
typings/mixpanel.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
declare module 'mixpanel';
|
4
typings/nplugm.d.ts
vendored
Normal file
4
typings/nplugm.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
declare module 'nplugm' {
|
||||
import Promise = require('bluebird');
|
||||
export function list(regexp: RegExp): Promise<Array<string>>;
|
||||
}
|
4
typings/package.json.d.ts
vendored
Normal file
4
typings/package.json.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
declare module '*/package.json' {
|
||||
export const name: string;
|
||||
export const version: string;
|
||||
}
|
3
typings/president.d.ts
vendored
Normal file
3
typings/president.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
declare module 'president' {
|
||||
export function execute(command: string[], callback: (err: Error) => void): void;
|
||||
}
|
1
typings/resin-cli-errors.d.ts
vendored
Normal file
1
typings/resin-cli-errors.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
declare module 'resin-cli-errors';
|
1
typings/resin-cli-form.d.ts
vendored
Normal file
1
typings/resin-cli-form.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
declare module 'resin-cli-form';
|
1
typings/resin-cli-visuals.d.ts
vendored
Normal file
1
typings/resin-cli-visuals.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
declare module 'resin-cli-visuals';
|
1
typings/resin-device-config.d.ts
vendored
Normal file
1
typings/resin-device-config.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
declare module 'resin-device-config';
|
60
typings/resin-device-init.d.ts
vendored
Normal file
60
typings/resin-device-init.d.ts
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
declare module 'resin-device-init' {
|
||||
import * as Promise from 'bluebird';
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
interface OperationState {
|
||||
operation: CopyOperation | ReplaceOperation | RunScriptOperation | BurnOperation;
|
||||
percentage: number;
|
||||
}
|
||||
|
||||
interface Operation {
|
||||
command: string;
|
||||
}
|
||||
|
||||
interface CopyOperation extends Operation {
|
||||
command: 'copy';
|
||||
from: { path: string };
|
||||
to: { path: string };
|
||||
}
|
||||
|
||||
interface ReplaceOperation extends Operation {
|
||||
command: 'replace';
|
||||
copy: string;
|
||||
replace: string;
|
||||
file: {
|
||||
path: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface RunScriptOperation extends Operation {
|
||||
command: 'run-script';
|
||||
script: string;
|
||||
arguments?: string[];
|
||||
}
|
||||
|
||||
interface BurnOperation extends Operation {
|
||||
command: 'burn';
|
||||
image?: string;
|
||||
}
|
||||
|
||||
interface BurnProgress {
|
||||
type: 'write' | 'check';
|
||||
percentage: number;
|
||||
transferred: number;
|
||||
length: number;
|
||||
remaining: number;
|
||||
eta: number;
|
||||
runtime: number;
|
||||
delta: number;
|
||||
speed: number;
|
||||
}
|
||||
|
||||
interface InitializeEmitter {
|
||||
on(event: 'stdout', callback: (msg: string) => void): void;
|
||||
on(event: 'stderr', callback: (msg: string) => void): void;
|
||||
on(event: 'state', callback: (state: OperationState) => void): void;
|
||||
on(event: 'burn', callback: (state: BurnProgress) => void): void;
|
||||
}
|
||||
|
||||
export function initialize(image: string, deviceType: string, config: {}): Promise<InitializeEmitter>;
|
||||
}
|
5
typings/resin-image-fs.d.ts
vendored
Normal file
5
typings/resin-image-fs.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
declare module 'resin-image-fs' {
|
||||
import Promise = require('bluebird');
|
||||
|
||||
export function read(options: {}): Promise<NodeJS.ReadableStream>;
|
||||
}
|
5
typings/resin-sdk-preconfigured.d.ts
vendored
Normal file
5
typings/resin-sdk-preconfigured.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
declare module 'resin-sdk-preconfigured' {
|
||||
import { ResinSDK } from 'resin-sdk';
|
||||
let sdk: ResinSDK;
|
||||
export = sdk;
|
||||
}
|
5
typings/resin-sync.d.ts
vendored
Normal file
5
typings/resin-sync.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
declare module 'resin-sync' {
|
||||
import { CommandDefinition } from 'capitano';
|
||||
|
||||
export function capitano(tool: 'resin-cli'): CommandDefinition;
|
||||
}
|
13
typings/rindle.d.ts
vendored
Normal file
13
typings/rindle.d.ts
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
declare module 'rindle' {
|
||||
export function extract(
|
||||
stream: NodeJS.ReadableStream,
|
||||
callback: (error: Error, data: string) => void,
|
||||
): void;
|
||||
|
||||
export function wait(
|
||||
stream: {
|
||||
on(event: string, callback: Function): void;
|
||||
},
|
||||
callback: (error: Error, data: string) => void,
|
||||
): void;
|
||||
}
|
53
typings/update-notifier.d.ts
vendored
Normal file
53
typings/update-notifier.d.ts
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
// 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' {
|
||||
export = UpdateNotifier;
|
||||
|
||||
function UpdateNotifier(settings?: UpdateNotifier.Settings): UpdateNotifier.UpdateNotifier;
|
||||
|
||||
namespace UpdateNotifier {
|
||||
class UpdateNotifier {
|
||||
constructor(settings?: Settings);
|
||||
|
||||
update: UpdateInfo;
|
||||
check(): void;
|
||||
checkNpm(): void;
|
||||
notify(customMessage?: NotifyOptions): void;
|
||||
}
|
||||
|
||||
interface Settings {
|
||||
pkg?: Package;
|
||||
callback?(update?: UpdateInfo): any;
|
||||
packageName?: string;
|
||||
packageVersion?: string;
|
||||
updateCheckInterval?: number; // in milliseconds, default 1000 * 60 * 60 * 24 (1 day)
|
||||
}
|
||||
|
||||
interface BoxenOptions {
|
||||
padding: number;
|
||||
margin: number;
|
||||
align: string;
|
||||
borderColor: string;
|
||||
borderStyle: string;
|
||||
}
|
||||
|
||||
interface NotifyOptions {
|
||||
message?: string;
|
||||
defer?: boolean;
|
||||
boxenOpts?: BoxenOptions;
|
||||
}
|
||||
|
||||
interface Package {
|
||||
name: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
interface UpdateInfo {
|
||||
latest: string;
|
||||
current: string;
|
||||
type: string;
|
||||
name: string;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user