/ cmd / cmd_controller.js
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;