diff --git a/chains.json b/chains.json deleted file mode 100644 index 0967ef42..00000000 --- a/chains.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/lib/constants.json b/lib/constants.json index ff5e643d..603e6711 100644 --- a/lib/constants.json +++ b/lib/constants.json @@ -1,3 +1,18 @@ { - "httpContractsDirectory": ".embark/contracts/" + "httpContractsDirectory": ".embark/contracts/", + "contexts": { + "simulator": "simulator", + "blockchain": "blockchain", + "templateGeneration": "templateGeneration", + "run": "run", + "upload": "upload", + "build": "build", + "graph": "graph", + "test": "test", + "reset": "reset", + "any": "any" + }, + "events": { + "contextChange": "contextChange" + } } diff --git a/lib/core/config.js b/lib/core/config.js index f83c1827..75e5b86f 100644 --- a/lib/core/config.js +++ b/lib/core/config.js @@ -20,6 +20,7 @@ var Config = function(options) { this.logger = options.logger; this.events = options.events; this.embarkConfig = {}; + this.context = options.context || [constants.contexts.any]; }; Config.prototype.loadConfigFiles = function(options) { @@ -36,7 +37,7 @@ Config.prototype.loadConfigFiles = function(options) { this.embarkConfig = fs.readJSONSync(options.embarkConfig); this.embarkConfig.plugins = this.embarkConfig.plugins || {}; - this.plugins = new Plugins({plugins: this.embarkConfig.plugins, logger: this.logger, interceptLogs: interceptLogs, events: this.events, config: this}); + this.plugins = new Plugins({plugins: this.embarkConfig.plugins, logger: this.logger, interceptLogs: interceptLogs, events: this.events, config: this, context: this.context}); this.plugins.loadPlugins(); this.loadEmbarkConfigFile(); @@ -112,7 +113,7 @@ Config.prototype.loadBlockchainConfigFile = function() { Config.prototype.loadContractsConfigFile = function() { var defaultVersions = { - "web3.js": "1.0.0-beta", + "web3": "1.0.0-beta", "solc": "0.4.17" }; var versions = utils.recursiveMerge(defaultVersions, this.embarkConfig.versions || {}); @@ -143,49 +144,6 @@ Config.prototype.loadContractsConfigFile = function() { this.contractsConfig = this._mergeConfig(configFilePath, configObject, this.env); }; -Config.prototype.getExternalContractUrl = function (contract) { - let url; - const RAW_URL = 'https://raw.githubusercontent.com/'; - const MALFORMED_ERROR = 'Malformed Github URL for '; - if (contract.file.startsWith('https://github')) { - const match = contract.file.match(/https:\/\/github\.[a-z]+\/(.*)/); - if (!match) { - this.logger.error(MALFORMED_ERROR + contract.file); - return null; - } - url = `${RAW_URL}${match[1].replace('blob/', '')}`; - } else if (contract.file.startsWith('git')) { - // Match values - // [0] entire input - // [1] git:// - // [2] user - // [3] repository - // [4] path - // [5] branch - const match = contract.file.match( - /(git:\/\/)?github\.[a-z]+\/([a-zA-Z0-9_\-.]+)\/([a-zA-Z0-9_\-]+)\/([a-zA-Z0-9_\-\/.]+)#?([a-zA-Z0-1_\-.]*)?/ - ); - if (!match) { - this.logger.error(MALFORMED_ERROR + contract.file); - return null; - } - let branch = match[5]; - if (!branch) { - branch = 'master'; - } - url = `${RAW_URL}${match[2]}/${match[3]}/${branch}/${match[4]}`; - } else { - url = contract.file; - } - const match = url.match( - /\.[a-z]+\/([a-zA-Z0-9_\-\/.]+)/ - ); - return { - url, - filePath: match[1] - }; -}; - Config.prototype.loadExternalContractsFiles = function() { let contracts = this.contractsConfig.contracts; for (let contractName in contracts) { @@ -194,11 +152,11 @@ Config.prototype.loadExternalContractsFiles = function() { continue; } if (contract.file.startsWith('http') || contract.file.startsWith('git')) { - const fileObj = this.getExternalContractUrl(contract); + const fileObj = utils.getExternalContractUrl(contract.file); if (!fileObj) { return this.logger.error("HTTP contract file not found: " + contract.file); } - const localFile = constants.httpContractsDirectory + fileObj.filePath; + const localFile = fileObj.filePath; this.contractsFiles.push(new File({filename: localFile, type: File.types.http, basedir: '', path: fileObj.url})); } else if (fs.existsSync(contract.file)) { this.contractsFiles.push(new File({filename: contract.file, type: File.types.dapp_file, basedir: '', path: contract.file})); diff --git a/lib/core/engine.js b/lib/core/engine.js index 583d1f50..da632e3f 100644 --- a/lib/core/engine.js +++ b/lib/core/engine.js @@ -19,6 +19,7 @@ class Engine { this.logFile = options.logFile; this.logLevel = options.logLevel; this.events = options.events; + this.context = options.context; } init(_options) { @@ -26,7 +27,7 @@ class Engine { let options = _options || {}; this.events = options.events || this.events || new Events(); this.logger = options.logger || new Logger({logLevel: options.logLevel || this.logLevel || 'debug', events: this.events, logFile: this.logFile}); - this.config = new Config({env: this.env, logger: this.logger, events: this.events}); + this.config = new Config({env: this.env, logger: this.logger, events: this.events, context: this.context}); this.config.loadConfigFiles({embarkConfig: this.embarkConfig, interceptLogs: this.interceptLogs}); this.plugins = this.config.plugins; @@ -231,6 +232,9 @@ class Engine { if (err) { return cb({name: "Ethereum node (version unknown)", status: 'on'}); } + if (version.indexOf("/") < 0) { + return cb({name: version, status: 'on'}); + } let nodeName = version.split("/")[0]; let versionNumber = version.split("/")[1].split("-")[0]; let name = nodeName + " " + versionNumber + " (Ethereum)"; diff --git a/lib/core/file.js b/lib/core/file.js index c2a65dd9..62cd859e 100644 --- a/lib/core/file.js +++ b/lib/core/file.js @@ -2,39 +2,63 @@ const async = require('async'); const fs = require('./fs.js'); const path = require('path'); const request = require('request'); +const utils = require('../utils/utils'); class File { constructor (options) { - this.filename = options.filename; + this.filename = options.filename.replace(/\\/g, '/'); this.type = options.type; this.path = options.path; this.basedir = options.basedir; this.resolver = options.resolver; } - parseFileForImport(content, callback) { + parseFileForImport(content, isHttpContract, callback) { const self = this; + if (typeof isHttpContract === 'function') { + callback = isHttpContract; + isHttpContract = false; + } if (self.filename.indexOf('.sol') < 0) { // Only supported in Solidity - return callback(); + return callback(null, content); } - const regex = /import "([a-zA-Z0-9_\-.\\\/]+)";/g; + const regex = /import ["|']([-a-zA-Z0-9@:%_+.~#?&\/=]+)["|'];/g; let matches; const filesToDownload = []; const pathWithoutFile = path.dirname(self.path); while ((matches = regex.exec(content))) { - filesToDownload.push({ + const httpFileObj = utils.getExternalContractUrl(matches[1]); + const fileObj = { fileRelativePath: path.join(path.dirname(self.filename), matches[1]), url: `${pathWithoutFile}/${matches[1]}` - }); + }; + if (httpFileObj) { + // Replace http import by filePath import in content + content = content.replace(matches[1], httpFileObj.filePath); + + fileObj.fileRelativePath = httpFileObj.filePath; + fileObj.url = httpFileObj.url; + } else if (!isHttpContract) { + // Just a normal import + continue; + } + filesToDownload.push(fileObj); } + if (self.downloadedImports) { + // We already parsed this file + return callback(null, content); + } + self.downloadedImports = true; async.each(filesToDownload, ((fileObj, eachCb) => { self.downloadFile(fileObj.fileRelativePath, fileObj.url, (_content) => { eachCb(); }); - }), callback); + }), (err) => { + callback(err, content); + }); } downloadFile (filename, url, callback) { @@ -63,7 +87,7 @@ class File { fs.readFile(filename, next); }, function parseForImports(content, next) { - self.parseFileForImport(content, (err) => { + self.parseFileForImport(content, true, (err) => { next(err, content); }); } @@ -77,14 +101,19 @@ class File { } content (callback) { + let content; if (this.type === File.types.embark_internal) { - return callback(fs.readFileSync(fs.embarkPath(this.path)).toString()); + content = fs.readFileSync(fs.embarkPath(this.path)).toString(); } else if (this.type === File.types.dapp_file) { - return callback(fs.readFileSync(this.path).toString()); + content = fs.readFileSync(this.path).toString(); } else if (this.type === File.types.custom) { - return this.resolver(callback); + return this.resolver((theContent) => { + this.parseFileForImport(theContent, (err, newContent) => { + callback(newContent); + }); + }); } else if (this.type === File.types.http) { - this.downloadFile(this.filename, this.path, (content) => { + return this.downloadFile(this.filename, this.path, (content) => { if (!content) { return callback(content); } @@ -95,6 +124,9 @@ class File { } else { throw new Error("unknown file: " + this.filename); } + return this.parseFileForImport(content, (err, newContent) => { + callback(newContent); + }); } } diff --git a/lib/core/plugin.js b/lib/core/plugin.js index 1d830524..00abe240 100644 --- a/lib/core/plugin.js +++ b/lib/core/plugin.js @@ -1,5 +1,6 @@ -var fs = require('./fs.js'); -var utils = require('../utils/utils.js'); +const fs = require('./fs.js'); +const utils = require('../utils/utils.js'); +const constants = require('../constants'); // TODO: pass other params like blockchainConfig, contract files, etc.. var Plugin = function(options) { @@ -27,9 +28,38 @@ var Plugin = function(options) { this.logger = options.logger; this.events = options.events; this.config = options.config; + this.loaded = false; + this.currentContext = options.context; + this.acceptedContext = options.pluginConfig.context || [constants.contexts.any]; + + if (!Array.isArray(this.currentContext)) { + this.currentContext = [this.currentContext]; + } + if (!Array.isArray(this.acceptedContext)) { + this.acceptedContext = [this.acceptedContext]; + } +}; + +Plugin.prototype.isContextValid = function() { + if (this.currentContext.includes(constants.contexts.any) || this.acceptedContext.includes(constants.contexts.any)) { + return true; + } + return this.acceptedContext.some(context => { + return this.currentContext.includes(context); + }); +}; + +Plugin.prototype.hasContext = function(context) { + return this.currentContext.includes(context); }; Plugin.prototype.loadPlugin = function() { + if (!this.isContextValid()) { + console.log(this.acceptedContext); + this.logger.warn(`Plugin ${this.name} can only be loaded in the context of "${this.acceptedContext.join(', ')}"`); + return false; + } + this.loaded = true; if (this.shouldInterceptLogs) { this.interceptLogs(this.pluginModule); } diff --git a/lib/core/plugins.js b/lib/core/plugins.js index 9117a25a..f12f421f 100644 --- a/lib/core/plugins.js +++ b/lib/core/plugins.js @@ -9,6 +9,7 @@ var Plugins = function(options) { this.logger = options.logger; this.events = options.events; this.config = options.config; + this.context = options.context; }; Plugins.prototype.loadPlugins = function() { @@ -20,10 +21,12 @@ Plugins.prototype.loadPlugins = function() { }; Plugins.prototype.listPlugins = function() { - var list = []; - for (var className in this.pluginList) { - list.push(className); - } + const list = []; + this.plugins.forEach(plugin => { + if (plugin.loaded) { + list.push(plugin.name); + } + }); return list; }; @@ -31,7 +34,18 @@ Plugins.prototype.listPlugins = function() { Plugins.prototype.createPlugin = function(pluginName, pluginConfig) { let plugin = {}; let pluginPath = false; - var pluginWrapper = new Plugin({name: pluginName, pluginModule: plugin, pluginConfig: pluginConfig, logger: this.logger, pluginPath: pluginPath, interceptLogs: this.interceptLogs, events: this.events, config: this.config, isInternal: true}); + var pluginWrapper = new Plugin({ + name: pluginName, + pluginModule: plugin, + pluginConfig: pluginConfig, + logger: this.logger, + pluginPath: pluginPath, + interceptLogs: this.interceptLogs, + events: this.events, + config: this.config, + isInternal: true, + context: this.context + }); this.plugins.push(pluginWrapper); return pluginWrapper; }; @@ -40,7 +54,18 @@ Plugins.prototype.loadInternalPlugin = function(pluginName, pluginConfig) { var pluginPath = utils.joinPath('../modules/', pluginName, 'index.js'); var plugin = require(pluginPath); - var pluginWrapper = new Plugin({name: pluginName, pluginModule: plugin, pluginConfig: pluginConfig, logger: this.logger, pluginPath: pluginPath, interceptLogs: this.interceptLogs, events: this.events, config: this.config, isInternal: true}); + var pluginWrapper = new Plugin({ + name: pluginName, + pluginModule: plugin, + pluginConfig: pluginConfig, + logger: this.logger, + pluginPath: pluginPath, + interceptLogs: this.interceptLogs, + events: this.events, + config: this.config, + isInternal: true, + context: this.context + }); pluginWrapper.loadInternalPlugin(); this.plugins.push(pluginWrapper); }; @@ -49,7 +74,18 @@ Plugins.prototype.loadPlugin = function(pluginName, pluginConfig) { var pluginPath = utils.joinPath(utils.pwd(), 'node_modules', pluginName); var plugin = require(pluginPath); - var pluginWrapper = new Plugin({name: pluginName, pluginModule: plugin, pluginConfig: pluginConfig, logger: this.logger, pluginPath: pluginPath, interceptLogs: this.interceptLogs, events: this.events, config: this.config, isInternal: false}); + var pluginWrapper = new Plugin({ + name: pluginName, + pluginModule: plugin, + pluginConfig: pluginConfig, + logger: this.logger, + pluginPath: pluginPath, + interceptLogs: this.interceptLogs, + events: this.events, + config: this.config, + isInternal: false, + context: this.context + }); pluginWrapper.loadPlugin(); this.plugins.push(pluginWrapper); }; diff --git a/lib/index.js b/lib/index.js index 0cf4fd0f..e321a4be 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,4 +1,5 @@ let async = require('async'); +const constants = require('./constants'); // require("./utils/debug_util.js")(__filename, async); require('colors'); @@ -31,22 +32,25 @@ class Embark { this.events = new Events(); this.logger = new Logger({logLevel: 'debug', events: this.events}); - this.config = new Config({env: env, logger: this.logger, events: this.events}); + this.config = new Config({env: env, logger: this.logger, events: this.events, context: this.context}); this.config.loadConfigFiles(options); this.plugins = this.config.plugins; } blockchain(env, client) { + this.context = [constants.contexts.blockchain]; return require('./cmds/blockchain/blockchain.js')(this.config.blockchainConfig, client, env).run(); } simulator(options) { + this.context = options.context || [constants.contexts.simulator, constants.contexts.blockchain]; let Simulator = require('./cmds/simulator.js'); let simulator = new Simulator({blockchainConfig: this.config.blockchainConfig}); simulator.run(options); } generateTemplate(templateName, destinationFolder, name) { + this.context = [constants.contexts.templateGeneration]; let TemplateGenerator = require('./cmds/template_generator.js'); let templateGenerator = new TemplateGenerator(templateName); templateGenerator.generate(destinationFolder, name); @@ -54,6 +58,7 @@ class Embark { run(options) { let self = this; + self.context = options.context || [constants.contexts.run, constants.contexts.build]; let Dashboard = require('./dashboard/dashboard.js'); let windowSize = require('window-size'); @@ -62,14 +67,15 @@ class Embark { version: this.version, embarkConfig: options.embarkConfig || 'embark.json', logFile: options.logFile, - logLevel: options.logLevel + logLevel: options.logLevel, + context: self.context }); engine.init(); if (!options.useDashboard) { - console.log('========================'.bold.green); - console.log(('Welcome to Embark ' + this.version).yellow.bold); - console.log('========================'.bold.green); + engine.logger.info('========================'.bold.green); + engine.logger.info(('Welcome to Embark ' + this.version).yellow.bold); + engine.logger.info('========================'.bold.green); } async.parallel([ @@ -152,6 +158,8 @@ class Embark { } build(options) { + this.context = options.context || [constants.contexts.build]; + let engine = new Engine({ env: options.env, version: this.version, @@ -162,7 +170,8 @@ class Embark { events: options.events, logger: options.logger, config: options.config, - plugins: options.plugins + plugins: options.plugins, + context: this.context }); engine.init(); @@ -199,18 +208,22 @@ class Embark { } initTests(options) { + this.context = options.context || [constants.contexts.test]; let Test = require('./tests/test.js'); + options.context = this.context; return new Test(options); } graph(options) { + this.context = options.context || [constants.contexts.graph]; options.onlyCompile = true; let engine = new Engine({ env: options.env, version: this.version, embarkConfig: options.embarkConfig || 'embark.json', - logFile: options.logFile + logFile: options.logFile, + context: this.context }); engine.init(); @@ -251,11 +264,14 @@ class Embark { } reset() { + this.context = [constants.contexts.reset]; let resetCmd = require('./cmds/reset.js'); resetCmd(); } upload(platform, options) { + + this.context = options.context || [constants.contexts.upload, constants.contexts.build]; let engine = new Engine({ env: options.env, @@ -337,6 +353,7 @@ class Embark { } runTests(file) { + this.context = [constants.contexts.test]; let RunTests = require('./tests/run_tests.js'); RunTests.run(file); } @@ -346,6 +363,7 @@ class Embark { // temporary until next refactor Embark.initTests = function(options) { let Test = require('./tests/test.js'); + options.context = [constants.contexts.test]; return new Test(options); }; diff --git a/lib/modules/solidity/solcP.js b/lib/modules/solidity/solcP.js index 8042ed83..2beb3305 100644 --- a/lib/modules/solidity/solcP.js +++ b/lib/modules/solidity/solcP.js @@ -3,8 +3,13 @@ let solc; const fs = require('fs-extra'); const path = require('path'); const constants = require('../../constants'); +const Utils = require('../../utils/utils'); function findImports(filename) { + if (filename.startsWith('http') || filename.startsWith('git')) { + const fileObj = Utils.getExternalContractUrl(filename); + filename = fileObj.filePath; + } if (fs.existsSync(filename)) { return {contents: fs.readFileSync(filename).toString()}; } diff --git a/lib/tests/test.js b/lib/tests/test.js index 0534bd28..7fc4f2e4 100644 --- a/lib/tests/test.js +++ b/lib/tests/test.js @@ -89,7 +89,7 @@ Test.prototype.deployAll = function(contractsConfig, cb) { ], function(err, result) { if (err) { console.log('terminating due to error'); - process.exit(); + process.exit(1); } // this should be part of the waterfall and not just something done at the // end diff --git a/lib/utils/utils.js b/lib/utils/utils.js index c38c96eb..0063c1ec 100644 --- a/lib/utils/utils.js +++ b/lib/utils/utils.js @@ -6,6 +6,7 @@ let https = require('follow-redirects').https; let shelljs = require('shelljs'); var tar = require('tar'); var propose = require('propose'); +const constants = require('../constants'); //let fs = require('../core/fs.js'); let o_fs = require('fs-extra'); @@ -127,6 +128,51 @@ function pwd() { return process.env.PWD || process.cwd(); } +function getExternalContractUrl(file) { + let url; + const RAW_URL = 'https://raw.githubusercontent.com/'; + const MALFORMED_ERROR = 'Malformed Github URL for '; + if (file.startsWith('https://github')) { + const match = file.match(/https:\/\/github\.[a-z]+\/(.*)/); + if (!match) { + console.error(MALFORMED_ERROR + file); + return null; + } + url = `${RAW_URL}${match[1].replace('blob/', '')}`; + } else if (file.startsWith('git')) { + // Match values + // [0] entire input + // [1] git:// + // [2] user + // [3] repository + // [4] path + // [5] branch + const match = file.match( + /(git:\/\/)?github\.[a-z]+\/([-a-zA-Z0-9@:%_+.~#?&=]+)\/([-a-zA-Z0-9@:%_+.~#?&=]+)\/([-a-zA-Z0-9@:%_+.~?\/&=]+)#?([a-zA-Z0-1\/_.-]*)?/ + ); + if (!match) { + console.error(MALFORMED_ERROR + file); + return null; + } + let branch = match[5]; + if (!branch) { + branch = 'master'; + } + url = `${RAW_URL}${match[2]}/${match[3]}/${branch}/${match[4]}`; + } else if (file.startsWith('http')) { + url = file; + } else { + return null; + } + const match = url.match( + /\.[a-z]+\/([-a-zA-Z0-9@:%_+.~#?&\/=]+)/ + ); + return { + url, + filePath: constants.httpContractsDirectory + match[1] + }; +} + module.exports = { joinPath: joinPath, filesMatchingPattern: filesMatchingPattern, @@ -143,5 +189,6 @@ module.exports = { downloadFile: downloadFile, extractTar: extractTar, proposeAlternative: proposeAlternative, - pwd: pwd + pwd: pwd, + getExternalContractUrl }; diff --git a/lib/versions/library_manager.js b/lib/versions/library_manager.js index 3589a837..05bf2879 100644 --- a/lib/versions/library_manager.js +++ b/lib/versions/library_manager.js @@ -21,7 +21,7 @@ class LibraryManager { this.versions = {}; let solcVersionInConfig = this.contractsConfig.versions.solc; - let web3VersionInConfig = this.contractsConfig.versions["web3.js"]; + let web3VersionInConfig = this.contractsConfig.versions["web3"]; let ipfsApiVersion = this.storageConfig.versions["ipfs-api"]; this.versions['solc'] = solcVersionInConfig; @@ -64,10 +64,7 @@ class LibraryManager { listenToCommandsToGetLibrary() { let npm = new Npm({logger: this.embark.logger}); this.embark.events.setCommandHandler('version:getPackageLocation', (libName, version, cb) => { - npm.getPackageVersion(libName, version, false, false, cb); - }); - this.embark.events.setCommandHandler('version:getPackageContent', (libName, version, cb) => { - npm.getPackageVersion(libName, version, false, true, cb); + npm.getPackageVersion(libName, version, cb); }); } diff --git a/lib/versions/npm.js b/lib/versions/npm.js index 44b9844f..a6d50246 100644 --- a/lib/versions/npm.js +++ b/lib/versions/npm.js @@ -1,113 +1,26 @@ -// here be dragons -// TODO: this is horrible and needs to be refactored ASAP -let utils = require('../utils/utils.js'); let fs = require('../core/fs.js'); +let PluginManager = require('live-plugin-manager').PluginManager; + class Npm { constructor(options) { this.logger = options.logger; } - downloadFromGit(registryJSON, packageName, version, returnContent, callback) { - let repoName = registryJSON.repository.url.replace("git+https://github.com/", "").replace(".git",""); - let gitHead = registryJSON.gitHead; - - if (!gitHead) { - this.logger.error("Could not download " + packageName + " " + version); - return callback("error"); - } - - let fileLocation = "https://raw.githubusercontent.com/" + repoName + "/" + gitHead + "/dist/web3.min.js"; - - let packageDirectory = './.embark/versions/' + packageName + '/' + version + '/'; - if (fs.existsSync(packageDirectory + "/" + packageName + ".js")) { - if (returnContent) { - let distFile = packageDirectory + packageName + ".js"; - callback(null, fs.readFileSync(distFile).toString()); - } else { - callback(null, packageDirectory); - } - } else { - fs.mkdirpSync(packageDirectory); - this.logger.info("downloading " + packageName + " " + version + "...."); - - utils.downloadFile(fileLocation, packageDirectory + "/" + packageName + ".js", function() { - utils.extractTar(packageDirectory + "/" + packageName + ".js", packageDirectory, function() { - if (returnContent) { - let distFile = packageDirectory + packageName + ".js"; - callback(null, fs.readFileSync(distFile).toString()); - } else { - callback(null, packageDirectory); - } - }); - }); - } - } - - downloadFromNpm(registryJSON, packageName, version, returnContent, callback) { - let tarball = registryJSON.dist.tarball; - - let packageDirectory = './.embark/versions/' + packageName + '/' + version + '/'; - if (fs.existsSync(packageDirectory + "/downloaded_package.tgz") && fs.existsSync(packageDirectory + "package.json")) { - if (returnContent) { - let distFile = packageDirectory + returnContent; - callback(null, fs.readFileSync(distFile).toString()); - } else { - callback(null, packageDirectory); - } - } else { - fs.mkdirpSync(packageDirectory); - this.logger.info("downloading " + packageName + " " + version + "...."); - - utils.downloadFile(tarball, packageDirectory + "/downloaded_package.tgz", function() { - utils.extractTar(packageDirectory + "/downloaded_package.tgz", packageDirectory, function() { - if (returnContent) { - let distFile = packageDirectory + returnContent; - callback(null, fs.readFileSync(distFile).toString()); - } else { - callback(null, packageDirectory); - } - }); - }); - } - } - - // TODO: callback should accept an error - getPackageVersion(packageName, version, returnContent, getFromGit, callback) { - let self = this; - let npmRegistry = "https://registry.npmjs.org/" + packageName + "/" + version; + getPackageVersion(packageName, version, callback) { let packageDirectory = './.embark/versions/' + packageName + '/' + version + '/'; - if (fs.existsSync(packageDirectory) && fs.existsSync(packageDirectory + "package.json")) { - let content; - if (getFromGit && returnContent) { - let distFile = packageDirectory + packageName + ".js"; - content = fs.readFileSync(distFile).toString(); - } else if (returnContent) { - let distFile = packageDirectory + returnContent; - content = fs.readFileSync(distFile).toString(); - } else { - content = packageDirectory; - } - return callback(null, content); + let manager = new PluginManager({pluginsPath: packageDirectory}); + + if (fs.existsSync(packageDirectory + packageName)) { + return callback(null, packageDirectory + packageName); } - utils.httpsGet(npmRegistry, function (err, body) { - if (err) { - if (err.code === 'ENOTFOUND') { - return callback("can't reach " + err.hostname + " to download " + packageName + " " + version + " - are you connected to the internet?"); - } - return callback(err); - } - let registryJSON = JSON.parse(body); - - if (getFromGit) { - self.downloadFromGit(registryJSON, packageName, version, returnContent, callback); - } else { - self.downloadFromNpm(registryJSON, packageName, version, returnContent, callback); - } - }); + this.logger.info("downloading " + packageName + " " + version + "...."); + manager.install(packageName, version).then((result) => { + callback(null , result.location); + }).catch(callback); } } diff --git a/package.json b/package.json index e34b98e4..28aa1df6 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "globule": "^1.1.0", "http-shutdown": "^1.2.0", "ipfs-api": "17.2.4", + "live-plugin-manager": "https://github.com/iurimatias/live-plugin-manager.git", "merge": "^1.2.0", "mocha": "^2.2.5", "orbit-db": "^0.17.3", diff --git a/templates/boilerplate/embark.json b/templates/boilerplate/embark.json index 37d16797..0a5bfef5 100644 --- a/templates/boilerplate/embark.json +++ b/templates/boilerplate/embark.json @@ -9,7 +9,7 @@ "buildDir": "dist/", "config": "config/", "versions": { - "web3.js": "1.0.0-beta", + "web3": "1.0.0-beta", "solc": "0.4.17", "ipfs-api": "17.2.4" }, diff --git a/templates/demo/embark.json b/templates/demo/embark.json index 9ad97017..90967372 100644 --- a/templates/demo/embark.json +++ b/templates/demo/embark.json @@ -8,7 +8,7 @@ "buildDir": "dist/", "config": "config/", "versions": { - "web3.js": "1.0.0-beta", + "web3": "1.0.0-beta", "solc": "0.4.17", "ipfs-api": "17.2.4" }, diff --git a/test/config.js b/test/config.js index 0f8af614..75aa7957 100644 --- a/test/config.js +++ b/test/config.js @@ -38,7 +38,7 @@ describe('embark.Config', function () { it('should load contract config correctly', function () { config.loadContractsConfigFile(); let expectedConfig = { - versions: {'web3.js': '1.0.0-beta', solc: '0.4.17'}, + versions: {'web3': '1.0.0-beta', solc: '0.4.17'}, deployment: {host: 'localhost', port: 8545, type: 'rpc'}, dappConnection: ['$WEB3', 'localhost:8545'], "gas": "auto", @@ -57,94 +57,6 @@ describe('embark.Config', function () { }); }); - describe('#getExternalContractUrl', function () { - it('should get the right url for a https://github file', function () { - const fileObj = config.getExternalContractUrl( - {file: 'https://github.com/embark-framework/embark/blob/master/test_app/app/contracts/simple_storage.sol'} - ); - assert.deepEqual(fileObj, - { - filePath: 'embark-framework/embark/master/test_app/app/contracts/simple_storage.sol', - url: 'https://raw.githubusercontent.com/embark-framework/embark/master/test_app/app/contracts/simple_storage.sol' - }); - }); - - it('should fail for a malformed https://github file', function () { - const fileObj = config.getExternalContractUrl( - {file: 'https://github/embark-framework/embark/blob/master/test_app/app/contracts/simple_storage.sol'} - ); - assert.strictEqual(fileObj, null); - }); - - it('should get the right url for a git:// file with no branch #', function () { - const fileObj = config.getExternalContractUrl( - {file: 'git://github.com/status-im/contracts/contracts/identity/ERC725.sol'} - ); - assert.deepEqual(fileObj, - { - filePath: 'status-im/contracts/master/contracts/identity/ERC725.sol', - url: 'https://raw.githubusercontent.com/status-im/contracts/master/contracts/identity/ERC725.sol' - }); - }); - - it('should get the right url for a git:// file with a branch #', function () { - const fileObj = config.getExternalContractUrl( - {file: 'git://github.com/status-im/contracts/contracts/identity/ERC725.sol#myBranch'} - ); - assert.deepEqual(fileObj, - { - filePath: 'status-im/contracts/myBranch/contracts/identity/ERC725.sol', - url: 'https://raw.githubusercontent.com/status-im/contracts/myBranch/contracts/identity/ERC725.sol' - }); - }); - - it('should fail when the git:// file is malformed', function () { - const fileObj = config.getExternalContractUrl( - {file: 'git://github.com/identity/ERC725.sol#myBranch'} - ); - assert.strictEqual(fileObj, null); - }); - - it('should get the right url with a github.com file without branch #', function () { - const fileObj = config.getExternalContractUrl( - {file: 'github.com/status-im/contracts/contracts/identity/ERC725.sol'} - ); - assert.deepEqual(fileObj, - { - filePath: 'status-im/contracts/master/contracts/identity/ERC725.sol', - url: 'https://raw.githubusercontent.com/status-im/contracts/master/contracts/identity/ERC725.sol' - }); - }); - - it('should get the right url with a github.com file with branch #', function () { - const fileObj = config.getExternalContractUrl( - {file: 'github.com/status-im/contracts/contracts/identity/ERC725.sol#theBranch'} - ); - assert.deepEqual(fileObj, - { - filePath: 'status-im/contracts/theBranch/contracts/identity/ERC725.sol', - url: 'https://raw.githubusercontent.com/status-im/contracts/theBranch/contracts/identity/ERC725.sol' - }); - }); - - it('should fail with a malformed github.com url', function () { - const fileObj = config.getExternalContractUrl( - {file: 'github/status-im/contracts/contracts/identity/ERC725.sol#theBranch'} - ); - assert.strictEqual(fileObj, null); - }); - - it('should succeed with a generic http url', function () { - const fileObj = config.getExternalContractUrl( - {file: 'http://myurl.com/myFile.sol'} - ); - assert.deepEqual(fileObj, { - filePath: 'myFile.sol', - url: 'http://myurl.com/myFile.sol' - }); - }); - }); - describe('#loadExternalContractsFiles', function () { it('should create the right list of files and download', function () { config.contractsFiles = []; diff --git a/test/contracts/contract_with_http_import.sol b/test/contracts/contract_with_http_import.sol new file mode 100644 index 00000000..29b08cda --- /dev/null +++ b/test/contracts/contract_with_http_import.sol @@ -0,0 +1,19 @@ +pragma solidity ^0.4.7; +import "https://github.com/embark-framework/embark/blob/develop/test_apps/contracts_app/contracts/contract_args.sol"; +contract SimpleStorage { + uint public storedData; + + function SimpleStorage(uint initialValue) { + storedData = initialValue; + } + + function set(uint x) { + storedData = x; + } + + function get() constant returns (uint retVal) { + return storedData; + } + +} + diff --git a/test/contracts/contract_with_import.sol b/test/contracts/contract_with_import.sol index 7a07a304..0f206a40 100644 --- a/test/contracts/contract_with_import.sol +++ b/test/contracts/contract_with_import.sol @@ -1,7 +1,7 @@ pragma solidity ^0.4.7; +import "./ownable.sol"; contract SimpleStorage { uint public storedData; - import "./ownable.sol"; function SimpleStorage(uint initialValue) { storedData = initialValue; diff --git a/test/file.js b/test/file.js index 1fb47405..bd261c45 100644 --- a/test/file.js +++ b/test/file.js @@ -16,7 +16,7 @@ describe('embark.File', function () { cb(); }); - file.parseFileForImport(contract, () => { + file.parseFileForImport(contract, true, () => { assert.strictEqual(downloadFileStub.callCount, 1); assert.strictEqual(downloadFileStub.firstCall.args[0], path.normalize('.embark/contracts/embark-framework/embark/master/test_app/app/contracts/ownable.sol')); @@ -25,5 +25,61 @@ describe('embark.File', function () { done(); }); }); + + it('should find all the imports but not call download because not a http contract', function (done) { + const contract = fs.readFileSync('./test/contracts/contract_with_import.sol').toString(); + const file = new File({filename: '.embark/contracts/embark-framework/embark/master/test_app/app/contracts/simple_storage.sol', + path: 'https://raw.githubusercontent.com/embark-framework/embark/develop/test_apps/test_app/app/contracts/simple_storage.sol'}); + const downloadFileStub = sinon.stub(file, 'downloadFile') + .callsFake((path, url, cb) => { + cb(); + }); + + file.parseFileForImport(contract, () => { + assert.strictEqual(downloadFileStub.callCount, 0); + done(); + }); + }); + + it('should find all the imports and call downlaod because it is an http import', function (done) { + const contract = fs.readFileSync('./test/contracts/contract_with_http_import.sol').toString(); + const file = new File({filename: '.embark/contracts/embark-framework/embark/master/test_app/app/contracts/simple_storage.sol', + path: 'https://raw.githubusercontent.com/embark-framework/embark/develop/test_apps/test_app/app/contracts/simple_storage.sol'}); + const downloadFileStub = sinon.stub(file, 'downloadFile') + .callsFake((path, url, cb) => { + cb(); + }); + + file.parseFileForImport(contract, () => { + assert.strictEqual(downloadFileStub.callCount, 1); + assert.strictEqual(downloadFileStub.firstCall.args[0], + '.embark/contracts/embark-framework/embark/develop/test_apps/contracts_app/contracts/contract_args.sol'); + assert.strictEqual(downloadFileStub.firstCall.args[1], + 'https://raw.githubusercontent.com/embark-framework/embark/develop/test_apps/contracts_app/contracts/contract_args.sol'); + done(); + }); + }); + + it('should find all the imports but only once if called twice', function (done) { + const contract = fs.readFileSync('./test/contracts/contract_with_http_import.sol').toString(); + const file = new File({filename: '.embark/contracts/embark-framework/embark/master/test_app/app/contracts/simple_storage.sol', + path: 'https://raw.githubusercontent.com/embark-framework/embark/develop/test_apps/test_app/app/contracts/simple_storage.sol'}); + const downloadFileStub = sinon.stub(file, 'downloadFile') + .callsFake((path, url, cb) => { + cb(); + }); + + file.parseFileForImport(contract, () => { + // Parse again + file.parseFileForImport(contract, () => { + assert.strictEqual(downloadFileStub.callCount, 1); + assert.strictEqual(downloadFileStub.firstCall.args[0], + '.embark/contracts/embark-framework/embark/develop/test_apps/contracts_app/contracts/contract_args.sol'); + assert.strictEqual(downloadFileStub.firstCall.args[1], + 'https://raw.githubusercontent.com/embark-framework/embark/develop/test_apps/contracts_app/contracts/contract_args.sol'); + done(); + }); + }); + }); }); }); diff --git a/test/utils.js b/test/utils.js new file mode 100644 index 00000000..e2d0ead7 --- /dev/null +++ b/test/utils.js @@ -0,0 +1,94 @@ +/*global describe, it*/ +const Utils = require('../lib/utils/utils'); +const assert = require('assert'); +const constants = require('../lib/constants'); + +describe('embark.utils', function () { + describe('#getExternalContractUrl', function () { + it('should get the right url for a https://github file', function () { + const fileObj = Utils.getExternalContractUrl( + 'https://github.com/embark-framework/embark/blob/master/test_app/app/contracts/simple_storage.sol' + ); + assert.deepEqual(fileObj, + { + filePath: constants.httpContractsDirectory + 'embark-framework/embark/master/test_app/app/contracts/simple_storage.sol', + url: 'https://raw.githubusercontent.com/embark-framework/embark/master/test_app/app/contracts/simple_storage.sol' + }); + }); + + it('should fail for a malformed https://github file', function () { + const fileObj = Utils.getExternalContractUrl( + 'https://github/embark-framework/embark/blob/master/test_app/app/contracts/simple_storage.sol' + ); + assert.strictEqual(fileObj, null); + }); + + it('should get the right url for a git:// file with no branch #', function () { + const fileObj = Utils.getExternalContractUrl( + 'git://github.com/status-im/contracts/contracts/identity/ERC725.sol' + ); + assert.deepEqual(fileObj, + { + filePath: constants.httpContractsDirectory + 'status-im/contracts/master/contracts/identity/ERC725.sol', + url: 'https://raw.githubusercontent.com/status-im/contracts/master/contracts/identity/ERC725.sol' + }); + }); + + it('should get the right url for a git:// file with a branch #', function () { + const fileObj = Utils.getExternalContractUrl( + 'git://github.com/status-im/contracts/contracts/identity/ERC725.sol#myBranch' + ); + assert.deepEqual(fileObj, + { + filePath: constants.httpContractsDirectory + 'status-im/contracts/myBranch/contracts/identity/ERC725.sol', + url: 'https://raw.githubusercontent.com/status-im/contracts/myBranch/contracts/identity/ERC725.sol' + }); + }); + + it('should fail when the git:// file is malformed', function () { + const fileObj = Utils.getExternalContractUrl( + 'git://github.com/identity/ERC725.sol#myBranch' + ); + assert.strictEqual(fileObj, null); + }); + + it('should get the right url with a github.com file without branch #', function () { + const fileObj = Utils.getExternalContractUrl( + 'github.com/status-im/contracts/contracts/identity/ERC725.sol' + ); + assert.deepEqual(fileObj, + { + filePath: constants.httpContractsDirectory + 'status-im/contracts/master/contracts/identity/ERC725.sol', + url: 'https://raw.githubusercontent.com/status-im/contracts/master/contracts/identity/ERC725.sol' + }); + }); + + it('should get the right url with a github.com file with branch #', function () { + const fileObj = Utils.getExternalContractUrl( + 'github.com/status-im/contracts/contracts/identity/ERC725.sol#theBranch' + ); + assert.deepEqual(fileObj, + { + filePath: constants.httpContractsDirectory + 'status-im/contracts/theBranch/contracts/identity/ERC725.sol', + url: 'https://raw.githubusercontent.com/status-im/contracts/theBranch/contracts/identity/ERC725.sol' + }); + }); + + it('should fail with a malformed github.com url', function () { + const fileObj = Utils.getExternalContractUrl( + 'github/status-im/contracts/contracts/identity/ERC725.sol#theBranch' + ); + assert.strictEqual(fileObj, null); + }); + + it('should succeed with a generic http url', function () { + const fileObj = Utils.getExternalContractUrl( + 'http://myurl.com/myFile.sol' + ); + assert.deepEqual(fileObj, { + filePath: constants.httpContractsDirectory + 'myFile.sol', + url: 'http://myurl.com/myFile.sol' + }); + }); + }); +}); diff --git a/test_apps/contracts_app/contracts.json b/test_apps/contracts_app/contracts.json index c15d84b0..d4112a28 100644 --- a/test_apps/contracts_app/contracts.json +++ b/test_apps/contracts_app/contracts.json @@ -1,7 +1,7 @@ { "default": { "versions": { - "web3.js": "1.0.0-beta.27", + "web3": "1.0.0-beta.27", "solc": "0.4.17" }, "deployment": { diff --git a/test_apps/contracts_app/embark.json b/test_apps/contracts_app/embark.json index 0eefbfab..4f2b9380 100644 --- a/test_apps/contracts_app/embark.json +++ b/test_apps/contracts_app/embark.json @@ -9,7 +9,7 @@ "webserver": false }, "versions": { - "web3.js": "1.0.0-beta", + "web3": "1.0.0-beta", "solc": "0.4.17" } } diff --git a/test_apps/test_app/app/contracts/SimpleStorageWithHttpImport.sol b/test_apps/test_app/app/contracts/SimpleStorageWithHttpImport.sol new file mode 100644 index 00000000..21c3505b --- /dev/null +++ b/test_apps/test_app/app/contracts/SimpleStorageWithHttpImport.sol @@ -0,0 +1,34 @@ +pragma solidity ^0.4.17; + +import "https://github.com/embark-framework/embark/blob/develop/test_apps/contracts_app/contracts/ownable.sol"; + + +contract SimpleStorageWithHttpImport is Ownable { + uint public storedData; + + function() public payable { } + + function SimpleStorageWithHttpImport(uint initialValue) public { + storedData = initialValue; + } + + function set(uint x) public { + storedData = x; + for(uint i = 0; i < 1000; i++) { + storedData += i; + } + } + + function set2(uint x, uint unusedGiveWarning) public onlyOwner { + storedData = x; + } + + function get() public view returns (uint retVal) { + return storedData; + } + + function getS() public pure returns (string d) { + return "hello"; + } + +} diff --git a/test_apps/test_app/app/contracts/another_storage.sol b/test_apps/test_app/app/contracts/another_storage.sol index 92233769..d8e0a1a8 100644 --- a/test_apps/test_app/app/contracts/another_storage.sol +++ b/test_apps/test_app/app/contracts/another_storage.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.17; +pragma solidity ^0.4.18; contract AnotherStorage { address public simpleStorageAddress; address simpleStorageAddress2; diff --git a/test_apps/test_app/config/contracts.json b/test_apps/test_app/config/contracts.json index 4f59c276..b1f0597f 100644 --- a/test_apps/test_app/config/contracts.json +++ b/test_apps/test_app/config/contracts.json @@ -74,6 +74,12 @@ }, "Identity": { "file": "https://github.com/status-im/contracts/blob/master/contracts/identity/Identity.sol" + }, + "SimpleStorageWithHttpImport": { + "fromIndex": 0, + "args": [ + 100 + ] } }, "afterDeploy": [ diff --git a/test_apps/test_app/embark.json b/test_apps/test_app/embark.json index 0d56876b..66dfb006 100644 --- a/test_apps/test_app/embark.json +++ b/test_apps/test_app/embark.json @@ -16,7 +16,8 @@ "config": "config/", "versions": { "solc": "0.4.18", - "ipfs-api": "17.2.6" + "web3": "1.0.0-beta.34", + "ipfs-api": "17.2.7" }, "plugins": { "embark-service": {} diff --git a/test_apps/test_app/test/http_contract_test.js b/test_apps/test_app/test/http_contract_test.js index c1cddd67..98b2d34e 100644 --- a/test_apps/test_app/test/http_contract_test.js +++ b/test_apps/test_app/test/http_contract_test.js @@ -3,10 +3,9 @@ const fs = require('fs-extra'); const assert = require('assert'); describe('http contracts', () => { - const contractPath = '.embark/contracts/status-im/contracts/master/contracts/identity/Identity.sol'; - const contractImportPath = '.embark/contracts/status-im/contracts/master/contracts/identity/ERC725.sol'; it('should have downloaded the file in .embark/contracts', (done) => { + const contractPath = '.embark/contracts/status-im/contracts/master/contracts/identity/Identity.sol'; fs.access(contractPath, (err) => { if (err) { assert.fail(contractPath + ' was not downloaded'); @@ -16,9 +15,20 @@ describe('http contracts', () => { }); it('should have downloaded the file import file too', (done) => { + const contractImportPath = '.embark/contracts/status-im/contracts/master/contracts/identity/ERC725.sol'; fs.access(contractImportPath, (err) => { if (err) { - assert.fail(contractPath + ' was not downloaded'); + assert.fail(contractImportPath + ' was not downloaded'); + } + done(); + }); + }); + + it('should have downloaded the http import in SimpleStorageWithHttpImport', (done) => { + const contractImportPath = '.embark/contracts/embark-framework/embark/develop/test_apps/contracts_app/contracts/ownable.sol'; + fs.access(contractImportPath, (err) => { + if (err) { + assert.fail(contractImportPath + ' was not downloaded'); } done(); });