Lint TypeScript and CoffeeScript with resin-lint

Change-Type: patch
This commit is contained in:
Tim Perry 2018-01-04 14:07:55 +00:00
parent f25442c036
commit 6daed83d88
30 changed files with 123 additions and 132 deletions

View File

@ -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 {

View File

@ -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}`);

View File

@ -10,6 +10,7 @@
"sourceMap": true
},
"include": [
"./**/*.ts"
"./**/*.ts",
"../typings/*.d.ts"
]
}

View File

@ -15,12 +15,13 @@ for (let commandCategory of capitanodoc.categories) {
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 != null) {
if (actions.signature) {
category.commands.push(_.omit(actions, 'action'));
} else {
for (let actionName in actions) {
for (let actionName of Object.keys(actions)) {
const actionCommand = actions[actionName];
category.commands.push(_.omit(actionCommand, 'action'));
}

View File

@ -25,7 +25,7 @@ ${option.description}\
}
return result;
};
}
export function renderCategory(category: Category) {
let result = `\
@ -37,7 +37,7 @@ export function renderCategory(category: Category) {
}
return result;
};
}
function getAnchor(command: Command) {
return '#' + command.signature
@ -71,7 +71,7 @@ export function renderToc(categories: Category[]) {
}
return result;
};
}
export function render(doc: Document) {
let result = `\
@ -87,4 +87,4 @@ ${renderToc(doc.categories)}\
}
return result;
};
}

View File

@ -8,7 +8,7 @@ export function getOptionPrefix(signature: string) {
} else {
return '-';
}
};
}
export function getOptionSignature(signature: string) {
return `${getOptionPrefix(signature)}${signature}`;
@ -32,4 +32,4 @@ export function parseSignature(option: OptionDefinition) {
}
return ent.encode(result);
};
}

View File

@ -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({

View File

@ -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')

View File

@ -401,7 +401,6 @@ exports.init =
tmp.setGracefulCleanup()
resin = require('resin-sdk-preconfigured')
helpers = require('../utils/helpers')
patterns = require('../utils/patterns')
Promise.try ->

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { CommandDefinition } from "capitano";
import { CommandDefinition } from 'capitano';
export const version: CommandDefinition = {
signature: 'version',
@ -26,5 +26,5 @@ Display the Resin CLI version.\
const packageJSON = await import('../../package.json');
console.log(packageJSON.version);
return done();
}
},
};

View File

@ -44,7 +44,6 @@ module.exports =
permission: 'user'
primary: true
action: (params, options, done) ->
_ = require('lodash')
resin = require('resin-sdk-preconfigured')
moment = require('moment')

View File

@ -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')

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { CommandDefinition } from "capitano";
import { CommandDefinition } from 'capitano';
export const list: CommandDefinition = {
signature: 'settings',
@ -34,5 +34,5 @@ Examples:
.then(prettyjson.render)
.then(console.log)
.nodeify(done);
}
},
};

View File

@ -14,4 +14,5 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
export = require('resin-sync').capitano('resin-cli');
import * as ResinSync from 'resin-sync';
export = ResinSync.capitano('resin-cli');

View File

@ -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)

View File

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

View File

@ -15,14 +15,14 @@ export function trackCommand(capitanoCli: Capitano.Cli) {
return Promise.props({
resinUrl: resin.settings.get('resinUrl'),
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
}
username,
},
});
return mixpanel.track(`[CLI] ${command.signature.toString()}`, {
@ -33,10 +33,10 @@ export function trackCommand(capitanoCli: Capitano.Cli) {
arch: process.arch,
resinUrl,
platform: process.platform,
command: capitanoCli
command: capitanoCli,
});
})
});
})
.timeout(100)
.catchReturn(undefined);
};
}

View File

@ -37,27 +37,27 @@ export function generateBaseConfig(application: ResinSdk.Application, options: {
registryUrl: resin.settings.get('registryUrl'),
deltaUrl: resin.settings.get('deltaUrl'),
pubNubKeys: resin.models.config.getAll().get('pubnub'),
mixpanelToken: resin.models.config.getAll().get('mixpanelToken')
mixpanelToken: resin.models.config.getAll().get('mixpanelToken'),
}).then((results) => {
return deviceConfig.generate({
application,
user: {
id: results.userId,
username: results.username
username: results.username,
},
endpoints: {
api: results.apiUrl,
vpn: results.vpnUrl,
registry: results.registryUrl,
delta: results.deltaUrl
delta: results.deltaUrl,
},
pubnub: results.pubNubKeys,
mixpanel: {
token: results.mixpanelToken
}
token: results.mixpanelToken,
},
}, options);
});
};
}
export function generateApplicationConfig(application: ResinSdk.Application, options: {}) {
return generateBaseConfig(application, options)
@ -67,7 +67,7 @@ export function generateApplicationConfig(application: ResinSdk.Application, opt
export function generateDeviceConfig(
device: ResinSdk.Device & { application_name: string },
deviceApiKey: string | null,
options: {}
options: {},
) {
return resin.models.application.get(device.application_name)
.then(application => {
@ -91,19 +91,19 @@ export function generateDeviceConfig(
return config;
});
};
}
function authenticateWithApplicationKey(config: any, applicationNameOrId: string | number) {
return resin.models.application.generateApiKey(applicationNameOrId)
.tap((apiKey) => {
config.apiKey = apiKey;
});
};
}
function authenticateWithDeviceKey(config: any, uuid: string, customDeviceApiKey: string) {
return Promise.try(() => {
return customDeviceApiKey || resin.models.device.generateDeviceKey(uuid)
return customDeviceApiKey || resin.models.device.generateDeviceKey(uuid);
}).tap((deviceApiKey) => {
config.deviceApiKey = deviceApiKey;
});
};
}

View File

@ -24,19 +24,19 @@ import visuals = require('resin-cli-visuals');
import ResinSdk = require('resin-sdk');
import { execute } from 'president';
import { InitializeEmitter, OperationState } from "resin-device-init";
import { InitializeEmitter, OperationState } from 'resin-device-init';
const resin = ResinSdk.fromSharedOptions();
export function getGroupDefaults(
group: { options: { name: string, default?: string }[] }
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') + '%';
@ -52,7 +52,7 @@ export function stateToString(state: OperationState) {
default:
throw new Error(`Unsupported operation: ${state.operation.command}`);
}
};
}
export function sudo(command: string[]) {
if (os.platform() !== 'win32') {
@ -63,7 +63,7 @@ export function sudo(command: string[]) {
const presidentExecuteAsync = Promise.promisify(execute);
return presidentExecuteAsync(command);
};
}
export function getManifest(image: string, deviceType: string): Promise<ResinSdk.DeviceType> {
// Attempt to read manifest from the first
@ -72,13 +72,13 @@ export function getManifest(image: string, deviceType: string): Promise<ResinSdk
return imagefs.read({
image,
partition: {
primary: 1
primary: 1,
},
path: '/device-type.json'
path: '/device-type.json',
}).then(Promise.promisify(rindle.extract))
.then(JSON.parse)
.catch(() => resin.models.device.getManifestBySlug(deviceType));
};
}
export function osProgressHandler(step: InitializeEmitter) {
step.on('stdout', process.stdout.write.bind(process.stdout));
@ -91,29 +91,29 @@ export function osProgressHandler(step: InitializeEmitter) {
const progressBars = {
write: new visuals.Progress('Writing Device OS'),
check: new visuals.Progress('Validating Device OS')
check: new visuals.Progress('Validating Device OS'),
};
step.on('burn', state => progressBars[state.type].update(state));
return Promise.promisify(rindle.wait)(step);
};
}
export function getArchAndDeviceType(applicationName: string): Promise<{ arch: string, device_type: string }> {
return Promise.join(
getApplication(applicationName),
resin.models.config.getDeviceTypes(),
function (app, deviceTypes) {
let config = _.find(deviceTypes, { 'slug': app.device_type });
let config = _.find(deviceTypes, { slug: app.device_type });
if (config == null) {
throw new Error('Could not read application information!');
}
return { device_type: app.device_type, arch: config.arch };
}
},
);
};
}
function getApplication(applicationName: string) {
let match;
@ -125,7 +125,7 @@ function getApplication(applicationName: string) {
}
return resin.models.application.get(applicationName);
};
}
// A function to reliably execute a command
// in all supported operating systems, including
@ -135,12 +135,12 @@ export function getSubShellCommand(command: string) {
if (os.platform() === 'win32') {
return {
program: 'cmd.exe',
args: [ '/s', '/c', command ]
args: [ '/s', '/c', command ],
};
} else {
return {
program: '/bin/sh',
args: [ '-c', command ]
args: [ '-c', command ],
};
}
};
}

View File

@ -30,7 +30,7 @@ export class Logger {
debug: logger.createLogStream('debug'),
success: logger.createLogStream('success'),
warn: logger.createLogStream('warn'),
error: logger.createLogStream('error')
error: logger.createLogStream('error'),
};
_.mapKeys(this.streams, function(stream, key) {

View File

@ -30,11 +30,11 @@ export function authenticate(options: {}) {
message: 'Email:',
name: 'email',
type: 'input',
validate: validation.validateEmail
validate: validation.validateEmail,
}, {
message: 'Password:',
name: 'password',
type: 'password'
type: 'password',
}], { override: options })
.then(resin.auth.login)
.then(resin.auth.twoFactor.isPassed)
@ -45,16 +45,14 @@ export function authenticate(options: {}) {
message: 'Two factor auth challenge:',
name: 'code',
type: 'input'}).then(resin.auth.twoFactor.challenge)
.catch((error: any) =>
resin.auth.logout().then(function() {
if ((error.name === 'ResinRequestError') && (error.statusCode === 401)) {
throw new Error('Invalid two factor authentication code');
}
throw error;
})
);
})
};
.catch((error: any) => 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({
@ -63,18 +61,18 @@ export function askLoginType() {
type: 'list',
choices: [{
name: 'Web authorization (recommended)',
value: 'web'
value: 'web',
}, {
name: 'Credentials',
value: 'credentials'
value: 'credentials',
}, {
name: 'Authentication token',
value: 'token'
value: 'token',
}, {
name: 'I don\'t have a Resin account!',
value: 'register'
}]
})
value: 'register',
}],
});
}
export function selectDeviceType() {
@ -83,8 +81,8 @@ export function selectDeviceType() {
return form.ask({
message: 'Device Type',
type: 'list',
choices: deviceTypes
})
choices: deviceTypes,
});
});
}
@ -98,13 +96,13 @@ export function confirm(yesOption: string, message: string, yesMessage: string)
return form.ask({
message,
type: 'confirm',
default: false
default: false,
});
}).then(function(confirmed) {
if (!confirmed) {
throw new Error('Aborted');
}
})
});
}
export function selectApplication(filter: (app: ResinSdk.Application) => boolean) {
@ -123,10 +121,10 @@ export function selectApplication(filter: (app: ResinSdk.Application) => boolean
choices: _.map(applications, application =>
({
name: `${application.app_name} (${application.device_type})`,
value: application.app_name
})
value: application.app_name,
}),
)});
})
});
}
export function selectOrCreateApplication() {
@ -137,18 +135,18 @@ export function selectOrCreateApplication() {
let appOptions: { name: string, value: string | null }[];
appOptions = _.map(applications, application => ({
name: `${application.app_name} (${application.device_type})`,
value: application.app_name
value: application.app_name,
}));
appOptions.unshift({
name: 'Create a new application',
value: null
value: null,
});
return form.ask({
message: 'Select an application',
type: 'list',
choices: appOptions
choices: appOptions,
});
});
}).then((application) => {
@ -157,9 +155,9 @@ export function selectOrCreateApplication() {
return form.ask({
message: 'Choose a Name for your new application',
type: 'input',
validate: validation.validateApplicationName
validate: validation.validateApplicationName,
});
})
});
}
export function awaitDevice(uuid: string) {
@ -182,7 +180,7 @@ export function awaitDevice(uuid: string) {
return Promise.delay(3000).then(poll);
}
});
}
};
console.info(`Waiting for ${deviceName} to connect to resin...`);
return poll().return(uuid);
@ -207,8 +205,8 @@ export function inferOrSelectDevice(preferredUuid: string) {
default: defaultUuid,
choices: _.map(onlineDevices, device => ({
name: `${device.name || 'Untitled'} (${device.uuid.slice(0, 7)})`,
value: device.uuid
})
value: device.uuid,
}),
)});
});
}
@ -216,7 +214,7 @@ export function inferOrSelectDevice(preferredUuid: string) {
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) {
@ -225,4 +223,4 @@ export function expectedError(message: string | Error) {
printErrorMessage(message);
process.exit(1);
};
}

View File

@ -19,7 +19,7 @@ import _ = require('lodash');
import capitano = require('capitano');
import patterns = require('./patterns');
exports.register = (regex: RegExp) => {
export function register(regex: RegExp): Promise<void> {
return nplugm.list(regex).map(async function(plugin: any) {
const command = await import(plugin);
command.plugin = true;
@ -28,6 +28,6 @@ exports.register = (regex: RegExp) => {
}
return _.each(command, capitano.command);
}).catch((error: Error) => {
return patterns.printErrorMessage(error.message)
})
return patterns.printErrorMessage(error.message);
});
}

View File

@ -17,4 +17,4 @@ export async function buffer(stream: NodeJS.ReadableStream, bufferFile: string)
resolve(this);
}).on('error', reject);
}));
};
}

View File

@ -30,7 +30,7 @@ let notifier: UpdateNotifier.UpdateNotifier;
if (!isRoot()) {
notifier = UpdateNotifier({
pkg: packageJSON,
updateCheckInterval: resinUpdateInterval
updateCheckInterval: resinUpdateInterval,
});
}
@ -48,4 +48,4 @@ export function notify() {
if (notifier.update != null) {
return console.log('Notice that you might need administrator privileges depending on your setup\n');
}
};
}

View File

@ -22,7 +22,7 @@ export function validateEmail(input: string) {
}
return true;
};
}
export function validatePassword(input: string) {
if (input.length < 8) {
@ -30,7 +30,7 @@ export function validatePassword(input: string) {
}
return true;
};
}
export function validateApplicationName(input: string) {
if (input.length < 4) {
@ -38,4 +38,4 @@ export function validateApplicationName(input: string) {
}
return true;
};
}

View File

@ -31,7 +31,7 @@
"scripts": {
"prebuild": "rimraf build/ build-bin/ build-zip/",
"build": "npm run build:src && npm run build:bin",
"build:src": "gulp build && tsc && npm run build: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",
@ -39,7 +39,7 @@
"test": "gulp test",
"ci": "npm run test && catch-uncommitted",
"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"
},
@ -66,7 +66,6 @@
"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",
@ -74,6 +73,7 @@
"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.4.0"
},

View File

@ -19,8 +19,8 @@ declare module 'capitano' {
signature: string;
description: string;
help: string;
options?: OptionDefinition[],
permission?: 'user',
options?: OptionDefinition[];
permission?: 'user';
action(params: P, options: O, done: () => void): void;
}

View File

@ -1,5 +1,5 @@
declare module 'resin-device-init' {
import { EventEmitter } from "events";
import { EventEmitter } from 'events';
interface OperationState {
operation: CopyOperation | ReplaceOperation | RunScriptOperation | BurnOperation;
@ -22,7 +22,7 @@ declare module 'resin-device-init' {
replace: string;
file: {
path: string;
}
};
}
interface RunScriptOperation extends Operation {
@ -55,5 +55,5 @@ declare module 'resin-device-init' {
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>;
}

5
typings/resin-sync.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
declare module 'resin-sync' {
import { CommandDefinition } from 'capitano';
export function capitano(tool: 'resin-cli'): CommandDefinition;
}

4
typings/rindle.d.ts vendored
View File

@ -1,13 +1,13 @@
declare module 'rindle' {
export function extract(
stream: NodeJS.ReadableStream,
callback: (error: Error, data: string) => void
callback: (error: Error, data: string) => void,
): void;
export function wait(
stream: {
on(event: string, callback: Function): void;
},
callback: (error: Error, data: string) => void
callback: (error: Error, data: string) => void,
): void;
}