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

Commit 7cdc04e

Browse files
Merge branch 'develop' into refactor/implement-backoff-retry-policy-in-websocket
2 parents 926e213 + ea24eff commit 7cdc04e

File tree

11 files changed

+1198
-139
lines changed

11 files changed

+1198
-139
lines changed

src/packages/cli/package-lock.json

Lines changed: 258 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/packages/cli/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@
7070
"@types/node": "17.0.0",
7171
"chalk": "4.1.0",
7272
"ethereumjs-util": "7.1.5",
73-
"marked-terminal": "4.1.0"
73+
"marked-terminal": "4.1.0",
74+
"cli-table": "0.3.11",
75+
"@trufflesuite/ps-list": "0.0.3"
7476
}
7577
}

src/packages/cli/src/args.ts

Lines changed: 119 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ import yargs, { Options } from "yargs";
33
import {
44
DefaultFlavor,
55
FilecoinFlavorName,
6-
DefaultOptionsByName
6+
DefaultOptionsByName,
7+
FlavorName
78
} from "@ganache/flavors";
89
import {
910
Base,
1011
Definitions,
1112
YargsPrimitiveCliTypeStrings
1213
} from "@ganache/options";
13-
import { Command, Argv } from "./types";
14+
import { FlavorCommand, StartArgs, GanacheArgs } from "./types";
1415
import chalk from "chalk";
1516
import { EOL } from "os";
1617
import marked from "marked";
@@ -86,9 +87,11 @@ function processOption(
8687
// the types held within each array
8788
const { cliType } = optionObj;
8889
const array = cliType && cliType.startsWith("array:"); // e.g. array:string or array:number
89-
const type = (array
90-
? cliType.slice(6) // remove the "array:" part
91-
: cliType) as YargsPrimitiveCliTypeStrings;
90+
const type = (
91+
array
92+
? cliType.slice(6) // remove the "array:" part
93+
: cliType
94+
) as YargsPrimitiveCliTypeStrings;
9295

9396
const options: Options = {
9497
group,
@@ -127,9 +130,9 @@ function applyDefaults(
127130
const group = `${category[0].toUpperCase()}${category.slice(
128131
1
129132
)}:` as GroupType;
130-
const categoryObj = (flavorDefaults[
133+
const categoryObj = flavorDefaults[
131134
category
132-
] as unknown) as Definitions<Base.Config>;
135+
] as unknown as Definitions<Base.Config>;
133136
const state = {};
134137
for (const option in categoryObj) {
135138
const optionObj = categoryObj[option];
@@ -146,13 +149,18 @@ function applyDefaults(
146149
}
147150
}
148151

149-
export default function (version: string, isDocker: boolean) {
152+
export default function (
153+
version: string,
154+
isDocker: boolean,
155+
rawArgs = process.argv.slice(2)
156+
) {
150157
const versionUsageOutputText = chalk`{hex("${
151158
TruffleColors.porsche
152159
}").bold ${center(version)}}`;
153-
let args = yargs
154-
// disable dot-notation because yargs just can't coerce args properly...
155-
// ...on purpose! https://github.com/yargs/yargs/issues/1021#issuecomment-352324693
160+
161+
// disable dot-notation because yargs just can't coerce args properly...
162+
// ...on purpose! https://github.com/yargs/yargs/issues/1021#issuecomment-352324693
163+
yargs
156164
.parserConfiguration({ "dot-notation": false })
157165
.strict()
158166
.usage(versionUsageOutputText)
@@ -168,7 +176,7 @@ export default function (version: string, isDocker: boolean) {
168176
let flavor: keyof typeof DefaultOptionsByName;
169177
for (flavor in DefaultOptionsByName) {
170178
const flavorDefaults = DefaultOptionsByName[flavor];
171-
let command: Command;
179+
let command: FlavorCommand;
172180
let defaultPort: number;
173181
switch (flavor) {
174182
// since "ethereum" is the DefaultFlavor we don't need a `case` for it
@@ -185,7 +193,7 @@ export default function (version: string, isDocker: boolean) {
185193
defaultPort = 8545;
186194
}
187195

188-
args = args.command(
196+
yargs.command(
189197
command,
190198
chalk`Use the {bold ${flavor}} flavor of Ganache`,
191199
flavorArgs => {
@@ -219,32 +227,116 @@ export default function (version: string, isDocker: boolean) {
219227
}
220228

221229
return true;
230+
})
231+
.option("detach", {
232+
description: highlight(
233+
"Run Ganache in detached (daemon) mode." +
234+
EOL +
235+
"See `ganache instances --help` for information on managing detached instances."
236+
),
237+
type: "boolean",
238+
alias: ["D", "😈"]
222239
});
240+
},
241+
parsedArgs => {
242+
parsedArgs.action = parsedArgs.detach ? "start-detached" : "start";
223243
}
224244
);
225245
}
226246

227-
args = args
247+
yargs
248+
.command(
249+
"instances",
250+
highlight(
251+
"Manage instances of Ganache running in detached mode." +
252+
EOL +
253+
"(Ganache can be run in detached mode by providing the `--detach` flag)"
254+
),
255+
_yargs => {
256+
_yargs
257+
.command(
258+
"list",
259+
"List instances running in detached mode",
260+
_ => {},
261+
listArgs => {
262+
listArgs.action = "list";
263+
}
264+
)
265+
.command(
266+
"stop <name>",
267+
"Stop the instance specified by <name>",
268+
stopArgs => {
269+
stopArgs.positional("name", { type: "string" });
270+
},
271+
stopArgs => {
272+
stopArgs.action = "stop";
273+
}
274+
)
275+
.version(false);
276+
}
277+
)
228278
.showHelpOnFail(false, "Specify -? or --help for available options")
229279
.alias("help", "?")
230280
.wrap(wrapWidth)
231281
.version(version);
232282

233-
const parsedArgs = args.argv;
234-
const finalArgs = {
235-
flavor: parsedArgs._.length > 0 ? parsedArgs._[0] : DefaultFlavor
236-
} as Argv;
237-
for (let key in parsedArgs) {
238-
// split on the first "."
239-
const [group, option] = key.split(/\.(.+)/);
240-
// only copy namespaced/group keys
241-
if (option) {
242-
if (!finalArgs[group]) {
243-
finalArgs[group] = {};
283+
const parsedArgs = yargs.parse(rawArgs);
284+
285+
let finalArgs: GanacheArgs;
286+
if (parsedArgs.action === "stop") {
287+
finalArgs = {
288+
action: "stop",
289+
name: parsedArgs.name as string
290+
};
291+
} else if (parsedArgs.action === "list") {
292+
finalArgs = { action: "list" };
293+
} else if (
294+
parsedArgs.action === "start" ||
295+
parsedArgs.action === "start-detached"
296+
) {
297+
const action = parsedArgs.action;
298+
const flavor = (parsedArgs._.length > 0
299+
? parsedArgs._[0]
300+
: DefaultFlavor) as any as FlavorName;
301+
302+
finalArgs = {
303+
flavor,
304+
action,
305+
...(expandArgs(parsedArgs) as Omit<
306+
StartArgs<FlavorName>,
307+
"flavor" | "action"
308+
>)
309+
};
310+
} else {
311+
throw new Error(`Unknown action: ${parsedArgs.action}`);
312+
}
313+
314+
return finalArgs;
315+
}
316+
317+
/**
318+
* Expands the arguments into an object including only namespaced keys from the
319+
* `args` argument.
320+
* @param {object} args to be expanded
321+
* @returns {object} with the expanded arguments
322+
*/
323+
export function expandArgs(args: object): object {
324+
const namespacedArgs = {};
325+
326+
for (const key in args) {
327+
// ignore keys that are kebab-cased - they will be duplicated as camelCase
328+
if (key.indexOf("-") === -1) {
329+
// split on the first "."
330+
const [namespace, option] = key.split(/\.(.+)/);
331+
// only copy namespaced/group keys, and ignore keys with kebab cases
332+
if (option) {
333+
if (!namespacedArgs[namespace]) {
334+
namespacedArgs[namespace] = {};
335+
}
336+
namespacedArgs[namespace][option] = args[key];
244337
}
245-
finalArgs[group][option] = parsedArgs[key];
246338
}
247339
}
248340

249-
return finalArgs;
341+
return namespacedArgs;
250342
}

0 commit comments

Comments
 (0)