gh-stack config.Config Struct and Test Overrides: Complete Configuration Guide
The config.Config struct centralizes all runtime settings for the gh-stack extension, bundling I/O streams, color formatting helpers, interactive prompt callbacks, and service overrides that enable deterministic testing through dependency injection.
The github/gh-stack repository implements a centralized configuration pattern to manage CLI behavior and facilitate unit testing. Understanding the gh-stack config.Config struct and test overrides is essential for extending functionality or debugging interaction flows. This configuration object aggregates everything from stdout/stderr writers to mockable GitHub client interfaces into a single injectable dependency consumed by all subcommands.
Config Struct Fields Overview
The Config struct defined in internal/config/config.go exposes several field categories that control both runtime behavior and testability.
I/O Streams and Color Formatting
The struct captures output destinations and text styling functions to ensure consistent terminal rendering across platforms:
- Stdout and Stderr (
io.Writer): Destination writers for normal and error output. In production, these map toos.Stdoutandos.Stderr, while tests replace them withbytes.Bufferinstances viaconfig.NewTestConfig. - ColorFn, BoldFn, ErrorFn, InfoFn, SuccessFn, WarningFn (
func(string) string): Helper functions that apply ANSI escape codes for terminal styling. Production implementations wrap text in color codes, whereas tests substitute identity functions (func(s string) string { return s }) to produce deterministic output.
Interactive Prompt Hooks
To enable non-interactive testing, the struct includes function fields that replace user input mechanisms:
- Interactive (
bool): Global flag indicating whether the environment supports user interaction. Set tofalsein CI environments and most test scenarios. - SelectFn (
func(prompt, defaultValue string, options []string) (int, error)): Replaces the "select one of N options" UI. Tests stub this to return predetermined indices. - ConfirmFn (
func(prompt string, defaultValue bool) (bool, error)): Hook for yes/no prompts. Test implementations return fixed boolean values. - InputFn (
func(prompt, defaultValue string) (string, error)): Handles free-form text input. Tests provide deterministic string responses.
Service and Repository Overrides
The struct supports dependency injection for external services:
- RepoOverride (
string): Forces commands to target a specific repository instead of detecting the current working directory's remote. Used extensively in tests with temporary fixture repositories. - GitHubClientOverride (
github.ClientOps): Swaps the live GitHub API client with a mock implementation (github.MockClient). Theconfig.NewTestConfighelper automatically initializes this field for test configurations. - GitOverride (
git.Ops): Though the field exists inConfigprimarily for completeness and is not always stored directly, it allows replacement of git operations withgit.MockOpsvia the globalgit.SetOps()function.
Internal Testing Flags
- TestMode (
bool): Signals execution under the test harness, enabling additional sanity checks such as strict mock restoration verification.
Test Configuration Patterns
The internal/config/testing.go file provides the NewTestConfig constructor that standardizes test setup across the suite.
Creating Test Configurations
The helper function returns a fully initialized config along with capture buffers:
func NewTestConfig() (*Config, *bytes.Buffer, *bytes.Buffer) {
stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer)
cfg := &Config{
Stdout: stdout,
Stderr: stderr,
ColorFn: func(s string) string { return s },
BoldFn: func(s string) string { return s },
Interactive: false,
}
return cfg, stdout, stderr
}
This implementation disables color formatting and interactivity by default, ensuring tests produce stable, parseable output regardless of terminal capabilities.
Customizing Test Behavior
Individual tests override specific hooks to simulate user interactions:
cfg, stdout, stderr := config.NewTestConfig()
// Force selection of the second option (index 1)
cfg.SelectFn = func(_, _ string, _ []string) (int, error) {
return 1, nil
}
// Always confirm prompts
cfg.ConfirmFn = func(_ string, _ bool) (bool, error) {
return true, nil
}
// Provide deterministic text input
cfg.InputFn = func(_, _ string) (string, error) {
return "feature-branch", nil
}
// Inject mock GitHub client
cfg.GitHubClientOverride = &github.MockClient{
ListStacksFn: func(_ context.Context, _, _ string) ([]github.Stack, error) {
return []github.Stack{{ID: "S1", Number: 42}}, nil
},
}
Implementation in Commands
Command implementations in the cmd/ directory receive *config.Config as their primary dependency. According to cmd/utils.go (lines 1420-1430 and 1760-1770), commands check whether prompt hooks are nil before invoking them, falling back to real interactive implementations when running in production.
For example, a command consuming the configuration:
func runAdd(cfg *config.Config, args []string) error {
// Use configured stdout
fmt.Fprintln(cfg.Stdout, "Processing...")
// Check if running in interactive mode
if cfg.Interactive && cfg.SelectFn != nil {
idx, err := cfg.SelectFn("Choose stack", "", []string{"A", "B"})
if err != nil {
return err
}
// process selection...
}
// Use potentially mocked GitHub client
client := cfg.GitHubClientOverride
if client == nil {
// initialize real client
}
}
Key Source File Locations
Understanding the complete configuration system requires examining these specific files in the github/gh-stack repository:
internal/config/config.go: Defines theConfigstruct and its field types.internal/config/testing.go: ImplementsNewTestConfigand test-specific initialization logic.cmd/utils.go: Contains UI helper functions that respectSelectFn,ConfirmFn, andInputFnoverrides (see lines 1420-1430 and 1760-1770).internal/github/mock_client.go: Provides theMockClienttype used withGitHubClientOverride.internal/git/mock_ops.go: DefinesMockOpsfor git operation substitution.
Summary
config.Configaggregates I/O writers, color helpers, prompt callbacks, and service overrides into a single injectable configuration object defined ininternal/config/config.go.- Test overrides include
NewTestConfigfor standardized setup, buffer-based stdout/stderr capture, identity color functions, and mockable client interfaces. - Interactive hooks (
SelectFn,ConfirmFn,InputFn) allow deterministic simulation of user input without actual terminal interaction. - Service overrides (
GitHubClientOverride,RepoOverride) enable testing against mock GitHub API responses and temporary fixture repositories. - Source locations span
internal/config/config.go,internal/config/testing.go, andcmd/utils.gofor the core configuration logic.
Frequently Asked Questions
How do I disable color output when testing gh-stack commands?
Set the color helper fields to identity functions that return strings unchanged. The config.NewTestConfig helper does this automatically by assigning func(s string) string { return s } to ColorFn, BoldFn, and other styling fields in internal/config/testing.go, producing plain text suitable for assertions.
What is the purpose of GitHubClientOverride in the Config struct?
GitHubClientOverride allows tests to substitute the live GitHub API client with a github.MockClient implementation. This field accepts any type satisfying the github.ClientOps interface, enabling deterministic testing of stack operations without network calls or authentication requirements.
How does the Interactive field affect command execution?
When Interactive is false, commands skip user prompts entirely or use default values, making the tool suitable for CI pipelines. In test environments, NewTestConfig sets this to false by default, while individual tests can combine it with stubbed SelectFn or ConfirmFn functions to simulate specific user choices programmatically.
Where are the mock implementations for git operations defined?
The internal/git/mock_ops.go file defines MockOps, which implements the git operation interface used throughout gh-stack. While Config contains fields for service overrides, git operations are typically replaced using git.SetOps(&git.MockOps{...}) in test setup, allowing commands to execute against synthetic repository states without modifying the filesystem.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →