brainwise-demo-cli
Version:
brainwise-Newcli is an open source chatbot platform based on Rasa.
208 lines (183 loc) • 7.62 kB
JavaScript
import open from 'open'
import ora from 'ora';
import yaml from 'js-yaml';
import fs from 'fs-extra';
import path from 'path';
import { Docker } from 'docker-cli-js';
import boxen from 'boxen';
import inquirer from 'inquirer';
import shell from 'shelljs';
import chalk from 'chalk';
import { initCommand } from './commands/init';
import {
dockerComposeUp,
dockerComposeDown,
dockerComposeFollow,
dockerComposeStop,
dockerComposeStart,
dockerComposeRestart,
stopRunningProjects,
getRunningDockerResources,
watchFolder,
} from './commands/services';
import {
wait,
isProjectDir,
verifySystem,
getbrainwiseVersion,
failSpinner,
stopSpinner,
getContainerAndImageNames,
succeedSpinner,
consoleError,
} from './utils';
const program = require('commander');
const version = getbrainwiseVersion();
function collect(value, previous) {
return previous.concat([value]);
}
program
.version(version)
.description('brainwise--demo-cli CLI')
.action(() => console.log(`\n${chalk.red.bold('ERROR:')} Unsupported command. Run ${chalk.cyan.bold('brainwise-demo-cli --help')} for more information.\n`));
program
.command('init')
.option('--path <path>', 'Desired project path')
.option('--img-brainwise <image:tag>', 'Image for the Brainwise service')
.option('--img-brainwise-api <image:tag>', 'Image used by the Brainwise--api service')
.option('--img-rasa <image:tag>', 'Image used by the Rasa service')
.option('--ci', 'No spinners, no prompt confirmations')
.description('Create a new brainwise chatbot project.')
.action(initCommand);
program
.command('up')
.option('-e, --exclude <service>', 'Do not run a given service', collect, [])
.option('-v, --verbose', 'Display Docker Compose start-up logs')
.option('--ci', 'No spinners, no prompt confirmations')
.description('Start a brainwise chatbot project. Must be executed in your project\'s directory')
.action(dockerComposeUp);
program
.command('down')
.option('-v, --verbose', 'Display Docker Compose start-up logs')
.description('Stops a brainwise chatbot project and releases Docker resources. Must be executed in your project\'s directory')
.action(dockerComposeDown);
program
.command('logs')
.option('--ci', 'Print out logs once and do not hook to TTY')
.description('Display brainwise chatbot logs. Must be executed in your project\'s directory')
.action(dockerComposeFollow);
program
.command('killall')
.option('--remove-images', 'Will also remove Brainwise-cli related Docker images')
.description('Stops any running brainwise chatbot project')
.action(killAllCommand);
program
.command('stop <service>')
.description('Stop a brainwise chatbot service (interactive). Must be executed in your project\'s directory')
.action(dockerComposeStop);
program
.command('start <service>')
.description('Start a brainwise chatbot service (interactive). Must be executed in your project\'s directory')
.action(dockerComposeStart);
program
.command('restart <service>')
.description('Restart a brainwise chatbot service (interactive). Must be executed in your project\'s directory')
.action(dockerComposeRestart);
program
.command('watch')
.description('Restart the Actions service automatically on file change. Must be executed in your project\'s directory')
.action(watchFolder);
program
.command('docs')
.description('Open the online documentation in your browser')
.action(openDocs);
async function openDocs() {
const spinner = ora()
spinner.start(`Opening ${chalk.green.bold('https://brainwise.me')} in your browser...`)
await wait(2000);
await open('https://brainwise.me')
spinner.succeed('Done')
console.log('\n');
}
async function killAllCommand(cmd) {
const { stop } = await inquirer.prompt({
type: 'confirm',
name: 'stop',
message: 'This will stop any running brainwise chatbot project and cleanup remaining Docker resources. This will not affect your project\'s data. Proceed ?',
default: true,
});
if (stop){
const spinner = ora();
try {
await stopRunningProjects(
'Attempting to stop a running project...',
`A project was stopped and all its resources released. Your data is safe and you can always restart it by running ${chalk.cyan.bold(
'brainwise-demo-cli up',
)} from your project\'s folder.\n`,
'All clear 👍.',
spinner,
);
cleanupDocker({rm: true, rmi: cmd.removeImages}, spinner)
stopSpinner(spinner)
} catch (e) {
failSpinner(spinner, e);
}
}
}
async function general() {
const choices = [];
try {
await verifySystem()
const { containers } = await getRunningDockerResources()
if (isProjectDir()){
if (containers && containers.length){
choices.push({ title: 'Stop brainwise-demo-cli', cmd: () => dockerComposeDown({ verbose: false }) });
choices.push({ title: 'Show logs', cmd: dockerComposeFollow });
} else {
choices.push({ title: 'Start project', cmd: () => dockerComposeUp({ verbose: false })});
}
} else {
if (containers && containers.length){
choices.push({ title: 'Stop brainwise-demo-cli', cmd: () => killAllCommand({ verbose: false }) });
}
choices.push({ title: 'Create a new project', cmd: initCommand });
}
choices.push({ title: 'Browse the online documentation', cmd: openDocs});
choices.push({ title: 'More options (display the --help)', cmd: () => shell.exec('Brainwise-cli -h') });
choices.push({ title: 'Exit', cmd: () => process.exit(0) });
console.log(boxen(`Welcome to ${chalk.green.bold('brainwise-demo-cli')}!\nversion: ${getbrainwiseVersion()}`, { padding: 1, margin: 1 }));
const { action } = await inquirer.prompt({
type: 'list',
name: 'action',
message: 'What do you want to do?',
choices: choices.map(choice => choice.title),
});
choices.find(c => c.title === action).cmd()
} catch (e) {
console.log(e)
}
}
async function cleanupDocker({rm, rmi}, spinner = ora()) {
const composePath = path.resolve(__dirname, '..', 'project-template', '.Brainwise-cli', 'docker-compose-template.yml');
const { services } = yaml.safeLoad(fs.readFileSync(composePath), 'utf-8');
const containersAndImageNames = getContainerAndImageNames(null, services);
if (rm) runDockerPromises('rm', containersAndImageNames, spinner);
if (rmi) runDockerPromises('rmi', containersAndImageNames, spinner);
}
async function runDockerPromises(cmd, { containers, images }, spinner) {
const docker = new Docker({});
const name = cmd === 'rm' ? 'containers' : 'images';
const array = cmd === 'rm' ? containers : images;
const promises = array.map(i => docker.command(`${cmd} ${i}`).catch(()=>{}));
try {
await Promise.all(promises);
return succeedSpinner(spinner, `Docker ${name} removed.`);
} catch (e) {
consoleError(e);
failSpinner(spinner, `Could not remove Docker ${name}.`);
} finally {
stopSpinner();
}
}
const commandr = program.parse(process.argv);
if (commandr.rawArgs.length == 2) general();