diff --git a/packages/embark-specialconfigs/src/functionConfigs.js b/packages/embark-specialconfigs/src/functionConfigs.js new file mode 100644 index 000000000..106825425 --- /dev/null +++ b/packages/embark-specialconfigs/src/functionConfigs.js @@ -0,0 +1,93 @@ +import { __ } from 'embark-i18n'; +const Utils = require('./utils.js'); + +class FunctionConfigs { + constructor(embark) { + this.embark = embark; + this.events = embark.events; + this.logger = embark.logger; + this.config = embark.config; + } + + async beforeAllDeployAction(cb) { + try { + const beforeDeployFn = this.config.contractsConfig.beforeDeploy; + const logger = Utils.createLoggerWithPrefix(this.logger, 'beforeDeploy >'); + await beforeDeployFn({ logger }); + cb(); + } catch (err) { + cb(new Error(`Error running beforeDeploy hook: ${err.message}`)); + } + } + + async afterAllDeployAction(cb) { + try { + const logger = Utils.createLoggerWithPrefix(this.logger, 'afterAllDeploy >'); + const dependencies = await this.getDependenciesObject(logger); + await this.config.contractsConfig.afterDeploy(dependencies); + return cb(); + } catch (err) { + return cb(new Error(`Error registering afterDeploy lifecycle hook: ${err.message}`)); + } + } + + async doOnDeployAction(contract, cb) { + try { + const logger = Utils.createLoggerWithPrefix(this.logger, `${contract.className} > onDeploy >`); + const dependencies = await this.getDependenciesObject(logger); + await contract.onDeploy(dependencies); + return cb(); + } catch (err) { + return cb(new Error(`Error when registering onDeploy hook for ${contract.className}: ${err.message}`)); + } + } + + async deployIfAction(params, cb) { + const contract = params.contract; + try { + const logger = Utils.createLoggerWithPrefix(this.logger, 'deployIf >'); + const dependencies = await this.getDependenciesObject(logger); + params.shouldDeploy = await contract.deployIf(dependencies); + return cb(null, params); + } catch (err) { + return cb(new Error(`Error when registering deployIf hook for ${contract.className}: ${err.message}`)); + } + } + + async beforeDeployAction(_params, cb) { + const beforeDeployFn = params.contract.beforeDeploy; + const contract = params.contract; + try { + const logger = Utils.createLoggerWithPrefix(this.logger, 'beforeDeploy >'); + const dependencies = await this.getDependenciesObject(logger); + await beforeDeployFn(dependencies); + cb(); + } catch (e) { + cb(new Error(`Error running beforeDeploy hook for ${contract.className}: ${e.message || e}`)); + } + } + + getDependenciesObject(logger) { + return new Promise(async (resolve, reject) => { + let contracts = await this.events.request2("contracts:list"); + + let args = { contracts: {}, logger}; + for (let contract of contracts) { + // TODO: for this to work correctly we need to add a default from address to the contract + let contractInstance = await this.events.request2("runcode:eval", contract.className); + args.contracts[contract.className] = contractInstance; + } + + try { + let web3Instance = await this.events.request2("runcode:eval", "web3"); + args.web3 = web3Instance; + } catch (_err) { + } + + resolve(args); + }); + } + +} + +module.exports = FunctionConfigs; diff --git a/packages/embark-specialconfigs/src/index.js b/packages/embark-specialconfigs/src/index.js index 349115bf8..5ae15efe0 100644 --- a/packages/embark-specialconfigs/src/index.js +++ b/packages/embark-specialconfigs/src/index.js @@ -1,9 +1,8 @@ /* global module require */ import { __ } from 'embark-i18n'; -const stringReplaceAsync = require('string-replace-async'); -const async = require('async'); -const {callbackify} = require('util'); +const ListConfigs = require('./listConfigs.js'); +const FunctionConfigs = require('./functionConfigs.js'); class SpecialConfigs { @@ -13,6 +12,8 @@ class SpecialConfigs { this.buildDir = options.buildDir; this.embark = embark; this.config = embark.config; + this.listConfigs = new ListConfigs(embark); + this.functionConfigs = new FunctionConfigs(embark); this.embark.registerActionForEvent('deployment:deployContracts:beforeAll', this.beforeAllDeployAction.bind(this)); this.embark.registerActionForEvent('deployment:deployContracts:afterAll', this.afterAllDeployAction.bind(this)); @@ -22,174 +23,24 @@ class SpecialConfigs { } async beforeAllDeployAction(cb) { - const beforeDeployFn = this.config.contractsConfig.beforeDeploy; - if (!beforeDeployFn || typeof beforeDeployFn !== 'function') { - return cb(); - } - try { - const logger = this.createLoggerWithPrefix('beforeDeploy >'); - await beforeDeployFn({ logger }); - cb(); - } catch (err) { - cb(new Error(`Error running beforeDeploy hook: ${err.message}`)); + if (typeof this.config.contractsConfig.beforeDeploy !== 'function') { + return this.listConfigs.beforeAllDeployAction(cb); } + return this.functionConfigs.beforeAllDeployAction(cb); } async afterAllDeployAction(cb) { - if (typeof this.config.contractsConfig.afterDeploy === 'function') { - try { - const dependencies = await this.getDependenciesObject(); - await this.config.contractsConfig.afterDeploy(dependencies); - return cb(); - } catch (err) { - return cb(new Error(`Error registering afterDeploy lifecycle hook: ${err.message}`)); - } + if (typeof this.config.contractsConfig.afterDeploy !== 'function') { + return this.listConfigs.afterAllDeployAction(cb); } - let afterDeployCmds = this.config.contractsConfig.afterDeploy || []; - async.mapLimit(afterDeployCmds, 1, (cmd, nextMapCb) => { - async.waterfall([ - (next) => { - this.replaceWithAddresses(cmd, next); - }, - this.replaceWithENSAddress.bind(this) - ], nextMapCb); - }, (err, onDeployCode) => { - if (err) { - this.logger.trace(err); - return cb(new Error("error running afterDeploy")); - } - - this.runOnDeployCode(onDeployCode, cb); - }); - } - - getDependenciesObject() { - return new Promise(async (resolve, reject) => { - let contracts = await this.events.request2("contracts:list"); - - const logger = this.createLoggerWithPrefix('afterDeploy >'); - let args = { contracts: {}, logger}; - for (let contract of contracts) { - // TODO: for this to work correctly we need to add a default from address to the contract - let contractInstance = await this.events.request2("runcode:eval", contract.className); - args.contracts[contract.className] = contractInstance; - } - - try { - let web3Instance = await this.events.request2("runcode:eval", "web3"); - args.web3 = web3Instance; - } catch (_err) { - } - - resolve(args); - }); - } - - replaceWithENSAddress(cmd, callback) { - const replaceWithENSAddress = (cmd) => { - let regex = /\'[a-zA-Z0-9.]+\.eth\'/g; - return stringReplaceAsync.seq(cmd, regex, (ensDomain) => { - ensDomain = ensDomain.slice(1, ensDomain.length - 1); - return (new Promise((resolve, reject) => { - this.events.request("ens:resolve", ensDomain, (err, address) => { - if(err) { - return reject(new Error(err)); - } - address = `'${address}'`; - return resolve(address); - }); - })); - }); - }; - - if (callback) { - return callbackify(replaceWithENSAddress)(cmd, callback); - } - return replaceWithENSAddress(cmd); - } - - replaceWithAddresses(cmd, callback) { - const replaceWithAddresses = (cmd) => { - let regex = /\$\w+\[?\d?\]?/g; - return stringReplaceAsync.seq(cmd, regex, (match, index) => { - return (new Promise((resolve, reject) => { - if (match.startsWith('$accounts')) { - let accountIndex = cmd.substring(index + 10, index + 12); - accountIndex = parseInt(accountIndex, 10); - return this.events.request('blockchain:getAccounts', (err, accounts) => { - if (err) { - return reject('Error getting accounts: ' + err.message || err); - } - if (!accounts[accountIndex]) { - return reject(__('No corresponding account at index %d', accountIndex)); - } - resolve(accounts[accountIndex]); - }); - } - - let referedContractName = match.slice(1); - this.events.request('contracts:contract', referedContractName, (referedContract) => { - if (!referedContract) { - this.logger.error(referedContractName + ' does not exist'); - this.logger.error("error running cmd: " + cmd); - return reject(new Error("ReferedContractDoesNotExist")); - } - if (referedContract && referedContract.deploy === false) { - this.logger.error(referedContractName + " exists but has been set to not deploy"); - this.logger.error("error running cmd: " + cmd); - return reject(new Error("ReferedContracSetToNotdeploy")); - } - if (referedContract && !referedContract.deployedAddress) { - this.logger.error( - "couldn't find a valid address for " + referedContractName + ". has it been deployed?" - ); - this.logger.error("error running cmd: " + cmd); - return reject(new Error("ReferedContractAddressNotFound")); - } - return resolve(referedContract.deployedAddress); - }); - })); - }); - }; - - if (callback) { - return callbackify(replaceWithAddresses)(cmd, callback); - } - return replaceWithAddresses(cmd); - } - - runOnDeployCode(onDeployCode, callback, silent) { - const logFunction = silent ? this.logger.trace.bind(this.logger) : this.logger.info.bind(this.logger); - async.each(onDeployCode, (cmd, eachCb) => { - if (!cmd) { - return eachCb(); - } - logFunction("==== executing: " + cmd); - this.events.request('runcode:eval', cmd, (err) => { - if (err && err.message.indexOf("invalid opcode") >= 0) { - this.logger.error('the transaction was rejected; this usually happens due to a throw or a require, it can also happen due to an invalid operation'); - } - eachCb(err); - }); - }, callback); + return this.functionConfigs.afterAllDeployAction(cb); } async beforeDeployAction(params, cb) { - const contract = params.contract; - const beforeDeployFn = params.contract.beforeDeploy; - if (!beforeDeployFn || typeof beforeDeployFn !== 'function') { - return cb(); - } - try { - const dependencies = await this.getOnDeployLifecycleHookDependencies({ - contractConfig: contract, - logPrefix: `${contract.className} > beforeDeploy >` - }); - await beforeDeployFn(dependencies); - cb(); - } catch (e) { - cb(new Error(`Error running beforeDeploy hook for ${contract.className}: ${e.message || e}`)); + if (typeof params.contract.beforeDeploy !== 'function') { + return this.listConfigs.beforeDeployAction(params, cb); } + return this.functionConfigs.beforeDeployAction(params, cb); } async doOnDeployAction(params, cb) { @@ -204,88 +55,23 @@ class SpecialConfigs { } if (typeof contract.onDeploy === 'function') { - try { - const dependencies = await this.getOnDeployLifecycleHookDependencies({ - contractConfig: contract, - logPrefix: `${contract.className} > onDeploy >` - }); - await contract.onDeploy(dependencies); - return cb(); - } catch (err) { - return cb(new Error(`Error when registering onDeploy hook for ${contract.className}: ${err.message}`)); - } + return this.functionConfigs.doOnDeployAction(contract, cb); } - let onDeployCmds = contract.onDeploy; - async.mapLimit(onDeployCmds, 1, (cmd, nextMapCb) => { - async.waterfall([ - (next) => { - this.replaceWithAddresses(cmd, next); - }, - this.replaceWithENSAddress.bind(this) - ], (err, code) => { - if (err) { - this.logger.error(err.message || err); - return nextMapCb(); // Don't return error as we just skip the failing command - } - nextMapCb(null, code); - }); - }, (err, onDeployCode) => { - if (err) { - return cb(new Error("error running onDeploy for " + contract.className.cyan)); - } - - this.runOnDeployCode(onDeployCode, cb, contract.silent); - }); + return this.listConfigs.doOnDeployAction(contract, cb); } async deployIfAction(params, cb) { let cmd = params.contract.deployIf; - const contract = params.contract; if (!cmd) { return cb(null, params); } if (typeof cmd === 'function') { - try { - const dependencies = await this.getDependenciesObject(); - params.shouldDeploy = await contract.deployIf(dependencies); - return cb(null, params); - } catch (err) { - return cb(new Error(`Error when registering deployIf hook for ${contract.className}: ${err.message}`)); - } + return this.functionConfigs.deployIfAction(params, cb); } - this.events.request('runcode:eval', cmd, (err, result) => { - if (err) { - this.logger.error(params.contract.className + ' deployIf directive has an error; contract will not deploy'); - this.logger.error(err.message || err); - params.shouldDeploy = false; - } else if (!result) { - this.logger.info(params.contract.className + ' deployIf directive returned false; contract will not deploy'); - params.shouldDeploy = false; - } + return this.listConfigs.deployIfAction(params, cb); + } - cb(null, params); - }); - } - - createLoggerWithPrefix(prefix) { - const logger = { - log: createLogWithPrefixFn(this.logger, 'log', prefix), - warn: createLogWithPrefixFn(this.logger, 'warn', prefix), - error: createLogWithPrefixFn(this.logger, 'error', prefix), - info: createLogWithPrefixFn(this.logger, 'info', prefix), - dir: createLogWithPrefixFn(this.logger, 'dir', prefix), - debug: createLogWithPrefixFn(this.logger, 'debug', prefix) - }; - return logger; - } -} - -function createLogWithPrefixFn(logger, method, prefix) { - return function () { - const args = Array.from(arguments).map(arg => `${prefix} ${arg}`); - args.forEach(arg => logger[method](arg)); - }; } module.exports = SpecialConfigs; diff --git a/packages/embark-specialconfigs/src/listConfigs.js b/packages/embark-specialconfigs/src/listConfigs.js new file mode 100644 index 000000000..0bf502977 --- /dev/null +++ b/packages/embark-specialconfigs/src/listConfigs.js @@ -0,0 +1,172 @@ +import { __ } from 'embark-i18n'; +const async = require('async'); +const stringReplaceAsync = require('string-replace-async'); +const {callbackify} = require('util'); + +class ListConfigs { + constructor(embark) { + this.embark = embark; + this.events = embark.events; + this.logger = embark.logger; + this.config = embark.config; + } + + beforeAllDeployAction(cb) { + return cb(); + } + + async afterAllDeployAction(cb) { + let afterDeployCmds = this.config.contractsConfig.afterDeploy || []; + async.mapLimit(afterDeployCmds, 1, (cmd, nextMapCb) => { + async.waterfall([ + (next) => { + this.replaceWithAddresses(cmd, next); + }, + this.replaceWithENSAddress.bind(this) + ], nextMapCb); + }, (err, onDeployCode) => { + if (err) { + this.logger.trace(err); + return cb(new Error("error running afterDeploy")); + } + + this.runOnDeployCode(onDeployCode, cb); + }); + } + + async doOnDeployAction(contract, cb) { + let onDeployCmds = contract.onDeploy; + async.mapLimit(onDeployCmds, 1, (cmd, nextMapCb) => { + async.waterfall([ + (next) => { + this.replaceWithAddresses(cmd, next); + }, + this.replaceWithENSAddress.bind(this) + ], (err, code) => { + if (err) { + this.logger.error(err.message || err); + return nextMapCb(); // Don't return error as we just skip the failing command + } + nextMapCb(null, code); + }); + }, (err, onDeployCode) => { + if (err) { + return cb(new Error("error running onDeploy for " + contract.className.cyan)); + } + + this.runOnDeployCode(onDeployCode, cb, contract.silent); + }); + } + + async deployIfAction(params, cb) { + let cmd = params.contract.deployIf; + this.events.request('runcode:eval', cmd, (err, result) => { + if (err) { + this.logger.error(params.contract.className + ' deployIf directive has an error; contract will not deploy'); + this.logger.error(err.message || err); + params.shouldDeploy = false; + } else if (!result) { + this.logger.info(params.contract.className + ' deployIf directive returned false; contract will not deploy'); + params.shouldDeploy = false; + } + + cb(null, params); + }); + } + + async beforeDeployAction(_params, cb) { + return cb(); + } + + runOnDeployCode(onDeployCode, callback, silent) { + const logFunction = silent ? this.logger.trace.bind(this.logger) : this.logger.info.bind(this.logger); + async.each(onDeployCode, (cmd, eachCb) => { + if (!cmd) { + return eachCb(); + } + logFunction("==== executing: " + cmd); + this.events.request('runcode:eval', cmd, (err) => { + if (err && err.message.indexOf("invalid opcode") >= 0) { + this.logger.error('the transaction was rejected; this usually happens due to a throw or a require, it can also happen due to an invalid operation'); + } + eachCb(err); + }); + }, callback); + } + + replaceWithENSAddress(cmd, callback) { + const replaceWithENSAddress = (cmd) => { + let regex = /\'[a-zA-Z0-9.]+\.eth\'/g; + return stringReplaceAsync.seq(cmd, regex, (ensDomain) => { + ensDomain = ensDomain.slice(1, ensDomain.length - 1); + return (new Promise((resolve, reject) => { + this.events.request("ens:resolve", ensDomain, (err, address) => { + if(err) { + return reject(new Error(err)); + } + address = `'${address}'`; + return resolve(address); + }); + })); + }); + }; + + if (callback) { + return callbackify(replaceWithENSAddress)(cmd, callback); + } + return replaceWithENSAddress(cmd); + } + + replaceWithAddresses(cmd, callback) { + const replaceWithAddresses = (cmd) => { + let regex = /\$\w+\[?\d?\]?/g; + return stringReplaceAsync.seq(cmd, regex, (match, index) => { + return (new Promise((resolve, reject) => { + if (match.startsWith('$accounts')) { + let accountIndex = cmd.substring(index + 10, index + 12); + accountIndex = parseInt(accountIndex, 10); + return this.events.request('blockchain:getAccounts', (err, accounts) => { + if (err) { + return reject('Error getting accounts: ' + err.message || err); + } + if (!accounts[accountIndex]) { + return reject(__('No corresponding account at index %d', accountIndex)); + } + resolve(accounts[accountIndex]); + }); + } + + let referedContractName = match.slice(1); + this.events.request('contracts:contract', referedContractName, (referedContract) => { + if (!referedContract) { + this.logger.error(referedContractName + ' does not exist'); + this.logger.error("error running cmd: " + cmd); + return reject(new Error("ReferedContractDoesNotExist")); + } + if (referedContract && referedContract.deploy === false) { + this.logger.error(referedContractName + " exists but has been set to not deploy"); + this.logger.error("error running cmd: " + cmd); + return reject(new Error("ReferedContracSetToNotdeploy")); + } + if (referedContract && !referedContract.deployedAddress) { + this.logger.error( + "couldn't find a valid address for " + referedContractName + ". has it been deployed?" + ); + this.logger.error("error running cmd: " + cmd); + return reject(new Error("ReferedContractAddressNotFound")); + } + return resolve(referedContract.deployedAddress); + }); + })); + }); + }; + + if (callback) { + return callbackify(replaceWithAddresses)(cmd, callback); + } + return replaceWithAddresses(cmd); + } + +} + +module.exports = ListConfigs; diff --git a/packages/embark-specialconfigs/src/utils.js b/packages/embark-specialconfigs/src/utils.js new file mode 100644 index 000000000..5c0927ece --- /dev/null +++ b/packages/embark-specialconfigs/src/utils.js @@ -0,0 +1,23 @@ + +function createLogWithPrefixFn(logger, method, prefix) { + return function () { + const args = Array.from(arguments).map(arg => `${prefix} ${arg}`); + args.forEach(arg => logger[method](arg)); + }; +} + +const Utils = { + createLoggerWithPrefix(embarkLogger, prefix) { + const logger = { + log: createLogWithPrefixFn(embarkLogger, 'log', prefix), + warn: createLogWithPrefixFn(embarkLogger, 'warn', prefix), + error: createLogWithPrefixFn(embarkLogger, 'error', prefix), + info: createLogWithPrefixFn(embarkLogger, 'info', prefix), + dir: createLogWithPrefixFn(embarkLogger, 'dir', prefix), + debug: createLogWithPrefixFn(embarkLogger, 'debug', prefix) + }; + return logger; + } +} + +module.exports = Utils;