This guide documents how Cobra is built — its internal architecture, the command-resolution and flag-merging machinery, the shell-completion protocol, the documentation generators, and its code conventions. It is written for maintainers, contributors, and architects who need to understand, extend, or audit the library rather than merely consume it.
If you are looking for how to use Cobra to build a CLI, see the companion Cobra User Guide. This document assumes that knowledge and goes one layer deeper: into the source.
Note
Cobra is a Go library (module
github.com/spf13/cobra,go 1.15). It has no server, no daemon, and no deployment of its own — it compiles into the consumer's binary. "Operations" in this guide therefore means building, testing, releasing, and extending the library, not running a service.
Table of Contents
- Executive Summary
- Architecture Overview
- The Command Type
- Execution Pipeline
- Flag Subsystem
- Argument & Flag-Group Validation
- The Completion Protocol
- The Template & Help System
- Documentation Generators (
docpackage) - Repository Structure
- Dependencies
- Security Analysis
- Code Conventions & Patterns
- Testing Strategy
- Build, Release & Contributing
- Known Issues & Improvement Opportunities
- Quick Reference
1. Executive Summary
Cobra is a command-organization library. Its entire surface area revolves around one recursive data structure — Command — that forms a tree. A consumer builds the tree declaratively, calls Execute() on the root, and Cobra does the rest: resolve the target command, parse and merge flags, validate arguments, run lifecycle hooks, and render help/usage/errors.
The codebase is deliberately small and single-package. The core lives in package cobra at the repository root; an optional doc subpackage generates reference documentation. There is no plugin system, no reflection-heavy magic, and no external runtime configuration — behavior is controlled by struct fields and a handful of package-level variables.
| Aspect | Reality |
|---|---|
| Language | Go (≥ 1.15) |
| Package layout | Single root package cobra + doc/ subpackage |
| Core type | Command (recursive tree node) |
| Flag engine | Delegated to github.com/spf13/pflag |
| Public config | Struct fields + ~6 package-level toggle variables |
| Side-channel | __complete hidden command implements the completion protocol |
| External I/O | stdin/stdout/stderr (overridable); env vars read only for completion/active-help |
The dominant design value is predictability: explicit struct fields over implicit behavior, a fixed hook order, and careful guards so that adding the internal completion/__complete commands never changes a program's observable behavior unless those features are actually used.
2. Architecture Overview
consumer's main()
│
▼
rootCmd.Execute() ── ExecuteC() ──┐
│ │
┌──────────────────────┴───────────┐ │
│ inject internal commands: │ │
│ • help (InitDefaultHelpCmd) │ │
│ • completion (InitDefaultComp..) │ │
│ • __complete (initCompleteCmd) │ │
└──────────────────────┬───────────┘ │
▼ │
Find(args) / Traverse(args) ── walk tree, strip flags
│
▼
matched *Command + remaining args
│
▼
cmd.execute(flags)
┌──────────────────────┴──────────────────────┐
│ ParseFlags → help/version short-circuit → │
│ ValidateArgs → PersistentPreRun(E) → │
│ PreRun(E) → ValidateRequiredFlags → │
│ ValidateFlagGroups → Run(E) → PostRun(E) → │
│ PersistentPostRun(E) │
└───────────────────────────────────────────────┘
│
▼
on error: print prefix + error, optionally usage
The tree itself is plain pointers: each Command holds commands []*Command (children) and parent *Command. Walking up the tree (Root, VisitParents, CommandPath) and down (Find, Traverse, Commands) is how nearly every feature is implemented — flag inheritance, help text, completion scope, and hook chaining all derive from tree position.
Source-file responsibilities
| File | Responsibility |
|---|---|
command.go |
The Command type, Execute/ExecuteC, Find, Traverse, flag accessors, default templates. The heart of the library (~2,000 lines). |
args.go |
Positional-argument validators (PositionalArgs). |
flag_groups.go |
Required-together / one-required / mutually-exclusive flag-group annotations and validation. |
completions.go |
The completion engine: __complete command, getCompletions, ShellCompDirective, the default completion command tree. |
shell_completions.go |
MarkFlag* helpers that set pflag annotations driving completion. |
active_help.go |
Active Help marker and env-var gating. |
bash_completions.go, bash_completionsV2.go, zsh_completions.go, fish_completions.go, powershell_completions.go |
Per-shell completion-script generators. |
command_win.go, command_notwin.go |
Build-tagged Windows mousetrap hook vs. no-op. |
cobra.go |
Package-level config vars, template-func registry, small string/helper utilities. |
doc/*.go |
Man / Markdown / reST / YAML documentation generators. |
3. The Command Type
Command is the single most important type. It is both the configuration object the consumer fills in and the runtime node Cobra operates on. Its fields fall into three groups.
Public configuration fields
These are set by the consumer (documented fully in the User Guide): Use, Aliases, SuggestFor, Short, Long, Example, GroupID, ValidArgs, ValidArgsFunction, Args, ArgAliases, BashCompletionFunction, Deprecated, Annotations, Version, the ten *Run/*RunE hook fields, FParseErrWhitelist, CompletionOptions, and the booleans TraverseChildren, Hidden, SilenceErrors, SilenceUsage, DisableFlagParsing, DisableAutoGenTag, DisableFlagsInUseLine, DisableSuggestions, plus SuggestionsMinimumDistance.
Private runtime state
Cobra maintains internal state that consumers never touch directly:
| Field(s) | Purpose |
|---|---|
commands, parent, commandgroups |
The command tree and its group definitions. |
flags, pflags, lflags, iflags, parentsPflags |
The five cached *pflag.FlagSets (full, persistent, local, inherited, parents'). |
commandsMaxUseLen, …MaxCommandPathLen, …MaxNameLen |
Cached column widths for help padding. |
commandsAreSorted |
Whether commands is currently sorted (lazy sort). |
commandCalledAs |
The name/alias actually used to invoke the command. |
ctx |
The context.Context from ExecuteContext. |
*Func / *Template fields |
User-overridable usage/help/version/flag-error functions and templates. |
Key derived accessors
Name()— the first space-delimited word ofUse.DisplayName()—Name(), unless theCommandDisplayNameAnnotationannotation overrides it.CommandPath()— the space-joined path from root to this command.UseLine()— the full usage line, appending[flags]when the command has available flags (unlessDisableFlagsInUseLine).Runnable()—trueifRunorRunEis set.IsAvailableCommand()— visible, non-deprecated, non-hidden, and runnable or has runnable children.IsAdditionalHelpTopicCommand()— non-runnable, non-hidden, non-deprecated leaf used purely as a help topic.
Note
The five flag sets are caches, rebuilt lazily and invalidated by
ResetFlags/ResetCommands.LocalFlags,InheritedFlags, etc. are documented in source as "not modifying state" — they return the current view, recomputing the cache as needed.
4. Execution Pipeline
Entry points
Execute() calls ExecuteC() and discards the matched command. ExecuteContext(ctx) / ExecuteContextC(ctx) set c.ctx first. All paths converge on ExecuteC().
ExecuteC() — the orchestrator
- Default the context. If
c.ctx == nil, set it tocontext.Background(). - Re-root. If
Executewas called on a non-root command, delegate toc.Root().ExecuteC()— execution always runs from the root. - Windows hook. If
preExecHookFnis set (only on Windows, viacommand_win.go), call it — this is the mousetrap splash. - Inject internal commands in order:
InitDefaultHelpCmd,initCompleteCmd(args),InitDefaultCompletionCmd(args...), thencheckCommandGroups()to panic early on undefined group IDs. - Resolve the target command. Use
Traverse(args)ifTraverseChildrenis set, otherwiseFind(args). - On resolution error, print the error prefix + message and a
Run '<path> --help' for usage.line (unlessSilenceErrors), then return. - Execute the matched command via
cmd.execute(flags). - On run error, special-case
flag.ErrHelp(print help, return nil); otherwise print the error (unless silenced) and the usage string (unlessSilenceUsage).
Find vs. Traverse
Both walk the tree to locate the command the user named, but differ in flag handling:
Findrepeatedly strips flags (stripFlags) to expose the next subcommand name, descends viafindNext, and removes the consumed name withargsMinusFirstX. It does not parse parents' flags during the walk.Traverseparses each parent command's flags as it descends. This is required when a parent's local (non-persistent) flag must be honored before reaching the child — henceTraverseChildren.
findNext matches a child by exact name or alias (respecting EnableCaseInsensitive via commandNameMatches), and — if EnablePrefixMatching is on — by unambiguous prefix.
execute(a []string) — running a single command
The matched command's execute method enforces the lifecycle (order from source):
- Nil-guard:
called Execute() on a nil Command. - Print the deprecation notice if
Deprecatedis set. InitDefaultHelpFlag+InitDefaultVersionFlag(added late so consumers can override).ParseFlags(a); on error route throughFlagErrorFunc.- Help short-circuit: if
--helpis set, returnflag.ErrHelp. - Version short-circuit: if
Version != ""and--versionis set, render the version template and return. - If the command is not
Runnable(), returnflag.ErrHelp(shows help for parent-only commands). preRun()(runs registeredinitializers);defer postRun()(runsfinalizers).ValidateArgs(argWoFlags).- Walk parents to run
PersistentPreRun(E)— all of them ifEnableTraverseRunHooks, otherwise only the first found. PreRun(E).ValidateRequiredFlags()thenValidateFlagGroups().Run(E).PostRun(E).- Walk parents to run
PersistentPostRun(E)(same first-vs-all rule).
Important
Steps 13–15 abort on the first error. A non-nil return from
RunE(or any earlier*RunE) meansPostRunE/PersistentPostRunEnever execute. Cleanup that must always run belongs in adeferinsideRunE, not in a post-run hook. This is the root cause of community issue #1893.
Note
OnInitialize/OnFinalizeregister into package-globalinitializers/finalizersslices, invoked bypreRun/postRun. They are global to the process, not per-command — every command'sexecuteruns them.
5. Flag Subsystem
Cobra does not parse flags itself; it composes pflag.FlagSets and delegates parsing to pflag. The complexity is in which flags apply to a command.
The five flag sets
| Accessor | Backing field | Contents |
|---|---|---|
Flags() |
flags |
The complete set actually parsed: local + persistent + inherited, after merge. |
PersistentFlags() |
pflags |
Flags declared persistent on this command. |
LocalFlags() |
lflags |
Flags specific to this command (not inherited parent persistent flags). |
InheritedFlags() |
iflags |
Persistent flags inherited from parents. |
parentsPflags |
parentsPflags |
Internal cache of all parents' persistent flags. |
Merging
mergePersistentFlags() is the linchpin, called before parsing and before any flag query that must see the full set:
mergePersistentFlags():
updateParentsPflags() // gather parents' persistent flags
Flags().AddFlagSet(PersistentFlags()) // fold in own persistent flags
Flags().AddFlagSet(parentsPflags) // fold in inherited flags
updateParentsPflags() lazily builds parentsPflags (sort disabled to preserve order), folds in pflag.CommandLine at the root, and visits parents via VisitParents. The result is that Flags() on any command exposes exactly the flags a user can pass to it.
Parsing
ParseFlags(args) short-circuits if DisableFlagParsing is set, otherwise merges flags, applies FParseErrWhitelist (the pflag.ParseErrorsAllowlist), parses, and prints any buffered warnings (e.g. deprecated-flag messages) to the command's output.
Note
DisableFlagParsingmakes a command receive all tokens as positional args. This is how programs that wrap another tool (passing flags through verbatim) are supported, and it interacts with completion — see §7.
6. Argument & Flag-Group Validation
Positional arguments (args.go)
PositionalArgs is func(cmd *Command, args []string) error. ValidateArgs calls c.Args (or ArbitraryArgs if Args == nil). The validators (NoArgs, OnlyValidArgs, MinimumNArgs, etc.) are closures over their bounds and return precise, format-stable error strings. MatchAll composes validators by short-circuiting on the first failure.
When Args == nil, Find applies legacyArgs for backward compatibility: root commands without subcommands accept any args; root commands with subcommands do subcommand-validity checking; subcommands accept arbitrary args.
Flag groups (flag_groups.go)
Flag groups are implemented entirely through pflag annotations — no new state on Command. The MarkFlags* methods append a space-joined group key to each member flag's annotation:
| Method | Annotation constant |
|---|---|
MarkFlagsRequiredTogether |
cobra_annotation_required_if_others_set |
MarkFlagsOneRequired |
cobra_annotation_one_required |
MarkFlagsMutuallyExclusive |
cobra_annotation_mutually_exclusive |
ValidateFlagGroups() (called during execute) visits all flags, reconstructs each group's set/unset status into maps, and runs three validators:
validateRequiredFlagGroups— error unless the count of unset members is0orall.validateOneRequiredFlagGroups— error unless ≥ 1 member is set.validateExclusiveFlagGroups— error if ≥ 2 members are set.
A separate enforceFlagGroupsForCompletion() reuses the same annotation parsing to influence completion: present-group members mark siblings required (so completion suggests them); mutually-exclusive members mark siblings hidden.
Warning
MarkFlags*andMarkFlagRequiredlook up flags eagerly and panic if a named flag is missing (Failed to find flag %q ...). Group definition must follow flag definition. This is intentional — it surfaces a coding error at startup rather than masking it.
7. The Completion Protocol
Shell completion is Cobra's most intricate subsystem. It has two halves: a protocol command that your binary answers at runtime, and script generators that emit the per-shell glue.
The __complete hidden command
initCompleteCmd(args) adds a hidden command named __complete (alias __completeNoDesc) to the root — but only if it is actually being invoked. Cobra calls Find(args) and removes the command again unless the first arg is __complete. This guard prevents a root-only program from suddenly gaining a subcommand (which would change its argument-acceptance behavior).
When a shell needs completions, it runs yourprog __complete <args...>. The command:
- Calls
getCompletions(args)to compute the candidate list and aShellCompDirective. - Strips descriptions if invoked as
__completeNoDescor if descriptions are disabled via env var. - Strips Active Help entries if Active Help is disabled.
- Prints one completion per line, then a final
:<directive>line that the shell script parses. - Prints a human-readable
Completion ended with directive: ...to stderr (which the script ignores).
getCompletions — the resolution core
This function (in completions.go) reproduces much of the execution pipeline without running the command:
- Splits off the last (incomplete) token as
toComplete. - Resolves the real target via
TraverseorFind, working around the injected__completecommand for root-only programs. - Adds
--help/--versionflags (unless flag parsing is disabled) so they can be completed. - Detects flag-value completion (
checkIfFlagCompletion) and removes the in-progress flag token so parsing doesn't fail on an incomplete value. - Double-parses flags (once with an appended
--) to detect whether the cursor is past the--terminator. - Honors flag annotations:
BashCompFilenameExt→ShellCompDirectiveFilterFileExt;BashCompSubdirsInDir→ShellCompDirectiveFilterDirs. - Calls
enforceFlagGroupsForCompletion(). - For flag-name completion, suggests
--flag/-fforms (skipping already-set non-repeatable flags); required flags are surfaced first viacompleteRequireFlags. - For argument completion, suggests subcommand names,
ValidArgs/ArgAliases, then invokes the registeredValidArgsFunctionor the flag's registered completion function.
ShellCompDirective bitmask
A ShellCompDirective is an int bitmask built with iota shifts. Values: Error, NoSpace, NoFileComp, FilterFileExt, FilterDirs, KeepOrder, with Default = 0. shellCompDirectiveMaxValue is an internal sentinel for range-checking. The .string() method renders active bits for the debug line.
Script generators & the default completion command
InitDefaultCompletionCmd builds the user-facing completion command with bash/zsh/fish/powershell children, each whose RunE calls the matching generator on the root: GenBashCompletionV2, GenZshCompletion(NoDesc), GenFishCompletion, GenPowerShellCompletion(WithDesc). Each child carries an optional --no-descriptions flag (suppressible via CompletionOptions.DisableNoDescFlag). Like __complete, the whole command is removed if it would be the only subcommand and isn't being invoked.
Flag annotations driving completion (shell_completions.go)
| Helper | pflag annotation set |
|---|---|
MarkFlagRequired |
BashCompOneRequiredFlag = "true" |
MarkFlagFilename |
BashCompFilenameExt = [extensions] |
MarkFlagDirname |
BashCompSubdirsInDir = [] |
MarkFlagCustom |
BashCompCustom = [bash func name] (bash only) |
Caution
MarkFlagCustomonly works for the legacy bash generator. The portable mechanism isRegisterFlagCompletionFunc, which registers a Go function in a package-global, mutex-guardedflagCompletionFunctionsmap keyed by the*pflag.Flagpointer — so it works across all shells.
8. The Template & Help System
Help, usage, and version output are produced by Go text/template templates with a registered function map, or by hand-written equivalents kept "in sync" with the templates.
templateFuncs(incobra.go) registers helpers:trim,trimRightSpace/trimTrailingWhitespaces,appendIfNotPresent,rpad,gt(Gt),eq(Eq). Consumers extend it withAddTemplateFunc/AddTemplateFuncs.defaultUsageTemplate,defaultHelpTemplate,defaultVersionTemplateare package constants. Each has a hand-coded counterpart (defaultUsageFunc,defaultHelpFunc,defaultVersionFunc) that must be changed in sync — the source comments say so explicitly.- Resolution climbs the tree:
getUsageTemplateFunc/getHelpTemplateFunc/getVersionTemplateFuncreturn the command's own override if set, else a parent's, else the default. The same climbing pattern governsUsageFunc,HelpFunc,FlagErrorFunc, and the I/O accessors (getOut/getErr/getIn). - Padding helpers (
UsagePadding,NamePadding,CommandPathPadding) use the cachedcommandsMax*Lenwidths to align help columns, with minimum constants (minUsagePadding=25,minCommandPathPadding=11,minNamePadding=11).
Note
UsageTemplate(),HelpTemplate(), andVersionTemplate()are retained "for backwards-compatibility reasons" — newer code paths call the template functions directly. The dual maintenance of constant-template and hand-written-func is a deliberate trade for performance and back-compat.
9. Documentation Generators (doc package)
The doc subpackage walks a command tree and emits reference docs. It is decoupled from the core: it imports cobra and operates only through the public API plus a few exported accessors (NonInheritedFlags, InheritedFlags, Commands, IsAvailableCommand, IsAdditionalHelpTopicCommand).
| Generator | Output | Notable detail |
|---|---|---|
GenMan / GenManTree / GenManTreeFromOpts |
troff man pages | Renders Markdown via go-md2man; GenManHeader carries title/section/date/source; honors SOURCE_DATE_EPOCH for reproducible builds. |
GenMarkdown / GenMarkdownTree / …Custom |
Markdown | Emits synopsis, options, and a "SEE ALSO" cross-link section. |
GenReST / GenReSTTree / …Custom |
reStructuredText | Custom link handler for .rst references. |
GenYaml / GenYamlTree / …Custom |
YAML | Marshals a cmdDoc struct (name, synopsis, options, inherited options, see-also) via yaml/v3. |
Shared traits:
- All call
InitDefaultHelpCmd/InitDefaultHelpFlagso generated docs include the auto-added flags. - All recurse over
cmd.Commands(), skipping non-available and additional-help-topic commands. - All append an "Auto generated by spf13/cobra" tag unless
DisableAutoGenTagis set (propagated from parents during the walk). util.goprovideshasSeeAlso, thebyNamesorter, andforceMultiLine(a workaround for the YAML library mangling long single-line strings).
Warning
Every tree generator documents the same caveat: command names containing
-can collide in the flattened output filenames (cmd-sub-third). This is an inherent ambiguity of joining the command path with-/_, not a bug to fix.
10. Repository Structure
cobra/
├── command.go Core Command type, Execute, Find/Traverse, flags, templates
├── args.go Positional-argument validators
├── flag_groups.go Flag-group annotations + validation
├── completions.go __complete command, getCompletions, ShellCompDirective
├── shell_completions.go MarkFlag* annotation helpers
├── active_help.go Active Help marker + env gating
├── bash_completions.go Legacy (v1) bash script generator
├── bash_completionsV2.go Current bash script generator
├── zsh_completions.go zsh script generator
├── fish_completions.go fish script generator
├── powershell_completions.go PowerShell script generator
├── cobra.go Package vars, template funcs, small utilities
├── command_win.go //go:build windows — mousetrap pre-exec hook
├── command_notwin.go //go:build !windows — no-op hook
├── *_test.go Extensive unit tests (one per source file)
├── doc/ Documentation generators (man/md/rest/yaml) + tests
├── site/content/ Project's own end-user docs (Hugo site source)
│ ├── user_guide.md (consumer-facing usage docs)
│ ├── active_help.md
│ ├── completions/ per-shell completion docs
│ └── docgen/ doc-generation docs
├── assets/ Logo image
├── go.mod / go.sum Module definition
├── Makefile Lint + test targets
├── .golangci.yml Linter configuration
├── CONTRIBUTING.md, CONDUCT.md, MAINTAINERS, SECURITY.md, LICENSE.txt
Note
The build-tag pair
command_win.go/command_notwin.gois the only platform-specific code.command_win.gosetspreExecHookFnto detect Explorer-launched binaries (viainconshreveable/mousetrap) and printMousetrapHelpText;command_notwin.goleaves the hook nil.
11. Dependencies
From go.mod (go 1.15):
| Module | Version | Why it is needed |
|---|---|---|
github.com/spf13/pflag |
v1.0.9 |
POSIX/GNU flag parsing. Cobra composes and queries pflag.FlagSets; flag annotations drive completion and groups. The single most important dependency. |
github.com/cpuguy83/go-md2man/v2 |
v2.0.6 |
Converts Markdown to troff for doc.GenMan. |
github.com/inconshreveable/mousetrap |
v1.1.0 |
Detects whether a Windows process was started from Explorer (used only in command_win.go). |
go.yaml.in/yaml/v3 |
v3.0.4 |
YAML marshaling for doc.GenYaml. |
Note
Only
pflagis part of the core runtime path. The other three are pulled in only by features a consumer may never use (man-page generation, YAML docs, Windows mousetrap). Community issue #2339 tracks a proposal to switch the YAML library togoccy/go-yamlfor better error semantics.
12. Security Analysis
Cobra is a parsing/dispatch library; it neither performs network I/O nor handles credentials. Its security surface is small but worth enumerating for auditors.
| Area | Assessment |
|---|---|
| Input handling | Parses os.Args / supplied args. No shell evaluation of user input within the core. Flag parsing is delegated to pflag. |
| File access | The core does not read or write files. The doc generators create files in a caller-supplied directory; bash_completions legacy paths and MarkFlagFilename only emit script text, they do not open files. |
| Environment variables | Read for completion/active-help only: BASH_COMP_DEBUG_FILE, COBRA_ACTIVE_HELP, <PROGRAM>_ACTIVE_HELP, <PROGRAM>_COMPLETION_DESCRIPTIONS, and SOURCE_DATE_EPOCH (man-page date). |
BASH_COMP_DEBUG_FILE |
Opens the user-specified path with O_APPEND|O_CREATE|O_WRONLY, mode 0644. The source explicitly annotates this as intentionally user-controlled (//nolint:gosec). Only active during completion of the user's own shell — the user controls the value. |
| Code execution | Generated completion scripts run in the user's shell and invoke yourprog __complete. The completion protocol writes only completion candidates to stdout and trims each to its first line to prevent newline-injected fake completions (see getCompletions output loop). |
| Panics as guardrails | AddCommand, MarkFlags*, and checkCommandGroups panic on programmer error (self-parenting, missing flags, undefined groups). These fire at startup, surfacing misconfiguration loudly rather than degrading silently. |
Important
The deliberate single-line trimming of each completion (
strings.SplitN(comp, "\n", 2)[0]) is a security-relevant detail: it stops a multi-line description from being interpreted by the shell script as additional (spoofed) completion entries.
Note
Vulnerability reporting follows
SECURITY.mdin the repository. As a library, Cobra inherits the security posture ofpflagfor the parsing path.
13. Code Conventions & Patterns
- Recursive tree traversal is the dominant pattern. Up-the-tree resolution with a "use mine, else parent's, else default" fallback recurs for templates, funcs, and I/O streams.
- Lazy caching with explicit invalidation. Flag sets, command sorting, and padding widths are cached and rebuilt on demand;
Reset*methods clear them. - Annotations over fields. Flag groups and completion behaviors are encoded as
pflagannotations rather than new struct fields, keepingCommandfrom ballooning and letting a flag belong to multiple groups. - Defensive command injection. Internal commands (
help,completion,__complete) are added and then conditionally removed so that programs which don't use them don't inadvertently gain a subcommand (which would change argument acceptance). E-variant convention. Every hook and several public functions come in a plain and an error-returning form; theEform wins when both are set.- Dual template/func maintenance. Default output exists as both a
text/templateconstant and an equivalent hand-written function, with comments mandating they change together. - Build tags for platform code, isolated to the two
command_*win.gofiles.
Linting is configured in .golangci.yml; gosec exceptions are inline-annotated with justifications.
14. Testing Strategy
Cobra ships an extensive unit-test suite — nearly every source file has a sibling _test.go, and the doc package has tests and benchmarks for each generator.
| Test file | Focus |
|---|---|
command_test.go |
Largest suite — execution flow, hooks, flag inheritance, help/usage, suggestions. |
args_test.go |
Every positional-arg validator, including MatchAll composition and legacy behavior. |
completions_test.go |
The completion engine end-to-end (the second-largest suite). |
flag_groups_test.go |
Required-together / one-required / mutually-exclusive validation and completion enforcement. |
*_completions_test.go |
Each shell generator's output. |
doc/*_test.go |
Each doc format, plus Benchmark*ToFile benchmarks. |
Testing patterns to follow when contributing:
- Use the internal
executeCommand(cmd, args...)helper to run a command and capture output/error. - Build command trees inline in the test (the
doc/cmd_test.gorootCmd/echoCmd/timesCmdfixtures are the canonical example). - Assert on exact error strings — many tests pin the precise message text, so changing a message is a breaking change that must update tests in lockstep.
Note
A few tests work around Go test harness quirks — e.g.
ExecuteCspecial-casesos.Args[0] == "cobra.test"sogo test -vdoesn't feed test flags into command resolution (source comment references issue #155).
15. Build, Release & Contributing
Building & testing locally
go build ./...
go test ./...
ok github.com/spf13/cobra (cached)
ok github.com/spf13/cobra/doc (cached)
Run the linter (configuration in .golangci.yml); the Makefile wires up the standard targets.
make test
make lint
Contributing checklist
- [ ] Read
CONTRIBUTING.mdandCONDUCT.md. - [ ] Add or update the sibling
_test.gofor any changed source file. - [ ] If you change default help/usage/version output, update both the template constant and the hand-written
default*Func(they must stay in sync). - [ ] If you change a user-visible error string, update every test that pins it.
- [ ] Keep platform-specific code inside the
command_*win.gobuild-tag pair. - [ ] Maintainers are listed in
MAINTAINERS; the project uses a singlemainbranch.
Tip
Because Cobra is consumed by an enormous number of downstream tools, backward compatibility is paramount. New behavior is almost always opt-in (a new field or a package variable defaulting to off), never a silent change to existing output or dispatch.
16. Known Issues & Improvement Opportunities
These are observable directly in the source or in the project's tracked issues.
Dead / compatibility code marked for removal
The source explicitly flags several functions as unused and slated for removal "in a version 2," kept only for API compatibility:
| Symbol | Source note |
|---|---|
Gt (and template func gt) |
FIXME ... is unused by cobra and should be removed in a version 2. |
Eq (and template func eq) |
Same FIXME. Eq panics on unsupported types. |
appendIfNotPresent |
Same FIXME. |
ExactValidArgs(n) |
Deprecated; use MatchAll(ExactArgs(n), OnlyValidArgs). |
SetOutput |
Deprecated; use SetOut/SetErr. |
Behavioral sharp edges (community-reported)
| Issue | Summary | Status in source |
|---|---|---|
| #1893 | PersistentPostRun{,E} not executed when RunE/PostRunE returns an error. |
By design — post-run runs only after a successful run. Document or refactor execute if changed. |
| #1860 | --version does not work on subcommands. |
The version flag is added only where Version is non-empty. |
| #2376 | ValidArgsFunction receives partial input when an argument contains colons in bash. |
Bash word-splitting interaction. |
| #2213 | Generated bash autocomplete broken with newer bash-completion versions. |
Tracked; affects the v2 bash generator. |
| #2339 | Proposal to replace go.yaml.in/yaml/v3 with goccy/go-yaml for better error semantics. |
Dependency change under discussion. |
Documentation-generation requests
Open issues request word-wrapping/line breaks in generated Markdown (#2282) and a way to append a footer in GenMarkdownTreeCustom (#1140) — both point at the doc package's formatting rigidity.
Tip
The cleanest standalone contribution is removing the
Gt/Eq/appendIfNotPresenttrio (and their template funcs) behind a major-version bump — they are self-documented as removable and have no internal callers.
17. Quick Reference
Internal command injection (during ExecuteC)
| Injector | Adds | Removed if unused? |
|---|---|---|
InitDefaultHelpCmd |
help command (only if subcommands exist) |
n/a |
initCompleteCmd |
hidden __complete / __completeNoDesc |
yes — unless being invoked |
InitDefaultCompletionCmd |
completion + shell subcommands |
yes — if it would be the only subcommand and isn't invoked |
InitDefaultHelpFlag |
-h, --help flag |
n/a |
InitDefaultVersionFlag |
--version flag (only if Version != "") |
n/a |
Package-level variables
EnablePrefixMatching · EnableCommandSorting · EnableCaseInsensitive · EnableTraverseRunHooks · MousetrapHelpText · MousetrapDisplayDuration · (globals) initializers · finalizers · templateFuncs · flagCompletionFunctions
Key annotation constants
| Constant | Value | Used by |
|---|---|---|
FlagSetByCobraAnnotation |
cobra_annotation_flag_set_by_cobra |
Marks help/version flags Cobra added. |
CommandDisplayNameAnnotation |
cobra_annotation_command_display_name |
Overrides DisplayName(). |
BashCompOneRequiredFlag |
(pflag) | Required-flag completion. |
BashCompFilenameExt / BashCompSubdirsInDir / BashCompCustom |
(pflag) | File/dir/custom flag completion. |
requiredAsGroupAnnotation / oneRequiredAnnotation / mutuallyExclusiveAnnotation |
flag-group keys | ValidateFlagGroups. |
Completion env vars & directives
Env: BASH_COMP_DEBUG_FILE, COBRA_ACTIVE_HELP, <PROG>_ACTIVE_HELP, <PROG>_COMPLETION_DESCRIPTIONS, SOURCE_DATE_EPOCH (man dates).
Directives: Default(0) · Error · NoSpace · NoFileComp · FilterFileExt · FilterDirs · KeepOrder.
Hook order (per command)
PersistentPreRun(E) → PreRun(E) → Run(E) → PostRun(E) → PersistentPostRun(E) — E form wins; post-run skipped on run error; persistent hooks chain only when EnableTraverseRunHooks.