GitHub CLI (gh) — Structure#
Layout pattern#
Modified Standard Go Layout (cmd/internal/pkg)
gh follows the standard Go community layout with cmd/, internal/, and pkg/ at the root, but makes an unconventional choice: the bulk of command implementations live in pkg/cmd/ (public) rather than internal/cmd/. This is by design — the extension author SDK (go-gh) and doc-generation tooling need to reference types from those packages. The actual main entry point (cmd/gh/main.go) is a 7-line stub that delegates everything to internal/ghcmd.
Directory map#
gh/
├── acceptance/ # Acceptance/E2E test suite (build-tagged, runs against live GitHub)
│ └── testdata/ # Fixtures for acceptance tests
├── api/ # GitHub REST + GraphQL API client (queries, HTTP client, model types)
├── build/ # Platform-specific build assets
│ ├── macOS/ # macOS pkg/notarization resources
│ └── windows/ # Windows installer resources
├── cmd/ # Binary entry points only
│ ├── gen-docs/ # CLI tool to generate man pages and website docs
│ └── gh/ # Main gh binary — 7-line wrapper calling internal/ghcmd
├── context/ # Remote resolver: maps git remotes to GitHub repos
├── docs/ # Doc assets (primer design guidelines)
├── git/ # Git subprocess wrapper (git client, URL handling, objects)
├── internal/ # Private implementation packages
│ ├── agents/ # GitHub Copilot agent task detection
│ ├── authflow/ # OAuth device + web flow implementation
│ ├── browser/ # Browser-open abstraction (interface + stub)
│ ├── build/ # Version/date injected via ldflags at build time
│ ├── codespaces/ # Codespaces API client, portforwarder, SSH tunnel
│ ├── config/ # User config file read/write (hosts, settings)
│ ├── docs/ # Cobra → man page and markdown doc generators
│ ├── featuredetection/# GHES version-gated feature flag detection
│ ├── gh/ # Core interfaces: Config, AuthConfig, and key option types
│ ├── ghcmd/ # Real main() logic: cobra root setup, update check dispatch
│ ├── ghinstance/ # GitHub instance hostname normalization (ghes, ghe, github.com)
│ ├── ghrepo/ # GitHub repo reference: owner/name parsing, interface
│ ├── keyring/ # System keyring abstraction for credential storage
│ ├── licenses/ # Embedded SPDX license data
│ ├── prompter/ # Terminal prompt abstraction (huh / accessible fallback)
│ ├── run/ # subprocess exec wrapper + stub for testing
│ ├── safepaths/ # Path sanitization utilities
│ ├── tableprinter/ # Terminal table rendering
│ ├── text/ # Text formatting helpers (truncate, pluralize, etc.)
│ ├── update/ # Background version-update checker
│ └── zip/ # ZIP extraction utility (for extension installs)
├── pkg/ # Public packages (usable by extension authors and tests)
│ ├── cmd/ # All 35+ command implementations (one subdir per command)
│ │ ├── accessibility/
│ │ ├── actions/
│ │ ├── agent-task/
│ │ ├── alias/
│ │ ├── api/
│ │ ├── attestation/
│ │ ├── auth/
│ │ ├── browse/
│ │ ├── cache/
│ │ ├── codespace/
│ │ ├── completion/
│ │ ├── config/
│ │ ├── copilot/
│ │ ├── extension/
│ │ ├── factory/ # Factory pattern: constructs shared dependencies (IOStreams, API client, Config)
│ │ ├── gist/
│ │ ├── gpg-key/
│ │ ├── issue/
│ │ ├── label/
│ │ ├── licenses/
│ │ ├── org/
│ │ ├── pr/
│ │ ├── preview/
│ │ ├── project/
│ │ ├── release/
│ │ ├── repo/
│ │ ├── root/ # Root cobra command, wires all subcommands together
│ │ ├── ruleset/
│ │ ├── run/
│ │ ├── search/
│ │ ├── secret/
│ │ ├── ssh-key/
│ │ ├── status/
│ │ ├── variable/
│ │ ├── version/
│ │ └── workflow/
│ ├── cmdutil/ # Shared command infrastructure: Factory struct, errors, flags
│ ├── extensions/ # Extension manager interface and types
│ ├── findsh/ # Locate sh interpreter on Windows
│ ├── githubtemplate/ # Issue/PR template discovery
│ ├── httpmock/ # HTTP mock registry for tests (public for extension test use)
│ ├── iostreams/ # IO abstraction (color, pager, tty detection, test helpers)
│ ├── jsoncolor/ # JSON colorizer for terminal output
│ ├── jsonfieldstest/ # Test helper for JSON field coverage assertions
│ ├── markdown/ # Markdown → terminal renderer (wraps glamour)
│ ├── option/ # Generic Option[T] type
│ ├── search/ # GitHub search query builder and response types
│ ├── set/ # Generic set type
│ ├── ssh/ # SSH config reader
│ └── surveyext/ # survey v2 custom widgets (editor launch, etc.)
├── script/ # Build automation
│ ├── build.go # Cross-platform build script (Go program, not shell)
│ └── release # Release automation script
├── test/
│ └── integration/ # Integration test helpers
└── utils/ # Legacy utility shims (minimal, kept for compatibility)Entry points#
| Binary | Path | Purpose |
|---|---|---|
gh | cmd/gh/main.go → internal/ghcmd.Main() | Primary GitHub CLI — all user-facing commands |
gen-docs | cmd/gen-docs/main.go | Generates man pages (--man-page) and website markdown (--website) from the Cobra command tree |
cmd/gh/main.go is deliberately minimal (7 lines). All cobra setup, update checks, and command registration happen in internal/ghcmd.
Package organization#
Internal packages:
internal/gh— coreConfigandAuthConfiginterfaces; the canonical contract between config and command layersinternal/ghcmd— true application bootstrap: cobra root construction, update check, exit code dispatchinternal/ghrepo—Interfacefor owner/name repo references, used everywhereinternal/config— reads/writes~/.config/gh/config filesinternal/authflow— OAuth device/web flow; credentials injected via build ldflagsinternal/codespaces— Codespace API + SSH port-forwarder (largest internal package)internal/featuredetection— GHES version feature gatesinternal/prompter— interactive prompt abstraction (huh TUI + accessible text fallback)internal/tableprinter— consistent table rendering across commands- All others are focused single-responsibility utilities (browser, run, text, update, zip, etc.)
Public packages (pkg/):
pkg/cmd/factory— constructs the sharedcmdutil.Factory(HTTP client, IOStreams, Config, ExtensionManager) used by every commandpkg/cmdutil—Factorystruct and error types that commands receive as dependenciespkg/iostreams— I/O layer with color detection, pager support, and test helperspkg/extensions—ExtensionManagerinterface +Extensiontypepkg/httpmock— test HTTP registry (public so extension authors can use it)pkg/search,pkg/set,pkg/option— domain utility types
Layering: The architecture follows a clean dependency direction:
cmd/gh → internal/ghcmd → pkg/cmd/root → pkg/cmd/<feature> → api/ + internal/*Commands depend on
cmdutil.Factory(received via DI), never constructing API clients directly.internal/packages do not import frompkg/cmd/. Theapi/package sits outsideinternal/as a first-class layer.
Build system#
- Build tool: Make + custom
script/build.goGo program - Key targets:
make bin/gh→ compiles main binary viascript/build.go(injects version, date, OAuth secrets via ldflags)make manpages→ runsgen-docs --man-page→ writes toshare/man/man1/make completions→ generates shell completions for bash/fish/zshmake test→go test ./...make acceptance→ acceptance suite with-tags acceptancemake site-docs→ clonescli.github.comand generates website markdown
- Docker: No — distributed as compiled binaries; platform packaging handled by
build/macOS/(pkg) andbuild/windows/(installer) directories plusscript/pkgmacos,script/distributionsfor Linux
Notable structural decisions#
Commands live in
pkg/cmd/, notinternal/cmd/: This is deliberate — the companion librarygo-ghand extension authors need to reference command-level types. The tradeoff is that all 35+ command packages are technically importable externally, though the team relies on convention rather than access control.internal/ghcmdas the real main: Thecmd/gh/main.gois a 7-line shell. This separation means the entire startup sequence (cobra wiring, update checking, exit code handling) is testable as a package, not locked insidemain.Cross-platform build system written in Go:
script/build.gois a Go program invoked viago run, avoiding Makefile portability issues on Windows. TheMakefileis a thin wrapper that compilesscript/build.gofirst.api/at root, not insideinternal/: The GitHub API client layer is prominent and top-level, signaling that it’s a first-class, long-lived concern (not an implementation detail). It contains both REST query functions and GraphQL model types.Testing infrastructure in
pkg/:pkg/httpmockandpkg/iostreams(with itsTest()constructor) are public packages so that extension authors and downstream consumers can write tests using the same mocking primitives as theghteam itself.Dual acceptance vs integration test scopes:
acceptance/(build-tagged, requires GitHub credentials, tests real API behavior) is separate fromtest/integration/(in-process integration helpers). This keeps the slow/credential-dependent tests isolated from the faster integration layer.