Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(neotracker): add neotracker command to neo-one build #1870

Merged
merged 1 commit into from
Nov 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 4 additions & 0 deletions packages/neo-one-cli-common-node/src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const configurationDefaults = {
neotracker: {
path: nodePath.join('.neo-one', 'neotracker'),
port: 9041,
skip: false,
},
};

Expand Down Expand Up @@ -135,6 +136,7 @@ const configurationSchema = {
properties: {
path: { type: 'string' },
port: { type: 'number', multipleOf: 1.0, minimum: 0 },
skip: { type: 'boolean' },
},
},
},
Expand Down Expand Up @@ -212,6 +214,8 @@ ${exportConfig} {
path: '${config.neotracker.path}',
// NEO•ONE will start an instance of NEO tracker using this port.
port: 9041,
// Set to true if you'd like NEO•ONE to skip starting a NEO tracker instance when running 'neo-one build'.
skip: false,
}
};
`;
Expand Down
1 change: 1 addition & 0 deletions packages/neo-one-cli-common/src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface NetworksConfiguration {
export interface NEOTrackerConfiguration {
readonly path: string;
readonly port: number;
readonly skip: boolean;
}

export interface MigrationConfiguration {
Expand Down
1 change: 1 addition & 0 deletions packages/neo-one-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"dependencies": {
"@babel/core": "^7.5.5",
"@babel/register": "^7.5.5",
"@neotracker/core": "^1.3.0",
"@neo-one/cli-common": "^2.3.0",
"@neo-one/cli-common-node": "^2.3.0",
"@neo-one/client-common": "^2.3.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ module.exports = {
neotracker: {
path: nodePath.join(tmpDir, 'neotracker'),
port: neotrackerPort,
skip: false,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ module.exports = {
neotracker: {
path: nodePath.join(tmpDir, 'neotracker'),
port: neotrackerPort,
skip: true,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ module.exports = {
neotracker: {
path: nodePath.join(tmpDir, 'neotracker'),
port: neotrackerPort,
skip: true,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ module.exports = {
neotracker: {
path: nodePath.join(tmpDir, 'neotracker'),
port: neotrackerPort,
skip: true,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ module.exports = {
neotracker: {
path: nodePath.join(tmpDir, 'neotracker'),
port: neotrackerPort,
skip: true,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ module.exports = {
neotracker: {
path: nodePath.join(tmpDir, 'neotracker'),
port: neotrackerPort,
skip: false,
},
};
25 changes: 25 additions & 0 deletions packages/neo-one-cli/src/__e2e__/cmd/start/neotracker.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import fetch from 'cross-fetch';
import { isRunning } from '../../../common';

describe('start network', () => {
it('starts a private network', async () => {
const execAsync = one.createExecAsync('ico');
execAsync('start network');
const config = await one.getProjectConfig('ico');

await one.until(async () => {
const [live, ready] = await Promise.all([
fetch(`http://localhost:${config.network.port}/live_health_check`),
fetch(`http://localhost:${config.network.port}/ready_health_check`),
]);
expect(live.ok).toEqual(true);
expect(ready.ok).toEqual(true);
});

execAsync('start neotracker');
await one.until(async () => {
const live = await isRunning(config.neotracker.port);
expect(live).toEqual(true);
});
});
});
35 changes: 35 additions & 0 deletions packages/neo-one-cli/src/__e2e__/cmd/stop/neotracker.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import fetch from 'cross-fetch';
import { isRunning } from '../../../common';

describe('start network', () => {
it('starts a private network', async () => {
const execAsync = one.createExecAsync('ico');
execAsync('start network');
const config = await one.getProjectConfig('ico');

await one.until(async () => {
const [live, ready] = await Promise.all([
fetch(`http://localhost:${config.network.port}/live_health_check`),
fetch(`http://localhost:${config.network.port}/ready_health_check`),
]);
expect(live.ok).toEqual(true);
expect(ready.ok).toEqual(true);
});

execAsync('start neotracker');

await one.until(async () => {
const live = await isRunning(config.neotracker.port);
expect(live).toEqual(true);
});

await one.createExec('ico')('stop neotracker');
let success = false;
try {
success = await isRunning(config.neotracker.port);
} catch {
// do nothing
}
expect(success).toEqual(false);
});
});
10 changes: 10 additions & 0 deletions packages/neo-one-cli/src/build/createTasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { findContracts } from './findContracts';
import { generateCode } from './generateCode';
import { generateCommonCode } from './generateCommonCode';
import { setupWallets } from './setupWallets';
import { startNeotracker } from './startNeotracker';
import { startNetwork } from './startNetwork';

export const createTasks = (cmd: Command, config: Configuration, reset: boolean) =>
Expand Down Expand Up @@ -95,4 +96,13 @@ export const createTasks = (cmd: Command, config: Configuration, reset: boolean)
await generateCommonCode(config, ctx.contracts, networks, ctx.sourceMaps);
},
},
{
title: 'Start NEO tracker instance',
task: async (_ctx, task) => {
if (config.neotracker.skip) {
task.skip('NEO tracker instance skipped.');
}
await startNeotracker(cmd, config, reset);
},
},
]);
44 changes: 44 additions & 0 deletions packages/neo-one-cli/src/build/startNeotracker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Configuration } from '@neo-one/cli-common';
import execa from 'execa';
import { isRunning } from '../common';
import { Command } from '../types';
import { findKillProcess } from '../utils';

export const startNeotracker = async (cmd: Command, config: Configuration, reset: boolean) => {
const args = cmd.args.concat(['start', 'neotracker']);
if (reset) {
await findKillProcess('neotracker', config);
}

const proc = execa(cmd.bin, reset ? args.concat(['--reset']) : args, {
cleanup: false,
detached: true,
stdio: 'ignore',
});
proc.unref();

const start = Date.now();
const timeoutMS = 30 * 1000;
let ready = false;
if (reset) {
await new Promise((resolve) => setTimeout(resolve, 3000));
}
// tslint:disable-next-line no-loop-statement
while (Date.now() - start < timeoutMS) {
try {
const response = await isRunning(config.neotracker.port);
if (response) {
ready = true;
break;
}
} catch {
// do nothing
}

await new Promise((resolve) => setTimeout(resolve, 500));
}

if (!ready) {
throw new Error('Neotracker is not ready.');
}
};
3 changes: 2 additions & 1 deletion packages/neo-one-cli/src/cmd/start/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import yargs from 'yargs';
import * as neotracker from './neotracker';
import * as network from './network';

export const command = 'start';
export const describe = 'Start NEO•ONE services.';
export const builder = (yargsBuilder: typeof yargs) => yargsBuilder.command(network);
export const builder = (yargsBuilder: typeof yargs) => yargsBuilder.command(network).command(neotracker);
55 changes: 55 additions & 0 deletions packages/neo-one-cli/src/cmd/start/neotracker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { cliLogger } from '@neo-one/logger';
import { Yarguments } from '@neo-one/utils-node';
import execa from 'execa';
import * as nodePath from 'path';
import yargs from 'yargs';
import { isRunning, start } from '../../common';
import { findKillProcess } from '../../utils';
import { writePidFile } from './writePidFile';

export const command = 'neotracker';
export const describe = 'Start a NEO tracker instance using the project configuration.';
export const builder = (yargsBuilder: typeof yargs) =>
yargsBuilder
.boolean('reset')
.describe('reset', 'Reset the NEO tracker database.')
.default('reset', false);
export const handler = (argv: Yarguments<ReturnType<typeof builder>>) => {
start(async (_cmd, config) => {
const running = await isRunning(config.neotracker.port);
if (running) {
if (argv.reset) {
await findKillProcess('neotracker', config);
} else {
cliLogger.info('NEO tracker is already running');

return undefined;
}
}

const args = [
'neotracker',
'--port',
`${config.neotracker.port}`,
'--nodeRpcUrl',
`http://localhost:${config.network.port}/rpc`,
'--dbFileName',
nodePath.resolve(config.neotracker.path, 'db.sqlite'),
];
const proc = execa(
nodePath.resolve(require.resolve('@neotracker/core/bin'), '../', 'neotracker'),
argv.reset ? args.concat(['--resetDB']) : args,
{
cleanup: false,
stdio: 'ignore',
},
);
proc.unref();

await writePidFile('neotracker', proc, config);

return async () => {
proc.kill();
};
});
};
50 changes: 3 additions & 47 deletions packages/neo-one-cli/src/cmd/start/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,9 @@ import { common, crypto } from '@neo-one/client-common';
import { cliLogger } from '@neo-one/logger';
import { FullNode } from '@neo-one/node';
import { createMain } from '@neo-one/node-neo-settings';
import * as fs from 'fs-extra';
import net from 'net';
import * as nodePath from 'path';
import yargs from 'yargs';
import { getNetworkProcessIDFile, getPrimaryKeys, start } from '../../common';

async function isRunning(port: number) {
let resolve: (running: boolean) => void;
let reject: (err: Error) => void;
// tslint:disable-next-line promise-must-complete
const promise = new Promise<boolean>((resolver, rejector) => {
resolve = resolver;
reject = rejector;
});

const cleanup = () => {
client.removeAllListeners('connect');
client.removeAllListeners('error');
client.end();
client.destroy();
client.unref();
};

const onConnect = () => {
resolve(true);
cleanup();
};

const onError = (error: Error) => {
// tslint:disable-next-line no-any
if ((error as any).code !== 'ECONNREFUSED') {
reject(error);
} else {
resolve(false);
}
cleanup();
};

const client = new net.Socket();
client.once('connect', onConnect);
client.once('error', onError);
client.connect({ port, host: '127.0.0.1' });

return promise;
}
import { getPrimaryKeys, isRunning, start } from '../../common';
import { writePidFile } from './writePidFile';

export const command = 'network';
export const describe = 'Start a NEO•ONE development network using the project configuration.';
Expand Down Expand Up @@ -88,9 +46,7 @@ export const handler = () => {

await fullNode.start();

const pidFile = getNetworkProcessIDFile(config);
await fs.ensureDir(nodePath.dirname(pidFile));
await fs.writeFile(pidFile, process.pid);
await writePidFile('network', process, config);

return async () => {
await fullNode.stop();
Expand Down
15 changes: 15 additions & 0 deletions packages/neo-one-cli/src/cmd/start/writePidFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Configuration } from '@neo-one/cli-common';
import { ExecaChildProcess } from 'execa';
import * as fs from 'fs-extra';
import * as nodePath from 'path';
import { getProcessIDFile } from '../../common';

export const writePidFile = async (
name: 'network' | 'neotracker',
proc: NodeJS.Process | ExecaChildProcess,
config: Configuration,
) => {
const pidFile = getProcessIDFile(config, name);
await fs.ensureDir(nodePath.dirname(pidFile));
await fs.writeFile(pidFile, proc.pid);
};
3 changes: 2 additions & 1 deletion packages/neo-one-cli/src/cmd/stop/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import yargs from 'yargs';
import * as neotracker from './neotracker';
import * as network from './network';

export const command = 'stop';
export const describe = 'Stop NEO•ONE services.';
export const builder = (yargsBuilder: typeof yargs) => yargsBuilder.command(network);
export const builder = (yargsBuilder: typeof yargs) => yargsBuilder.command(network).command(neotracker);
12 changes: 12 additions & 0 deletions packages/neo-one-cli/src/cmd/stop/neotracker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import yargs from 'yargs';
import { start } from '../../common';
import { findKillProcess } from '../../utils';

export const command = 'neotracker';
export const describe = 'Stops the local neotracker instance.';
export const builder = (yargsBuilder: typeof yargs) => yargsBuilder;
export const handler = () => {
start(async (_cmd, config) => {
await findKillProcess('neotracker', config);
});
};
Loading