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

Run each console command in its own child process #3319

Merged
merged 16 commits into from
Sep 10, 2020
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
3a34db1
Add proper context handling to ensure artifacts available in child pr…
fainashalts Aug 26, 2020
67a0ff5
Change stderr from parent to pipe because we don't need to see the sa…
fainashalts Aug 26, 2020
66f81a6
Fix context setting to ensure changes within repl are picked up by ch…
fainashalts Aug 26, 2020
f0ee0f6
Revert "Revert "Run each console command in its own child process""
fainashalts Aug 27, 2020
0750361
fix yargs to properly pass network
fainashalts Aug 27, 2020
f711427
Remove console logs from earlier debugging
fainashalts Aug 28, 2020
217192b
Replace setContextVars fx to use ES6 spread operators
fainashalts Aug 28, 2020
2186ab0
Rework resetContractsInConsoleContext to preserve existing context vars
fainashalts Aug 28, 2020
bab017a
Remove spread operator use to ensure JS scripts persist between comm…
fainashalts Aug 28, 2020
6f13f08
Remove this.repl check and move provisioning in start() to after repl…
fainashalts Aug 28, 2020
029d6b5
Move repl.start outside of fetching accounts, move exit listener to s…
fainashalts Aug 28, 2020
dc5bc5f
Merge pull request #3317 from trufflesuite/set-context-vars
fainashalts Sep 1, 2020
be9b260
Split code shared by cli.js and console-child.js out into its own bundle
eggplantzzz Sep 9, 2020
eb8112c
Add conditional logic to either use the raw JS or the bundles dependi…
eggplantzzz Sep 9, 2020
334b42d
Merge pull request #3351 from trufflesuite/bundle-split
fainashalts Sep 9, 2020
b5995da
Merge branch 'develop' into revert-3314-revert-3255-spawn-repl
fainashalts Sep 10, 2020
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
4 changes: 2 additions & 2 deletions packages/core/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ 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();
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
46 changes: 46 additions & 0 deletions packages/core/lib/console-child.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
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 });
}
};

const command = new Command(require("../lib/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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should rewrite this method to not take a callback. This does not hold up this PR from being merged, I just wanted to note that here.

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
32 changes: 9 additions & 23 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 } = 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 All @@ -304,10 +293,7 @@ class DebugInterpreter {
}

//split arguments for commands that want that; split on runs of spaces
splitArgs = cmd
.trim()
.split(/ +/)
.slice(1);
splitArgs = cmd.trim().split(/ +/).slice(1);
debug("splitArgs %O", splitArgs);

//warning: this bit *alters* cmd!
Expand All @@ -324,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();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also eventually set the exit code here, no? Again, I don't think this should hold this PR up.

}

let alreadyFinished = this.session.view(trace.finishedOrUnloaded);
Expand Down Expand Up @@ -407,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 @@ -430,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