Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Commit

Permalink
Merge pull request #3319 from trufflesuite/revert-3314-revert-3255-sp…
Browse files Browse the repository at this point in the history
…awn-repl

Run each console command in its own child process
  • Loading branch information
fainashalts authored Sep 10, 2020
2 parents acf10c0 + b5995da commit 63d2e4a
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 200 deletions.
13 changes: 9 additions & 4 deletions packages/core/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ if (!semver.satisfies(process.version, ">=" + minimumNodeVersion)) {

const Command = require("./lib/command");

const command = new Command(require("./lib/commands"));
// enable Truffle to run both from the bundles out of packages/dist
// and using the raw JS directly - we inject BUNDLE_VERSION when building
const command =
typeof BUNDLE_VERSION !== "undefined"
? new Command(require("./commands.bundled.js"))
: new Command(require("./lib/commands"));

// This should be removed when issue is resolved upstream:
// https://github.com/ethereum/web3.js/issues/1648
Expand All @@ -41,15 +46,15 @@ listeners.forEach(listener => process.removeListener("warning", listener));
let options = { logger: console };

const inputArguments = process.argv.slice(2);
const userWantsGeneralHelp =
inputArguments.length === 1 && ['help', '--help'].includes(inputArguments[0]);
const userWantsGeneralHelp =
inputArguments.length === 1 && ["help", "--help"].includes(inputArguments[0]);

if (userWantsGeneralHelp) {
command.displayGeneralHelp();
process.exit(0);
}

command.run(inputArguments, options, function(err) {
command.run(inputArguments, options, function (err) {
if (err) {
if (err instanceof TaskError) {
analytics.send({
Expand Down
1 change: 0 additions & 1 deletion packages/core/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ var pkg = require("./package.json");
module.exports = {
build: require("./lib/build"),
create: require("./lib/commands/create/helpers"),
console: require("./lib/repl"),
contracts: require("@truffle/workflow-compile"),
package: require("./lib/package"),
test: require("./lib/test"),
Expand Down
7 changes: 4 additions & 3 deletions packages/core/lib/command.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class Command {

let args = yargs();

Object.keys(this.commands).forEach(function(command) {
Object.keys(this.commands).forEach(function (command) {
args = args.command(commands[command]);
});

Expand Down Expand Up @@ -68,7 +68,7 @@ class Command {
return {
name: chosenCommand,
argv,
command
command,
};
}

Expand Down Expand Up @@ -134,10 +134,11 @@ class Command {
const newOptions = Object.assign({}, clone, argv);

result.command.run(newOptions, callback);

analytics.send({
command: result.name ? result.name : "other",
args: result.argv._,
version: bundled || "(unbundled) " + core
version: bundled || "(unbundled) " + core,
});
} catch (err) {
callback(err);
Expand Down
51 changes: 51 additions & 0 deletions packages/core/lib/console-child.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
const Command = require("../lib/command");
const TruffleError = require("@truffle/error");
const Config = require("@truffle/config");
const Web3 = require("web3");
const yargs = require("yargs");

const input = process.argv[2].split(" -- ");
const inputStrings = input[1];

//detect config so we can get the provider and resolver without having to serialize
//and deserialize them
const detectedConfig = Config.detect({ network: yargs(input[0]).argv.network });
const customConfig = detectedConfig.networks.develop || {};

//need host and port for provider url
const ganacheOptions = {
host: customConfig.host || "127.0.0.1",
port: customConfig.port || 9545
};
const url = `http://${ganacheOptions.host}:${ganacheOptions.port}/`;

//set up the develop network to use, including setting up provider
detectedConfig.networks.develop = {
host: customConfig.host || "127.0.0.1",
port: customConfig.port || 9545,
network_id: customConfig.network_id || 5777,
provider: function () {
return new Web3.providers.HttpProvider(url, { keepAlive: false });
}
};

// enable Truffle to run both from the bundles out of packages/dist
// and using the raw JS directly - we inject BUNDLE_VERSION when building
const command =
typeof BUNDLE_VERSION !== "undefined"
? new Command(require("./commands.bundled.js"))
: new Command(require("./commands"));

command.run(inputStrings, detectedConfig, error => {
if (error) {
// Perform error handling ourselves.
if (error instanceof TruffleError) {
console.log(error.message);
} else {
// Bubble up all other unexpected errors.
console.log(error.stack || error.toString());
}
process.exit(1);
}
process.exit(0);
});
90 changes: 57 additions & 33 deletions packages/core/lib/console.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const ReplManager = require("./repl");
const repl = require("repl");
const Command = require("./command");
const provision = require("@truffle/provisioner");
const {
Expand All @@ -12,6 +12,7 @@ const TruffleError = require("@truffle/error");
const fse = require("fs-extra");
const path = require("path");
const EventEmitter = require("events");
const spawnSync = require("child_process").spawnSync;

const processInput = input => {
const inputComponents = input.trim().split(" ");
Expand Down Expand Up @@ -43,9 +44,10 @@ class Console extends EventEmitter {

this.options = options;

this.repl = options.repl || new ReplManager(options);
this.command = new Command(tasks);

this.repl = null;

this.interfaceAdapter = createInterfaceAdapter({
provider: options.provider,
networkType: options.networks[options.network].type
Expand All @@ -54,38 +56,25 @@ class Console extends EventEmitter {
provider: options.provider,
networkType: options.networks[options.network].type
});

// Bubble the ReplManager's exit event
this.repl.on("exit", () => this.emit("exit"));

// Bubble the ReplManager's reset event
this.repl.on("reset", () => this.emit("reset"));
}

start(callback) {
if (!this.repl) this.repl = new Repl(this.options);

// TODO: This should probalby be elsewhere.
// It's here to ensure the repl manager instance gets
// passed down to commands.
this.options.repl = this.repl;

start() {
try {
this.repl = repl.start({
prompt: "truffle(" + this.options.network + ")> ",
eval: this.interpret.bind(this)
});

this.interfaceAdapter.getAccounts().then(fetchedAccounts => {
const abstractions = this.provision();

this.repl.start({
prompt: "truffle(" + this.options.network + ")> ",
context: {
web3: this.web3,
interfaceAdapter: this.interfaceAdapter,
accounts: fetchedAccounts
},
interpreter: this.interpret.bind(this),
done: callback
});

this.resetContractsInConsoleContext(abstractions);
this.repl.context.web3 = this.web3;
this.repl.context.interfaceAdapter = this.interfaceAdapter;
this.repl.context.accounts = fetchedAccounts;
});
this.provision();

//want repl to exit when it receives an exit command
this.repl.on("exit", () => {
process.exit();
});
} catch (error) {
this.options.logger.log(
Expand Down Expand Up @@ -131,7 +120,6 @@ class Console extends EventEmitter {
});

this.resetContractsInConsoleContext(abstractions);

return abstractions;
}

Expand All @@ -144,15 +132,49 @@ class Console extends EventEmitter {
contextVars[abstraction.contract_name] = abstraction;
});

this.repl.setContextVars(contextVars);
// make sure the repl gets the new contracts in its context
Object.keys(contextVars || {}).forEach(key => {
this.repl.context[key] = contextVars[key];
});
}

runSpawn(inputStrings, options, callback) {
let childPath;
if (typeof BUNDLE_CONSOLE_CHILD_FILENAME !== "undefined") {
childPath = path.join(__dirname, BUNDLE_CONSOLE_CHILD_FILENAME);
} else {
childPath = path.join(__dirname, "../lib/console-child.js");
}

// stderr is piped here because we don't need to repeatedly see the parent
// errors/warnings in child process - specifically the error re: having
// multiple config files
const spawnOptions = { stdio: ["inherit", "inherit", "pipe"] };

const spawnInput = "--network " + options.network + " -- " + inputStrings;
try {
spawnSync(
"node",
["--no-deprecation", childPath, spawnInput],
spawnOptions
);

// re-provision to ensure any changes are available in the repl
this.provision();
} catch (err) {
callback(err);
}

//display prompt when child repl process is finished
this.repl.displayPrompt();
}

interpret(input, context, filename, callback) {
const processedInput = processInput(input);
if (
this.command.getCommand(processedInput, this.options.noAliases) != null
) {
return this.command.run(processedInput, this.options, error => {
return this.runSpawn(processedInput, this.options, error => {
if (error) {
// Perform error handling ourselves.
if (error instanceof TruffleError) {
Expand Down Expand Up @@ -224,6 +246,8 @@ class Console extends EventEmitter {
breakOnSigint: true,
filename: filename
};

vm.createContext(context);
return script.runInContext(context, options);
};

Expand Down
27 changes: 8 additions & 19 deletions packages/core/lib/debug/interpreter.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const selectors = require("@truffle/debugger").selectors;
const { session, solidity, trace, evm, controller, stacktrace } = selectors;

const analytics = require("../services/analytics");
const ReplManager = require("../repl");
const repl = require("repl");

const { DebugPrinter } = require("./printer");

Expand Down Expand Up @@ -40,9 +40,8 @@ class DebugInterpreter {
this.printer = new DebugPrinter(config, session);
this.txHash = txHash;
this.lastCommand = "n";

this.repl = config.repl || new ReplManager(config);
this.enabledExpressions = new Set();
this.repl = null;
}

async setOrClearBreakpoint(args, setOrClear) {
Expand Down Expand Up @@ -276,24 +275,14 @@ class DebugInterpreter {
? DebugUtils.formatPrompt(this.network, this.txHash)
: DebugUtils.formatPrompt(this.network);

this.repl.start({
prompt,
interpreter: util.callbackify(this.interpreter.bind(this)),
this.repl = repl.start({
prompt: prompt,
eval: util.callbackify(this.interpreter.bind(this)),
ignoreUndefined: true,
done: terminate
});
}

setPrompt(prompt) {
this.repl.activate.bind(this.repl)({
prompt,
context: {},
//this argument only *adds* things, so it's safe to set it to {}
ignoreUndefined: true
//set to true because it's set to true below :P
});
}

async interpreter(cmd) {
cmd = cmd.trim();
let cmdArgs, splitArgs;
Expand Down Expand Up @@ -321,7 +310,7 @@ class DebugInterpreter {

//quit if that's what we were given
if (cmd === "q") {
return await util.promisify(this.repl.stop.bind(this.repl))();
process.exit();
}

let alreadyFinished = this.session.view(trace.finishedOrUnloaded);
Expand Down Expand Up @@ -404,7 +393,7 @@ class DebugInterpreter {
if (this.session.view(selectors.session.status.success)) {
txSpinner.succeed();
//if successful, change prompt
this.setPrompt(DebugUtils.formatPrompt(this.network, cmdArgs));
this.repl.setPrompt(DebugUtils.formatPrompt(this.network, cmdArgs));
} else {
txSpinner.fail();
loadFailed = true;
Expand All @@ -427,7 +416,7 @@ class DebugInterpreter {
if (this.session.view(selectors.session.status.loaded)) {
await this.session.unload();
this.printer.print("Transaction unloaded.");
this.setPrompt(DebugUtils.formatPrompt(this.network));
this.repl.setPrompt(DebugUtils.formatPrompt(this.network));
} else {
this.printer.print("No transaction to unload.");
this.printer.print("");
Expand Down
Loading

0 comments on commit 63d2e4a

Please sign in to comment.