Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
fcc4228
feat: allow users to specify a option, which causes Ganache to start…
jeffsmale90 Aug 22, 2022
7a787f2
Stream stderr from the child to the parent
jeffsmale90 Aug 23, 2022
301c500
Tidy up of --detach argument parsing
jeffsmale90 Aug 23, 2022
7dec4c0
Improve detach configuration
jeffsmale90 Aug 24, 2022
9ed54db
Add tests for parsing detach arguments. Change default value to false…
jeffsmale90 Aug 24, 2022
20579ae
Make parent aware of detached instances, and allow stopping them via cli
jeffsmale90 Sep 4, 2022
1da7911
improve args tests, remove placeholder
jeffsmale90 Sep 6, 2022
9cc5b59
Implement , and allow different actions to be executed via the CLI
jeffsmale90 Sep 7, 2022
e5d7e60
Clean up orphaned instance process files. uses a terrible hack to fet…
jeffsmale90 Sep 7, 2022
295b1a3
Implement `instances` command with subcommands. Clean up orphaned ins…
jeffsmale90 Sep 7, 2022
75ffbb0
Tidy up args types and parsing
jeffsmale90 Sep 12, 2022
17732d7
Refactor, add flavor to instance data, and to list output. Change ins…
jeffsmale90 Sep 12, 2022
dea1ddb
Fix createFlatChildArgs import in args.test.ts
jeffsmale90 Sep 20, 2022
07a4b03
Merge branch 'develop' into feat/detach-mode
jeffsmale90 Sep 20, 2022
a74d0a3
Update package-lock.json files - required for new deps
jeffsmale90 Sep 20, 2022
1eaea51
Fix module resolution
jeffsmale90 Sep 20, 2022
cba7589
Tidy ups and refactors
jeffsmale90 Sep 20, 2022
c39cae6
Tidy up arg parsing
jeffsmale90 Sep 20, 2022
bc61cb1
Revert package-lock.json files
jeffsmale90 Sep 20, 2022
f5650f2
Fix miss-typed action name
jeffsmale90 Sep 20, 2022
90a2305
Remove dependency on ps-list, move data files to Ganache/instances, i…
jeffsmale90 Sep 22, 2022
c1e5f43
Remove kebab-case names when expanding arguments. Add tests for expan…
jeffsmale90 Sep 22, 2022
757220b
Reintroduce ps-list, only list instances where the process co
jeffsmale90 Sep 23, 2022
c432c35
Move createFlatChildArgs() to detach.ts, add version to list, ensure …
jeffsmale90 Sep 23, 2022
2dec992
Create instances directory recursively, because now it's
jeffsmale90 Sep 23, 2022
773f21a
Tidy ups from code review, add section on Detached mode to README.
jeffsmale90 Sep 27, 2022
5fb72d1
Fix incorrect command name in README.md
jeffsmale90 Oct 10, 2022
9bf6064
Merge branch 'develop' into feat/detach-mode
jeffsmale90 Oct 10, 2022
d27e6bc
Update to use @trufflesuite/ps-list instead of ps-list
jeffsmale90 Nov 6, 2022
eafc679
If no instances are found, using , print a friendly message instead o…
jeffsmale90 Nov 6, 2022
e6ae996
Revert ganache package version to match develop
jeffsmale90 Nov 6, 2022
eb14694
Merge branch 'develop' into feat/detach-mode
jeffsmale90 Nov 6, 2022
876b972
Bump @trufflesuite/ps-list to include support for node 12
jeffsmale90 Nov 6, 2022
208b149
upgrade @trufflesuite/ps-list to support nodejs < 12.17.0
jeffsmale90 Nov 7, 2022
5f4b80d
Merge branch 'develop' into feat/detach-mode
jeffsmale90 Nov 9, 2022
6b61cba
Improve wording of `--detach` description in README.md
jeffsmale90 Nov 13, 2022
7f36f1f
Switched out for (existing dependency via marked-terminal)
jeffsmale90 Nov 13, 2022
ac5aba0
Move instance list sorting to the consumer that needs the list sorted
jeffsmale90 Nov 15, 2022
bfc5cf9
use detached instance friendly name for instance file (instead of pid)
jeffsmale90 Nov 15, 2022
1a52d4c
pass original process.args to startDetachedInstance, calculate expect…
jeffsmale90 Nov 15, 2022
7a4c256
Fix 'cmd' argument for win32 platform
jeffsmale90 Nov 15, 2022
67d11fc
Add attribution for duration formatter
jeffsmale90 Nov 15, 2022
683c201
Change all detach mode file access to async
jeffsmale90 Nov 15, 2022
60ae164
Adds warning messages if instances removed
jeffsmale90 Nov 15, 2022
1726ec9
Add PID to warning message.
jeffsmale90 Nov 15, 2022
0811874
Remove unnecessary detach.test.ts
jeffsmale90 Nov 15, 2022
485ff64
improvements / refactor as per pr review.
jeffsmale90 Nov 16, 2022
1701140
Don't check for existence of the folder each time - just catch the Er…
jeffsmale90 Nov 16, 2022
78a9d0c
Accidentally recreated the instance name twice
jeffsmale90 Nov 16, 2022
04719a6
Move comment to relevant code
jeffsmale90 Nov 16, 2022
3ea9968
Improvements to detach re PR
jeffsmale90 Nov 16, 2022
6a7f3b3
Revert package-lock.json
jeffsmale90 Nov 16, 2022
c5c0c31
_Actually_ fix package-lock.json this time
jeffsmale90 Nov 16, 2022
bc8d269
Remove nyc dev-dependency from cli/package.json - correct, but irrele…
jeffsmale90 Nov 16, 2022
aa72840
Strip the detach argument correctly before passing arguments to child…
jeffsmale90 Nov 16, 2022
4e8f933
Add tests for duration formatting - move implementation into detach.ts
jeffsmale90 Nov 18, 2022
6b49cbb
Rename formatDuration to formatUptime, add --detach example to README…
jeffsmale90 Nov 18, 2022
0435951
Revert shrinkwrap :/
jeffsmale90 Nov 18, 2022
959c6dc
Pin cli-table dependency to latest release
jeffsmale90 Nov 18, 2022
dfb8b72
Fix failing test
jeffsmale90 Nov 18, 2022
c495cc9
Improve duration formatting, improve readme
jeffsmale90 Nov 22, 2022
c16cb20
Annoyingly, args parsing is complicated. Don't try to strip out the d…
jeffsmale90 Nov 22, 2022
b272237
Instance files now have .json extension. Remove any files / folders f…
jeffsmale90 Nov 22, 2022
c85d022
Remove unused import
jeffsmale90 Nov 22, 2022
359cb6b
Handle errors betterer
jeffsmale90 Nov 23, 2022
f94442f
Shorten duration formatting, combine host and port, right align PID i…
jeffsmale90 Nov 23, 2022
26af55e
RH align Uptime
jeffsmale90 Nov 23, 2022
d717d3f
remove unnecessary type assertion, remove datapath suffix
jeffsmale90 Nov 23, 2022
393702c
Use unlink instead of rm, because node 12
jeffsmale90 Nov 25, 2022
0507aa4
Support ndoe 12 correctly for removal of files and directories
jeffsmale90 Nov 28, 2022
2d92da1
Make PowerShell example consistent with non-Windows example
jeffsmale90 Dec 1, 2022
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
18 changes: 13 additions & 5 deletions src/packages/cli/src/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,11 @@ function processOption(
// the types held within each array
const { cliType } = optionObj;
const array = cliType && cliType.startsWith("array:"); // e.g. array:string or array:number
const type = (array
? cliType.slice(6) // remove the "array:" part
: cliType) as YargsPrimitiveCliTypeStrings;
const type = (
array
? cliType.slice(6) // remove the "array:" part
: cliType
) as YargsPrimitiveCliTypeStrings;

const options: Options = {
group,
Expand Down Expand Up @@ -127,9 +129,9 @@ function applyDefaults(
const group = `${category[0].toUpperCase()}${category.slice(
1
)}:` as GroupType;
const categoryObj = (flavorDefaults[
const categoryObj = flavorDefaults[
category
] as unknown) as Definitions<Base.Config>;
] as unknown as Definitions<Base.Config>;
const state = {};
for (const option in categoryObj) {
const optionObj = categoryObj[option];
Expand Down Expand Up @@ -219,6 +221,12 @@ export default function (version: string, isDocker: boolean) {
}

return true;
})
.option("cli.detach", {
Comment thread
jeffsmale90 marked this conversation as resolved.
Outdated
description: "Run Ganache in detached mode.",
type: "boolean",
alias: ["D", "detach"],
default: () => false
});
}
);
Expand Down
229 changes: 140 additions & 89 deletions src/packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import initializeEthereum from "./initialize/ethereum";
import initializeFilecoin from "./initialize/filecoin";
import type { FilecoinProvider } from "@ganache/filecoin";
import type { EthereumProvider } from "@ganache/ethereum";
import { fork } from "child_process";

