Skip to content

[go-fan] Go Module Review: go-gh (github.com/cli/go-gh/v2)Β #43983

Description

@github-actions

🐹 Go Fan Report: cli/go-gh

Today's pick, selected round-robin by most-recent upstream activity: github.com/cli/go-gh/v2 @ v2.13.0 β€” the most recently pushed direct dependency (upstream pushed_at 2026-07-06). Fittingly, it's GitHub's own Go toolkit and one of the load-bearing deps in gh-aw. πŸŽ‰

Module Overview

go-gh is the official Go SDK behind the gh CLI. It hands you authenticated REST and GraphQL clients that auto-resolve host, token, and config from the local gh environment, plus gh.Exec (shell out to the gh binary) and repository.Current() for repo context.

Current Usage in gh-aw

  • Files: 9 non-test files across pkg/parser, pkg/cli, pkg/workflow
  • Import Count: 13 import lines (incl. tests)
  • Key APIs Used: api.NewRESTClient / api.DefaultRESTClient, RESTClient.DoWithContext, RESTClient.Get, api.ClientOptions{Timeout,Host}, gh.Exec, repository.Current

The REST client is used well β€” pkg/parser centralizes construction in createRESTClientForHost and threads context through DoWithContext, with a layered fallback chain (authenticated API β†’ git ls-remote/clone β†’ unauthenticated public API) that's genuinely thoughtful.

Per-file breakdown
File Usage
pkg/parser/remote_client.go createRESTClientForHost, DoWithContext (Contents API)
pkg/parser/remote_download_file.go REST client, symlink resolution
pkg/parser/remote_list_files.go REST client, dir/recursive listing
pkg/parser/remote_resolve_sha.go gh.Exec("api", ...) commit lookup
pkg/cli/copilot_billing_check.go DoWithContext w/ 3s timeout
pkg/cli/update_check.go NewRESTClient(opts)
pkg/cli/update_cooldown.go NewRESTClient(ClientOptions{})
pkg/cli/secret_set_command.go NewRESTClient(opts)
pkg/workflow/repository_features_validation.go DefaultRESTClient+Get, gh.Exec GraphQL, repository.Current

Research Findings

  • Version: v2.13.0 is the latest (2025-11-04); requires Go 1.25.0 β€” repo is on go 1.26.3 βœ… and already on the newest tag.
  • v2.13.0 changes: expose sprig replace template func, handle HTTP 205 correctly, bump to Go 1.25.0. No breaking changes vs v2.12.x β€” no upgrade work needed.

Best Practices (from maintainers)

  • Prefer the native api.RESTClient / api.GraphQLClient over shelling out to gh when you're already in Go β€” no dependency on the gh binary, native context support, structured api.HTTPError (with StatusCode).
  • GraphQLClient offers typed Query/Mutate (struct-derived) and raw Do/DoWithContext (query string) variants.

Improvement Opportunities

πŸƒ Quick Wins

  • Swap the discussions gh.Exec("api","graphql",...) for a native GraphQL client β€” pkg/workflow/repository_features_validation.go:281. It's the only GraphQL call and the only reason that check needs the gh binary on PATH. api.NewGraphQLClient(...).DoWithContext(ctx, checkRepositoryHasDiscussionsQuery, map[string]any{"owner":owner,"name":name}, &resp) gives you native timeout/cancel and matches the REST pattern used everywhere else.
  • Bound checkRepositoryHasIssuesUncached with a timeout β€” repository_features_validation.go:313-320 uses client.Get(...) with no context; switch to DoWithContext with a short context (mirror the 3s copilot-billing probe) so feature validation can't hang on a slow network.

✨ Feature Opportunities

  • Typed GraphQL (GraphQLClient.Query) could replace the hand-written query string + response struct for the discussions check with one schema struct.
  • Response caching (ClientOptions.EnableCache + CacheTTL) for repeated reads (update checks, repo-feature probes) to cut redundant round-trips across CLI runs.

πŸ“ Best Practice Alignment

  • remote_resolve_sha.go:93 uses gh.Exec("api", ...) for a REST commit lookup, in a package that otherwise uses api.RESTClient via createRESTClientForHost. Moving it to DoWithContext makes the resolver self-contained (no gh binary) and enables native cancellation. Nuance: the auth-error fallback currently keys on gh stderr via gitutil.IsAuthError; with the REST client it would key on api.HTTPError.StatusCode (401/403) β€” arguably cleaner. Worth weighing against the intentional simplicity of reusing gh's auth chain.
  • NewRESTClient(api.ClientOptions{}) with empty opts (copilot_billing_check.go:56, update_cooldown.go:64) is equivalent to api.DefaultRESTClient(); the latter reads more clearly as "use gh-resolved config."

πŸ”§ General Improvements

  • Consider a shared REST-client constructor in pkg/cli (mirroring parser.createRESTClientForHost) to standardize timeouts/options across the four cli call sites.

Recommendations (prioritized)

  1. Migrate the discussions GraphQL check off gh.Exec to api.GraphQLClient (removes gh-binary dependency for a validation path; highest value).
  2. Add a timeout to checkRepositoryHasIssuesUncached.
  3. Optionally migrate remote_resolve_sha.go REST lookup to DoWithContext, adapting the auth-error detection to api.HTTPError.
  4. Tidy: DefaultRESTClient() over empty-opts NewRESTClient.

Next Steps

  • No dependency bump required β€” already on latest v2.13.0.
  • The two gh.Exec sites are the main modernization targets; each is a self-contained, low-risk change with existing test coverage nearby (copilot_billing_check_test.go, remote_fetch_integration_test.go).

Generated by Go Fan
Module summary saved to: scratchpad/mods/go-gh.md

Generated by 🐹 Go Fan Β· 167.4 AIC Β· βŒ– 13.5 AIC Β· ⊞ 7.3K Β· β—·

  • expires on Jul 8, 2026, 1:05 AM UTC-08:00

Activity

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

Metadata

Metadata

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions