πΉ 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)
- Migrate the discussions GraphQL check off
gh.Exec to api.GraphQLClient (removes gh-binary dependency for a validation path; highest value).
- Add a timeout to
checkRepositoryHasIssuesUncached.
- Optionally migrate
remote_resolve_sha.go REST lookup to DoWithContext, adapting the auth-error detection to api.HTTPError.
- 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 Β· β·
πΉ 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 (upstreampushed_at2026-07-06). Fittingly, it's GitHub's own Go toolkit and one of the load-bearing deps in gh-aw. πModule Overview
go-ghis the official Go SDK behind theghCLI. It hands you authenticated REST and GraphQL clients that auto-resolve host, token, and config from the localghenvironment, plusgh.Exec(shell out to theghbinary) andrepository.Current()for repo context.Current Usage in gh-aw
pkg/parser,pkg/cli,pkg/workflowapi.NewRESTClient/api.DefaultRESTClient,RESTClient.DoWithContext,RESTClient.Get,api.ClientOptions{Timeout,Host},gh.Exec,repository.CurrentThe REST client is used well β
pkg/parsercentralizes construction increateRESTClientForHostand threadscontextthroughDoWithContext, with a layered fallback chain (authenticated API βgit ls-remote/clone β unauthenticated public API) that's genuinely thoughtful.Per-file breakdown
pkg/parser/remote_client.gocreateRESTClientForHost,DoWithContext(Contents API)pkg/parser/remote_download_file.gopkg/parser/remote_list_files.gopkg/parser/remote_resolve_sha.gogh.Exec("api", ...)commit lookuppkg/cli/copilot_billing_check.goDoWithContextw/ 3s timeoutpkg/cli/update_check.goNewRESTClient(opts)pkg/cli/update_cooldown.goNewRESTClient(ClientOptions{})pkg/cli/secret_set_command.goNewRESTClient(opts)pkg/workflow/repository_features_validation.goDefaultRESTClient+Get,gh.ExecGraphQL,repository.CurrentResearch Findings
replacetemplate 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)
api.RESTClient/api.GraphQLClientover shelling out toghwhen you're already in Go β no dependency on theghbinary, nativecontextsupport, structuredapi.HTTPError(withStatusCode).GraphQLClientoffers typedQuery/Mutate(struct-derived) and rawDo/DoWithContext(query string) variants.Improvement Opportunities
π Quick Wins
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 theghbinary 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.checkRepositoryHasIssuesUncachedwith a timeout βrepository_features_validation.go:313-320usesclient.Get(...)with no context; switch toDoWithContextwith a short context (mirror the 3s copilot-billing probe) so feature validation can't hang on a slow network.β¨ Feature Opportunities
GraphQLClient.Query) could replace the hand-written query string + response struct for the discussions check with one schema struct.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:93usesgh.Exec("api", ...)for a REST commit lookup, in a package that otherwise usesapi.RESTClientviacreateRESTClientForHost. Moving it toDoWithContextmakes the resolver self-contained (noghbinary) and enables native cancellation. Nuance: the auth-error fallback currently keys onghstderr viagitutil.IsAuthError; with the REST client it would key onapi.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 toapi.DefaultRESTClient(); the latter reads more clearly as "use gh-resolved config."π§ General Improvements
pkg/cli(mirroringparser.createRESTClientForHost) to standardize timeouts/options across the fourclicall sites.Recommendations (prioritized)
gh.Exectoapi.GraphQLClient(removes gh-binary dependency for a validation path; highest value).checkRepositoryHasIssuesUncached.remote_resolve_sha.goREST lookup toDoWithContext, adapting the auth-error detection toapi.HTTPError.DefaultRESTClient()over empty-optsNewRESTClient.Next Steps
gh.Execsites 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