const logAndForceExit = (messages: any[], exitCode = 0) => {
// https://nodejs.org/api/process.html#process_process_exit_code
Expand Down Expand Up @@ -39,108 +40,158 @@ const isDocker =

const argv = args(detailedVersion, isDocker);

const flavor = argv.flavor;
if (argv.cli.detach) {
// Start Ganache in a child process, and allow it to run in the background.
// The only output to stdout should be the PID of the child process.
const module = process.argv[1];
const args = process.argv.slice(2);
args.splice(args.indexOf("--detach"), 1);

const child = fork(module, args, {
stdio: ["ignore", "ignore", "pipe", "ipc"],
detached: true
});

// Any messages output to stderr by the child process (before the `ready`
// event is emitted) will be streamed to stderr on the parent.
child.stderr.pipe(process.stderr);

child.on("message", message => {
if (message === "ready") {
console.log(child.pid);

// Destroy the ReadableStream exposed by the child process, to allow the
// parent to exit gracefully.
child.stderr.destroy();
child.unref();
child.disconnect();
}
});

const cliSettings = argv.server;
child.on("error", err => {
// This only happens if there's an error starting the child process, not if
// the application throws within the child process.
console.error(
`An error occurred starting Ganache in detached mode: ${err}`
);
process.exit(1);
Comment thread
jeffsmale90 marked this conversation as resolved.
Outdated
});

console.log(detailedVersion);
child.on("exit", (code: number) => {
// If the child process exits before the parent, something has gone wrong,
// so let the user know (even if the exit code is 0).
console.error(`The child process exited with exit code ${code}`);
process.exit(code);
Comment thread
jeffsmale90 marked this conversation as resolved.
Outdated
});
Comment thread
jeffsmale90 marked this conversation as resolved.
Outdated
} else {
const flavor = argv.flavor;

let server: ReturnType<typeof Ganache.server>;
try {
server = Ganache.server(argv);
} catch (error: any) {
console.error(error.message);
process.exit(1);
}
const cliSettings = argv.server;

let started = false;
process.on("uncaughtException", function (e) {
if (started) {
logAndForceExit([e], 1);
} else {
logAndForceExit([e.stack], 1);
}
});
console.log(detailedVersion);

let receivedShutdownSignal: boolean = false;
const handleSignal = async (signal: NodeJS.Signals) => {
console.log(`\nReceived shutdown signal: ${signal}`);
closeHandler();
};
const closeHandler = async () => {
let server: ReturnType<typeof Ganache.server>;
try {
// graceful shutdown
switch (server.status) {
case ServerStatus.opening:
receivedShutdownSignal = true;
console.log("Server is currently starting; waiting…");
return;
case ServerStatus.open:
console.log("Shutting down…");
await server.close();
console.log("Server has been shut down");
break;
}
// don't just call `process.exit()` here, as we don't want to hide shutdown
// errors behind a forced shutdown. Note: `process.exitCode` doesn't do
// anything other than act as a place to anchor this comment :-)
process.exitCode = 0;
} catch (err: any) {
logAndForceExit(
[
"\nReceived an error while attempting to shut down the server: ",
err.stack || err
],
1
);
server = Ganache.server(argv);
} catch (error: any) {
console.error(error.message);
process.exit(1);
}
};

// See http://stackoverflow.com/questions/10021373/what-is-the-windows-equivalent-of-process-onsigint-in-node-js
if (process.platform === "win32") {
const rl = (require("readline") as typeof Readline)
.createInterface({
input: process.stdin,
output: process.stdout
})
.on("SIGINT", () => {
// we must "close" the RL interface otherwise the process will think we
// are still listening
// https://nodejs.org/api/readline.html#readline_event_sigint
rl.close();
handleSignal("SIGINT");
});
}

process.on("SIGINT", handleSignal);
process.on("SIGTERM", handleSignal);
process.on("SIGHUP", handleSignal);
let started = false;
process.on("uncaughtException", function (e) {
if (started) {
logAndForceExit([e], 1);
} else {
logAndForceExit([e.stack], 1);
}
});

async function startGanache(err: Error) {
if (err) {
console.log(err);
process.exitCode = 1;
return;
} else if (receivedShutdownSignal) {
let receivedShutdownSignal: boolean = false;
const handleSignal = async (signal: NodeJS.Signals) => {
console.log(`\nReceived shutdown signal: ${signal}`);
closeHandler();
return;
};
const closeHandler = async () => {
try {
// graceful shutdown
switch (server.status) {
case ServerStatus.opening:
receivedShutdownSignal = true;
console.log("Server is currently starting; waiting…");
return;
case ServerStatus.open:
console.log("Shutting down…");
await server.close();
console.log("Server has been shut down");
break;
}
// don't just call `process.exit()` here, as we don't want to hide shutdown
// errors behind a forced shutdown. Note: `process.exitCode` doesn't do
// anything other than act as a place to anchor this comment :-)
process.exitCode = 0;
} catch (err: any) {
logAndForceExit(
[
"\nReceived an error while attempting to shut down the server: ",
err.stack || err
],
1
);
}
};

// See http://stackoverflow.com/questions/10021373/what-is-the-windows-equivalent-of-process-onsigint-in-node-js
if (process.platform === "win32") {
const rl = (require("readline") as typeof Readline)
.createInterface({
input: process.stdin,
output: process.stdout
})
.on("SIGINT", () => {
// we must "close" the RL interface otherwise the process will think we
// are still listening
// https://nodejs.org/api/readline.html#readline_event_sigint
rl.close();
handleSignal("SIGINT");
});
}
started = true;

switch (flavor) {
case FilecoinFlavorName: {
await initializeFilecoin(
server.provider as FilecoinProvider,
cliSettings
);
break;
process.on("SIGINT", handleSignal);
process.on("SIGTERM", handleSignal);
process.on("SIGHUP", handleSignal);

async function startGanache(err: Error) {
if (err) {
console.error(err);
process.exitCode = 1;
return;
} else if (receivedShutdownSignal) {
closeHandler();
return;
}
case EthereumFlavorName:
default: {
initializeEthereum(server.provider as EthereumProvider, cliSettings);
break;
started = true;
switch (flavor) {
case FilecoinFlavorName: {
await initializeFilecoin(
server.provider as FilecoinProvider,
cliSettings
);
break;
}
case EthereumFlavorName:
default: {
initializeEthereum(server.provider as EthereumProvider, cliSettings);
break;
}
}

// in "detach" mode, the parent will wait until the "ready" message is
// received before disconnecting from the child process.
if (process.send) {
process.send("ready");
}
}
console.log("Starting RPC server");
server.listen(cliSettings.port, cliSettings.host, startGanache);
}
console.log("Starting RPC server");
server.listen(cliSettings.port, cliSettings.host, startGanache);
12 changes: 9 additions & 3 deletions src/packages/cli/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { DefaultFlavor, FlavorName } from "@ganache/flavors";
import { ServerOptions } from "@ganache/core";

type CliOptions = {
type CliServerOptions = {
host: string;
port: number;
};

type CliOptions = {
detach: boolean;
};

export type Argv = ServerOptions<FlavorName> & {
_: [FlavorName];
server: CliOptions;
server: CliServerOptions;
cli: CliOptions;
};

export type CliSettings = { host: string; port: number };
export type CliSettings = CliServerOptions;

export type Command = FlavorName | ["$0", typeof DefaultFlavor];