diff --git a/bin/cli.ts b/bin/cli.ts index 8d0d611..e52b4bf 100755 --- a/bin/cli.ts +++ b/bin/cli.ts @@ -7,15 +7,25 @@ const { fromName: serverFromName } = require('../lib/net/server') import Node from '../lib/node' import { Server as RPCServer } from 'jayson' import { Config } from '../lib/config' +import {Logger} from 'winston' const RPCManager = require('../lib/rpc') const level = require('level') const os = require('os') const path = require('path') const fs = require('fs-extra') +const yargs = require('yargs') + +type UserConfig = { + logger? : Logger +} const networks = Object.entries(chains.names) -const args = require('yargs') +const args = yargs .options({ + config: { + describe: 'Path to ethereumjs.config.js', + default: undefined, + }, network: { describe: `Network`, choices: networks.map((n) => n[1]), @@ -80,7 +90,18 @@ const args = require('yargs') }, }) .locale('en_EN').argv -const logger = getLogger({ loglevel: args.loglevel }) + +const config: UserConfig = {} + +if (args.config) { + const userConfig: UserConfig = require( + path.resolve(process.cwd(), args.config) + ) + + Object.assign(config, userConfig) +} + +const logger = config.logger ?? getLogger({ loglevel: args.loglevel }) async function runNode(options: any) { logger.info('Initializing Ethereumjs client...') @@ -153,21 +174,20 @@ async function run() { minPeers: args.minPeers, maxPeers: args.maxPeers, } - const node = await runNode(options) - const server = args.rpc ? runRpcServer(node, options) : null - process.once('SIGINT', async () => { - process.once('SIGINT', () => { - logger.info('Force shutdown. Exit immediately.') - process.exit(1) - }) + let node: Node|null + let server: RPCServer|null + process.on('SIGINT', async () => { logger.info('Caught interrupt signal. Shutting down...') if (server) server.http().close() - await node.stop() + if (node) await node.stop() logger.info('Exiting.') - process.exit() + process.exit(0) }) + + node = await runNode(options) + server = args.rpc ? runRpcServer(node, options) : null } run().catch((err) => logger.error(err)) diff --git a/test/cli/cli.spec.ts b/test/cli/cli.spec.ts new file mode 100644 index 0000000..9786984 --- /dev/null +++ b/test/cli/cli.spec.ts @@ -0,0 +1,62 @@ +import path from 'path' +import { spawn } from 'child_process' + +import tape from 'tape' + +tape('[CLI]', (t) => { + t.test('should handle SIGINT', { timeout: 160000 }, (t) => { + t.plan(1) + const file = require.resolve('../../dist/bin/cli.js') + const child = spawn(process.execPath, [ + file, + '--config', + path.join(__dirname, '/fixtures/ethereumjs.config.js') + ], { + stdio: ['pipe', 'pipe', 'pipe', 'ipc'], + }) + + const timeout = setTimeout(() => { + child.kill('SIGINT') + }, 120000) + + const end = () => { + clearTimeout(timeout) + child.kill('SIGINT') + t.end() + } + + function onServiceStarted([level, message]: [string, string]) { + if (level === 'info') { + if (message === 'Started eth service.') { + child.removeListener('message', onServiceStarted) + child.on('message', onFirstSigintSent) + child.kill('SIGINT') + } + } + } + + function onFirstSigintSent([level, message]: [string, string]) { + if (level === 'info') { + if (message === 'Exiting.') { + child.removeListener('message', onFirstSigintSent) + t.pass('Client exited') + clearTimeout(timeout) + } + } + } + + child.on('message', onServiceStarted) + child.on('message', ([level, message]: [string, string]) => console.log(level, message)) + + child.on('error', (error) => { + t.fail(`Error: ${error}`) + }) + + child.on('close', (code, signal) => { + if (code !== 0) { + t.fail(`child process exited with code ${code}, signal ${signal}`) + end() + } + }) + }) +}) diff --git a/test/cli/fixtures/ethereumjs.config.js b/test/cli/fixtures/ethereumjs.config.js new file mode 100644 index 0000000..b3b99d8 --- /dev/null +++ b/test/cli/fixtures/ethereumjs.config.js @@ -0,0 +1,12 @@ +function send(level, message) { + process.send([level, message]) +} + +module.exports = { + logger: { + warn: send.bind(null, 'warn'), + info: send.bind(null, 'info'), + debug: send.bind(null, 'debug'), + error: send.bind(null, 'error'), + } +}