@graphprotocol/graph-cli 0.40.0 → 0.40.1-alpha-20230216170945-963badb

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,77 +7,30 @@ const child_process_1 = require("child_process");
7
7
  const http_1 = __importDefault(require("http"));
8
8
  const net_1 = __importDefault(require("net"));
9
9
  const path_1 = __importDefault(require("path"));
10
- const chalk_1 = __importDefault(require("chalk"));
10
+ const core_1 = require("@oclif/core");
11
11
  const docker_compose_1 = __importDefault(require("docker-compose"));
12
+ const gluegun_1 = require("gluegun");
12
13
  const strip_ansi_1 = __importDefault(require("strip-ansi"));
13
14
  const tmp_promise_1 = __importDefault(require("tmp-promise"));
14
- const gluegun_1 = require("../command-helpers/gluegun");
15
15
  const spinner_1 = require("../command-helpers/spinner");
16
16
  // Clean up temporary files even when an uncaught exception occurs
17
17
  tmp_promise_1.default.setGracefulCleanup();
18
- const HELP = `
19
- ${chalk_1.default.bold('graph local')} [options] ${chalk_1.default.bold('<local-command>')}
20
-
21
- Options:
22
-
23
- -h, --help Show usage information
24
- --node-logs Print the Graph Node logs (optional)
25
- --ethereum-logs Print the Ethereum logs (optional)
26
- --compose-file <file> Custom Docker Compose file for additional services (optional)
27
- --node-image <image> Custom Graph Node image to test against (default: graphprotocol/graph-node:latest)
28
- --standalone-node <cmd> Use a standalone Graph Node outside Docker Compose (optional)
29
- --standalone-node-args Custom arguments to be passed to the standalone Graph Node (optional)
30
- --skip-wait-for-ipfs Don't wait for IPFS to be up at localhost:15001 (optional)
31
- --skip-wait-for-ethereum Don't wait for Ethereum to be up at localhost:18545 (optional)
32
- --skip-wait-for-postgres Don't wait for Postgres to be up at localhost:15432 (optional)
33
- --timeout Time to wait for service containers. (optional, defaults to 120000 milliseconds)
34
- `;
35
- exports.default = {
36
- description: 'Runs local tests against a Graph Node environment (using Ganache by default)',
37
- run: async (toolbox) => {
38
- // Obtain tools
39
- const { filesystem, print } = toolbox;
40
- // Parse CLI parameters
41
- let { composeFile, ethereumLogs, h, help, nodeImage, nodeLogs, skipWaitForEthereum, skipWaitForIpfs, skipWaitForPostgres, standaloneNode, standaloneNodeArgs, timeout, } = toolbox.parameters.options;
42
- // Support both short and long option variants
43
- help || (help = h);
44
- // Extract test command
45
- const params = (0, gluegun_1.fixParameters)(toolbox.parameters, {
46
- ethereumLogs,
47
- h,
48
- help,
49
- nodeLogs,
50
- skipWaitForEthereum,
51
- skipWaitForIpfs,
52
- skipWaitForPostgres,
53
- });
54
- // Show help text if requested
55
- if (help) {
56
- print.info(HELP);
57
- return;
58
- }
59
- if (params.length == 0) {
60
- print.error(`Test command not provided as the last argument`);
61
- process.exitCode = 1;
62
- return;
63
- }
64
- const testCommand = params[0];
18
+ class LocalCommand extends core_1.Command {
19
+ async run() {
20
+ const { args: { 'local-command': testCommand }, flags: { 'compose-file': composeFileFlag, 'ethereum-logs': ethereumLogsFlag, 'node-image': nodeImage, 'node-logs': nodeLogsFlag, 'skip-wait-for-etherium': skipWaitForEthereum, 'skip-wait-for-ipfs': skipWaitForIpfs, 'skip-wait-for-postgres': skipWaitForPostgres, 'standalone-node': standaloneNode, 'standalone-node-args': standaloneNodeArgs, timeout, }, } = await this.parse(LocalCommand);
65
21
  // Obtain the Docker Compose file for services that the tests run against
66
- composeFile || (composeFile = path_1.default.join(__dirname, '..', '..', 'resources', 'test', standaloneNode ? 'docker-compose-standalone-node.yml' : 'docker-compose.yml'));
67
- // parse timeout. Defaults to 120 seconds
68
- timeout = Math.abs(parseInt(timeout)) || 120000;
69
- if (!filesystem.exists(composeFile)) {
70
- print.error(`Docker Compose file \`${composeFile}\` not found`);
71
- process.exitCode = 1;
72
- return;
22
+ const composeFile = composeFileFlag ||
23
+ path_1.default.join(__dirname, '..', '..', 'resources', 'test', standaloneNode ? 'docker-compose-standalone-node.yml' : 'docker-compose.yml');
24
+ if (!gluegun_1.filesystem.exists(composeFile)) {
25
+ this.error(`Docker Compose file "${composeFile}" not found`, { exit: 1 });
73
26
  }
74
27
  // Create temporary directory to operate in
75
28
  const { path: tempdir } = await tmp_promise_1.default.dir({ prefix: 'graph-test', unsafeCleanup: true });
76
29
  try {
77
- await configureTestEnvironment(toolbox, tempdir, composeFile, nodeImage);
30
+ await configureTestEnvironment(tempdir, composeFile, nodeImage);
78
31
  }
79
32
  catch (e) {
80
- process.exitCode = 1;
33
+ this.exit(1);
81
34
  return;
82
35
  }
83
36
  // Bring up test environment
@@ -85,9 +38,7 @@ exports.default = {
85
38
  await startTestEnvironment(tempdir);
86
39
  }
87
40
  catch (e) {
88
- print.error(e);
89
- process.exitCode = 1;
90
- return;
41
+ this.error(e, { exit: 1 });
91
42
  }
92
43
  // Wait for test environment to come up
93
44
  try {
@@ -100,7 +51,7 @@ exports.default = {
100
51
  }
101
52
  catch (e) {
102
53
  await stopTestEnvironment(tempdir);
103
- process.exitCode = 1;
54
+ this.exit(1);
104
55
  return;
105
56
  }
106
57
  // Bring up Graph Node separately, if a standalone node is used
@@ -111,14 +62,13 @@ exports.default = {
111
62
  nodeProcess = await startGraphNode(standaloneNode, standaloneNodeArgs, nodeOutputChunks);
112
63
  }
113
64
  catch (e) {
114
- toolbox.print.error('');
115
- toolbox.print.error(' Graph Node');
116
- toolbox.print.error(' ----------');
117
- toolbox.print.error(indent(' ', Buffer.concat(nodeOutputChunks).toString('utf-8')));
118
- toolbox.print.error('');
119
65
  await stopTestEnvironment(tempdir);
120
- process.exitCode = 1;
121
- return;
66
+ let errorMessage = '\n';
67
+ errorMessage += ' Graph Node';
68
+ errorMessage += ' ----------';
69
+ errorMessage += indent(' ', Buffer.concat(nodeOutputChunks).toString('utf-8'));
70
+ errorMessage += '\n';
71
+ this.error(errorMessage, { exit: 1 });
122
72
  }
123
73
  }
124
74
  // Wait for Graph Node to come up
@@ -126,14 +76,13 @@ exports.default = {
126
76
  await waitForGraphNode(timeout);
127
77
  }
128
78
  catch (e) {
129
- toolbox.print.error('');
130
- toolbox.print.error(' Graph Node');
131
- toolbox.print.error(' ----------');
132
- toolbox.print.error(indent(' ', await collectGraphNodeLogs(tempdir, standaloneNode, nodeOutputChunks)));
133
- toolbox.print.error('');
134
79
  await stopTestEnvironment(tempdir);
135
- process.exitCode = 1;
136
- return;
80
+ let errorMessage = '\n';
81
+ errorMessage += ' Graph Node';
82
+ errorMessage += ' ----------';
83
+ errorMessage += indent(' ', await collectGraphNodeLogs(tempdir, standaloneNode, nodeOutputChunks));
84
+ errorMessage += '\n';
85
+ this.error(errorMessage, { exit: 1 });
137
86
  }
138
87
  // Run tests
139
88
  const result = await runTests(testCommand);
@@ -147,17 +96,16 @@ exports.default = {
147
96
  }
148
97
  }
149
98
  if (result.exitCode == 0) {
150
- toolbox.print.success('✔ Tests passed');
99
+ this.log('✔ Tests passed');
151
100
  }
152
101
  else {
153
- toolbox.print.error('✖ Tests failed');
102
+ this.log('✖ Tests failed');
154
103
  }
155
104
  // Capture logs
156
- nodeLogs =
157
- nodeLogs || result.exitCode !== 0
158
- ? await collectGraphNodeLogs(tempdir, standaloneNode, nodeOutputChunks)
159
- : undefined;
160
- ethereumLogs = ethereumLogs ? await collectEthereumLogs(tempdir) : undefined;
105
+ const nodeLogs = nodeLogsFlag || result.exitCode !== 0
106
+ ? await collectGraphNodeLogs(tempdir, standaloneNode, nodeOutputChunks)
107
+ : undefined;
108
+ const ethereumLogs = ethereumLogsFlag ? await collectEthereumLogs(tempdir) : undefined;
161
109
  // Bring down the test environment
162
110
  try {
163
111
  await stopTestEnvironment(tempdir);
@@ -166,28 +114,70 @@ exports.default = {
166
114
  // do nothing (the spinner already logs the problem)
167
115
  }
168
116
  if (nodeLogs) {
169
- toolbox.print.info('');
170
- toolbox.print.info(' Graph node');
171
- toolbox.print.info(' ----------');
172
- toolbox.print.info('');
173
- toolbox.print.info(indent(' ', nodeLogs));
117
+ this.log('');
118
+ this.log(' Graph node');
119
+ this.log(' ----------');
120
+ this.log('');
121
+ this.log(indent(' ', nodeLogs));
174
122
  }
175
123
  if (ethereumLogs) {
176
- toolbox.print.info('');
177
- toolbox.print.info(' Ethereum');
178
- toolbox.print.info(' --------');
179
- toolbox.print.info('');
180
- toolbox.print.info(indent(' ', ethereumLogs));
124
+ this.log('');
125
+ this.log(' Ethereum');
126
+ this.log(' --------');
127
+ this.log('');
128
+ this.log(indent(' ', ethereumLogs));
181
129
  }
182
130
  // Always print the test output
183
- toolbox.print.info('');
184
- toolbox.print.info(' Output');
185
- toolbox.print.info(' ------');
186
- toolbox.print.info('');
187
- toolbox.print.info(indent(' ', result.output));
131
+ this.log('');
132
+ this.log(' Output');
133
+ this.log(' ------');
134
+ this.log('');
135
+ this.log(indent(' ', result.output));
188
136
  // Propagate the exit code from the test run
189
- process.exitCode = result.exitCode;
190
- },
137
+ this.exit(result.exitCode);
138
+ }
139
+ }
140
+ exports.default = LocalCommand;
141
+ LocalCommand.description = 'Runs local tests against a Graph Node environment (using Ganache by default).';
142
+ LocalCommand.args = {
143
+ 'local-command': core_1.Args.string({
144
+ required: true,
145
+ }),
146
+ };
147
+ LocalCommand.flags = {
148
+ 'node-logs': core_1.Flags.boolean({
149
+ summary: 'Print the Graph Node logs.',
150
+ }),
151
+ 'ethereum-logs': core_1.Flags.boolean({
152
+ summary: 'Print the Ethereum logs.',
153
+ }),
154
+ 'compose-file': core_1.Flags.file({
155
+ summary: 'Custom Docker Compose file for additional services.',
156
+ }),
157
+ 'node-image': core_1.Flags.string({
158
+ summary: 'Custom Graph Node image to test against.',
159
+ default: 'graphprotocol/graph-node:latest',
160
+ }),
161
+ 'standalone-node': core_1.Flags.string({
162
+ summary: 'Use a standalone Graph Node outside Docker Compose.',
163
+ }),
164
+ 'standalone-node-args': core_1.Flags.string({
165
+ summary: 'Custom arguments to be passed to the standalone Graph Node.',
166
+ dependsOn: ['standalone-node'],
167
+ }),
168
+ 'skip-wait-for-ipfs': core_1.Flags.boolean({
169
+ summary: "Don't wait for IPFS to be up at localhost:15001",
170
+ }),
171
+ 'skip-wait-for-etherium': core_1.Flags.boolean({
172
+ summary: "Don't wait for Ethereum to be up at localhost:18545",
173
+ }),
174
+ 'skip-wait-for-postgres': core_1.Flags.boolean({
175
+ summary: "Don't wait for Postgres to be up at localhost:15432",
176
+ }),
177
+ timeout: core_1.Flags.integer({
178
+ summary: 'Time to wait for service containers in milliseconds.',
179
+ default: 120000,
180
+ }),
191
181
  };
192
182
  /**
193
183
  * Indents all lines of a string
@@ -198,14 +188,14 @@ const indent = (indentation, str) => str
198
188
  // Remove whitespace from empty lines
199
189
  .map(s => s.replace(/^\s+$/g, ''))
200
190
  .join('\n');
201
- const configureTestEnvironment = async (toolbox, tempdir, composeFile, nodeImage) => await (0, spinner_1.withSpinner)(`Configure test environment`, `Failed to configure test environment`, `Warnings configuring test environment`, async () => {
191
+ const configureTestEnvironment = async (tempdir, composeFile, nodeImage) => await (0, spinner_1.withSpinner)(`Configure test environment`, `Failed to configure test environment`, `Warnings configuring test environment`, async () => {
202
192
  // Temporary compose file
203
193
  const tempComposeFile = path_1.default.join(tempdir, 'compose', 'docker-compose.yml');
204
194
  // Copy the compose file to the temporary directory
205
- toolbox.filesystem.copy(composeFile, tempComposeFile);
195
+ gluegun_1.filesystem.copy(composeFile, tempComposeFile);
206
196
  // Substitute the graph-node image with the custom one, if appropriate
207
197
  if (nodeImage) {
208
- await toolbox.patching.replace(tempComposeFile, 'graphprotocol/graph-node:latest', nodeImage);
198
+ await gluegun_1.patching.replace(tempComposeFile, 'graphprotocol/graph-node:latest', nodeImage);
209
199
  }
210
200
  });
211
201
  const waitFor = async (timeout, testFn) => {
@@ -1,74 +1,33 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  const url_1 = require("url");
7
- const chalk_1 = __importDefault(require("chalk"));
4
+ const core_1 = require("@oclif/core");
8
5
  const auth_1 = require("../command-helpers/auth");
9
6
  const jsonrpc_1 = require("../command-helpers/jsonrpc");
10
7
  const node_1 = require("../command-helpers/node");
11
- const HELP = `
12
- ${chalk_1.default.bold('graph remove')} ${chalk_1.default.dim('[options]')} ${chalk_1.default.bold('<subgraph-name>')}
13
-
14
- ${chalk_1.default.dim('Options:')}
15
-
16
- --access-token <token> Graph access token
17
- -h, --help Show usage information
18
- -g, --node <url> Graph node to create the subgraph in
19
- `;
20
- exports.default = {
21
- description: 'Unregisters a subgraph name',
22
- run: async (toolbox) => {
23
- // Obtain tools
24
- const { print } = toolbox;
25
- // Read CLI parameters
26
- let { accessToken, g, h, help, node } = toolbox.parameters.options;
27
- const subgraphName = toolbox.parameters.first;
28
- // Support both long and short option variants
29
- help || (help = h);
30
- node || (node = g);
31
- // Show help text if requested
32
- if (help) {
33
- print.info(HELP);
34
- return;
35
- }
36
- // Validate the subgraph name
37
- if (!subgraphName) {
38
- print.error('No subgraph name provided');
39
- print.info(HELP);
40
- process.exitCode = 1;
41
- return;
42
- }
43
- // Validate node
44
- if (!node) {
45
- print.error(`No Graph node provided`);
46
- print.info(HELP);
47
- process.exitCode = 1;
48
- return;
49
- }
8
+ class RemoveCommand extends core_1.Command {
9
+ async run() {
10
+ const { args: { 'subgraph-name': subgraphName }, flags: { 'access-token': accessTokenFlag, node }, } = await this.parse(RemoveCommand);
50
11
  try {
51
12
  (0, node_1.validateNodeUrl)(node);
52
13
  }
53
14
  catch (e) {
54
- print.error(`Graph node "${node}" is invalid: ${e.message}`);
55
- process.exitCode = 1;
56
- return;
15
+ this.error(`Graph node "${node}" is invalid: ${e.message}`, { exit: 1 });
57
16
  }
58
17
  const requestUrl = new url_1.URL(node);
59
18
  const client = (0, jsonrpc_1.createJsonRpcClient)(requestUrl);
60
19
  // Exit with an error code if the client couldn't be created
61
20
  if (!client) {
62
- process.exitCode = 1;
21
+ this.exit(1);
63
22
  return;
64
23
  }
65
24
  // Use the access token, if one is set
66
- accessToken = await (0, auth_1.identifyDeployKey)(node, accessToken);
25
+ const accessToken = await (0, auth_1.identifyDeployKey)(node, accessTokenFlag);
67
26
  if (accessToken !== undefined && accessToken !== null) {
68
27
  // @ts-expect-error options property seems to exist
69
28
  client.options.headers = { Authorization: `Bearer ${accessToken}` };
70
29
  }
71
- const spinner = print.spin(`Creating subgraph in Graph node: ${requestUrl}`);
30
+ core_1.ux.action.start(`Removing subgraph in Graph node: ${requestUrl}`);
72
31
  client.request('subgraph_remove', { name: subgraphName }, (
73
32
  // @ts-expect-error TODO: why are the arguments not typed?
74
33
  requestError,
@@ -79,17 +38,34 @@ exports.default = {
79
38
  // @ts-expect-error TODO: why are the arguments not typed?
80
39
  _res) => {
81
40
  if (jsonRpcError) {
82
- spinner.fail(`Error removing the subgraph: ${jsonRpcError.message}`);
83
- process.exitCode = 1;
41
+ core_1.ux.action.stop(`✖ Error removed the subgraph: ${jsonRpcError.message}`);
42
+ this.exit(1);
84
43
  }
85
44
  else if (requestError) {
86
- spinner.fail(`HTTP error removing the subgraph: ${requestError.code}`);
87
- process.exitCode = 1;
45
+ core_1.ux.action.stop(`✖ HTTP error removed the subgraph: ${requestError.code}`);
46
+ this.exit(1);
88
47
  }
89
48
  else {
90
- spinner.stop();
91
- print.success(`Removed subgraph: ${subgraphName}`);
49
+ core_1.ux.action.stop('Subgraph removed');
50
+ this.exit(1);
92
51
  }
93
52
  });
94
- },
53
+ }
54
+ }
55
+ exports.default = RemoveCommand;
56
+ RemoveCommand.description = 'Unregisters a subgraph name';
57
+ RemoveCommand.args = {
58
+ 'subgraph-name': core_1.Args.string({
59
+ required: true,
60
+ }),
61
+ };
62
+ RemoveCommand.flags = {
63
+ node: core_1.Flags.string({
64
+ summary: 'Graph node to delete the subgraph from.',
65
+ char: 'g',
66
+ required: true,
67
+ }),
68
+ 'access-token': core_1.Flags.string({
69
+ summary: 'Graph access token.',
70
+ }),
95
71
  };