@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.
@@ -28,11 +28,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  const path_1 = __importDefault(require("path"));
30
30
  const url_1 = require("url");
31
- const chalk_1 = __importDefault(require("chalk"));
31
+ const core_1 = require("@oclif/core");
32
32
  const auth_1 = require("../command-helpers/auth");
33
33
  const compiler_1 = require("../command-helpers/compiler");
34
34
  const DataSourcesExtractor = __importStar(require("../command-helpers/data-sources"));
35
- const gluegun_1 = require("../command-helpers/gluegun");
36
35
  const ipfs_1 = require("../command-helpers/ipfs");
37
36
  const jsonrpc_1 = require("../command-helpers/jsonrpc");
38
37
  const network_1 = require("../command-helpers/network");
@@ -40,100 +39,15 @@ const node_1 = require("../command-helpers/node");
40
39
  const studio_1 = require("../command-helpers/studio");
41
40
  const version_1 = require("../command-helpers/version");
42
41
  const protocols_1 = __importDefault(require("../protocols"));
43
- const HELP = `
44
- ${chalk_1.default.bold('graph deploy')} [options] ${chalk_1.default.bold('<subgraph-name>')} ${chalk_1.default.bold('[<subgraph-manifest>]')}
45
-
46
- Options:
47
-
48
- --product <subgraph-studio|hosted-service>
49
- Selects the product to which to deploy
50
- --studio Shortcut for --product subgraph-studio
51
- -g, --node <node> Graph node to which to deploy
52
- --deploy-key <key> User deploy key
53
- -l --version-label <label> Version label used for the deployment
54
- -h, --help Show usage information
55
- -i, --ipfs <node> Upload build results to an IPFS node (default: ${ipfs_1.DEFAULT_IPFS_URL})
56
- -hdr, --headers <map> Add custom headers that will be used by the IPFS HTTP client (default: {})
57
- --debug-fork ID of a remote subgraph whose store will be GraphQL queried
58
- -o, --output-dir <path> Output directory for build results (default: build/)
59
- --skip-migrations Skip subgraph migrations (default: false)
60
- -w, --watch Regenerate types when subgraph files change (default: false)
61
- --network <name> Network configuration to use from the networks config file
62
- --network-file <path> Networks config file path (default: "./networks.json")
63
- `;
64
- const processForm = async (toolbox, { product, studio, node, versionLabel }) => {
65
- const questions = [
66
- {
67
- type: 'select',
68
- name: 'product',
69
- message: 'Product for which to deploy',
70
- choices: ['subgraph-studio', 'hosted-service'],
71
- skip: product === 'subgraph-studio' ||
72
- product === 'hosted-service' ||
73
- studio !== undefined ||
74
- node !== undefined,
75
- },
76
- {
77
- type: 'input',
78
- name: 'versionLabel',
79
- message: 'Version Label (e.g. v0.0.1)',
80
- skip: versionLabel !== undefined,
81
- },
82
- ];
83
- try {
84
- const answers = await toolbox.prompt.ask(questions);
85
- return answers;
86
- }
87
- catch (e) {
88
- return undefined;
89
- }
90
- };
91
- exports.default = {
92
- description: 'Deploys the subgraph to a Graph node',
93
- run: async (toolbox) => {
94
- // Obtain tools
95
- const { filesystem, print } = toolbox;
96
- // Parse CLI parameters
97
- let { product, studio, deployKey, accessToken, versionLabel, l, g, h, i, help, ipfs, headers, hdr, node, o, outputDir, skipMigrations, w, watch, debugFork, network, networkFile, } = toolbox.parameters.options;
98
- // Support both long and short option variants
99
- help || (help = h);
100
- ipfs = ipfs || i || ipfs_1.DEFAULT_IPFS_URL;
101
- headers = headers || hdr || '{}';
102
- node || (node = g);
103
- outputDir || (outputDir = o);
104
- watch || (watch = w);
105
- versionLabel || (versionLabel = l);
106
- try {
107
- headers = JSON.parse(headers);
108
- }
109
- catch (e) {
110
- print.error('Please make sure headers is a valid JSON value');
111
- process.exitCode = 1;
112
- return;
113
- }
114
- let subgraphName, manifest;
115
- try {
116
- [subgraphName, manifest] = (0, gluegun_1.fixParameters)(toolbox.parameters, {
117
- h,
118
- help,
119
- w,
120
- watch,
121
- studio,
122
- });
123
- }
124
- catch (e) {
125
- print.error(e.message);
126
- process.exitCode = 1;
127
- return;
128
- }
129
- // Fall back to default values for options / parameters
130
- outputDir = outputDir && outputDir !== '' ? outputDir : filesystem.path('build');
131
- manifest =
132
- manifest !== undefined && manifest !== '' ? manifest : filesystem.resolve('subgraph.yaml');
133
- networkFile =
134
- networkFile !== undefined && networkFile !== ''
135
- ? networkFile
136
- : filesystem.resolve('networks.json');
42
+ const headersFlag = core_1.Flags.custom({
43
+ summary: 'Add custom headers that will be used by the IPFS HTTP client.',
44
+ aliases: ['hdr'],
45
+ parse: val => JSON.parse(val),
46
+ default: {},
47
+ });
48
+ class DeployCommand extends core_1.Command {
49
+ async run() {
50
+ const { args: { 'subgraph-name': subgraphName, 'subgraph-manifest': manifest }, flags: { product, studio, 'deploy-key': deployKeyFlag, 'access-token': accessToken, 'version-label': versionLabelFlag, ipfs, headers, node: nodeFlag, 'output-dir': outputDir, 'skip-migrations': skipMigrations, watch, 'debug-fork': debugFork, network, 'network-file': networkFile, }, } = await this.parse(DeployCommand);
137
51
  try {
138
52
  const dataSourcesAndTemplates = await DataSourcesExtractor.fromFilePath(manifest);
139
53
  for (const { network } of dataSourcesAndTemplates) {
@@ -141,53 +55,19 @@ exports.default = {
141
55
  }
142
56
  }
143
57
  catch (e) {
144
- print.error(e.message);
145
- process.exitCode = 1;
146
- return;
58
+ this.error(e, { exit: 1 });
147
59
  }
148
- // Show help text if requested
149
- if (help) {
150
- print.info(HELP);
151
- return;
152
- }
153
- ({ node } = (0, node_1.chooseNodeUrl)({ product, studio, node }));
154
- if (!node) {
155
- const inputs = await processForm(toolbox, {
156
- product,
157
- studio,
158
- node,
159
- versionLabel: 'skip', // determine label requirement later
160
- });
161
- if (inputs === undefined) {
162
- process.exit(1);
163
- }
164
- product = inputs.product;
165
- ({ node } = (0, node_1.chooseNodeUrl)({
166
- product,
167
- studio,
168
- node,
169
- }));
170
- }
171
- // Validate the subgraph name
172
- if (!subgraphName) {
173
- print.error(`No subgraph ${product == 'subgraph-studio' || studio ? 'slug' : 'name'} provided`);
174
- print.info(HELP);
175
- process.exitCode = 1;
176
- return;
177
- }
178
- // Validate node
60
+ const { node } = (0, node_1.chooseNodeUrl)({
61
+ product,
62
+ studio,
63
+ node: nodeFlag ||
64
+ (await core_1.ux.prompt('Which product to deploy for?', {
65
+ required: true,
66
+ })),
67
+ });
179
68
  if (!node) {
180
- print.error(`No Graph node provided`);
181
- print.info(HELP);
182
- process.exitCode = 1;
183
- return;
184
- }
185
- // Validate IPFS
186
- if (!ipfs) {
187
- print.error(`No IPFS node provided`);
188
- print.info(HELP);
189
- process.exitCode = 1;
190
- return;
69
+ // shouldn't happen, but we do the check to satisfy TS
70
+ this.error('No Graph node provided');
191
71
  }
192
72
  let protocol;
193
73
  try {
@@ -203,13 +83,11 @@ exports.default = {
203
83
  protocol = protocols_1.default.fromDataSources(dataSourcesAndTemplates);
204
84
  }
205
85
  catch (e) {
206
- print.error(e.message);
207
- process.exitCode = 1;
208
- return;
86
+ this.error(e, { exit: 1 });
209
87
  }
210
88
  if (network) {
211
89
  const identifierName = protocol.getContract().identifierName();
212
- await (0, network_1.updateSubgraphNetwork)(toolbox, manifest, network, networkFile, identifierName);
90
+ await (0, network_1.updateSubgraphNetwork)(manifest, network, networkFile, identifierName);
213
91
  }
214
92
  const isStudio = node.match(/studio/);
215
93
  const isHostedService = node.match(/thegraph.com/) && !isStudio;
@@ -219,35 +97,30 @@ exports.default = {
219
97
  outputDir,
220
98
  outputFormat: 'wasm',
221
99
  skipMigrations,
222
- blockIpfsMethods: isStudio,
100
+ blockIpfsMethods: isStudio || undefined,
223
101
  protocol,
224
102
  });
225
103
  // Exit with an error code if the compiler couldn't be created
226
104
  if (!compiler) {
227
- process.exitCode = 1;
105
+ this.exit(1);
228
106
  return;
229
107
  }
230
108
  // Ask for label if not on hosted service
109
+ let versionLabel = versionLabelFlag;
231
110
  if (!versionLabel && !isHostedService) {
232
- const inputs = await processForm(toolbox, {
233
- product,
234
- studio,
235
- node,
236
- versionLabel,
111
+ versionLabel = await core_1.ux.prompt('Which version label to use? (e.g. "v0.0.1")', {
112
+ required: true,
237
113
  });
238
- if (inputs === undefined) {
239
- process.exit(1);
240
- }
241
- versionLabel = inputs.versionLabel;
242
114
  }
243
115
  const requestUrl = new url_1.URL(node);
244
116
  const client = (0, jsonrpc_1.createJsonRpcClient)(requestUrl);
245
117
  // Exit with an error code if the client couldn't be created
246
118
  if (!client) {
247
- process.exitCode = 1;
119
+ this.exit(1);
248
120
  return;
249
121
  }
250
122
  // Use the deploy key, if one is set
123
+ let deployKey = deployKeyFlag;
251
124
  if (!deployKey && accessToken) {
252
125
  deployKey = accessToken; // backwards compatibility
253
126
  }
@@ -256,9 +129,10 @@ exports.default = {
256
129
  // @ts-expect-error options property seems to exist
257
130
  client.options.headers = { Authorization: 'Bearer ' + deployKey };
258
131
  }
132
+ // eslint-disable-next-line @typescript-eslint/no-this-alias -- request needs it
133
+ const self = this;
259
134
  const deploySubgraph = async (ipfsHash) => {
260
- const spinner = print.spin(`Deploying to Graph node ${requestUrl}`);
261
- // `Failed to deploy to Graph node ${requestUrl}`,
135
+ core_1.ux.action.start(`Deploying to Graph node ${requestUrl}`);
262
136
  client.request('subgraph_deploy', {
263
137
  name: subgraphName,
264
138
  ipfs_hash: ipfsHash,
@@ -272,27 +146,25 @@ exports.default = {
272
146
  // @ts-expect-error TODO: why are the arguments not typed?
273
147
  res) => {
274
148
  if (jsonRpcError) {
275
- spinner.fail(`Failed to deploy to Graph node ${requestUrl}: ${jsonRpcError.message}`);
149
+ let errorMessage = `Failed to deploy to Graph node ${requestUrl}: ${jsonRpcError.message}`;
276
150
  // Provide helpful advice when the subgraph has not been created yet
277
151
  if (jsonRpcError.message.match(/subgraph name not found/)) {
278
152
  if (isHostedService) {
279
- print.info(`
280
- You may need to create it at https://thegraph.com/explorer/dashboard.`);
153
+ errorMessage +=
154
+ '\nYou may need to create it at https://thegraph.com/explorer/dashboard.';
281
155
  }
282
156
  else {
283
- print.info(`
157
+ errorMessage += `
284
158
  Make sure to create the subgraph first by running the following command:
285
- $ graph create --node ${node} ${subgraphName}`);
159
+ $ graph create --node ${node} ${subgraphName}`;
286
160
  }
287
161
  }
288
- process.exitCode = 1;
162
+ self.error(errorMessage, { exit: 1 });
289
163
  }
290
164
  else if (requestError) {
291
- spinner.fail(`HTTP error deploying the subgraph ${requestError.code}`);
292
- process.exitCode = 1;
165
+ self.error(`HTTP error deploying the subgraph ${requestError.code}`, { exit: 1 });
293
166
  }
294
167
  else {
295
- spinner.stop();
296
168
  const base = requestUrl.protocol + '//' + requestUrl.hostname;
297
169
  let playground = res.playground;
298
170
  let queries = res.queries;
@@ -304,14 +176,13 @@ $ graph create --node ${node} ${subgraphName}`);
304
176
  queries = base + queries;
305
177
  }
306
178
  if (isHostedService) {
307
- print.success(`Deployed to ${chalk_1.default.blue(`https://thegraph.com/explorer/subgraph/${subgraphName}`)}`);
179
+ core_1.ux.action.stop(`Deployed to https://thegraph.com/explorer/subgraph/${subgraphName}`);
308
180
  }
309
181
  else {
310
- print.success(`Deployed to ${chalk_1.default.blue(String(playground))}`);
182
+ core_1.ux.action.stop(`Deployed to ${String(playground)}`);
311
183
  }
312
- print.info('\nSubgraph endpoints:');
313
- print.info(`Queries (HTTP): ${queries}`);
314
- print.info(``);
184
+ self.log('Subgraph endpoints:');
185
+ self.log(`Queries (HTTP):\n${queries}`);
315
186
  }
316
187
  });
317
188
  };
@@ -331,5 +202,72 @@ $ graph create --node ${node} ${subgraphName}`);
331
202
  }
332
203
  await deploySubgraph(result);
333
204
  }
334
- },
205
+ }
206
+ }
207
+ exports.default = DeployCommand;
208
+ DeployCommand.description = 'Deploys a subgraph to a Graph node.';
209
+ DeployCommand.args = {
210
+ 'subgraph-name': core_1.Args.string({
211
+ required: true,
212
+ }),
213
+ 'subgraph-manifest': core_1.Args.string({
214
+ default: 'subgraph.yaml',
215
+ }),
216
+ };
217
+ DeployCommand.flags = {
218
+ product: core_1.Flags.string({
219
+ summary: 'Select a product for which to authenticate.',
220
+ options: ['subgraph-studio', 'hosted-service'],
221
+ }),
222
+ studio: core_1.Flags.boolean({
223
+ summary: 'Shortcut for "--product subgraph-studio".',
224
+ exclusive: ['product'],
225
+ }),
226
+ node: core_1.Flags.string({
227
+ summary: 'Graph node for which to initialize.',
228
+ char: 'g',
229
+ }),
230
+ 'deploy-key': core_1.Flags.string({
231
+ summary: 'User deploy key.',
232
+ required: true,
233
+ exclusive: ['access-token'],
234
+ }),
235
+ 'access-token': core_1.Flags.string({
236
+ deprecated: true,
237
+ summary: 'Graph access key. DEPRECATED: Use "--deploy-key" instead.',
238
+ exclusive: ['deploy-key'],
239
+ }),
240
+ 'version-label': core_1.Flags.string({
241
+ summary: 'Version label used for the deployment.',
242
+ char: 'l',
243
+ }),
244
+ ipfs: core_1.Flags.string({
245
+ summary: 'Upload build results to an IPFS node.',
246
+ char: 'i',
247
+ default: ipfs_1.DEFAULT_IPFS_URL,
248
+ }),
249
+ headers: headersFlag(),
250
+ 'debug-fork': core_1.Flags.string({
251
+ summary: 'ID of a remote subgraph whose store will be GraphQL queried.',
252
+ }),
253
+ 'output-dir': core_1.Flags.directory({
254
+ summary: 'Output directory for build results.',
255
+ char: 'o',
256
+ default: 'build/',
257
+ }),
258
+ 'skip-migrations': core_1.Flags.boolean({
259
+ summary: 'Skip subgraph migrations.',
260
+ }),
261
+ watch: core_1.Flags.boolean({
262
+ summary: 'Regenerate types when subgraph files change.',
263
+ char: 'w',
264
+ }),
265
+ network: core_1.Flags.string({
266
+ summary: 'Network configuration to use from the networks config file.',
267
+ }),
268
+ // TODO: should be networksFile (with an "s"), or?
269
+ 'network-file': core_1.Flags.file({
270
+ summary: 'Networks config file path.',
271
+ default: 'networks.json',
272
+ }),
335
273
  };