Merge pull request #1793 from balena-io/js-lib-app-capitano

Convert lib/app-capitano.coffee and gulpfile.coffee to javascript
This commit is contained in:
Page- 2020-04-30 19:31:42 +01:00 committed by GitHub
commit 6f51807e8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 199 additions and 400 deletions

View File

@ -1,5 +1,2 @@
coffee_script:
config_file: coffeelint.json
javascript:
enabled: false

View File

@ -24,7 +24,7 @@ import { CapitanoCommand, Category, Document, OclifCommand } from './doc-types';
import * as utils from './utils';
function renderCapitanoCommand(command: CapitanoCommand): string[] {
const result = [`## ${ent.encode(command.signature)}`, command.help];
const result = [`## ${ent.encode(command.signature)}`, command.help!];
if (!_.isEmpty(command.options)) {
result.push('### Options');

View File

@ -25,12 +25,7 @@ const ROOT = path.normalize(path.join(__dirname, '..'));
/**
* Compare the timestamp of cli.markdown with the timestamp of staged files,
* issuing an error if cli.markdown is older. Besides the purpose of ensuring
* that cli.markdown is updated, it effectively also ensures that coffeelint
* is executed (via `npm run build` or `npm test`) on the developers laptop,
* so that there is at least a chance that the developer will spot any linter
* warnings (that could reveal bugs) sooner than later. (The CI does not
* currently fail in case of coffeelint warnings.)
* issuing an error if cli.markdown is older.
* If cli.markdown does not require updating and the developer cannot run
* `npm run build` on their laptop, the error message suggests a workaround
* using `touch`.
@ -67,9 +62,7 @@ async function checkBuildTimestamps() {
file) for three reasons:
1. To update the CLI markdown documentation (in case any command-line options
were updated, added or removed).
2. To reveal coffeelint warnings that the CI currently ignores (in case any
Coffeescript files were modified).
3. To catch Typescript type check errors sooner and reduce overall waiting time,
2. To catch Typescript type check errors sooner and reduce overall waiting time,
given that balena-cli CI builds/tests are currently rather lengthy.
If you need/wish to bypass this check without running \`npm run build\`, run:

View File

@ -13,7 +13,6 @@ process.env.UV_THREADPOOL_SIZE = '64';
require('fast-boot2').start({
cacheFile: '.fast-boot.json',
});
require('coffeescript/register');
const path = require('path');
const rootDir = path.join(__dirname, '..');

View File

@ -1,127 +0,0 @@
{
"coffeescript_error": {
"level": "error"
},
"arrow_spacing": {
"name": "arrow_spacing",
"level": "error"
},
"no_tabs": {
"name": "no_tabs",
"level": "ignore"
},
"no_trailing_whitespace": {
"name": "no_trailing_whitespace",
"level": "error",
"allowed_in_comments": false,
"allowed_in_empty_lines": false
},
"max_line_length": {
"name": "max_line_length",
"value": 120,
"level": "error",
"limitComments": true
},
"line_endings": {
"name": "line_endings",
"level": "ignore",
"value": "unix"
},
"no_trailing_semicolons": {
"name": "no_trailing_semicolons",
"level": "error"
},
"indentation": {
"name": "indentation",
"value": 1,
"level": "error"
},
"camel_case_classes": {
"name": "camel_case_classes",
"level": "error"
},
"colon_assignment_spacing": {
"name": "colon_assignment_spacing",
"level": "error",
"spacing": {
"left": 0,
"right": 1
}
},
"no_implicit_braces": {
"name": "no_implicit_braces",
"level": "ignore",
"strict": false
},
"no_plusplus": {
"name": "no_plusplus",
"level": "ignore"
},
"no_throwing_strings": {
"name": "no_throwing_strings",
"level": "error"
},
"no_backticks": {
"name": "no_backticks",
"level": "error"
},
"no_implicit_parens": {
"name": "no_implicit_parens",
"strict": false,
"level": "ignore"
},
"no_empty_param_list": {
"name": "no_empty_param_list",
"level": "error"
},
"no_stand_alone_at": {
"name": "no_stand_alone_at",
"level": "ignore"
},
"space_operators": {
"name": "space_operators",
"level": "error"
},
"duplicate_key": {
"name": "duplicate_key",
"level": "error"
},
"empty_constructor_needs_parens": {
"name": "empty_constructor_needs_parens",
"level": "ignore"
},
"cyclomatic_complexity": {
"name": "cyclomatic_complexity",
"value": 10,
"level": "ignore"
},
"newlines_after_classes": {
"name": "newlines_after_classes",
"value": 3,
"level": "ignore"
},
"no_unnecessary_fat_arrows": {
"name": "no_unnecessary_fat_arrows",
"level": "error"
},
"missing_fat_arrows": {
"name": "missing_fat_arrows",
"level": "ignore"
},
"non_empty_constructor_needs_parens": {
"name": "non_empty_constructor_needs_parens",
"level": "ignore"
},
"no_unnecessary_double_quotes": {
"name": "no_unnecessary_double_quotes",
"level": "error"
},
"no_debugger": {
"name": "no_debugger",
"level": "warn"
},
"no_interpolation_in_single_quotes": {
"name": "no_interpolation_in_single_quotes",
"level": "error"
}
}

View File

@ -1,33 +0,0 @@
path = require('path')
gulp = require('gulp')
coffee = require('gulp-coffee')
inlinesource = require('gulp-inline-source')
shell = require('gulp-shell')
packageJSON = require('./package.json')
OPTIONS =
files:
coffee: [ 'lib/**/*.coffee', 'gulpfile.coffee' ]
app: 'lib/**/*.coffee'
tests: 'tests/**/*.spec.js'
pages: 'lib/auth/pages/*.ejs'
directories:
build: 'build/'
gulp.task 'pages', ->
gulp.src(OPTIONS.files.pages)
.pipe(inlinesource())
.pipe(gulp.dest('build/auth/pages'))
gulp.task 'coffee', ->
gulp.src(OPTIONS.files.app)
.pipe(coffee(bare: true, header: true))
.pipe(gulp.dest(OPTIONS.directories.build))
gulp.task 'build', gulp.series [
'coffee',
'pages'
]
gulp.task 'watch', gulp.series [ 'build' ], ->
gulp.watch([ OPTIONS.files.coffee ], [ 'build' ])

15
gulpfile.js Normal file
View File

@ -0,0 +1,15 @@
const gulp = require('gulp');
const inlinesource = require('gulp-inline-source');
const OPTIONS = {
files: {
pages: 'lib/auth/pages/*.ejs',
},
};
gulp.task('pages', () =>
gulp
.src(OPTIONS.files.pages)
.pipe(inlinesource())
.pipe(gulp.dest('build/auth/pages')),
);

View File

@ -1,143 +0,0 @@
###
Copyright 2016-2019 Balena
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')
capitano = require('capitano')
actions = require('./actions')
events = require('./events')
capitano.permission 'user', (done) ->
require('./utils/patterns').exitIfNotLoggedIn()
.then(done, done)
capitano.command
signature: '*'
action: (params, options, done) ->
capitano.execute(command: 'help', done)
process.exitCode = process.exitCode || 1
capitano.globalOption
signature: 'help'
boolean: true
alias: 'h'
capitano.globalOption
signature: 'version'
boolean: true
alias: 'v'
# ---------- Help Module ----------
capitano.command(actions.help.help)
# ---------- Api key module ----------
capitano.command(actions.apiKey.generate)
# ---------- App Module ----------
capitano.command(actions.app.create)
capitano.command(actions.app.list)
capitano.command(actions.app.remove)
capitano.command(actions.app.restart)
capitano.command(actions.app.info)
# ---------- Auth Module ----------
capitano.command(actions.auth.login)
capitano.command(actions.auth.logout)
capitano.command(actions.auth.whoami)
# ---------- Device Module ----------
capitano.command(actions.device.list)
capitano.command(actions.device.rename)
capitano.command(actions.device.init)
capitano.command(actions.device.remove)
capitano.command(actions.device.identify)
capitano.command(actions.device.reboot)
capitano.command(actions.device.shutdown)
capitano.command(actions.device.enableDeviceUrl)
capitano.command(actions.device.disableDeviceUrl)
capitano.command(actions.device.getDeviceUrl)
capitano.command(actions.device.hasDeviceUrl)
capitano.command(actions.device.register)
capitano.command(actions.device.move)
capitano.command(actions.device.osUpdate)
capitano.command(actions.device.info)
# ---------- Tags Module ----------
capitano.command(actions.tags.list)
capitano.command(actions.tags.set)
capitano.command(actions.tags.remove)
# ---------- OS Module ----------
capitano.command(actions.os.versions)
capitano.command(actions.os.download)
capitano.command(actions.os.buildConfig)
capitano.command(actions.os.initialize)
# ---------- Config Module ----------
capitano.command(actions.config.read)
capitano.command(actions.config.write)
capitano.command(actions.config.inject)
capitano.command(actions.config.reconfigure)
capitano.command(actions.config.generate)
# ---------- Logs Module ----------
capitano.command(actions.logs.logs)
# ---------- Tunnel Module ----------
capitano.command(actions.tunnel.tunnel)
# ---------- Preload Module ----------
capitano.command(actions.preload)
# ---------- SSH Module ----------
capitano.command(actions.ssh.ssh)
# ---------- Local balenaOS Module ----------
capitano.command(actions.local.configure)
capitano.command(actions.local.flash)
# ---------- Public utils ----------
capitano.command(actions.util.availableDrives)
#------------ Local build and deploy -------
capitano.command(actions.build)
capitano.command(actions.deploy)
#------------ Push/remote builds -------
capitano.command(actions.push.push)
exports.run = (argv) ->
cli = capitano.parse(argv.slice(2))
runCommand = ->
capitanoExecuteAsync = Promise.promisify(capitano.execute)
if cli.global?.help
capitanoExecuteAsync(command: "help #{cli.command ? ''}")
else
capitanoExecuteAsync(cli)
trackCommand = ->
getMatchCommandAsync = Promise.promisify(capitano.state.getMatchCommand)
getMatchCommandAsync(cli.command)
.then (command) ->
# cmdSignature is literally a string like, for example:
# "push <applicationOrDevice>"
# ("applicationOrDevice" is NOT replaced with its actual value)
# In case of failures like an nonexistent or invalid command,
# command.signature.toString() returns '*'
cmdSignature = command.signature.toString()
events.trackCommand(cmdSignature)
Promise.all([trackCommand(), runCommand()])
.catch(require('./errors').handleError)

18
lib/app-capitano.d.ts vendored
View File

@ -1,18 +0,0 @@
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export async function run(argv: string[]);

159
lib/app-capitano.js Normal file
View File

@ -0,0 +1,159 @@
/*
Copyright 2016-2020 Balena
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 Promise from 'bluebird';
import * as capitano from 'capitano';
import * as actions from './actions';
import * as events from './events';
capitano.permission('user', done =>
require('./utils/patterns')
.exitIfNotLoggedIn()
.then(done, done),
);
capitano.command({
signature: '*',
action(_params, _options, done) {
capitano.execute({ command: 'help' }, done);
process.exitCode = process.exitCode || 1;
},
});
capitano.globalOption({
signature: 'help',
boolean: true,
alias: 'h',
});
capitano.globalOption({
signature: 'version',
boolean: true,
alias: 'v',
});
// ---------- Help Module ----------
capitano.command(actions.help.help);
// ---------- Api key module ----------
capitano.command(actions.apiKey.generate);
// ---------- App Module ----------
capitano.command(actions.app.create);
capitano.command(actions.app.list);
capitano.command(actions.app.remove);
capitano.command(actions.app.restart);
capitano.command(actions.app.info);
// ---------- Auth Module ----------
capitano.command(actions.auth.login);
capitano.command(actions.auth.logout);
capitano.command(actions.auth.whoami);
// ---------- Device Module ----------
capitano.command(actions.device.list);
capitano.command(actions.device.rename);
capitano.command(actions.device.init);
capitano.command(actions.device.remove);
capitano.command(actions.device.identify);
capitano.command(actions.device.reboot);
capitano.command(actions.device.shutdown);
capitano.command(actions.device.enableDeviceUrl);
capitano.command(actions.device.disableDeviceUrl);
capitano.command(actions.device.getDeviceUrl);
capitano.command(actions.device.hasDeviceUrl);
capitano.command(actions.device.register);
capitano.command(actions.device.move);
capitano.command(actions.device.osUpdate);
capitano.command(actions.device.info);
// ---------- Tags Module ----------
capitano.command(actions.tags.list);
capitano.command(actions.tags.set);
capitano.command(actions.tags.remove);
// ---------- OS Module ----------
capitano.command(actions.os.versions);
capitano.command(actions.os.download);
capitano.command(actions.os.buildConfig);
capitano.command(actions.os.initialize);
// ---------- Config Module ----------
capitano.command(actions.config.read);
capitano.command(actions.config.write);
capitano.command(actions.config.inject);
capitano.command(actions.config.reconfigure);
capitano.command(actions.config.generate);
// ---------- Logs Module ----------
capitano.command(actions.logs.logs);
// ---------- Tunnel Module ----------
capitano.command(actions.tunnel.tunnel);
// ---------- Preload Module ----------
capitano.command(actions.preload);
// ---------- SSH Module ----------
capitano.command(actions.ssh.ssh);
// ---------- Local balenaOS Module ----------
capitano.command(actions.local.configure);
capitano.command(actions.local.flash);
// ---------- Public utils ----------
capitano.command(actions.util.availableDrives);
//------------ Local build and deploy -------
capitano.command(actions.build);
capitano.command(actions.deploy);
//------------ Push/remote builds -------
capitano.command(actions.push.push);
export function run(argv) {
const cli = capitano.parse(argv.slice(2));
const runCommand = function() {
const capitanoExecuteAsync = Promise.promisify(capitano.execute);
if (cli.global?.help) {
return capitanoExecuteAsync({
command: `help ${cli.command ?? ''}`,
});
} else {
return capitanoExecuteAsync(cli);
}
};
const trackCommand = function() {
const getMatchCommandAsync = Promise.promisify(
capitano.state.getMatchCommand,
);
return getMatchCommandAsync(cli.command).then(function(command) {
// cmdSignature is literally a string like, for example:
// "push <applicationOrDevice>"
// ("applicationOrDevice" is NOT replaced with its actual value)
// In case of failures like an nonexistent or invalid command,
// command.signature.toString() returns '*'
const cmdSignature = command.signature.toString();
return events.trackCommand(cmdSignature);
});
};
return Promise.all([trackCommand(), runCommand()]).catch(
require('./errors').handleError,
);
}

View File

@ -214,7 +214,7 @@ let BluebirdConfigured = false;
/**
* Configure Bluebird and assign it as the global promise library.
* Modules like `stream-to-promise` will otherwise produce native promises,
* which leads to errors as much of the CLI CoffeeScript code expects Bluebird
* which leads to errors as much of the CLI JavaScript code expects Bluebird
* promises.
*/
export function configureBluebird() {

53
npm-shrinkwrap.json generated
View File

@ -3526,11 +3526,6 @@
"globals": "^10.1.0"
}
},
"coffeescript": {
"version": "1.12.7",
"resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.12.7.tgz",
"integrity": "sha512-pLXHFxQMPklVoEekowk8b3erNynC+DVJzChxS/LCBBgR6/8AJkHivkm//zbowcfc7BTCAjryuhx6gPqPRfsFoA=="
},
"collection-map": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz",
@ -7027,19 +7022,6 @@
}
}
},
"gulp-coffee": {
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/gulp-coffee/-/gulp-coffee-2.3.5.tgz",
"integrity": "sha512-PbgPGZVyYFnBTYtfYkVN6jcK8Qsuh3BxycPzvu8y5lZroCw3/x1m25KeyEDX110KsVLDmJxoULjscR21VEN4wA==",
"dev": true,
"requires": {
"coffeescript": "^1.10.0",
"gulp-util": "^3.0.2",
"merge": "^1.2.0",
"through2": "^2.0.1",
"vinyl-sourcemaps-apply": "^0.2.1"
}
},
"gulp-inline-source": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/gulp-inline-source/-/gulp-inline-source-2.1.0.tgz",
@ -7051,26 +7033,6 @@
"through2": "~2.0.0"
}
},
"gulp-shell": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/gulp-shell/-/gulp-shell-0.5.2.tgz",
"integrity": "sha1-pJWcoGUa0ce7/nCy0K27tOGuqY0=",
"dev": true,
"requires": {
"async": "^1.5.0",
"gulp-util": "^3.0.7",
"lodash": "^4.0.0",
"through2": "^2.0.0"
},
"dependencies": {
"async": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
"integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
"dev": true
}
}
},
"gulp-util": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz",
@ -9162,12 +9124,6 @@
"trim-newlines": "^1.0.0"
}
},
"merge": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz",
"integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==",
"dev": true
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
@ -18201,15 +18157,6 @@
}
}
},
"vinyl-sourcemaps-apply": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz",
"integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=",
"dev": true,
"requires": {
"source-map": "^0.5.1"
}
},
"walkdir": {
"version": "0.0.11",
"resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.0.11.tgz",

View File

@ -45,7 +45,7 @@
"prebuild": "rimraf build/ build-bin/",
"build": "npm run build:src && npm run catch-uncommitted",
"build:src": "npm run lint && npm run build:fast && npm run build:test && npm run build:doc",
"build:fast": "gulp build && tsc",
"build:fast": "gulp pages && tsc",
"build:test": "tsc -P ./tsconfig.dev.json --noEmit && tsc -P ./tsconfig.js.json --noEmit",
"build:doc": "mkdirp doc/ && ts-node --transpile-only automation/capitanodoc/index.ts > doc/cli.markdown",
"build:standalone": "ts-node --transpile-only automation/run.ts build:standalone",
@ -58,7 +58,7 @@
"catch-uncommitted": "ts-node --transpile-only automation/run.ts catch-uncommitted",
"ci": "npm run test && npm run catch-uncommitted",
"watch": "gulp watch",
"lint": "balena-lint lib/ tests/ && balena-lint -e ts -e js --typescript --fix automation/ lib/ typings/ tests/",
"lint": "balena-lint -e ts -e js --typescript --fix automation/ lib/ typings/ tests/ gulpfile.js",
"update": "ts-node --transpile-only ./automation/update-module.ts",
"prepublishOnly": "npm run build"
},
@ -148,9 +148,7 @@
"filehound": "^1.17.4",
"fs-extra": "^8.0.1",
"gulp": "^4.0.1",
"gulp-coffee": "^2.2.0",
"gulp-inline-source": "^2.1.0",
"gulp-shell": "^0.5.2",
"husky": "^4.2.5",
"intercept-stdout": "^0.1.2",
"mocha": "^6.2.3",
@ -191,7 +189,6 @@
"chalk": "^3.0.0",
"chokidar": "^2.1.8",
"cli-truncate": "^1.1.0",
"coffeescript": "^1.12.6",
"color-hash": "^1.0.3",
"columnify": "^1.5.2",
"common-tags": "^1.7.2",

