From 69147a64b35622d423936145fc0b2465a2afab17 Mon Sep 17 00:00:00 2001 From: Saihajpreet Singh Date: Sat, 8 Jun 2024 11:10:39 +0300 Subject: [PATCH 1/7] sunset of hosted --- .changeset/gold-deers-kick.md | 18 ++ packages/cli/src/command-helpers/node.ts | 70 +------ packages/cli/src/command-helpers/subgraph.ts | 17 -- packages/cli/src/commands/auth.ts | 40 +--- packages/cli/src/commands/deploy.ts | 196 +----------------- packages/cli/src/commands/init.ts | 200 ++----------------- 6 files changed, 51 insertions(+), 490 deletions(-) create mode 100644 .changeset/gold-deers-kick.md diff --git a/.changeset/gold-deers-kick.md b/.changeset/gold-deers-kick.md new file mode 100644 index 000000000..b123b0cad --- /dev/null +++ b/.changeset/gold-deers-kick.md @@ -0,0 +1,18 @@ +--- +'@graphprotocol/graph-cli': minor +--- + +Breaking changes to the CLI to prepare for the sunset of the hosted service. + +- `graph auth` + - Removed `--product` flag + - Removed `--studio` flag + - Removed `node` argument +- `graph deploy` + - Removed `--product` flag + - Removed `--studio` flag + - Removed `--from-hosted-service` flag +- `graph init` + - Removed `--product` flag + - Removed `--studio` flag + - Removed `--allow-simple-name` flag diff --git a/packages/cli/src/command-helpers/node.ts b/packages/cli/src/command-helpers/node.ts index 292db1f2d..f4136b258 100644 --- a/packages/cli/src/command-helpers/node.ts +++ b/packages/cli/src/command-helpers/node.ts @@ -1,92 +1,26 @@ import { URL } from 'url'; import { print } from 'gluegun'; -import { GRAPH_CLI_SHARED_HEADERS } from '../constants'; export const SUBGRAPH_STUDIO_URL = 'https://api.studio.thegraph.com/deploy/'; -const HOSTED_SERVICE_URL = 'https://api.thegraph.com/deploy/'; -const HOSTED_SERVICE_INDEX_NODE_URL = 'https://api.thegraph.com/index-node/graphql'; export const validateNodeUrl = (node: string) => new URL(node); export const normalizeNodeUrl = (node: string) => new URL(node).toString(); export function chooseNodeUrl({ - product, - studio, node, - allowSimpleName, }: { - product: string | undefined; - studio: boolean | undefined; node?: string; - allowSimpleName?: boolean; }) { if (node) { try { validateNodeUrl(node); + return { node } } catch (e) { print.error(`Graph node "${node}" is invalid: ${e.message}`); process.exit(1); } - } else { - if (studio) { - product = 'subgraph-studio'; - } - switch (product) { - case 'subgraph-studio': - node = SUBGRAPH_STUDIO_URL; - break; - case 'hosted-service': - node = HOSTED_SERVICE_URL; - break; - } - } - if (node?.match(/studio/) || product === 'subgraph-studio') { - allowSimpleName = true; - } - return { node, allowSimpleName }; -} - -export async function getHostedServiceSubgraphId({ - subgraphName, -}: { - subgraphName: string; -}): Promise<{ - subgraph: string; - synced: boolean; - health: 'healthy' | 'unhealthy' | 'failed'; -}> { - const response = await fetch(HOSTED_SERVICE_INDEX_NODE_URL, { - method: 'POST', - body: JSON.stringify({ - query: /* GraphQL */ ` - query GraphCli_getSubgraphId($subgraphName: String!) { - indexingStatusForCurrentVersion(subgraphName: $subgraphName) { - subgraph - synced - health - } - } - `, - variables: { - subgraphName, - }, - }), - headers: { - 'content-type': 'application/json', - ...GRAPH_CLI_SHARED_HEADERS, - }, - }); - - const { data, errors } = await response.json(); - - if (errors) { - throw new Error(errors[0].message); - } - - if (!data.indexingStatusForCurrentVersion) { - throw new Error(`Subgraph "${subgraphName}" not found on the hosted service`); } - return data.indexingStatusForCurrentVersion; + return { node: SUBGRAPH_STUDIO_URL }; } diff --git a/packages/cli/src/command-helpers/subgraph.ts b/packages/cli/src/command-helpers/subgraph.ts index 0fad46dbc..6902ca155 100644 --- a/packages/cli/src/command-helpers/subgraph.ts +++ b/packages/cli/src/command-helpers/subgraph.ts @@ -1,20 +1,3 @@ -export const validateSubgraphName = ( - name: string, - { allowSimpleName }: { allowSimpleName?: boolean }, -) => { - if (allowSimpleName) { - return name; - } - if (name.split('/').length !== 2) { - throw new Error(`Subgraph name "${name}" needs to have the format "/${name}". - -When using the Hosted Service at https://thegraph.com, is the -name of your GitHub user or organization. - -You can bypass this check with --allow-simple-name.`); - } -}; - export const getSubgraphBasename = (name: string) => { const segments = name.split('/', 2); return segments[segments.length - 1]; diff --git a/packages/cli/src/commands/auth.ts b/packages/cli/src/commands/auth.ts index c677f8517..a0bb591c9 100644 --- a/packages/cli/src/commands/auth.ts +++ b/packages/cli/src/commands/auth.ts @@ -7,7 +7,6 @@ export default class AuthCommand extends Command { static description = 'Sets the deploy key to use when deploying to a Graph node.'; static args = { - node: Args.string(), 'deploy-key': Args.string(), }; @@ -15,47 +14,14 @@ export default class AuthCommand extends Command { help: Flags.help({ char: 'h', }), - - product: Flags.string({ - summary: 'Select a product for which to authenticate.', - options: ['subgraph-studio', 'hosted-service'], - deprecated: { - message: - 'In next major version, this flag will be removed. By default we will deploy to the Graph Studio. Learn more about Sunrise of Decentralized Data https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/', - }, - }), - studio: Flags.boolean({ - summary: 'Shortcut for "--product subgraph-studio".', - exclusive: ['product'], - deprecated: { - message: - 'In next major version, this flag will be removed. By default we will deploy to the Graph Studio. Learn more about Sunrise of Decentralized Data https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/', - }, - }), }; async run() { - const { - args: { node: nodeOrDeployKey, 'deploy-key': deployKeyFlag }, - flags: { product, studio }, + let { + args: { 'deploy-key': deployKey }, } = await this.parse(AuthCommand); - // if user specifies --product or --studio then deployKey is the first parameter - let node: string | undefined; - let deployKey = deployKeyFlag; - if (product || studio) { - ({ node } = chooseNodeUrl({ product, studio, node })); - deployKey = nodeOrDeployKey; - } else { - node = nodeOrDeployKey; - } - - // eslint-disable-next-line -- prettier has problems with ||= - node = - node || - (await ux.prompt('Which product to initialize?', { - required: true, - })); + const { node } = chooseNodeUrl({}) // eslint-disable-next-line -- prettier has problems with ||= deployKey = diff --git a/packages/cli/src/commands/deploy.ts b/packages/cli/src/commands/deploy.ts index c538eecc3..ba78178ac 100644 --- a/packages/cli/src/commands/deploy.ts +++ b/packages/cli/src/commands/deploy.ts @@ -1,6 +1,6 @@ import path from 'path'; import { URL } from 'url'; -import { print, prompt } from 'gluegun'; +import { print, } from 'gluegun'; import { create } from 'ipfs-http-client'; import { Args, Command, Flags, ux } from '@oclif/core'; import { identifyDeployKey } from '../command-helpers/auth'; @@ -9,7 +9,7 @@ import * as DataSourcesExtractor from '../command-helpers/data-sources'; import { DEFAULT_IPFS_URL } from '../command-helpers/ipfs'; import { createJsonRpcClient } from '../command-helpers/jsonrpc'; import { updateSubgraphNetwork } from '../command-helpers/network'; -import { chooseNodeUrl, getHostedServiceSubgraphId } from '../command-helpers/node'; +import { chooseNodeUrl } from '../command-helpers/node'; import { assertGraphTsVersion, assertManifestApiVersion } from '../command-helpers/version'; import { GRAPH_CLI_SHARED_HEADERS } from '../constants'; import debugFactory from '../debug'; @@ -22,8 +22,6 @@ const headersFlag = Flags.custom>({ default: {}, }); -const productOptions = ['subgraph-studio', 'hosted-service']; - const deployDebugger = debugFactory('graph-cli:deploy'); export default class DeployCommand extends Command { @@ -40,15 +38,6 @@ export default class DeployCommand extends Command { help: Flags.help({ char: 'h', }), - - product: Flags.string({ - summary: 'Select a product for which to authenticate.', - options: productOptions, - }), - studio: Flags.boolean({ - summary: 'Shortcut for "--product subgraph-studio".', - exclusive: ['product'], - }), node: Flags.string({ summary: 'Graph node for which to initialize.', char: 'g', @@ -96,22 +85,16 @@ export default class DeployCommand extends Command { network: Flags.string({ summary: 'Network configuration to use from the networks config file.', }), - // TODO: should be networksFile (with an "s"), or? 'network-file': Flags.file({ summary: 'Networks config file path.', default: 'networks.json', }), - 'from-hosted-service': Flags.string({ - summary: 'Hosted service Subgraph Name to deploy to studio.', - }), }; async run() { const { args: { 'subgraph-name': subgraphNameArg, 'subgraph-manifest': manifest }, flags: { - product: productFlag, - studio, 'deploy-key': deployKeyFlag, 'access-token': accessToken, 'version-label': versionLabelFlag, @@ -125,137 +108,8 @@ export default class DeployCommand extends Command { network, 'network-file': networkFile, 'ipfs-hash': ipfsHash, - 'from-hosted-service': hostedServiceSubgraphName, }, } = await this.parse(DeployCommand); - if (hostedServiceSubgraphName) { - const { health, subgraph: subgraphIpfsHash } = await getHostedServiceSubgraphId({ - subgraphName: hostedServiceSubgraphName, - }); - - const safeToDeployFailedSubgraph = - health === 'failed' - ? await ux.confirm( - 'This subgraph has failed indexing on hosted service. Do you wish to continue the deploy?', - ) - : true; - if (!safeToDeployFailedSubgraph) return; - - const safeToDeployUnhealthySubgraph = - health === 'unhealthy' - ? await ux.confirm( - 'This subgraph is not healthy on hosted service. Do you wish to continue the deploy?', - ) - : true; - if (!safeToDeployUnhealthySubgraph) return; - - const { node } = chooseNodeUrl({ studio: true, product: undefined }); - - // shouldn't happen, but we need to satisfy the compiler - if (!node) { - this.error('No node URL available'); - } - - const requestUrl = new URL(node); - const client = createJsonRpcClient(requestUrl); - - // Exit with an error code if the client couldn't be created - if (!client) { - this.error('Failed to create RPC client'); - } - - // Use the deploy key, if one is set - let deployKey = deployKeyFlag; - if (!deployKey && accessToken) { - deployKey = accessToken; // backwards compatibility - } - deployKey = await identifyDeployKey(node, deployKey); - if (!deployKey) { - this.error('No deploy key available'); - } - - // @ts-expect-error options property seems to exist - client.options.headers = { - ...GRAPH_CLI_SHARED_HEADERS, - Authorization: 'Bearer ' + deployKey, - }; - - const subgraphName = await ux.prompt('What is the name of the subgraph you want to deploy?', { - required: true, - }); - - // Ask for label if not on hosted service - const versionLabel = - versionLabelFlag || - (await ux.prompt('Which version label to use? (e.g. "v0.0.1")', { - required: true, - })); - - const spinner = print.spin(`Deploying to Graph node ${requestUrl}`); - client.request( - 'subgraph_deploy', - { - name: subgraphName, - ipfs_hash: subgraphIpfsHash, - version_label: versionLabel, - debug_fork: debugFork, - }, - async ( - // @ts-expect-error TODO: why are the arguments not typed? - requestError, - // @ts-expect-error TODO: why are the arguments not typed? - jsonRpcError, - // @ts-expect-error TODO: why are the arguments not typed? - res, - ) => { - deployDebugger('requestError: %O', requestError); - deployDebugger('jsonRpcError: %O', jsonRpcError); - if (jsonRpcError) { - const message = jsonRpcError?.message || jsonRpcError?.code?.toString(); - deployDebugger('message: %O', message); - let errorMessage = `Failed to deploy to Graph node ${requestUrl}: ${message}`; - - // Provide helpful advice when the subgraph has not been created yet - if (message?.match(/subgraph name not found/)) { - errorMessage += ` - Make sure to create the subgraph first by running the following command: - $ graph create --node ${node} ${subgraphName}`; - } - - if (message?.match(/auth failure/)) { - errorMessage += '\nYou may need to authenticate first.'; - } - - spinner.fail(errorMessage); - process.exit(1); - } else if (requestError) { - spinner.fail(`HTTP error deploying the subgraph ${requestError.code}`); - process.exit(1); - } else { - spinner.stop(); - - const base = requestUrl.protocol + '//' + requestUrl.hostname; - let playground = res.playground; - let queries = res.queries; - - // Add a base URL if graph-node did not return the full URL - if (playground.charAt(0) === ':') { - playground = base + playground; - } - if (queries.charAt(0) === ':') { - queries = base + queries; - } - - print.success(`Deployed to ${playground}`); - print.info('\nSubgraph endpoints:'); - print.info(`Queries (HTTP): ${queries}`); - print.info(``); - process.exit(0); - } - }, - ); - return; - } const subgraphName = subgraphNameArg || @@ -263,27 +117,7 @@ export default class DeployCommand extends Command { required: true, })); - // We are given a node URL, so we prioritize that over the product flag - const product = nodeFlag - ? productFlag - : studio - ? 'subgraph-studio' - : productFlag || - (await prompt - .ask([ - { - name: 'product', - message: 'Which product to deploy for?', - required: true, - type: 'select', - choices: productOptions, - }, - ]) - .then(({ product }) => product as string)); - const { node } = chooseNodeUrl({ - product, - studio, node: nodeFlag, }); if (!node) { @@ -292,7 +126,6 @@ export default class DeployCommand extends Command { } const isStudio = node.match(/studio/); - const isHostedService = node.match(/thegraph.com/) && !isStudio; const requestUrl = new URL(node); const client = createJsonRpcClient(requestUrl); @@ -319,11 +152,9 @@ export default class DeployCommand extends Command { // Ask for label if not on hosted service let versionLabel = versionLabelFlag; - if (!versionLabel && !isHostedService) { - versionLabel = await ux.prompt('Which version label to use? (e.g. "v0.0.1")', { - required: true, - }); - } + versionLabel ||= await ux.prompt('Which version label to use? (e.g. "v0.0.1")', { + required: true, + }); const deploySubgraph = async (ipfsHash: string) => { const spinner = print.spin(`Deploying to Graph node ${requestUrl}`); @@ -350,17 +181,6 @@ export default class DeployCommand extends Command { deployDebugger('message: %O', message); let errorMessage = `Failed to deploy to Graph node ${requestUrl}: ${message}`; - // Provide helpful advice when the subgraph has not been created yet - if (message?.match(/subgraph name not found/)) { - if (isHostedService) { - errorMessage += - '\nYou may need to create it at https://thegraph.com/explorer/dashboard.'; - } else { - errorMessage += ` - Make sure to create the subgraph first by running the following command: - $ graph create --node ${node} ${subgraphName}`; - } - } if (message?.match(/auth failure/)) { errorMessage += '\nYou may need to authenticate first.'; } @@ -384,11 +204,7 @@ export default class DeployCommand extends Command { queries = base + queries; } - if (isHostedService) { - print.success(`Deployed to https://thegraph.com/explorer/subgraph/${subgraphName}`); - } else { - print.success(`Deployed to ${playground}`); - } + print.success(`Deployed to ${playground}`); print.info('\nSubgraph endpoints:'); print.info(`Queries (HTTP): ${queries}`); print.info(``); diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index d9966eb4e..db48923c1 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -15,7 +15,7 @@ import { chooseNodeUrl, SUBGRAPH_STUDIO_URL } from '../command-helpers/node'; import { generateScaffold, writeScaffold } from '../command-helpers/scaffold'; import { sortWithPriority } from '../command-helpers/sort'; import { withSpinner } from '../command-helpers/spinner'; -import { getSubgraphBasename, validateSubgraphName } from '../command-helpers/subgraph'; +import { getSubgraphBasename } from '../command-helpers/subgraph'; import { GRAPH_CLI_SHARED_HEADERS } from '../constants'; import debugFactory from '../debug'; import Protocol, { ProtocolName } from '../protocols'; @@ -89,35 +89,10 @@ export default class InitCommand extends Command { protocol: Flags.string({ options: protocolChoices, }), - product: Flags.string({ - summary: 'Selects the product for which to initialize.', - options: ['subgraph-studio', 'hosted-service'], - deprecated: { - message: - 'In next major version, this flag will be removed. By default we will deploy to the Graph Studio. Learn more about Sunrise of Decentralized Data https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/', - }, - }), - studio: Flags.boolean({ - summary: 'Shortcut for "--product subgraph-studio".', - exclusive: ['product'], - deprecated: { - message: - 'In next major version, this flag will be removed. By default we will deploy to the Graph Studio. Learn more about Sunrise of Decentralized Data https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/', - }, - }), node: Flags.string({ summary: 'Graph node for which to initialize.', char: 'g', }), - 'allow-simple-name': Flags.boolean({ - description: 'Use a subgraph name without a prefix.', - default: false, - deprecated: { - message: - 'In next major version, this flag will be removed. By default we will deploy to the Graph Studio. Learn more about Sunrise of Decentralized Data https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/', - }, - }), - 'from-contract': Flags.string({ description: 'Creates a scaffold based on an existing contract.', exclusive: ['from-example'], @@ -184,10 +159,7 @@ export default class InitCommand extends Command { const { protocol, - product, - studio, node: nodeFlag, - 'allow-simple-name': allowSimpleNameFlag, 'from-contract': fromContract, 'contract-name': contractName, 'from-example': fromExample, @@ -201,12 +173,8 @@ export default class InitCommand extends Command { } = flags; initDebugger('Flags: %O', flags); - let { node, allowSimpleName } = chooseNodeUrl({ - product, - // if we are loading example, we want to ensure we are using studio - studio: studio || fromExample !== undefined, + let { node } = chooseNodeUrl({ node: nodeFlag, - allowSimpleName: allowSimpleNameFlag, }); if (fromContract && fromExample) { @@ -245,7 +213,6 @@ export default class InitCommand extends Command { await initSubgraphFromExample.bind(this)( { fromExample, - allowSimpleName, directory, subgraphName, skipInstall, @@ -300,7 +267,6 @@ export default class InitCommand extends Command { { protocolInstance, abi, - allowSimpleName, directory, contract: fromContract, indexEvents, @@ -321,7 +287,6 @@ export default class InitCommand extends Command { if (fromExample) { const answers = await processFromExampleInitForm.bind(this)({ - allowSimpleName, subgraphName, directory, }); @@ -333,7 +298,6 @@ export default class InitCommand extends Command { await initSubgraphFromExample.bind(this)( { - allowSimpleName, fromExample, subgraphName: answers.subgraphName, directory: answers.directory, @@ -346,12 +310,8 @@ export default class InitCommand extends Command { // Otherwise, take the user through the interactive form const answers = await processInitForm.bind(this)({ protocol: protocol as ProtocolName | undefined, - product, - studio, - node, abi, abiPath, - allowSimpleName, directory, contract: fromContract, indexEvents, @@ -367,16 +327,12 @@ export default class InitCommand extends Command { return; } - ({ node, allowSimpleName } = chooseNodeUrl({ - product: answers.product, - studio: answers.studio, + ({ node, } = chooseNodeUrl({ node, - allowSimpleName, })); await initSubgraphFromContract.bind(this)( { protocolInstance: answers.protocolInstance, - allowSimpleName, subgraphName: answers.subgraphName, directory: answers.directory, abi: answers.abi, @@ -403,17 +359,15 @@ async function processFromExampleInitForm( { directory: initDirectory, subgraphName: initSubgraphName, - allowSimpleName: initAllowSimpleName, }: { directory?: string; subgraphName?: string; - allowSimpleName: boolean | undefined; }, ): Promise< | { - subgraphName: string; - directory: string; - } + subgraphName: string; + directory: string; + } | undefined > { try { @@ -421,24 +375,8 @@ async function processFromExampleInitForm( { type: 'input', name: 'subgraphName', - // TODO: is defaulting to studio ok? message: () => 'Subgraph slug', initial: initSubgraphName, - validate: name => { - try { - validateSubgraphName(name, { - allowSimpleName: initAllowSimpleName, - }); - return true; - } catch (e) { - return `${e.message} - - Examples: - - $ graph init ${os.userInfo().username}/${name} - $ graph init ${name} --allow-simple-name`; - } - }, }, ]); @@ -465,7 +403,7 @@ async function processFromExampleInitForm( } async function retryWithPrompt(func: () => Promise): Promise { - for (;;) { + for (; ;) { try { return await func(); } catch (_) { @@ -488,9 +426,6 @@ async function processInitForm( this: InitCommand, { protocol: initProtocol, - product: initProduct, - studio: initStudio, - node: initNode, abi: initAbi, abiPath: initAbiPath, directory: initDirectory, @@ -501,16 +436,11 @@ async function processInitForm( subgraphName: initSubgraphName, contractName: initContractName, startBlock: initStartBlock, - allowSimpleName: initAllowSimpleName, spkgPath: initSpkgPath, }: { protocol?: ProtocolName; - product?: string; - studio: boolean; - node?: string; abi: EthereumABI; abiPath?: string; - allowSimpleName: boolean | undefined; directory?: string; contract?: string; indexEvents: boolean; @@ -523,20 +453,18 @@ async function processInitForm( }, ): Promise< | { - abi: EthereumABI; - protocolInstance: Protocol; - subgraphName: string; - directory: string; - studio: boolean; - product: string; - network: string; - contract: string; - indexEvents: boolean; - contractName: string; - startBlock: string; - fromExample: boolean; - spkgPath: string | undefined; - } + abi: EthereumABI; + protocolInstance: Protocol; + subgraphName: string; + directory: string; + network: string; + contract: string; + indexEvents: boolean; + contractName: string; + startBlock: string; + fromExample: boolean; + spkgPath: string | undefined; + } | undefined > { let abiFromEtherscan: EthereumABI | undefined = undefined; @@ -562,60 +490,13 @@ async function processInitForm( const isSubstreams = protocol === 'substreams'; initDebugger.extend('processInitForm')('isSubstreams: %O', isSubstreams); - const { product } = await prompt.ask<{ - product: 'subgraph-studio' | 'hosted-service'; - }>([ - { - type: 'select', - name: 'product', - message: 'Product for which to initialize', - choices: ['subgraph-studio', 'hosted-service'], - skip: - protocol === 'arweave' || - protocol === 'cosmos' || - protocol === 'near' || - initProduct === 'subgraph-studio' || - initProduct === 'hosted-service' || - initStudio !== undefined || - initNode !== undefined, - result: value => { - if (initProduct) return initProduct; - if (initStudio) return 'subgraph-studio'; - // For now we only support NEAR subgraphs in the Hosted Service - if (protocol === 'near') { - return 'hosted-service'; - } - - if (value == 'subgraph-studio') { - initAllowSimpleName = true; - } - - return value; - }, - }, - ]); const { subgraphName } = await prompt.ask<{ subgraphName: string }>([ { type: 'input', name: 'subgraphName', - message: () => (product == 'subgraph-studio' ? 'Subgraph slug' : 'Subgraph name'), + message: 'Subgraph slug', initial: initSubgraphName, - validate: name => { - try { - validateSubgraphName(name, { - allowSimpleName: initAllowSimpleName, - }); - return true; - } catch (e) { - return `${e.message} - - Examples: - - $ graph init ${os.userInfo().username}/${name} - $ graph init ${name} --allow-simple-name`; - } - }, }, ]); @@ -633,7 +514,7 @@ async function processInitForm( ]); let choices = (await AVAILABLE_NETWORKS())?.[ - product === 'subgraph-studio' ? 'studio' : 'hostedService' + 'studio' ]; if (!choices) { @@ -848,10 +729,8 @@ async function processInitForm( protocolInstance, subgraphName, directory, - studio: product === 'subgraph-studio', startBlock, fromExample: !!initFromExample, - product, network, contractName, contract, @@ -877,25 +756,6 @@ const loadAbiFromFile = (ABI: typeof EthereumABI, filename: string) => { } }; -function revalidateSubgraphName( - this: InitCommand, - subgraphName: string, - { allowSimpleName }: { allowSimpleName: boolean | undefined }, -) { - // Fail if the subgraph name is invalid - try { - validateSubgraphName(subgraphName, { allowSimpleName }); - return true; - } catch (e) { - this.error(`${e.message} - - Examples: - - $ graph init ${os.userInfo().username}/${subgraphName} - $ graph init ${subgraphName} --allow-simple-name`); - } -} - // Inspired from: https://github.com/graphprotocol/graph-tooling/issues/1450#issuecomment-1713992618 async function isInRepo() { try { @@ -1009,14 +869,12 @@ async function initSubgraphFromExample( this: InitCommand, { fromExample, - allowSimpleName, subgraphName, directory, skipInstall, skipGit, }: { fromExample: string | boolean; - allowSimpleName?: boolean; subgraphName: string; directory: string; skipInstall: boolean; @@ -1033,12 +891,6 @@ async function initSubgraphFromExample( }; }, ) { - // Fail if the subgraph name is invalid - if (!revalidateSubgraphName.bind(this)(subgraphName, { allowSimpleName })) { - process.exitCode = 1; - return; - } - // Fail if the output directory already exists if (filesystem.exists(directory)) { this.error(`Directory or file "${directory}" already exists`, { exit: 1 }); @@ -1158,7 +1010,6 @@ async function initSubgraphFromContract( this: InitCommand, { protocolInstance, - allowSimpleName, subgraphName, directory, abi, @@ -1173,7 +1024,6 @@ async function initSubgraphFromContract( skipGit, }: { protocolInstance: Protocol; - allowSimpleName: boolean | undefined; subgraphName: string; directory: string; abi: EthereumABI; @@ -1202,12 +1052,6 @@ async function initSubgraphFromContract( ) { const isSubstreams = protocolInstance.name === 'substreams'; - // Fail if the subgraph name is invalid - if (!revalidateSubgraphName.bind(this)(subgraphName, { allowSimpleName })) { - this.exit(1); - return; - } - // Fail if the output directory already exists if (filesystem.exists(directory)) { this.error(`Directory or file "${directory}" already exists`, { exit: 1 }); @@ -1320,7 +1164,7 @@ async function addAnotherContract( const ProtocolContract = protocolInstance.getContract()!; let contract = ''; - for (;;) { + for (; ;) { contract = await ux.prompt(`\nContract ${ProtocolContract.identifierName()}`, { required: true, }); From 2563f762154ddc6eb756bb65b64c91f307e63208 Mon Sep 17 00:00:00 2001 From: Saihajpreet Singh Date: Sat, 8 Jun 2024 11:13:55 +0300 Subject: [PATCH 2/7] format --- packages/cli/src/command-helpers/node.ts | 8 ++--- packages/cli/src/commands/auth.ts | 2 +- packages/cli/src/commands/deploy.ts | 2 +- packages/cli/src/commands/init.ts | 41 +++++++++++------------- 4 files changed, 23 insertions(+), 30 deletions(-) diff --git a/packages/cli/src/command-helpers/node.ts b/packages/cli/src/command-helpers/node.ts index f4136b258..16d90660d 100644 --- a/packages/cli/src/command-helpers/node.ts +++ b/packages/cli/src/command-helpers/node.ts @@ -7,15 +7,11 @@ export const validateNodeUrl = (node: string) => new URL(node); export const normalizeNodeUrl = (node: string) => new URL(node).toString(); -export function chooseNodeUrl({ - node, -}: { - node?: string; -}) { +export function chooseNodeUrl({ node }: { node?: string }) { if (node) { try { validateNodeUrl(node); - return { node } + return { node }; } catch (e) { print.error(`Graph node "${node}" is invalid: ${e.message}`); process.exit(1); diff --git a/packages/cli/src/commands/auth.ts b/packages/cli/src/commands/auth.ts index a0bb591c9..32227efb8 100644 --- a/packages/cli/src/commands/auth.ts +++ b/packages/cli/src/commands/auth.ts @@ -21,7 +21,7 @@ export default class AuthCommand extends Command { args: { 'deploy-key': deployKey }, } = await this.parse(AuthCommand); - const { node } = chooseNodeUrl({}) + const { node } = chooseNodeUrl({}); // eslint-disable-next-line -- prettier has problems with ||= deployKey = diff --git a/packages/cli/src/commands/deploy.ts b/packages/cli/src/commands/deploy.ts index ba78178ac..aea80d726 100644 --- a/packages/cli/src/commands/deploy.ts +++ b/packages/cli/src/commands/deploy.ts @@ -1,6 +1,6 @@ import path from 'path'; import { URL } from 'url'; -import { print, } from 'gluegun'; +import { print } from 'gluegun'; import { create } from 'ipfs-http-client'; import { Args, Command, Flags, ux } from '@oclif/core'; import { identifyDeployKey } from '../command-helpers/auth'; diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index db48923c1..553ecd9cc 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -327,7 +327,7 @@ export default class InitCommand extends Command { return; } - ({ node, } = chooseNodeUrl({ + ({ node } = chooseNodeUrl({ node, })); await initSubgraphFromContract.bind(this)( @@ -365,9 +365,9 @@ async function processFromExampleInitForm( }, ): Promise< | { - subgraphName: string; - directory: string; - } + subgraphName: string; + directory: string; + } | undefined > { try { @@ -403,7 +403,7 @@ async function processFromExampleInitForm( } async function retryWithPrompt(func: () => Promise): Promise { - for (; ;) { + for (;;) { try { return await func(); } catch (_) { @@ -453,18 +453,18 @@ async function processInitForm( }, ): Promise< | { - abi: EthereumABI; - protocolInstance: Protocol; - subgraphName: string; - directory: string; - network: string; - contract: string; - indexEvents: boolean; - contractName: string; - startBlock: string; - fromExample: boolean; - spkgPath: string | undefined; - } + abi: EthereumABI; + protocolInstance: Protocol; + subgraphName: string; + directory: string; + network: string; + contract: string; + indexEvents: boolean; + contractName: string; + startBlock: string; + fromExample: boolean; + spkgPath: string | undefined; + } | undefined > { let abiFromEtherscan: EthereumABI | undefined = undefined; @@ -490,7 +490,6 @@ async function processInitForm( const isSubstreams = protocol === 'substreams'; initDebugger.extend('processInitForm')('isSubstreams: %O', isSubstreams); - const { subgraphName } = await prompt.ask<{ subgraphName: string }>([ { type: 'input', @@ -513,9 +512,7 @@ async function processInitForm( }, ]); - let choices = (await AVAILABLE_NETWORKS())?.[ - 'studio' - ]; + let choices = (await AVAILABLE_NETWORKS())?.['studio']; if (!choices) { this.error( @@ -1164,7 +1161,7 @@ async function addAnotherContract( const ProtocolContract = protocolInstance.getContract()!; let contract = ''; - for (; ;) { + for (;;) { contract = await ux.prompt(`\nContract ${ProtocolContract.identifierName()}`, { required: true, }); From 6bbe953bb1e85dd2b6a23da4802f9fd5c4ceb63c Mon Sep 17 00:00:00 2001 From: Saihajpreet Singh Date: Sat, 8 Jun 2024 11:30:08 +0300 Subject: [PATCH 3/7] update snapshot --- .../tests/cli/__snapshots__/init.test.ts.snap | 98 +------------------ packages/cli/tests/cli/init.test.ts | 7 -- 2 files changed, 4 insertions(+), 101 deletions(-) diff --git a/packages/cli/tests/cli/__snapshots__/init.test.ts.snap b/packages/cli/tests/cli/__snapshots__/init.test.ts.snap index c8424ca84..5548455c7 100644 --- a/packages/cli/tests/cli/__snapshots__/init.test.ts.snap +++ b/packages/cli/tests/cli/__snapshots__/init.test.ts.snap @@ -3,14 +3,6 @@ exports[`Init > Ethereum > From contract 1`] = ` " › Warning: In next major version, this flag will be removed. By default we › will stop initializing a Git repository. - › Warning: In next major version, this flag will be removed. By default we - › will deploy to the Graph Studio. Learn more about Sunrise of Decentralized - › Data - › https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/ - › Warning: In next major version, this flag will be removed. By default we - › will deploy to the Graph Studio. Learn more about Sunrise of Decentralized - › Data - › https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/ - Fetching ABI from Etherscan ✔ Fetching ABI from Etherscan - Create subgraph scaffold @@ -49,14 +41,6 @@ Make sure to visit the documentation on https://thegraph.com/docs/ for further i exports[`Init > Ethereum > From contract with abi 1`] = ` " › Warning: In next major version, this flag will be removed. By default we › will stop initializing a Git repository. - › Warning: In next major version, this flag will be removed. By default we - › will deploy to the Graph Studio. Learn more about Sunrise of Decentralized - › Data - › https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/ - › Warning: In next major version, this flag will be removed. By default we - › will deploy to the Graph Studio. Learn more about Sunrise of Decentralized - › Data - › https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/ - Create subgraph scaffold Generate subgraph - Create subgraph scaffold @@ -93,14 +77,6 @@ Make sure to visit the documentation on https://thegraph.com/docs/ for further i exports[`Init > Ethereum > From contract with abi and structs 1`] = ` " › Warning: In next major version, this flag will be removed. By default we › will stop initializing a Git repository. - › Warning: In next major version, this flag will be removed. By default we - › will deploy to the Graph Studio. Learn more about Sunrise of Decentralized - › Data - › https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/ - › Warning: In next major version, this flag will be removed. By default we - › will deploy to the Graph Studio. Learn more about Sunrise of Decentralized - › Data - › https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/ - Create subgraph scaffold Generate subgraph - Create subgraph scaffold @@ -137,14 +113,6 @@ Make sure to visit the documentation on https://thegraph.com/docs/ for further i exports[`Init > Ethereum > From contract with index events and abi with ID in events 1`] = ` " › Warning: In next major version, this flag will be removed. By default we › will stop initializing a Git repository. - › Warning: In next major version, this flag will be removed. By default we - › will deploy to the Graph Studio. Learn more about Sunrise of Decentralized - › Data - › https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/ - › Warning: In next major version, this flag will be removed. By default we - › will deploy to the Graph Studio. Learn more about Sunrise of Decentralized - › Data - › https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/ - Create subgraph scaffold Generate subgraph - Create subgraph scaffold @@ -181,14 +149,6 @@ Make sure to visit the documentation on https://thegraph.com/docs/ for further i exports[`Init > Ethereum > From contract with list items in abi 1`] = ` " › Warning: In next major version, this flag will be removed. By default we › will stop initializing a Git repository. - › Warning: In next major version, this flag will be removed. By default we - › will deploy to the Graph Studio. Learn more about Sunrise of Decentralized - › Data - › https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/ - › Warning: In next major version, this flag will be removed. By default we - › will deploy to the Graph Studio. Learn more about Sunrise of Decentralized - › Data - › https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/ - Create subgraph scaffold Generate subgraph - Create subgraph scaffold @@ -225,14 +185,6 @@ Make sure to visit the documentation on https://thegraph.com/docs/ for further i exports[`Init > Ethereum > From contract with overloaded elements 1`] = ` " › Warning: In next major version, this flag will be removed. By default we › will stop initializing a Git repository. - › Warning: In next major version, this flag will be removed. By default we - › will deploy to the Graph Studio. Learn more about Sunrise of Decentralized - › Data - › https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/ - › Warning: In next major version, this flag will be removed. By default we - › will deploy to the Graph Studio. Learn more about Sunrise of Decentralized - › Data - › https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/ - Create subgraph scaffold Generate subgraph - Create subgraph scaffold @@ -269,14 +221,6 @@ Make sure to visit the documentation on https://thegraph.com/docs/ for further i exports[`Init > Ethereum > From example 1`] = ` " › Warning: In next major version, this flag will be removed. By default we › will stop initializing a Git repository. - › Warning: In next major version, this flag will be removed. By default we - › will deploy to the Graph Studio. Learn more about Sunrise of Decentralized - › Data - › https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/ - › Warning: In next major version, this flag will be removed. By default we - › will deploy to the Graph Studio. Learn more about Sunrise of Decentralized - › Data - › https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/ - Cloning example subgraph ✔ Cloning example subgraph - Initialize networks config @@ -309,45 +253,11 @@ Make sure to visit the documentation on https://thegraph.com/docs/ for further i `; exports[`Init > NEAR > From contract 1`] = ` -" › Warning: In next major version, this flag will be removed. By default we - › will stop initializing a Git repository. - › Warning: In next major version, this flag will be removed. By default we - › will deploy to the Graph Studio. Learn more about Sunrise of Decentralized - › Data - › https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/ - › Warning: In next major version, this flag will be removed. By default we - › will deploy to the Graph Studio. Learn more about Sunrise of Decentralized - › Data - › https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/ -- Create subgraph scaffold - Generate subgraph -- Create subgraph scaffold - Write subgraph to directory -- Create subgraph scaffold -✔ Create subgraph scaffold -- Initialize networks config -✔ Initialize networks config -- Install dependencies with yarn -✔ Install dependencies with yarn -- Generate ABI and schema types with yarn codegen -✔ Generate ABI and schema types with yarn codegen +" › Error: Nonexistent flag: --product + › See more help with --help " `; -exports[`Init > NEAR > From contract 2`] = `0`; +exports[`Init > NEAR > From contract 2`] = `2`; -exports[`Init > NEAR > From contract 3`] = ` -" -Subgraph user/near-from-contract created in from-contract - -Next steps: - - 1. Run \`graph auth\` to authenticate with your deploy key. - - 2. Type \`cd from-contract\` to enter the subgraph. - - 3. Run \`yarn deploy\` to deploy the subgraph. - -Make sure to visit the documentation on https://thegraph.com/docs/ for further information. -" -`; +exports[`Init > NEAR > From contract 3`] = `""`; diff --git a/packages/cli/tests/cli/init.test.ts b/packages/cli/tests/cli/init.test.ts index 8218ab1fb..82b973ea4 100644 --- a/packages/cli/tests/cli/init.test.ts +++ b/packages/cli/tests/cli/init.test.ts @@ -19,7 +19,6 @@ describe.sequential( '--skip-git', '--protocol', 'ethereum', - '--studio', '--from-example', 'ethereum/gravatar', 'user/example-subgraph', @@ -41,7 +40,6 @@ describe.sequential( '--skip-git', '--protocol', 'ethereum', - '--studio', '--from-contract', '0xF87E31492Faf9A91B02Ee0dEAAd50d51d56D5d4d', '--network', @@ -65,7 +63,6 @@ describe.sequential( '--skip-git', '--protocol', 'ethereum', - '--studio', '--from-contract', '0xF87E31492Faf9A91B02Ee0dEAAd50d51d56D5d4d', '--abi', @@ -91,7 +88,6 @@ describe.sequential( '--skip-git', '--protocol', 'ethereum', - '--studio', '--from-contract', '0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e', '--abi', @@ -117,7 +113,6 @@ describe.sequential( '--skip-git', '--protocol', 'ethereum', - '--studio', '--from-contract', '0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e', '--abi', @@ -143,7 +138,6 @@ describe.sequential( '--skip-git', '--protocol', 'ethereum', - '--studio', '--from-contract', '0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e', '--abi', @@ -169,7 +163,6 @@ describe.sequential( '--skip-git', '--protocol', 'ethereum', - '--studio', '--from-contract', '0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85', '--index-events', From fd1fcd932e53c5e104c04f8ed0eac207b2be5456 Mon Sep 17 00:00:00 2001 From: Etienne Donneger Date: Fri, 25 Oct 2024 10:12:24 -0400 Subject: [PATCH 4/7] Fix missing refs --- packages/cli/src/commands/auth.ts | 4 ---- packages/cli/src/commands/deploy.ts | 2 -- 2 files changed, 6 deletions(-) diff --git a/packages/cli/src/commands/auth.ts b/packages/cli/src/commands/auth.ts index bcb100d93..32227efb8 100644 --- a/packages/cli/src/commands/auth.ts +++ b/packages/cli/src/commands/auth.ts @@ -34,10 +34,6 @@ export default class AuthCommand extends Command { this.error('✖ Deploy key must not exceed 200 characters', { exit: 1 }); } - if (product === 'hosted-service' || node?.match(/api.thegraph.com/)) { - this.error('✖ The hosted service is deprecated', { exit: 1 }); - } - try { await saveDeployKey(node!, deployKey); print.success(`Deploy key set for ${node}`); diff --git a/packages/cli/src/commands/deploy.ts b/packages/cli/src/commands/deploy.ts index fe7d416f5..f55b5f770 100644 --- a/packages/cli/src/commands/deploy.ts +++ b/packages/cli/src/commands/deploy.ts @@ -125,8 +125,6 @@ export default class DeployCommand extends Command { this.error('No Graph node provided'); } - const isStudio = node.match(/studio/); - const requestUrl = new URL(node); const client = createJsonRpcClient(requestUrl); From f92631e58340cdeeccc275af5f1c5db6b11277f1 Mon Sep 17 00:00:00 2001 From: Etienne Donneger Date: Fri, 25 Oct 2024 15:15:53 -0400 Subject: [PATCH 5/7] Update `init` snapshot --- packages/cli/tests/cli/__snapshots__/init.test.ts.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/tests/cli/__snapshots__/init.test.ts.snap b/packages/cli/tests/cli/__snapshots__/init.test.ts.snap index 5548455c7..9b49ad043 100644 --- a/packages/cli/tests/cli/__snapshots__/init.test.ts.snap +++ b/packages/cli/tests/cli/__snapshots__/init.test.ts.snap @@ -253,7 +253,7 @@ Make sure to visit the documentation on https://thegraph.com/docs/ for further i `; exports[`Init > NEAR > From contract 1`] = ` -" › Error: Nonexistent flag: --product +" › Error: Nonexistent flag: --studio › See more help with --help " `; From 208cac87b59614a17031f198f390075bc9343ce8 Mon Sep 17 00:00:00 2001 From: YaroShkvorets Date: Thu, 31 Oct 2024 00:42:31 -0400 Subject: [PATCH 6/7] Remove hosted service from readme, add more relevant information --- packages/cli/README.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/cli/README.md b/packages/cli/README.md index 295dbecac..8929e2335 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -29,12 +29,14 @@ As of today, the command line interface supports the following commands: The Graph CLI takes a subgraph manifest (defaults to `subgraph.yaml`) with references to: - A GraphQL schema, -- Smart contract ABIs, and -- Mappings written in AssemblyScript. +- Smart contract ABIs, +- Mappings written in AssemblyScript for traditional subgraphs, +- Substreams package and triggers for substreams-based subgraphs It compiles the mappings to WebAssembly, builds a ready-to-use version of the subgraph saved to IPFS or a local directory for debugging, and deploys the subgraph to a -[Graph Node](https://github.com/graphprotocol/graph-node). +[Graph Node](https://github.com/graphprotocol/graph-node) instance or [Subgraph Studio](https://thegraph.com/studio/). +Additionally it allows you to publish your subgraph to the decentralized network directly, making it available for indexing via [Graph Explorer](https://thegraph.com/explorer) ## Installation @@ -71,13 +73,14 @@ Use one of the following commands depending on your distribution: ## Getting Started -The Graph CLI can be used with a local or self-hosted -[Graph Node](https://github.com/graphprotocol/graph-node) or with the -[Hosted Service](https://thegraph.com/explorer/). To help you get going, there are -[quick start guides](https://thegraph.com/docs/en/developer/quick-start/) available for both. +The Graph CLI can be used with a local or self-hosted [Graph Node](https://github.com/graphprotocol/graph-node) or with the +[Subgraph Studio](https://thegraph.com/studio/). To help you get going, there are +[quick start guides](https://thegraph.com/docs/en/quick-start/) available for both. + +Additionally, you can use Graph CLI to [publish](https://thegraph.com/docs/en/quick-start/#publishing-from-the-cli) your subgraph to the decentralized network directly. If you are ready to dive into the details of building a subgraph from scratch, there is a -[detailed walkthrough](https://thegraph.com/docs/en/developer/create-subgraph-hosted/) for that as +[detailed walkthrough](https://thegraph.com/docs/en/developing/creating-a-subgraph/) for that as well, along with API documentation for the [AssemblyScript API](https://thegraph.com/docs/en/developer/assemblyscript-api/). From 2d9c5cd8f312cf24c8e1de4ebf7bc3255c83e841 Mon Sep 17 00:00:00 2001 From: Etienne Donneger Date: Thu, 31 Oct 2024 10:47:50 -0400 Subject: [PATCH 7/7] Fix lint --- packages/cli/README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/cli/README.md b/packages/cli/README.md index 8929e2335..5ecdf7380 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -35,8 +35,10 @@ The Graph CLI takes a subgraph manifest (defaults to `subgraph.yaml`) with refer It compiles the mappings to WebAssembly, builds a ready-to-use version of the subgraph saved to IPFS or a local directory for debugging, and deploys the subgraph to a -[Graph Node](https://github.com/graphprotocol/graph-node) instance or [Subgraph Studio](https://thegraph.com/studio/). -Additionally it allows you to publish your subgraph to the decentralized network directly, making it available for indexing via [Graph Explorer](https://thegraph.com/explorer) +[Graph Node](https://github.com/graphprotocol/graph-node) instance or +[Subgraph Studio](https://thegraph.com/studio/). Additionally it allows you to publish your subgraph +to the decentralized network directly, making it available for indexing via +[Graph Explorer](https://thegraph.com/explorer) ## Installation @@ -73,11 +75,14 @@ Use one of the following commands depending on your distribution: ## Getting Started -The Graph CLI can be used with a local or self-hosted [Graph Node](https://github.com/graphprotocol/graph-node) or with the +The Graph CLI can be used with a local or self-hosted +[Graph Node](https://github.com/graphprotocol/graph-node) or with the [Subgraph Studio](https://thegraph.com/studio/). To help you get going, there are [quick start guides](https://thegraph.com/docs/en/quick-start/) available for both. -Additionally, you can use Graph CLI to [publish](https://thegraph.com/docs/en/quick-start/#publishing-from-the-cli) your subgraph to the decentralized network directly. +Additionally, you can use Graph CLI to +[publish](https://thegraph.com/docs/en/quick-start/#publishing-from-the-cli) your subgraph to the +decentralized network directly. If you are ready to dive into the details of building a subgraph from scratch, there is a [detailed walkthrough](https://thegraph.com/docs/en/developing/creating-a-subgraph/) for that as