Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 2 additions & 45 deletions lib/core/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,49 +143,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) {
Expand All @@ -194,11 +151,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}));
Expand Down
54 changes: 43 additions & 11 deletions lib/core/file.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const async = require('async');
const fs = require('./fs.js');
const path = require('path');
const request = require('request');
const utils = require('../utils/utils');

class File {

Expand All @@ -13,28 +14,51 @@ class File {
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) {
Expand Down Expand Up @@ -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);
});
}
Expand All @@ -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);
}
Expand All @@ -95,6 +124,9 @@ class File {
} else {
throw new Error("unknown file: " + this.filename);
}
return this.parseFileForImport(content, (err, newContent) => {
callback(newContent);
});
}

}
Expand Down
5 changes: 5 additions & 0 deletions lib/modules/solidity/solcP.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()};
}
Expand Down
49 changes: 48 additions & 1 deletion lib/utils/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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,
Expand All @@ -143,5 +189,6 @@ module.exports = {
downloadFile: downloadFile,
extractTar: extractTar,
proposeAlternative: proposeAlternative,
pwd: pwd
pwd: pwd,
getExternalContractUrl
};
88 changes: 0 additions & 88 deletions test/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [];
Expand Down
19 changes: 19 additions & 0 deletions test/contracts/contract_with_http_import.sol
Original file line number Diff line number Diff line change
@@ -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;
}

}

2 changes: 1 addition & 1 deletion test/contracts/contract_with_import.sol
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Loading