cmd_controller.js
1 let async = require('async'); 2 const constants = require('../lib/constants'); 3 const Logger = require('../lib/core/logger'); 4 5 require('colors'); 6 7 let version = require('../package.json').version; 8 9 class EmbarkController { 10 11 constructor(options) { 12 this.version = version; 13 this.options = options || {}; 14 15 // set a default context. should be overwritten by an action 16 // method before being used 17 this.context = {}; 18 } 19 20 initConfig(env, options) { 21 let Events = require('../lib/core/events.js'); 22 let Logger = require('../lib/core/logger.js'); 23 let Config = require('../lib/core/config.js'); 24 25 this.events = new Events(); 26 this.logger = new Logger({logLevel: Logger.logLevels.debug, events: this.events, context: this.context}); 27 28 this.config = new Config({env: env, logger: this.logger, events: this.events, context: this.context}); 29 this.config.loadConfigFiles(options); 30 this.plugins = this.config.plugins; 31 } 32 33 blockchain(env, client) { 34 this.context = [constants.contexts.blockchain]; 35 return require('../lib/modules/blockchain_process/blockchain.js')(this.config.blockchainConfig, client, env, null, null, this.logger, this.events, true).run(); 36 } 37 38 simulator(options) { 39 this.context = options.context || [constants.contexts.simulator, constants.contexts.blockchain]; 40 let Simulator = require('../lib/modules/blockchain_process/simulator.js'); 41 let simulator = new Simulator({ 42 blockchainConfig: this.config.blockchainConfig, 43 logger: this.logger 44 }); 45 simulator.run(options); 46 } 47 48 generateTemplate(templateName, destinationFolder, name, url) { 49 this.context = [constants.contexts.templateGeneration]; 50 let TemplateGenerator = require('../lib/utils/template_generator.js'); 51 let templateGenerator = new TemplateGenerator(templateName); 52 53 if (url) { 54 return templateGenerator.downloadAndGenerate(url, destinationFolder, name); 55 } 56 templateGenerator.generate(destinationFolder, name); 57 } 58 59 run(options) { 60 let self = this; 61 self.context = options.context || [constants.contexts.run, constants.contexts.build]; 62 let Dashboard = require('./dashboard/dashboard.js'); 63 64 const webServerConfig = {}; 65 66 if (options.runWebserver !== null && options.runWebserver !== undefined) { 67 webServerConfig.enabled = options.runWebserver; 68 } 69 70 if (options.serverHost !== null && options.serverHost !== undefined) { 71 webServerConfig.host = options.serverHost; 72 } 73 74 if (options.serverPort !== null && options.serverPort !== undefined) { 75 webServerConfig.port = options.serverPort; 76 } 77 78 if (options.openBrowser !== null && options.openBrowser !== undefined) { 79 webServerConfig.openBrowser = options.openBrowser; 80 } 81 82 const Engine = require('../lib/core/engine.js'); 83 const engine = new Engine({ 84 env: options.env, 85 client: options.client, 86 locale: options.locale, 87 version: this.version, 88 embarkConfig: options.embarkConfig || 'embark.json', 89 logFile: options.logFile, 90 logLevel: options.logLevel, 91 context: self.context, 92 useDashboard: options.useDashboard, 93 webServerConfig: webServerConfig, 94 webpackConfigName: options.webpackConfigName, 95 ipcRole: 'server' 96 }); 97 98 async.waterfall([ 99 function initEngine(callback) { 100 engine.init({}, () => { 101 if (!options.useDashboard) { 102 engine.logger.info('========================'.bold.green); 103 engine.logger.info((__('Welcome to Embark') + ' ' + engine.version).yellow.bold); 104 engine.logger.info('========================'.bold.green); 105 } 106 callback(); 107 }); 108 }, 109 function startDashboard(callback) { 110 if (!options.useDashboard) { 111 return callback(); 112 } 113 114 let dashboard = new Dashboard({ 115 events: engine.events, 116 logger: engine.logger, 117 plugins: engine.plugins, 118 version: self.version, 119 env: engine.env 120 }); 121 dashboard.start(function () { 122 engine.logger.info(__('dashboard start')); 123 callback(); 124 }); 125 }, 126 function (callback) { 127 let pluginList = engine.plugins.listPlugins(); 128 if (pluginList.length > 0) { 129 engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", ")); 130 } 131 132 engine.startService("processManager"); 133 engine.startService("coreProcess"); 134 engine.startService("embarkListener"); 135 engine.startService("blockchainListener"); 136 engine.startService("serviceMonitor"); 137 engine.startService("libraryManager"); 138 engine.startService("codeRunner"); 139 engine.startService("web3"); 140 engine.startService("pipeline"); 141 engine.startService("deployment"); 142 engine.startService("storage"); 143 engine.startService("codeGenerator"); 144 engine.startService("console"); 145 engine.startService("pluginCommand"); 146 147 engine.events.on('check:backOnline:Ethereum', function () { 148 engine.logger.info(__('Ethereum node detected') + '..'); 149 engine.config.reloadConfig(); 150 engine.events.request('deploy:contracts', function (err) { 151 if (err) { 152 return; 153 } 154 engine.logger.info(__('Deployment Done')); 155 }); 156 }); 157 158 engine.events.on('outputDone', function () { 159 engine.logger.info((__("Looking for documentation? You can find it at") + " ").cyan + "http://embark.status.im/docs/".green.underline + ".".cyan); 160 engine.logger.info(__("Ready").underline); 161 engine.events.emit("status", __("Ready").green); 162 }); 163 164 if (webServerConfig.enabled !== false) { 165 engine.startService("webServer"); 166 } 167 engine.startService("fileWatcher"); 168 callback(); 169 } 170 ], function (err, _result) { 171 if (err) { 172 engine.logger.error(err.message); 173 engine.logger.info(err.stack); 174 } else { 175 engine.events.emit('firstDeploymentDone'); 176 } 177 }); 178 } 179 180 build(options) { 181 this.context = options.context || [constants.contexts.build]; 182 183 const Engine = require('../lib/core/engine.js'); 184 const engine = new Engine({ 185 env: options.env, 186 client: options.client, 187 locale: options.locale, 188 version: this.version, 189 embarkConfig: 'embark.json', 190 interceptLogs: false, 191 logFile: options.logFile, 192 logLevel: options.logLevel, 193 events: options.events, 194 logger: options.logger, 195 config: options.config, 196 plugins: options.plugins, 197 context: this.context, 198 webpackConfigName: options.webpackConfigName 199 }); 200 201 202 async.waterfall([ 203 function initEngine(callback) { 204 engine.init({}, callback); 205 }, 206 function startServices(callback) { 207 let pluginList = engine.plugins.listPlugins(); 208 if (pluginList.length > 0) { 209 engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", ")); 210 } 211 212 engine.startService("processManager"); 213 engine.startService("libraryManager"); 214 engine.startService("codeRunner"); 215 if (!options.onlyCompile) { 216 engine.startService("web3"); 217 engine.startService("deployment", {onlyCompile: options.onlyCompile}); 218 engine.startService("pipeline"); 219 engine.startService("storage"); 220 engine.startService("codeGenerator"); 221 } else { 222 engine.startService('compiler'); 223 } 224 225 callback(); 226 }, 227 function buildOrBuildAndDeploy(callback) { 228 if (options.onlyCompile) { 229 engine.events.request('contracts:build', {}, err => callback(err)); 230 } else { 231 // deploy:contracts will trigger a build as well 232 engine.events.request('deploy:contracts', err => callback(err)); 233 } 234 }, 235 function waitForWriteFinish(callback) { 236 if (options.onlyCompile) { 237 engine.logger.info("Finished compiling".underline); 238 return callback(null, true); 239 } 240 engine.logger.info("Finished deploying".underline); 241 engine.events.on('outputDone', (err) => { 242 engine.logger.info(__("Finished building").underline); 243 callback(err, true); 244 }); 245 } 246 ], function (_err, canExit) { 247 // TODO: this should be moved out and determined somewhere else 248 if (canExit || !engine.config.contractsConfig.afterDeploy || !engine.config.contractsConfig.afterDeploy.length) { 249 process.exit(); 250 } 251 engine.logger.info(__('Waiting for after deploy to finish...')); 252 engine.logger.info(__('You can exit with CTRL+C when after deploy completes')); 253 }); 254 } 255 256 console(options) { 257 this.context = options.context || [constants.contexts.run, constants.contexts.console]; 258 const REPL = require('./dashboard/repl.js'); 259 const Engine = require('../lib/core/engine.js'); 260 const engine = new Engine({ 261 env: options.env, 262 client: options.client, 263 locale: options.locale, 264 version: this.version, 265 embarkConfig: options.embarkConfig || 'embark.json', 266 logFile: options.logFile, 267 logLevel: options.logLevel, 268 context: this.context, 269 webpackConfigName: options.webpackConfigName 270 }); 271 272 async.waterfall([ 273 function initEngine(callback) { 274 engine.init({}, callback); 275 }, 276 function startServices(callback) { 277 let pluginList = engine.plugins.listPlugins(); 278 if (pluginList.length > 0) { 279 engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", ")); 280 } 281 282 if (engine.ipc.connected) { 283 engine.startService("codeRunner"); 284 engine.startService("console"); 285 return callback(); 286 } 287 engine.startService("processManager"); 288 engine.startService("serviceMonitor"); 289 engine.startService("libraryManager"); 290 engine.startService("codeRunner"); 291 engine.startService("web3"); 292 engine.startService("pipeline"); 293 engine.startService("deployment"); 294 engine.startService("storage"); 295 engine.startService("codeGenerator"); 296 engine.startService("console"); 297 engine.startService("pluginCommand"); 298 engine.events.once('check:backOnline:Ethereum', () => callback()); 299 }, 300 function ipcConnect(callback) { 301 // Do specific work in case we are connected to a socket: 302 // - Setup Web3 303 // - Apply history 304 if(!engine.ipc.connected || engine.ipc.isServer()) { 305 return callback(); 306 } 307 const Provider = require('../lib/modules/blockchain_connector/provider'); 308 const Web3 = require('web3'); 309 let web3 = new Web3(); 310 engine.ipc.request("runcode:getCommands", null, (_, {web3Config, commands}) => { 311 const providerOptions = { 312 web3: web3, 313 accountsConfig: engine.config.contractsConfig.deployment.accounts, 314 blockchainConfig: engine.config.blockchainConfig, 315 logger: engine.logger, 316 isDev: engine.isDev, 317 type: engine.config.contractsConfig.deployment.type, 318 web3Endpoint: web3Config.providerUrl 319 }; 320 const provider = new Provider(providerOptions); 321 web3.eth.defaultAccount = web3Config.defaultAccount; 322 provider.startWeb3Provider(() => { 323 engine.events.emit("runcode:register", "web3", web3); 324 async.each(commands, ({varName, code}, next) => { 325 if (varName) { 326 engine.events.emit("runcode:register", varName, code); 327 } else { 328 engine.events.request("runcode:eval", code); 329 } 330 next(); 331 }, callback); 332 }); 333 }); 334 }, 335 function deploy(callback) { 336 // Skip if we are connected to a websocket, the server will do it 337 if(engine.ipc.connected && engine.ipc.isClient()) { 338 return callback(); 339 } 340 engine.config.reloadConfig(); 341 engine.events.request('deploy:contracts', function (err) { 342 callback(err); 343 }); 344 }, 345 function waitForWriteFinish(callback) { 346 // Skip if we are connected to a websocket, the server will do it 347 if(engine.ipc.connected && engine.ipc.isClient()) { 348 return callback(); 349 } 350 engine.logger.info("Finished deploying".underline); 351 engine.events.once('outputDone', (err) => { 352 engine.logger.info(__("finished building").underline); 353 callback(err); 354 }); 355 }, 356 function startREPL(callback) { 357 new REPL({events: engine.events, env: engine.env}).start(callback); 358 } 359 ], function (err, _result) { 360 if (err) { 361 engine.logger.error(err.message); 362 engine.logger.info(err.stack); 363 } else { 364 engine.events.emit('firstDeploymentDone'); 365 } 366 }); 367 } 368 369 graph(options) { 370 this.context = options.context || [constants.contexts.graph]; 371 options.onlyCompile = true; 372 373 const Engine = require('../lib/core/engine.js'); 374 const engine = new Engine({ 375 env: options.env, 376 version: this.version, 377 embarkConfig: options.embarkConfig || 'embark.json', 378 logFile: options.logFile, 379 context: this.context 380 }); 381 382 383 async.waterfall([ 384 function (callback) { 385 engine.init({}, callback); 386 }, 387 function (callback) { 388 let pluginList = engine.plugins.listPlugins(); 389 if (pluginList.length > 0) { 390 engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", ")); 391 } 392 393 engine.startService("processManager"); 394 engine.startService("serviceMonitor"); 395 engine.startService("libraryManager"); 396 engine.startService("compiler"); 397 engine.startService("codeGenerator"); 398 engine.startService("graph"); 399 engine.events.request('contracts:build', {}, callback); 400 } 401 ], (err) => { 402 if (err) { 403 engine.logger.error(err.message); 404 engine.logger.info(err.stack); 405 } else { 406 407 engine.events.request("graph:create", options, () => { 408 engine.logger.info(__("Done. %s generated", options.output).underline); 409 }); 410 } 411 process.exit(); 412 }); 413 414 } 415 416 reset() { 417 var fs = require('../lib/core/fs.js'); 418 fs.removeSync('./chains.json'); 419 fs.removeSync('.embark/'); 420 fs.removeSync('node_modules/.cache'); 421 fs.removeSync('dist/'); 422 fs.removeSync('coverage/'); 423 console.log(__("reset done!").green); 424 } 425 426 ejectWebpack() { 427 var fs = require('../lib/core/fs.js'); 428 var embarkConfig = fs.embarkPath('lib/modules/pipeline/webpack.config.js'); 429 var dappConfig = fs.dappPath('webpack.config.js'); 430 fs.copyPreserve(embarkConfig, dappConfig); 431 console.log(__('webpack config ejected to:').dim.yellow); 432 console.log(`${dappConfig}`.green); 433 var embarkOverrides = fs.embarkPath('lib/modules/pipeline/babel-loader-overrides.js'); 434 var dappOverrides = fs.dappPath('babel-loader-overrides.js'); 435 fs.copyPreserve(embarkOverrides, dappOverrides); 436 console.log(__('webpack overrides ejected to:').dim.yellow); 437 console.log(`${dappOverrides}`.green); 438 } 439 440 scaffold(options) { 441 this.context = options.context || [constants.contexts.scaffold]; 442 443 const Engine = require('../lib/core/engine.js'); 444 const engine = new Engine({ 445 env: options.env, 446 client: options.client, 447 locale: options.locale, 448 version: this.version, 449 embarkConfig: 'embark.json', 450 interceptLogs: false, 451 logFile: options.logFile, 452 logLevel: options.logLevel, 453 events: options.events, 454 logger: options.logger, 455 config: options.config, 456 plugins: options.plugins, 457 context: this.context, 458 webpackConfigName: options.webpackConfigName 459 }); 460 461 async.waterfall([ 462 function initEngine(callback) { 463 engine.init({}, callback); 464 }, 465 function startServices(callback) { 466 engine.startService("scaffolding"); 467 callback(); 468 }, 469 function generateContract(callback) { 470 engine.events.request('scaffolding:generate:contract', options, function(err, file) { 471 // Add contract file to the manager 472 engine.events.request('config:contractsFiles:add', file); 473 callback(); 474 }); 475 }, 476 function initEngineServices(callback) { 477 let pluginList = engine.plugins.listPlugins(); 478 if (pluginList.length > 0) { 479 engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", ")); 480 } 481 engine.startService("processManager"); 482 engine.startService("libraryManager"); 483 engine.startService("codeRunner"); 484 engine.startService("web3"); 485 engine.startService("deployment", {onlyCompile: true}); 486 487 callback(); 488 }, 489 function deploy(callback) { 490 engine.events.request('deploy:contracts', function(err) { 491 callback(err); 492 }); 493 }, 494 function generateUI(callback) { 495 engine.events.request("scaffolding:generate:ui", options, () => { 496 callback(); 497 }); 498 } 499 ], function(err) { 500 if (err) { 501 engine.logger.error(__("Error generating the UI: ")); 502 engine.logger.error(err.message || err); 503 process.exit(1); 504 } 505 engine.logger.info(__("finished generating the UI").underline); 506 engine.logger.info(__("To see the result, execute {{cmd}} and go to /{{contract}}.html", {cmd: 'embark run'.underline, contract: options.contract})); 507 process.exit(); 508 }); 509 } 510 511 upload(options) { 512 this.context = options.context || [constants.contexts.upload, constants.contexts.build]; 513 514 const Engine = require('../lib/core/engine.js'); 515 const engine = new Engine({ 516 env: options.env, 517 client: options.client, 518 locale: options.locale, 519 version: this.version, 520 embarkConfig: 'embark.json', 521 interceptLogs: false, 522 logFile: options.logFile, 523 logLevel: options.logLevel, 524 events: options.events, 525 logger: options.logger, 526 config: options.config, 527 plugins: options.plugins, 528 context: this.context, 529 webpackConfigName: options.webpackConfigName 530 }); 531 532 let platform; 533 534 async.waterfall([ 535 function initEngine(callback) { 536 engine.init({}, () => { 537 if (engine.config.embarkConfig.config.storage === false || engine.config.storageConfig.enabled === false) { 538 engine.logger.error(__('Storage configuration is disabled in embark.json. Please provide a storage file before uploading')); 539 engine.logger.info(__('You can find an example here: %s', 'https://github.com/embark-framework/embark/blob/master/templates/demo/config/storage.js'.underline)); 540 process.exit(1); 541 } 542 platform = engine.config.storageConfig.upload.provider; 543 callback(); 544 }); 545 }, 546 function startServices(callback) { 547 548 engine.startService("processManager"); 549 engine.startService("serviceMonitor"); 550 engine.startService("libraryManager"); 551 engine.startService("codeRunner"); 552 engine.startService("web3"); 553 engine.startService("pipeline"); 554 engine.startService("deployment"); 555 engine.startService("storage"); 556 engine.startService("codeGenerator"); 557 callback(); 558 }, 559 function listLoadedPlugin(callback) { 560 let pluginList = engine.plugins.listPlugins(); 561 if (pluginList.length > 0) { 562 engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", ")); 563 } 564 callback(); 565 }, 566 function deploy(callback) { 567 engine.events.on('outputDone', function () { 568 engine.events.request("storage:upload", callback); 569 }); 570 engine.events.on('check:backOnline:Ethereum', function () { 571 engine.logger.info(__('Ethereum node detected') + '..'); 572 engine.config.reloadConfig(); 573 engine.events.request('deploy:contracts', function (err) { 574 if (err) { 575 return; 576 } 577 engine.logger.info(__('Deployment Done')); 578 }); 579 }); 580 }, 581 function associateToENS(hash, callback) { 582 if(!options.ensDomain) { 583 return callback(null, hash); 584 } 585 engine.events.request("storage:ens:associate", 586 {name: options.ensDomain, storageHash: hash}, (err) => { 587 if (err) { 588 return callback(err); 589 } 590 engine.logger.info(__('ENS association completed for {{hash}} at {{domain}}', {hash, domain: options.ensDomain})); 591 callback(); 592 }); 593 } 594 ], function (err) { 595 if (err) { 596 if (err.message) { 597 engine.logger.error(err.message); 598 return engine.logger.debug(err.stack); 599 } 600 engine.logger.error(err); 601 } else { 602 engine.logger.info((__("finished building DApp and deploying to") + " " + platform).underline); 603 } 604 605 // needed due to child processes 606 process.exit(); 607 }); 608 } 609 610 runTests(options) { 611 this.context = [constants.contexts.test]; 612 613 const Engine = require('../lib/core/engine.js'); 614 const engine = new Engine({ 615 env: options.env, 616 client: options.client, 617 locale: options.locale, 618 version: this.version, 619 embarkConfig: options.embarkConfig || 'embark.json', 620 logFile: options.logFile, 621 logLevel: options.logLevel || Logger.logLevels.warn, 622 context: this.context, 623 useDashboard: options.useDashboard, 624 webpackConfigName: options.webpackConfigName, 625 ipcRole: 'client', 626 interceptLogs: false 627 }); 628 629 async.waterfall([ 630 function initEngine(callback) { 631 engine.init({}, callback); 632 }, 633 function startServices(callback) { 634 engine.startService("processManager"); 635 engine.startService("libraryManager"); 636 engine.startService("web3", {wait: true}); 637 engine.startService("deployment", { 638 trackContracts: false, 639 compileOnceOnly: true, 640 disableOptimizations: options.coverage 641 }); 642 engine.startService("codeGenerator"); 643 engine.startService("codeRunner"); 644 engine.startService("codeCoverage"); 645 engine.startService("testRunner"); 646 callback(); 647 }, 648 function runTests(callback) { 649 engine.events.request('tests:run', options, callback); 650 } 651 ], function (err) { 652 if (err) { 653 engine.logger.error(err.message || err); 654 } 655 656 process.exit(err ? 1 : 0); 657 }); 658 } 659 } 660 661 module.exports = EmbarkController;