2019-08-28 18:51:56 +00:00
|
|
|
/**
|
|
|
|
* @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.
|
|
|
|
*/
|
2020-05-04 14:57:23 +00:00
|
|
|
import { stripIndent } from './utils/lazy';
|
2020-09-08 11:13:53 +00:00
|
|
|
import { exitWithExpectedError } from './errors';
|
2019-08-28 18:51:56 +00:00
|
|
|
|
|
|
|
export interface AppOptions {
|
2020-01-03 18:37:55 +00:00
|
|
|
// Prevent the default behavior of flushing stdout after running a command
|
2019-08-28 18:51:56 +00:00
|
|
|
noFlush?: boolean;
|
|
|
|
}
|
|
|
|
|
2020-09-04 14:34:34 +00:00
|
|
|
export async function preparseArgs(argv: string[]): Promise<string[]> {
|
2019-08-28 18:51:56 +00:00
|
|
|
if (process.env.DEBUG) {
|
|
|
|
console.log(
|
2020-01-20 21:21:05 +00:00
|
|
|
`[debug] original argv0="${process.argv0}" argv=[${argv}] length=${argv.length}`,
|
2019-08-28 18:51:56 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
const cmdSlice = argv.slice(2);
|
|
|
|
|
|
|
|
if (cmdSlice.length > 0) {
|
|
|
|
// convert 'balena --version' or 'balena -v' to 'balena version'
|
|
|
|
if (['--version', '-v'].includes(cmdSlice[0])) {
|
|
|
|
cmdSlice[0] = 'version';
|
|
|
|
}
|
|
|
|
// convert 'balena --help' or 'balena -h' to 'balena help'
|
|
|
|
else if (['--help', '-h'].includes(cmdSlice[0])) {
|
|
|
|
cmdSlice[0] = 'help';
|
|
|
|
}
|
|
|
|
// convert e.g. 'balena help env add' to 'balena env add --help'
|
2020-09-04 14:34:34 +00:00
|
|
|
if (
|
|
|
|
cmdSlice.length > 1 &&
|
|
|
|
cmdSlice[0] === 'help' &&
|
|
|
|
cmdSlice[1][0] !== '-'
|
|
|
|
) {
|
2019-08-28 18:51:56 +00:00
|
|
|
cmdSlice.shift();
|
|
|
|
cmdSlice.push('--help');
|
|
|
|
}
|
2020-04-29 10:58:52 +00:00
|
|
|
|
|
|
|
// support global --debug flag
|
|
|
|
const debugIndex = cmdSlice.indexOf('--debug');
|
|
|
|
if (debugIndex > -1) {
|
|
|
|
process.env.DEBUG = '1';
|
|
|
|
cmdSlice.splice(debugIndex, 1);
|
|
|
|
}
|
2019-08-28 18:51:56 +00:00
|
|
|
}
|
|
|
|
|
2020-06-30 23:27:21 +00:00
|
|
|
// Enable bluebird long stack traces when in debug mode, must be set
|
|
|
|
// before the first bluebird require - done here so that it will also
|
|
|
|
// be enabled when using the `--debug` flag to enable debug mode
|
|
|
|
if (process.env.DEBUG) {
|
|
|
|
process.env.BLUEBIRD_LONG_STACK_TRACES = '1';
|
|
|
|
}
|
|
|
|
|
2020-05-01 11:18:11 +00:00
|
|
|
const Logger = await import('./utils/logger');
|
|
|
|
Logger.command = cmdSlice[0];
|
|
|
|
|
2020-09-04 14:34:34 +00:00
|
|
|
let args = cmdSlice;
|
2019-08-28 18:51:56 +00:00
|
|
|
|
2020-09-04 14:34:34 +00:00
|
|
|
// Convert space separated subcommands (e.g. `end add`), to colon-separated format (e.g. `env:add`)
|
|
|
|
if (isSubcommand(cmdSlice)) {
|
|
|
|
// convert space-separated commands to oclif's topic:command syntax
|
|
|
|
args = [cmdSlice[0] + ':' + cmdSlice[1], ...cmdSlice.slice(2)];
|
|
|
|
Logger.command = `${cmdSlice[0]} ${cmdSlice[1]}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (process.env.DEBUG) {
|
|
|
|
console.log(
|
|
|
|
`[debug] new argv=[${[argv[0], argv[1], ...args]}] length=${
|
|
|
|
args.length + 2
|
|
|
|
}`,
|
|
|
|
);
|
2019-08-28 18:51:56 +00:00
|
|
|
}
|
2020-09-04 14:34:34 +00:00
|
|
|
|
|
|
|
return args;
|
2019-08-28 18:51:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check whether the command line refers to a command that has been deprecated
|
|
|
|
* and removed and, if so, exit with an informative error message.
|
|
|
|
*/
|
2020-09-04 14:34:34 +00:00
|
|
|
export function checkDeletedCommand(argvSlice: string[]): void {
|
2019-08-28 18:51:56 +00:00
|
|
|
if (argvSlice[0] === 'help') {
|
|
|
|
argvSlice = argvSlice.slice(1);
|
|
|
|
}
|
|
|
|
function replaced(
|
|
|
|
oldCmd: string,
|
|
|
|
alternative: string,
|
|
|
|
version: string,
|
|
|
|
verb = 'replaced',
|
|
|
|
) {
|
2020-09-08 11:13:53 +00:00
|
|
|
exitWithExpectedError(stripIndent`
|
2019-08-28 18:51:56 +00:00
|
|
|
Note: the command "balena ${oldCmd}" was ${verb} in CLI version ${version}.
|
|
|
|
Please use "balena ${alternative}" instead.
|
|
|
|
`);
|
|
|
|
}
|
|
|
|
function removed(oldCmd: string, alternative: string, version: string) {
|
|
|
|
let msg = `Note: the command "balena ${oldCmd}" was removed in CLI version ${version}.`;
|
|
|
|
if (alternative) {
|
|
|
|
msg = [msg, alternative].join('\n');
|
|
|
|
}
|
2020-09-08 11:13:53 +00:00
|
|
|
exitWithExpectedError(msg);
|
2019-08-28 18:51:56 +00:00
|
|
|
}
|
|
|
|
const stopAlternative =
|
|
|
|
'Please use "balena ssh -s" to access the host OS, then use `balena-engine stop`.';
|
|
|
|
const cmds: { [cmd: string]: [(...args: any) => void, ...string[]] } = {
|
|
|
|
sync: [replaced, 'push', 'v11.0.0', 'removed'],
|
|
|
|
'local logs': [replaced, 'logs', 'v11.0.0'],
|
|
|
|
'local push': [replaced, 'push', 'v11.0.0'],
|
|
|
|
'local scan': [replaced, 'scan', 'v11.0.0'],
|
|
|
|
'local ssh': [replaced, 'ssh', 'v11.0.0'],
|
|
|
|
'local stop': [removed, stopAlternative, 'v11.0.0'],
|
|
|
|
};
|
|
|
|
let cmd: string | undefined;
|
|
|
|
if (argvSlice.length > 1) {
|
|
|
|
cmd = [argvSlice[0], argvSlice[1]].join(' ');
|
|
|
|
} else if (argvSlice.length > 0) {
|
|
|
|
cmd = argvSlice[0];
|
|
|
|
}
|
|
|
|
if (cmd && Object.getOwnPropertyNames(cmds).includes(cmd)) {
|
|
|
|
cmds[cmd][0](cmd, ...cmds[cmd].slice(1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-04 14:34:34 +00:00
|
|
|
// Check if this is a space separated 'topic command' style command subcommand (e.g. `end add`)
|
|
|
|
// by comparing with oclif style colon-separated subcommand list (e.g. `env:add`)
|
|
|
|
// TODO: Need to find a way of doing this that does not require maintaining list of IDs
|
|
|
|
export function isSubcommand(args: string[]) {
|
|
|
|
return oclifCommandIds.includes(`${args[0] || ''}:${args[1] || ''}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
export const oclifCommandIds = [
|
2020-08-21 08:57:14 +00:00
|
|
|
'api-key:generate',
|
2020-04-22 16:08:11 +00:00
|
|
|
'app',
|
|
|
|
'app:create',
|
2020-09-16 13:23:36 +00:00
|
|
|
'app:rename',
|
2020-04-22 16:08:11 +00:00
|
|
|
'app:restart',
|
|
|
|
'app:rm',
|
|
|
|
'apps',
|
2020-08-21 08:57:14 +00:00
|
|
|
'build',
|
2020-07-28 12:40:47 +00:00
|
|
|
'config:generate',
|
|
|
|
'config:inject',
|
|
|
|
'config:read',
|
|
|
|
'config:reconfigure',
|
|
|
|
'config:write',
|
2020-08-21 08:57:14 +00:00
|
|
|
'deploy',
|
2020-06-17 13:46:25 +00:00
|
|
|
'device',
|
|
|
|
'device:identify',
|
2020-07-02 13:56:32 +00:00
|
|
|
'device:init',
|
2020-06-17 13:46:25 +00:00
|
|
|
'device:move',
|
2020-06-18 13:36:46 +00:00
|
|
|
'device:os-update',
|
2020-06-02 08:01:04 +00:00
|
|
|
'device:public-url',
|
2020-06-17 13:46:25 +00:00
|
|
|
'device:reboot',
|
|
|
|
'device:register',
|
|
|
|
'device:rename',
|
|
|
|
'device:rm',
|
|
|
|
'device:shutdown',
|
|
|
|
'devices',
|
2019-12-26 22:59:44 +00:00
|
|
|
'devices:supported',
|
2019-09-06 13:43:53 +00:00
|
|
|
'envs',
|
|
|
|
'env:add',
|
|
|
|
'env:rename',
|
|
|
|
'env:rm',
|
2020-09-04 14:34:34 +00:00
|
|
|
'help',
|
2020-04-02 13:26:52 +00:00
|
|
|
'internal:scandevices',
|
|
|
|
'internal:osinit',
|
2020-04-09 14:34:24 +00:00
|
|
|
'join',
|
2020-04-17 13:19:33 +00:00
|
|
|
'keys',
|
|
|
|
'key',
|
|
|
|
'key:add',
|
|
|
|
'key:rm',
|
2020-04-09 14:34:24 +00:00
|
|
|
'leave',
|
2020-07-10 11:27:55 +00:00
|
|
|
'local:configure',
|
2020-07-09 13:38:04 +00:00
|
|
|
'local:flash',
|
2020-06-23 10:01:31 +00:00
|
|
|
'login',
|
|
|
|
'logout',
|
2020-07-01 13:52:33 +00:00
|
|
|
'logs',
|
2020-03-25 17:07:17 +00:00
|
|
|
'note',
|
2020-07-16 10:15:06 +00:00
|
|
|
'os:build-config',
|
2019-10-31 01:46:14 +00:00
|
|
|
'os:configure',
|
2020-07-14 13:23:45 +00:00
|
|
|
'os:versions',
|
2020-07-13 13:07:51 +00:00
|
|
|
'os:download',
|
2020-07-15 19:13:18 +00:00
|
|
|
'os:initialize',
|
2020-08-26 08:39:47 +00:00
|
|
|
'preload',
|
2020-07-24 12:25:46 +00:00
|
|
|
'push',
|
2020-06-23 10:01:31 +00:00
|
|
|
'scan',
|
2020-03-24 10:44:32 +00:00
|
|
|
'settings',
|
2020-06-24 13:24:54 +00:00
|
|
|
'ssh',
|
2020-05-26 09:17:30 +00:00
|
|
|
'tags',
|
|
|
|
'tag:rm',
|
|
|
|
'tag:set',
|
2020-06-29 12:45:32 +00:00
|
|
|
'tunnel',
|
2020-07-09 11:58:59 +00:00
|
|
|
'util:available-drives',
|
2020-03-25 17:07:17 +00:00
|
|
|
'version',
|
2020-06-23 10:01:31 +00:00
|
|
|
'whoami',
|
2019-09-06 13:43:53 +00:00
|
|
|
];
|