Merge branch 'develop' into bug_fix/upload-to-swarm

This commit is contained in:
Eric Mastro 2018-04-26 19:05:56 +10:00 committed by GitHub
commit 4c39a3f589
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 470 additions and 283 deletions

View File

@ -1 +0,0 @@
{}

View File

@ -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"
}
} }

View File

@ -20,6 +20,7 @@ var Config = function(options) {
this.logger = options.logger; this.logger = options.logger;
this.events = options.events; this.events = options.events;
this.embarkConfig = {}; this.embarkConfig = {};
this.context = options.context || [constants.contexts.any];
}; };
Config.prototype.loadConfigFiles = function(options) { Config.prototype.loadConfigFiles = function(options) {
@ -36,7 +37,7 @@ Config.prototype.loadConfigFiles = function(options) {
this.embarkConfig = fs.readJSONSync(options.embarkConfig); this.embarkConfig = fs.readJSONSync(options.embarkConfig);
this.embarkConfig.plugins = this.embarkConfig.plugins || {}; 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.plugins.loadPlugins();
this.loadEmbarkConfigFile(); this.loadEmbarkConfigFile();
@ -112,7 +113,7 @@ Config.prototype.loadBlockchainConfigFile = function() {
Config.prototype.loadContractsConfigFile = function() { Config.prototype.loadContractsConfigFile = function() {
var defaultVersions = { var defaultVersions = {
"web3.js": "1.0.0-beta", "web3": "1.0.0-beta",
"solc": "0.4.17" "solc": "0.4.17"
}; };
var versions = utils.recursiveMerge(defaultVersions, this.embarkConfig.versions || {}); var versions = utils.recursiveMerge(defaultVersions, this.embarkConfig.versions || {});
@ -143,49 +144,6 @@ Config.prototype.loadContractsConfigFile = function() {
this.contractsConfig = this._mergeConfig(configFilePath, configObject, this.env); 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() { Config.prototype.loadExternalContractsFiles = function() {
let contracts = this.contractsConfig.contracts; let contracts = this.contractsConfig.contracts;
for (let contractName in contracts) { for (let contractName in contracts) {
@ -194,11 +152,11 @@ Config.prototype.loadExternalContractsFiles = function() {
continue; continue;
} }
if (contract.file.startsWith('http') || contract.file.startsWith('git')) { if (contract.file.startsWith('http') || contract.file.startsWith('git')) {
const fileObj = this.getExternalContractUrl(contract); const fileObj = utils.getExternalContractUrl(contract.file);
if (!fileObj) { if (!fileObj) {
return this.logger.error("HTTP contract file not found: " + contract.file); 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})); this.contractsFiles.push(new File({filename: localFile, type: File.types.http, basedir: '', path: fileObj.url}));
} else if (fs.existsSync(contract.file)) { } else if (fs.existsSync(contract.file)) {
this.contractsFiles.push(new File({filename: contract.file, type: File.types.dapp_file, basedir: '', path: contract.file})); this.contractsFiles.push(new File({filename: contract.file, type: File.types.dapp_file, basedir: '', path: contract.file}));

View File

@ -19,6 +19,7 @@ class Engine {
this.logFile = options.logFile; this.logFile = options.logFile;
this.logLevel = options.logLevel; this.logLevel = options.logLevel;
this.events = options.events; this.events = options.events;
this.context = options.context;
} }
init(_options) { init(_options) {
@ -26,7 +27,7 @@ class Engine {
let options = _options || {}; let options = _options || {};
this.events = options.events || this.events || new Events(); 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.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.config.loadConfigFiles({embarkConfig: this.embarkConfig, interceptLogs: this.interceptLogs});
this.plugins = this.config.plugins; this.plugins = this.config.plugins;
@ -231,6 +232,9 @@ class Engine {
if (err) { if (err) {
return cb({name: "Ethereum node (version unknown)", status: 'on'}); 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 nodeName = version.split("/")[0];
let versionNumber = version.split("/")[1].split("-")[0]; let versionNumber = version.split("/")[1].split("-")[0];
let name = nodeName + " " + versionNumber + " (Ethereum)"; let name = nodeName + " " + versionNumber + " (Ethereum)";

View File

@ -2,39 +2,63 @@ const async = require('async');
const fs = require('./fs.js'); const fs = require('./fs.js');
const path = require('path'); const path = require('path');
const request = require('request'); const request = require('request');
const utils = require('../utils/utils');
class File { class File {
constructor (options) { constructor (options) {
this.filename = options.filename; this.filename = options.filename.replace(/\\/g, '/');
this.type = options.type; this.type = options.type;
this.path = options.path; this.path = options.path;
this.basedir = options.basedir; this.basedir = options.basedir;
this.resolver = options.resolver; this.resolver = options.resolver;
} }
parseFileForImport(content, callback) { parseFileForImport(content, isHttpContract, callback) {
const self = this; const self = this;
if (typeof isHttpContract === 'function') {
callback = isHttpContract;
isHttpContract = false;
}
if (self.filename.indexOf('.sol') < 0) { if (self.filename.indexOf('.sol') < 0) {
// Only supported in Solidity // 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; let matches;
const filesToDownload = []; const filesToDownload = [];
const pathWithoutFile = path.dirname(self.path); const pathWithoutFile = path.dirname(self.path);
while ((matches = regex.exec(content))) { while ((matches = regex.exec(content))) {
filesToDownload.push({ const httpFileObj = utils.getExternalContractUrl(matches[1]);
const fileObj = {
fileRelativePath: path.join(path.dirname(self.filename), matches[1]), fileRelativePath: path.join(path.dirname(self.filename), matches[1]),
url: `${pathWithoutFile}/${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) => { async.each(filesToDownload, ((fileObj, eachCb) => {
self.downloadFile(fileObj.fileRelativePath, fileObj.url, (_content) => { self.downloadFile(fileObj.fileRelativePath, fileObj.url, (_content) => {
eachCb(); eachCb();
}); });
}), callback); }), (err) => {
callback(err, content);
});
} }
downloadFile (filename, url, callback) { downloadFile (filename, url, callback) {
@ -63,7 +87,7 @@ class File {
fs.readFile(filename, next); fs.readFile(filename, next);
}, },
function parseForImports(content, next) { function parseForImports(content, next) {
self.parseFileForImport(content, (err) => { self.parseFileForImport(content, true, (err) => {
next(err, content); next(err, content);
}); });
} }
@ -77,14 +101,19 @@ class File {
} }
content (callback) { content (callback) {
let content;
if (this.type === File.types.embark_internal) { 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) { } 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) { } 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) { } else if (this.type === File.types.http) {
this.downloadFile(this.filename, this.path, (content) => { return this.downloadFile(this.filename, this.path, (content) => {
if (!content) { if (!content) {
return callback(content); return callback(content);
} }
@ -95,6 +124,9 @@ class File {
} else { } else {
throw new Error("unknown file: " + this.filename); throw new Error("unknown file: " + this.filename);
} }
return this.parseFileForImport(content, (err, newContent) => {
callback(newContent);
});
} }
} }

View File

@ -1,5 +1,6 @@
var fs = require('./fs.js'); const fs = require('./fs.js');
var utils = require('../utils/utils.js'); const utils = require('../utils/utils.js');
const constants = require('../constants');
// TODO: pass other params like blockchainConfig, contract files, etc.. // TODO: pass other params like blockchainConfig, contract files, etc..
var Plugin = function(options) { var Plugin = function(options) {
@ -27,9 +28,38 @@ var Plugin = function(options) {
this.logger = options.logger; this.logger = options.logger;
this.events = options.events; this.events = options.events;
this.config = options.config; 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() { 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) { if (this.shouldInterceptLogs) {
this.interceptLogs(this.pluginModule); this.interceptLogs(this.pluginModule);
} }

View File

@ -9,6 +9,7 @@ var Plugins = function(options) {
this.logger = options.logger; this.logger = options.logger;
this.events = options.events; this.events = options.events;
this.config = options.config; this.config = options.config;
this.context = options.context;
}; };
Plugins.prototype.loadPlugins = function() { Plugins.prototype.loadPlugins = function() {
@ -20,10 +21,12 @@ Plugins.prototype.loadPlugins = function() {
}; };
Plugins.prototype.listPlugins = function() { Plugins.prototype.listPlugins = function() {
var list = []; const list = [];
for (var className in this.pluginList) { this.plugins.forEach(plugin => {
list.push(className); if (plugin.loaded) {
} list.push(plugin.name);
}
});
return list; return list;
}; };
@ -31,7 +34,18 @@ Plugins.prototype.listPlugins = function() {
Plugins.prototype.createPlugin = function(pluginName, pluginConfig) { Plugins.prototype.createPlugin = function(pluginName, pluginConfig) {
let plugin = {}; let plugin = {};
let pluginPath = false; 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); this.plugins.push(pluginWrapper);
return pluginWrapper; return pluginWrapper;
}; };
@ -40,7 +54,18 @@ Plugins.prototype.loadInternalPlugin = function(pluginName, pluginConfig) {
var pluginPath = utils.joinPath('../modules/', pluginName, 'index.js'); var pluginPath = utils.joinPath('../modules/', pluginName, 'index.js');
var plugin = require(pluginPath); 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(); pluginWrapper.loadInternalPlugin();
this.plugins.push(pluginWrapper); this.plugins.push(pluginWrapper);
}; };
@ -49,7 +74,18 @@ Plugins.prototype.loadPlugin = function(pluginName, pluginConfig) {
var pluginPath = utils.joinPath(utils.pwd(), 'node_modules', pluginName); var pluginPath = utils.joinPath(utils.pwd(), 'node_modules', pluginName);
var plugin = require(pluginPath); 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(); pluginWrapper.loadPlugin();
this.plugins.push(pluginWrapper); this.plugins.push(pluginWrapper);
}; };

View File

@ -1,4 +1,5 @@
let async = require('async'); let async = require('async');
const constants = require('./constants');
// require("./utils/debug_util.js")(__filename, async); // require("./utils/debug_util.js")(__filename, async);
require('colors'); require('colors');
@ -31,22 +32,25 @@ class Embark {
this.events = new Events(); this.events = new Events();
this.logger = new Logger({logLevel: 'debug', events: this.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.config.loadConfigFiles(options);
this.plugins = this.config.plugins; this.plugins = this.config.plugins;
} }
blockchain(env, client) { blockchain(env, client) {
this.context = [constants.contexts.blockchain];
return require('./cmds/blockchain/blockchain.js')(this.config.blockchainConfig, client, env).run(); return require('./cmds/blockchain/blockchain.js')(this.config.blockchainConfig, client, env).run();
} }
simulator(options) { simulator(options) {
this.context = options.context || [constants.contexts.simulator, constants.contexts.blockchain];
let Simulator = require('./cmds/simulator.js'); let Simulator = require('./cmds/simulator.js');
let simulator = new Simulator({blockchainConfig: this.config.blockchainConfig}); let simulator = new Simulator({blockchainConfig: this.config.blockchainConfig});
simulator.run(options); simulator.run(options);
} }
generateTemplate(templateName, destinationFolder, name) { generateTemplate(templateName, destinationFolder, name) {
this.context = [constants.contexts.templateGeneration];
let TemplateGenerator = require('./cmds/template_generator.js'); let TemplateGenerator = require('./cmds/template_generator.js');
let templateGenerator = new TemplateGenerator(templateName); let templateGenerator = new TemplateGenerator(templateName);
templateGenerator.generate(destinationFolder, name); templateGenerator.generate(destinationFolder, name);
@ -54,6 +58,7 @@ class Embark {
run(options) { run(options) {
let self = this; let self = this;
self.context = options.context || [constants.contexts.run, constants.contexts.build];
let Dashboard = require('./dashboard/dashboard.js'); let Dashboard = require('./dashboard/dashboard.js');
let windowSize = require('window-size'); let windowSize = require('window-size');
@ -62,14 +67,15 @@ class Embark {
version: this.version, version: this.version,
embarkConfig: options.embarkConfig || 'embark.json', embarkConfig: options.embarkConfig || 'embark.json',
logFile: options.logFile, logFile: options.logFile,
logLevel: options.logLevel logLevel: options.logLevel,
context: self.context
}); });
engine.init(); engine.init();
if (!options.useDashboard) { if (!options.useDashboard) {
console.log('========================'.bold.green); engine.logger.info('========================'.bold.green);
console.log(('Welcome to Embark ' + this.version).yellow.bold); engine.logger.info(('Welcome to Embark ' + this.version).yellow.bold);
console.log('========================'.bold.green); engine.logger.info('========================'.bold.green);
} }
async.parallel([ async.parallel([
@ -152,6 +158,8 @@ class Embark {
} }
build(options) { build(options) {
this.context = options.context || [constants.contexts.build];
let engine = new Engine({ let engine = new Engine({
env: options.env, env: options.env,
version: this.version, version: this.version,
@ -162,7 +170,8 @@ class Embark {
events: options.events, events: options.events,
logger: options.logger, logger: options.logger,
config: options.config, config: options.config,
plugins: options.plugins plugins: options.plugins,
context: this.context
}); });
engine.init(); engine.init();
@ -199,18 +208,22 @@ class Embark {
} }
initTests(options) { initTests(options) {
this.context = options.context || [constants.contexts.test];
let Test = require('./tests/test.js'); let Test = require('./tests/test.js');
options.context = this.context;
return new Test(options); return new Test(options);
} }
graph(options) { graph(options) {
this.context = options.context || [constants.contexts.graph];
options.onlyCompile = true; options.onlyCompile = true;
let engine = new Engine({ let engine = new Engine({
env: options.env, env: options.env,
version: this.version, version: this.version,
embarkConfig: options.embarkConfig || 'embark.json', embarkConfig: options.embarkConfig || 'embark.json',
logFile: options.logFile logFile: options.logFile,
context: this.context
}); });
engine.init(); engine.init();
@ -251,11 +264,14 @@ class Embark {
} }
reset() { reset() {
this.context = [constants.contexts.reset];
let resetCmd = require('./cmds/reset.js'); let resetCmd = require('./cmds/reset.js');
resetCmd(); resetCmd();
} }
upload(platform, options) { upload(platform, options) {
this.context = options.context || [constants.contexts.upload, constants.contexts.build];
let engine = new Engine({ let engine = new Engine({
env: options.env, env: options.env,
@ -337,6 +353,7 @@ class Embark {
} }
runTests(file) { runTests(file) {
this.context = [constants.contexts.test];
let RunTests = require('./tests/run_tests.js'); let RunTests = require('./tests/run_tests.js');
RunTests.run(file); RunTests.run(file);
} }
@ -346,6 +363,7 @@ class Embark {
// temporary until next refactor // temporary until next refactor
Embark.initTests = function(options) { Embark.initTests = function(options) {
let Test = require('./tests/test.js'); let Test = require('./tests/test.js');
options.context = [constants.contexts.test];
return new Test(options); return new Test(options);
}; };

View File

@ -3,8 +3,13 @@ let solc;
const fs = require('fs-extra'); const fs = require('fs-extra');
const path = require('path'); const path = require('path');
const constants = require('../../constants'); const constants = require('../../constants');
const Utils = require('../../utils/utils');
function findImports(filename) { function findImports(filename) {
if (filename.startsWith('http') || filename.startsWith('git')) {
const fileObj = Utils.getExternalContractUrl(filename);
filename = fileObj.filePath;
}
if (fs.existsSync(filename)) { if (fs.existsSync(filename)) {
return {contents: fs.readFileSync(filename).toString()}; return {contents: fs.readFileSync(filename).toString()};
} }

View File

@ -89,7 +89,7 @@ Test.prototype.deployAll = function(contractsConfig, cb) {
], function(err, result) { ], function(err, result) {
if (err) { if (err) {
console.log('terminating due to error'); 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 // this should be part of the waterfall and not just something done at the
// end // end

View File

@ -6,6 +6,7 @@ let https = require('follow-redirects').https;
let shelljs = require('shelljs'); let shelljs = require('shelljs');
var tar = require('tar'); var tar = require('tar');
var propose = require('propose'); var propose = require('propose');
const constants = require('../constants');
//let fs = require('../core/fs.js'); //let fs = require('../core/fs.js');
let o_fs = require('fs-extra'); let o_fs = require('fs-extra');
@ -127,6 +128,51 @@ function pwd() {
return process.env.PWD || process.cwd(); 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 = { module.exports = {
joinPath: joinPath, joinPath: joinPath,
filesMatchingPattern: filesMatchingPattern, filesMatchingPattern: filesMatchingPattern,
@ -143,5 +189,6 @@ module.exports = {
downloadFile: downloadFile, downloadFile: downloadFile,
extractTar: extractTar, extractTar: extractTar,
proposeAlternative: proposeAlternative, proposeAlternative: proposeAlternative,
pwd: pwd pwd: pwd,
getExternalContractUrl
}; };

View File

@ -21,7 +21,7 @@ class LibraryManager {
this.versions = {}; this.versions = {};
let solcVersionInConfig = this.contractsConfig.versions.solc; 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"]; let ipfsApiVersion = this.storageConfig.versions["ipfs-api"];
this.versions['solc'] = solcVersionInConfig; this.versions['solc'] = solcVersionInConfig;
@ -64,10 +64,7 @@ class LibraryManager {
listenToCommandsToGetLibrary() { listenToCommandsToGetLibrary() {
let npm = new Npm({logger: this.embark.logger}); let npm = new Npm({logger: this.embark.logger});
this.embark.events.setCommandHandler('version:getPackageLocation', (libName, version, cb) => { this.embark.events.setCommandHandler('version:getPackageLocation', (libName, version, cb) => {
npm.getPackageVersion(libName, version, false, false, cb); npm.getPackageVersion(libName, version, cb);
});
this.embark.events.setCommandHandler('version:getPackageContent', (libName, version, cb) => {
npm.getPackageVersion(libName, version, false, true, cb);
}); });
} }

View File

@ -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 fs = require('../core/fs.js');
let PluginManager = require('live-plugin-manager').PluginManager;
class Npm { class Npm {
constructor(options) { constructor(options) {
this.logger = options.logger; this.logger = options.logger;
} }
downloadFromGit(registryJSON, packageName, version, returnContent, callback) { getPackageVersion(packageName, version, 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;
let packageDirectory = './.embark/versions/' + packageName + '/' + version + '/'; let packageDirectory = './.embark/versions/' + packageName + '/' + version + '/';
if (fs.existsSync(packageDirectory) && fs.existsSync(packageDirectory + "package.json")) { let manager = new PluginManager({pluginsPath: packageDirectory});
let content;
if (getFromGit && returnContent) { if (fs.existsSync(packageDirectory + packageName)) {
let distFile = packageDirectory + packageName + ".js"; return callback(null, packageDirectory + packageName);
content = fs.readFileSync(distFile).toString();
} else if (returnContent) {
let distFile = packageDirectory + returnContent;
content = fs.readFileSync(distFile).toString();
} else {
content = packageDirectory;
}
return callback(null, content);
} }
utils.httpsGet(npmRegistry, function (err, body) { this.logger.info("downloading " + packageName + " " + version + "....");
if (err) { manager.install(packageName, version).then((result) => {
if (err.code === 'ENOTFOUND') { callback(null , result.location);
return callback("can't reach " + err.hostname + " to download " + packageName + " " + version + " - are you connected to the internet?"); }).catch(callback);
}
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);
}
});
} }
} }

View File

@ -43,6 +43,7 @@
"globule": "^1.1.0", "globule": "^1.1.0",
"http-shutdown": "^1.2.0", "http-shutdown": "^1.2.0",
"ipfs-api": "17.2.4", "ipfs-api": "17.2.4",
"live-plugin-manager": "https://github.com/iurimatias/live-plugin-manager.git",
"merge": "^1.2.0", "merge": "^1.2.0",
"mocha": "^2.2.5", "mocha": "^2.2.5",
"orbit-db": "^0.17.3", "orbit-db": "^0.17.3",

View File

@ -9,7 +9,7 @@
"buildDir": "dist/", "buildDir": "dist/",
"config": "config/", "config": "config/",
"versions": { "versions": {
"web3.js": "1.0.0-beta", "web3": "1.0.0-beta",
"solc": "0.4.17", "solc": "0.4.17",
"ipfs-api": "17.2.4" "ipfs-api": "17.2.4"
}, },

View File

@ -8,7 +8,7 @@
"buildDir": "dist/", "buildDir": "dist/",
"config": "config/", "config": "config/",
"versions": { "versions": {
"web3.js": "1.0.0-beta", "web3": "1.0.0-beta",
"solc": "0.4.17", "solc": "0.4.17",
"ipfs-api": "17.2.4" "ipfs-api": "17.2.4"
}, },

View File

@ -38,7 +38,7 @@ describe('embark.Config', function () {
it('should load contract config correctly', function () { it('should load contract config correctly', function () {
config.loadContractsConfigFile(); config.loadContractsConfigFile();
let expectedConfig = { 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'}, deployment: {host: 'localhost', port: 8545, type: 'rpc'},
dappConnection: ['$WEB3', 'localhost:8545'], dappConnection: ['$WEB3', 'localhost:8545'],
"gas": "auto", "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 () { describe('#loadExternalContractsFiles', function () {
it('should create the right list of files and download', function () { it('should create the right list of files and download', function () {
config.contractsFiles = []; config.contractsFiles = [];

View File

@ -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;
}
}

View File

@ -1,7 +1,7 @@
pragma solidity ^0.4.7; pragma solidity ^0.4.7;
import "./ownable.sol";
contract SimpleStorage { contract SimpleStorage {
uint public storedData; uint public storedData;
import "./ownable.sol";
function SimpleStorage(uint initialValue) { function SimpleStorage(uint initialValue) {
storedData = initialValue; storedData = initialValue;

View File

@ -16,7 +16,7 @@ describe('embark.File', function () {
cb(); cb();
}); });
file.parseFileForImport(contract, () => { file.parseFileForImport(contract, true, () => {
assert.strictEqual(downloadFileStub.callCount, 1); assert.strictEqual(downloadFileStub.callCount, 1);
assert.strictEqual(downloadFileStub.firstCall.args[0], assert.strictEqual(downloadFileStub.firstCall.args[0],
path.normalize('.embark/contracts/embark-framework/embark/master/test_app/app/contracts/ownable.sol')); path.normalize('.embark/contracts/embark-framework/embark/master/test_app/app/contracts/ownable.sol'));
@ -25,5 +25,61 @@ describe('embark.File', function () {
done(); 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();
});
});
});
}); });
}); });

94
test/utils.js Normal file
View File

@ -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'
});
});
});
});

View File

@ -1,7 +1,7 @@
{ {
"default": { "default": {
"versions": { "versions": {
"web3.js": "1.0.0-beta.27", "web3": "1.0.0-beta.27",
"solc": "0.4.17" "solc": "0.4.17"
}, },
"deployment": { "deployment": {

View File

@ -9,7 +9,7 @@
"webserver": false "webserver": false
}, },
"versions": { "versions": {
"web3.js": "1.0.0-beta", "web3": "1.0.0-beta",
"solc": "0.4.17" "solc": "0.4.17"
} }
} }

View File

@ -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";
}
}

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.17; pragma solidity ^0.4.18;
contract AnotherStorage { contract AnotherStorage {
address public simpleStorageAddress; address public simpleStorageAddress;
address simpleStorageAddress2; address simpleStorageAddress2;

View File

@ -74,6 +74,12 @@
}, },
"Identity": { "Identity": {
"file": "https://github.com/status-im/contracts/blob/master/contracts/identity/Identity.sol" "file": "https://github.com/status-im/contracts/blob/master/contracts/identity/Identity.sol"
},
"SimpleStorageWithHttpImport": {
"fromIndex": 0,
"args": [
100
]
} }
}, },
"afterDeploy": [ "afterDeploy": [

View File

@ -16,7 +16,8 @@
"config": "config/", "config": "config/",
"versions": { "versions": {
"solc": "0.4.18", "solc": "0.4.18",
"ipfs-api": "17.2.6" "web3": "1.0.0-beta.34",
"ipfs-api": "17.2.7"
}, },
"plugins": { "plugins": {
"embark-service": {} "embark-service": {}

View File

@ -3,10 +3,9 @@ const fs = require('fs-extra');
const assert = require('assert'); const assert = require('assert');
describe('http contracts', () => { 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) => { 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) => { fs.access(contractPath, (err) => {
if (err) { if (err) {
assert.fail(contractPath + ' was not downloaded'); assert.fail(contractPath + ' was not downloaded');
@ -16,9 +15,20 @@ describe('http contracts', () => {
}); });
it('should have downloaded the file import file too', (done) => { 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) => { fs.access(contractImportPath, (err) => {
if (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(); done();
}); });