Skip to content

cli: build CliContext in main and centralize error rendering#3755

Open
juan-malbeclabs wants to merge 2 commits into
jo/2-cli-global-flagsfrom
jo/3-cli-context-and-errors
Open

cli: build CliContext in main and centralize error rendering#3755
juan-malbeclabs wants to merge 2 commits into
jo/2-cli-global-flagsfrom
jo/3-cli-context-and-errors

Conversation

@juan-malbeclabs
Copy link
Copy Markdown
Contributor

@juan-malbeclabs juan-malbeclabs commented May 22, 2026

RFC-20 implementation stack

This PR is part of a 9-PR chain delivering RFC-20: CLI standardization. Each PR's diff is only its own contribution; reviewers should consume them in order.

# PR Scope
1 #3753 doublezero-cli-core foundation crate + solana_l1_rpc_url
2 #3754 --solana-url + --log-verbose global flags + tracing init
3 #3755 CliContext built in main + centralized error rendering
4 #3756 rename doublezero_clidoublezero-serviceability-cli
5 #3757 rewrite location get as the async + CliContext reference verb
6 #3758 docs/cli-standard.md + CLAUDE.md pointer
7 #3759 move per-resource subcommand wrappers into the module crate
8 #3760 add ServiceabilityCommand enum + async dispatcher
9 #3761 #[command(flatten)] + collapse binary dispatch

This PR: #3755 — position 3 of 9. Previous: #3754 · Next: #3756


Summary of Changes

  • Builds a CliContext once at binary startup from --env and the per-field global overrides (--url, --ws, --solana-url, --keypair, --sock-file), per RFC-20 (§CliContext). The context resolves to Environment::default() (devnet) when --env is absent. DZClient continues to consume the legacy Option<String> tuple via a thin bridge that forwards None when neither --env nor a per-field override is set, preserving today's fall-back to ~/.config/solana/cli/config.yml. Verbs that migrate to the RFC-20 module contract will consume CliContext directly and the bridge shrinks.
  • Centralizes top-level error rendering through doublezero_cli_core::error::render_eyre. Replaces three ad-hoc eprintln!("Error: {e}") sites in client/doublezero/src/main.rs (env-parse failure, env-config resolution failure, top-level command failure) with a single helper that prints Error: <head> followed by the full chain of causes on stderr.

Diff Breakdown

Category Files Lines (+/-) Net
Core logic 1 +55 / -23 +32
Docs 1 +2 / -1 +1
Total 2 +57 / -24 +33

Replaces the ad-hoc env-resolution tuple in main.rs with a CliContextBuilder invocation and unifies error rendering; no change to subcommand dispatch shape.

Key files (click to expand)
  • client/doublezero/src/main.rs - constructs CliContext via CliContextBuilder after App::parse(), bridges back to DZClient::new for the legacy Option<String> signature without changing the no-flags fall-back behavior, and routes the three error-exit sites through doublezero_cli_core::error::render_eyre.

Testing Verification

  • cargo check --workspace clean.
  • doublezero --env totally-bogus address prints a single-line Error: Invalid environment ... on stderr (via the new render_eyre) and exits 1, matching the previous behavior.
  • doublezero --env devnet address resolves and prints the address (CliContext populated from Environment::Devnet); doublezero address with no flags still falls back to ~/.config/solana/cli/config.yml via the bridge.
  • Targets jo/2-cli-global-flags; the diff shown is only this PR's contribution.

@vihu
Copy link
Copy Markdown
Contributor

vihu commented May 22, 2026

The bridge currently sets ws = Some(ctx.ledger_ws_rpc_url.clone()) whenever env_explicit || app.ws.is_some(). That means doublezero --env devnet --url <custom-rpc> with no --ws pairs the custom RPC URL with the env default websocket URL.

DZClient::new only derives WS from the selected RPC URL when websocket_url is None, so this loses the expected derivation behavior for custom RPC overrides. Could we pass None for WS when --url is overridden and --ws is absent, or otherwise derive WS from the chosen URL?

@juan-malbeclabs juan-malbeclabs force-pushed the jo/2-cli-global-flags branch from 7a7f8b6 to 19c7707 Compare May 24, 2026 14:39
Replaces the three explicit if/else blocks that translated CliContext
into DZClient::new arguments with a flat `any_url_explicit.then(...)`
form. The behavior is identical: when no env or per-field override is
present, all three fields stay None so DZClient falls through to the
on-disk config; otherwise the resolved CliContext values flow through.

Drops the assumption that env_explicit alone resolves URL/program-id
fields: any explicit override (--url, --ws, --program-id) now also
opts into using the resolved context, which keeps the bridge in step
with the builder's WS-from-RPC derivation introduced in jo/1.

Also corrects the stale comment that referenced
`~/.config/solana/cli/config.yml`; DZClient actually reads
`~/.config/doublezero/cli/config.yml`.
@juan-malbeclabs juan-malbeclabs force-pushed the jo/3-cli-context-and-errors branch from 02beec8 to d121d83 Compare May 24, 2026 14:39
@juan-malbeclabs
Copy link
Copy Markdown
Contributor Author

The change was applied to the corresponding PRs.

┌─────────────────────────────┬───────┬──────────────┬─────────────────────────────────────────────────────────────────────────┐
│           Branch            │  PR   │ Local commit │                                 Change                                  │
├─────────────────────────────┼───────┼──────────────┼─────────────────────────────────────────────────────────────────────────┤
│ jo/1-cli-core-foundation    │ #3753 │ 5e6002d64    │ cli/core: derive WS URL from RPC override in CliContextBuilder          │
├─────────────────────────────┼───────┼──────────────┼─────────────────────────────────────────────────────────────────────────┤
│ jo/2-cli-global-flags       │ #3754 │ b0424349e    │ cli: make --env mutually exclusive with per-field URL/program overrides │
├─────────────────────────────┼───────┼──────────────┼─────────────────────────────────────────────────────────────────────────┤
│ jo/3-cli-context-and-errors │ #3755 │ 853a3069a    │ cli: collapse CliContext bridge and fix config-path comment             │
└─────────────────────────────┴───────┴──────────────┴─────────────────────────────────────────────────────────────────────────┘

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants