diff --git a/gnd/README.md b/gnd/README.md index 7d6ed59c7db..7ab0bfae7b0 100644 --- a/gnd/README.md +++ b/gnd/README.md @@ -378,6 +378,52 @@ gnd dev [OPTIONS] | `--ethereum-rpc` | | Ethereum RPC URL | | `--ipfs` | | IPFS node URL | +### `gnd indexer` + +Manage indexer operations via [`indexer-cli`](https://github.com/graphprotocol/indexer/tree/main/packages/indexer-cli). + +Requires `graph-indexer` to be installed and on `$PATH`: + +```bash +npm install -g @graphprotocol/indexer-cli +``` + +```bash +gnd indexer [args...] +``` + +**Help:** + +There are two ways to get help: + +| Command | What it shows | +|---------|---------------| +| `gnd indexer --help` | gnd's own help for the indexer subcommand (works without `graph-indexer` installed) | +| `gnd indexer help` | Full `graph-indexer` help with all available commands (requires `graph-indexer`) | + +**Examples:** + +```bash +# Check indexer status +gnd indexer status --network arbitrum-one + +# Manage indexing rules +gnd indexer rules get all --network mainnet +gnd indexer rules set decisionBasis always --network mainnet + +# Manage allocations +gnd indexer allocations get --network arbitrum-one + +# Manage cost models +gnd indexer cost get + +# View available indexer commands +gnd indexer help + +# Check graph-indexer version +gnd indexer version +``` + ### `gnd completions` Generate shell completions. diff --git a/gnd/docs/migrating-from-graph-cli.md b/gnd/docs/migrating-from-graph-cli.md index 792f5727d06..960f2104f11 100644 --- a/gnd/docs/migrating-from-graph-cli.md +++ b/gnd/docs/migrating-from-graph-cli.md @@ -33,6 +33,7 @@ gnd deploy my-subgraph # defaults to Studio | `graph test` | `gnd test` | Identical flags | | `graph clean` | `gnd clean` | Identical flags (new in graph-cli 0.80+) | | `graph publish` | `gnd publish` | Identical flags | +| `graph indexer` | `gnd indexer` | Delegates to `graph-indexer` — requires `indexer-cli` installed | | `graph local` | N/A | Not implemented - use graph-node's test infrastructure | | `graph node` | N/A | Not implemented - use `graphman` | @@ -67,6 +68,7 @@ Same success checkmarks, step descriptions, and progress indicators: - `0` for success - `1` for any error +- `gnd indexer` passes through the exit code from `graph-indexer` ### Configuration Files @@ -83,6 +85,20 @@ Code generation produces identical AssemblyScript output (after formatting). You ## What's Different +### Indexer Commands + +`gnd indexer` provides access to indexer management commands via [`indexer-cli`](https://github.com/graphprotocol/indexer/tree/main/packages/indexer-cli). Requires `graph-indexer` on `$PATH`: + +```bash +npm install -g @graphprotocol/indexer-cli + +gnd indexer status --network arbitrum-one +gnd indexer rules get all --network mainnet +gnd indexer allocations get --network arbitrum-one +``` + +Note: `gnd indexer --help` shows gnd's own help and works without `graph-indexer` installed. Use `gnd indexer help` to see the full list of `graph-indexer` commands. + ### Commands Not Available #### `graph local` diff --git a/gnd/src/main.rs b/gnd/src/main.rs index d4c90f72ba3..94243ca1f16 100644 --- a/gnd/src/main.rs +++ b/gnd/src/main.rs @@ -1,6 +1,8 @@ +use std::ffi::OsString; use std::io; +use std::process::Command; -use anyhow::Result; +use anyhow::{bail, Result}; use clap::{CommandFactory, Parser, Subcommand, ValueEnum}; use clap_complete::{generate, Shell}; use git_testament::{git_testament, render_testament}; @@ -83,6 +85,22 @@ enum Commands { /// Remove build artifacts and generated files Clean(CleanOpt), + /// Manage indexer operations. Requires `graph-indexer` to be installed and available on $PATH. + /// + /// Install it with: + /// + /// npm install -g @graphprotocol/indexer-cli + /// + /// Arguments are forwarded to `graph-indexer`. For example: + /// + /// gnd indexer allocations get --network arbitrum-one + /// + /// Use `gnd indexer help` for more details on available commands. + Indexer { + #[arg(trailing_var_arg = true, allow_hyphen_values = true)] + args: Vec, + }, + /// Generate shell completions Completions(CompletionsOpt), } @@ -153,6 +171,57 @@ fn generate_completions(completions_opt: CompletionsOpt) -> Result<()> { Ok(()) } +/// Delegates to the `graph-indexer` binary from the `indexer-cli` npm package. +/// +/// The `graph-indexer` CLI expects all indexer management commands to be +/// prefixed with `indexer` (e.g. `graph-indexer indexer allocations get ...`). +/// This is because `graph-indexer` uses Gluegun's plugin system where `indexer` +/// is the plugin namespace. We prepend it automatically so that users can write +/// `gnd indexer allocations get` instead of the longer form. +fn run_indexer(args: Vec) -> Result<()> { + let mut cmd = Command::new("graph-indexer"); + // `version` and `help` are top-level commands, but all other commands + // require the `indexer` namespace prefix (e.g. `graph-indexer indexer allocations get`). + let first = args.first().and_then(|s| s.to_str()); + match first { + Some("version" | "--version" | "v" | "-v" | "help" | "h") => {} + // If no arguments are provided, default to showing top-level help + None => { + cmd.arg("help"); + } + _ => { + cmd.arg("indexer"); + } + } + cmd.args(&args); + + #[cfg(unix)] + { + use std::os::unix::process::CommandExt; + let err = cmd.exec(); + if err.kind() == io::ErrorKind::NotFound { + bail!( + "'graph-indexer' not found on $PATH. Install indexer-cli: npm install -g @graphprotocol/indexer-cli" + ); + } + bail!("failed to exec 'graph-indexer': {err}"); + } + + #[cfg(not(unix))] + { + match cmd.status() { + Ok(status) if status.success() => Ok(()), + Ok(status) => std::process::exit(status.code().unwrap_or(1)), + Err(e) if e.kind() == io::ErrorKind::NotFound => { + bail!( + "'graph-indexer' not found on $PATH. Install indexer-cli: npm install -g @graphprotocol/indexer-cli" + ); + } + Err(e) => bail!("failed to run 'graph-indexer': {e}"), + } + } +} + #[tokio::main] async fn main() -> Result<()> { std::env::set_var("ETHEREUM_REORG_THRESHOLD", "10"); @@ -186,6 +255,7 @@ async fn main() -> Result<()> { run_test(test_opt).await } Commands::Clean(clean_opt) => run_clean(clean_opt), + Commands::Indexer { args } => run_indexer(args), Commands::Completions(completions_opt) => generate_completions(completions_opt), }; diff --git a/gnd/tests/cli_commands.rs b/gnd/tests/cli_commands.rs index e1b5cde09ac..2b4967bfb7b 100644 --- a/gnd/tests/cli_commands.rs +++ b/gnd/tests/cli_commands.rs @@ -80,6 +80,61 @@ fn run_gnd_success(args: &[&str], cwd: &Path) -> std::process::Output { output } +// ============================================================================ +// gnd indexer tests +// ============================================================================ + +#[test] +fn test_indexer_missing_binary_error() { + let gnd = verify_gnd_binary(); + + // Run with an empty PATH so graph-indexer cannot be found. + let output = Command::new(&gnd) + .args(["indexer", "status", "--network", "mainnet"]) + .env("PATH", "") + .output() + .expect("Failed to execute gnd"); + + assert!( + !output.status.success(), + "gnd indexer should fail when graph-indexer is not on $PATH" + ); + + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + stderr.contains("graph-indexer") && stderr.contains("not found"), + "Error should mention graph-indexer not found. Got:\n{stderr}" + ); + assert!( + stderr.contains("indexer-cli"), + "Error should mention indexer-cli install instructions. Got:\n{stderr}" + ); +} + +#[test] +fn test_indexer_clap_help() { + let gnd = verify_gnd_binary(); + + // `--help` is intercepted by clap, not forwarded to graph-indexer. + let output = Command::new(&gnd) + .args(["indexer", "--help"]) + .env("PATH", "") + .output() + .expect("Failed to execute gnd"); + + assert!(output.status.success(), "gnd indexer --help should succeed"); + + let stdout = String::from_utf8_lossy(&output.stdout); + assert!( + stdout.contains("indexer-cli"), + "Help should mention indexer-cli. Got:\n{stdout}" + ); + assert!( + stdout.contains("graph-indexer"), + "Help should mention graph-indexer. Got:\n{stdout}" + ); +} + // ============================================================================ // gnd init tests // ============================================================================