View File

@ -4,6 +4,7 @@
"./automation/**/*",
"./lib/**/*",
"./tests/**/*",
"./typings/**/*"
"./typings/**/*",
"gulpfile.js"
]
}

View File

@ -21,7 +21,9 @@ declare module 'capitano' {
export interface Cli {
command: string;
options: {};
global: {};
global: {
help?: boolean;
};
}
export interface OptionDefinition {
@ -35,10 +37,10 @@ declare module 'capitano' {
export interface CommandDefinition<P = {}, O = {}> {
signature: string;
description: string;
help: string;
description?: string;
help?: string;
options?: Partial<OptionDefinition[]>;
permission?: 'user';
permission?: string; // This should be 'user' but without full typescript we cannot enforce it
root?: boolean;
primary?: boolean;
hidden?: boolean;
@ -68,7 +70,7 @@ declare module 'capitano' {
required: boolean | string;
}
export function command(command: CommandDefinition): void;
export function command<P, O>(command: CommandDefinition<P, O>): void;
export const state: {
getMatchCommand: (
@ -78,4 +80,14 @@ declare module 'capitano' {
commands: Command[];
globalOptions: OptionDefinition[];
};
export function execute(
args: any,
callback: (err?: any, result: any) => void,
): void;
export function globalOption(option: OptionDefinition): void;
export function permission(
permissionName: string,
callback: (done: () => void) => void,
): void;
}