Skip to content

Conversation

shubhexists
Copy link

Issue Addressed

#524

Which issue # does this PR address?
Auto generation of docs from clap cli args

Additional Info

I thought implementing it directly via clap's CommandFactory is a much cleaner way to get all the arguments and generate docs out of it. This is my implementation of how I think it could be done.

Tried to match the exact structure of the docs which were currently there. Current issues/points that could be discussed could be -

  1. The description of some clap commands are pretty long which are eventually reflected in the md docs.
  2. Default values of some commands are not provided via clap which is making it hard to get the default values.....
  3. Not sure if examples in md could be autogenerated. Haven't thought much about that yet.

PS - The current docs in this branch are generated via the docgen.

cc for reviews: @chong-he @dknopik

Copy link

cla-assistant bot commented Aug 26, 2025

CLA assistant check
All committers have signed the CLA.

@chong-he chong-he added the documentation Improvements or additions to documentation label Sep 2, 2025
@chong-he chong-he self-requested a review September 2, 2025 07:44
Copy link
Member

@chong-he chong-he left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the PR. This is nice! I like the use of clap built-in functions to extract the CLI help text. Some comments:

  1. cargo fmt is failing. You can run cargo +nightly fmt --all to fix this

  2. The goal of the docgen is to have it integrated in the CI, so that whenever a doc is modified, it must be updated or fails the CI.

Therefore, could you also modify this script and integrate in the CI? The script is copied from Lighthouse where we generate the CLI help text using --help, but in this case, we can do so with anchor docgen, and then compare with the existing docs. In the future, when devs are updating the doc, they just have to run the script and the doc can be updated.

Thanks!

Comment on lines 227 to 236
let default = if arg.get_default_values().is_empty() {
"None".to_string()
} else {
let values: Vec<String> = arg
.get_default_values()
.iter()
.map(|v| v.to_string_lossy().to_string())
.collect();
format!("`{}`", values.join(", "))
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is about #2 in your original post on default values, I can see that the docgen can capture the default values if there is default_value in the clap, example:

default_value = "0.0.0.0",

There are some flags with default_value_if that are not captured. Not sure if we can have another else if arm to check for these flags? The get_default_values is returning this field: https://docs.rs/clap_builder/4.5.47/src/clap_builder/builder/arg.rs.html#4341

Although I don't see the method to "get default values if" but I wonder if we can do something similar like arg.default_vals_ifs?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we put the condition in long_help?

@@ -24,6 +24,7 @@ http_api = { workspace = true }
http_metrics = { workspace = true }
hyper = { workspace = true }
keygen = { workspace = true }
keysplit = { workspace = true }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to add keysplit?

Copy link
Member

@dknopik dknopik Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. It would be nicer to have the docgen as a separate binary target of the top level crate IMO.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi what would be better, a separate binary target for docgen or have a top level library crate that it then called into the main anchor binary as a CLI command?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My recommendation: make docgen a separate workspace binary (e.g. tools/docgen) rather than an anchor client subcommand. This keeps the shipping CLI lean, avoids linking extra deps (e.g., keysplit) into the client crate.

Sketch:
• tools/docgen/Cargo.toml (bin = docgen)
• Depends on the crates that define the clap trees (e.g. anchor-client, keygen, keysplit) via path = "../anchor/client" etc.
• Each of those crates exposes a small pub fn cli() -> clap::Command (or impl CommandFactory) so docgen can introspect without booting the whole app.

Copilot

This comment was marked as outdated.

Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces an automated documentation generation system for CLI commands using clap's CommandFactory trait. The system generates markdown documentation for the node, keygen, and keysplit commands by introspecting their CLI argument definitions.

Key changes:

  • Added a new docgen subcommand that generates CLI documentation automatically
  • Refactored the Node CLI structure into separate grouped structs for better organization
  • Updated documentation files with auto-generated content and version bumps

Reviewed Changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
anchor/src/main.rs Added new docgen subcommand to main CLI
anchor/client/src/docs.rs New documentation generator implementation using clap introspection
anchor/client/src/cli.rs Refactored Node struct into grouped option structs for better organization
anchor/client/src/config.rs Updated to use new nested CLI structure for configuration parsing
docs/docs/pages/*.mdx Auto-generated documentation files with updated content and version numbers
docs/vocs.config.ts Version bump from v0.2.0 to v0.3.1
Comments suppressed due to low confidence (1)

anchor/client/src/docs.rs:1

  • The condition id.contains(\"nodes\") is checked twice in consecutive match arms. This creates unreachable code since line 285 will always match before line 286 for IDs containing "nodes".
use std::{env, fs};

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +34 to +40
| `--keystore-path <PATH>` | Path to the validator keystore file | None |
| `--password <VALUE>` | Password for the validator keystore | None |
| `--owner <VALUE>` | EOA address that owns the validator | None |
| `--output-path <PATH>` | Path for output | None |
| `--operators <VALUE>` | Operators to split key among | None |
| `--nonce <VALUE>` | Nonce for the owner address | None |
| `--public-keys <VALUE>` | RSA public keys for the operators | None |
Copy link
Preview

Copilot AI Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The manual-specific options table duplicates all the shared options. This creates maintenance overhead as changes to shared options need to be updated in multiple places.

Copilot uses AI. Check for mistakes.

@shubhexists
Copy link
Author

Hi guys, any suggestions on the 2 unsolved comments?

@diegomrsantos
Copy link
Contributor

Would using clap_markdown and a slim post-processor for the MDX layout be a better solution than creating a custom renderer? The less code unrelated to our project goals that we have to maintain, the better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants