@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.
- package/CHANGELOG.md +10 -0
- package/dist/bin.js +31 -3
- package/dist/command-helpers/network.js +20 -13
- package/dist/command-helpers/network.test.js +2 -2
- package/dist/command-helpers/spinner.js +37 -1
- package/dist/commands/add.js +48 -67
- package/dist/commands/auth.js +40 -102
- package/dist/commands/build.js +51 -68
- package/dist/commands/codegen.js +38 -61
- package/dist/commands/create.js +31 -55
- package/dist/commands/deploy.js +112 -174
- package/dist/commands/init.js +526 -512
- package/dist/commands/local.js +93 -103
- package/dist/commands/remove.js +32 -56
- package/dist/commands/test.js +81 -88
- package/dist/index.js +5 -0
- package/oclif.manifest.json +726 -0
- package/package.json +10 -3
- package/dist/cli.js +0 -26
- package/dist/command-helpers/gluegun.js +0 -48
package/dist/commands/local.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
19
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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(
|
|
30
|
+
await configureTestEnvironment(tempdir, composeFile, nodeImage);
|
|
78
31
|
}
|
|
79
32
|
catch (e) {
|
|
80
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
121
|
-
|
|
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
|
-
|
|
136
|
-
|
|
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
|
-
|
|
99
|
+
this.log('✔ Tests passed');
|
|
151
100
|
}
|
|
152
101
|
else {
|
|
153
|
-
|
|
102
|
+
this.log('✖ Tests failed');
|
|
154
103
|
}
|
|
155
104
|
// Capture logs
|
|
156
|
-
nodeLogs =
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
|
198
|
+
await gluegun_1.patching.replace(tempComposeFile, 'graphprotocol/graph-node:latest', nodeImage);
|
|
209
199
|
}
|
|
210
200
|
});
|
|
211
201
|
const waitFor = async (timeout, testFn) => {
|
package/dist/commands/remove.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
83
|
-
|
|
41
|
+
core_1.ux.action.stop(`✖ Error removed the subgraph: ${jsonRpcError.message}`);
|
|
42
|
+
this.exit(1);
|
|
84
43
|
}
|
|
85
44
|
else if (requestError) {
|
|
86
|
-
|
|
87
|
-
|
|
45
|
+
core_1.ux.action.stop(`✖ HTTP error removed the subgraph: ${requestError.code}`);
|
|
46
|
+
this.exit(1);
|
|
88
47
|
}
|
|
89
48
|
else {
|
|
90
|
-
|
|
91
|
-
|
|
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
|
};
|