UNPKG

@kui-shell/core

Version:

An Electron-based shell for cloud-native development

334 lines (332 loc) • 11.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createWindow = void 0; exports.initHeadless = initHeadless; exports.main = void 0; var _debug = _interopRequireDefault(require("debug")); var _exec = require("../repl/exec"); var _mimicDom = _interopRequireDefault(require("../util/mimic-dom")); var _plugins = require("../plugins/plugins"); var _usageError = _interopRequireDefault(require("../core/usage-error")); var _capabilities = require("../core/capabilities"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /* * Copyright 2017 The Kubernetes Authors * * 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. */ var __awaiter = void 0 && (void 0).__awaiter || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; /* eslint-disable prefer-rest-params, prefer-spread */ const debug = (0, _debug.default)('main/headless'); debug('loading'); (0, _capabilities.setMedia)(_capabilities.Media.Headless); // by default, we'll exit with an exit code of 0 when success is // called; this bit is necessary, as process.exit doesn't seem to // really exit the first time let exitCode = 0; // electron pops up a window by default, for uncaught exceptions process.on('uncaughtException', err => { console.error('Uncaught Exception', err); }); process.on('exit', code => { debug('exiting', code); }); const commandContextPattern = /--command-context/; let commandContext; const log = console.log; const error = console.error; debug('modules loaded'); /** * Certain commands may open the graphical shell; remember this, so * we know not to process.exit * */ let graphicalShellIsOpen = false; /** * Are we in the middle of a hasty retreat? * */ const noAuth = false; /** completion handlers for success and failure */ const success = quit => out => __awaiter(void 0, void 0, void 0, function* () { debug('success!'); if (process.env.KUI_REPL_MODE) { debug('returning repl mode result'); return out; } const { print } = yield Promise.resolve().then(() => require('./headless-pretty-print')); yield print(out, log, process.stdout); if (!graphicalShellIsOpen) { quit(); process.exit(exitCode); } else { // log('The graphical shell should now be open. This process will stay alive till you close the window.'.red) // log('You may background this process, but do not kill it, unless you also want to kill the graphical shell.'.red) debug('graphical shell is open'); } }); const failure = (quit, execOptions) => err => __awaiter(void 0, void 0, void 0, function* () { if (execOptions && execOptions.rethrowErrors) { throw err; } else if (!err) { return; } const code = err.code || err.statusCode; debug('failure', code, err); let completion = Promise.resolve(); if (!noAuth) { // we're not in a corner case of having no credentials, so print // the error if (process.env.TEST_CODE_ONLY && err.statusCode) { // client asked us to emit only the error code error(err.statusCode); } else { let msg; if (_usageError.default.isUsageError(err)) { msg = _usageError.default.getFormattedMessage(err); } else { const { oopsMessage } = yield Promise.resolve().then(() => require('../core/oops')); msg = oopsMessage(err); } if (typeof msg === 'string') { const colors = yield Promise.resolve().then(() => require('colors/safe')); if (process.env.TEST_INCLUDE_CODE && err.statusCode) { // client asked for the code and the message error(colors.blue(err.statusCode.toString()) + ' ' + colors.red(msg)); } else { // client did not ask for the error code error(colors.red(msg)); } } else { const { print } = yield Promise.resolve().then(() => require('./headless-pretty-print')); completion = print(msg, error, process.stderr, undefined, 'error') || Promise.resolve(); } } } else { error(`No credentials found. Consider trying again with "kui help" command.`); } return completion.then(() => { if (!graphicalShellIsOpen) { // if the graphical shell isn't open, then we're done here exitCode = typeof code === 'number' ? code : 1; if (!process.env.KUI_REPL_MODE) { process.exit(exitCode > 128 ? exitCode - 256 : exitCode); // eslint-disable-next-line no-unreachable if (quit) { quit(); } } } return false; }); }); /** * Insufficient arguments provided? * */ const insufficientArgs = argv => argv.length === 0 || argv.length === 1 && /(-h)|(--help)/.test(argv[0]); /** * Opens the full UI * */ let electronCreateWindowFn; const createWindow = (argv, subwindowPlease, subwindowPrefs, isPopup) => __awaiter(void 0, void 0, void 0, function* () { try { graphicalShellIsOpen = true; const { setGraphicalShellIsOpen } = yield Promise.resolve().then(() => require('./headless-pretty-print')); if (!setGraphicalShellIsOpen(isPopup)) { debug('probably a bug somewhere; detected loop in opening window from headless mode'); return; } const commandLine = argv; if (commandContext) { commandLine.push(commandContext); } electronCreateWindowFn(commandLine, subwindowPlease, subwindowPrefs); } catch (err) { error(err); } }); /** * * */ exports.createWindow = createWindow; const initCommandContext = commandContext => __awaiter(void 0, void 0, void 0, function* () { if (commandContext) { try { debug('setting command context', commandContext); (yield Promise.resolve().then(() => require('../commands/context'))).setDefaultCommandContext(JSON.parse(commandContext.substring(commandContext.indexOf('=') + 1))); } catch (err) { debug('Error initializing command context', err); } } }); function initCommandRegistrar() { return __awaiter(this, void 0, void 0, function* () { const { init } = yield Promise.resolve().then(() => require('../commands/tree')); yield init(); }); } /** * Initialize headless mode * */ const main = (app, mainFunctions, rawArgv = process.argv, execOptions) => __awaiter(void 0, void 0, void 0, function* () { debug('main'); // get this started right away, to amortize the cost of loading the // prescan model; this saves us around 5ms as of 20190709 const waitForPlugins = (0, _plugins.init)(); const waitForCommands = yield initCommandRegistrar(); const ourCommandContext = rawArgv.find(_ => commandContextPattern.test(_)); debug('commandContext', ourCommandContext); if (!commandContext && ourCommandContext) { commandContext = ourCommandContext; } const dashDash = rawArgv.indexOf('--'); const argStart = dashDash >= 0 ? dashDash + 1 : 1; const argv = rawArgv.slice(argStart).filter(arg => !commandContextPattern.test(arg) && arg !== '--kui-headless' && arg !== '--raw-output' && arg !== '--no-color' && arg !== '--no-colors' && arg !== '--color=always' && arg !== '--ui'); debug('argv', argv); const { quit } = app; // installOopsHandler(() => failure(quit, execOptions)) // TODO should be repl.installOopsHandler electronCreateWindowFn = mainFunctions.createWindow; // set up the fake dom (0, _mimicDom.default)(); /** * Evaluate the given command * */ const eOptions = Object.assign({ rethrowErrors: true }, execOptions); const evaluate = cmd => Promise.resolve((0, _exec.exec)(cmd, eOptions)).then(success(quit)); /** main work starts here */ debug('bootstrap'); return Promise.all([waitForPlugins, waitForCommands]).then(([initWasPerformed]) => __awaiter(void 0, void 0, void 0, function* () { debug('plugins initialized'); if (initWasPerformed) { yield initCommandContext(commandContext); } const maybeRetry = err => { // nothing, yet return failure(quit, execOptions)(err); }; // warning: this must occur before insufficientArgs as, // currently, the help command is registered as a preload // handler. i'm not sure why, but that's how it works right now. if (initWasPerformed) { debug('invoking plugin preloader'); yield (0, _plugins.preload)(); debug('invoking plugin preloader... done'); } if (insufficientArgs(argv)) { debug('insufficient args, invoking help command'); return evaluate('help'); } // // execute a single command from the CLI // const cmd = argv.map(_ => _.match(/\s+/) ? `"${_}"` : _).join(' ').trim(); if (cmd && cmd.length > 0) { debug('about to execute command', cmd); return evaluate(cmd).catch(maybeRetry); } else { debug('exiting, no command'); if (!process.env.KUI_REPL_MODE) { process.exit(0); } } })).catch(failure(quit, execOptions)); }); /** * Bootstrap headless mode * */ exports.main = main; function initHeadless(argv, force = false, isRunningHeadless = false, execOptions) { if ( /* noHeadless !== true && */force || isRunningHeadless) { debug('initHeadless'); const app = { quit: () => process.exit(0) }; // // HEADLESS MODE // try { return main(app, { createWindow: (executeThisArgvPlease, subwindowPlease, subwindowPrefs) => __awaiter(this, void 0, void 0, function* () { // craft a createWindow that has a first argument of true, which will indicate `noHeadless` // because this will be called for cases where we want a headless -> GUI transition const { createWindow: createElectronWindow } = yield Promise.resolve().then(() => require('./spawn-electron')); return createElectronWindow(true, process.env, executeThisArgvPlease, subwindowPlease, subwindowPrefs, false, true); }) }, argv, execOptions); } catch (err) { // oof, something real bad happened console.error('Internal Error, please report this bug:'); console.error(err); if (!process.env.KUI_REPL_MODE) { process.exit(1); } else { throw err; } } } else { // in case the second argument isn't undefined... /* if (noHeadless !== true) { executeThisArgvPlease = undefined } */ } } /* initHeadless */