serpent
packagemoduleThis package is not in the latest version of its module.
Details
Validgo.mod file
The Go module system was introduced in Go 1.11 and is the official dependency management solution for Go.
Redistributable license
Redistributable licenses place minimal restrictions on how software can be used, modified, and redistributed.
Tagged version
Modules with tagged versions give importers more predictable builds.
Stable version
When a project reaches major version v1 it is considered stable.
- Learn more about best practices
Repository
Links
README¶
serpent
serpent
is a Go CLI configuration framework based oncobra and used bycoder/coder.It's designed for large-scale CLIs with dozens of commands and hundredsof options. If you're building a small, self-contained tool, go withcobra.
When compared to cobra, serpent strives for:
- Better default help output inspired by the Go toolchain
- Greater flexibility in accepting options that span across multiple sources
- Composition via middleware
- Testability (e.g. OS Stdout and Stderr is only available to commands explicitly)
Basic Usage
Seeexample/echo
:
package mainimport ("os""strings""github.com/coder/serpent")func main() {var upper boolcmd := serpent.Command{Use: "echo <text>",Short: "Prints the given text to the console.",Options: serpent.OptionSet{{Name: "upper",Value: serpent.BoolOf(&upper),Flag: "upper",Description: "Prints the text in upper case.",},},Handler: func(inv *serpent.Invocation) error {if len(inv.Args) == 0 {inv.Stderr.Write([]byte("error: missing text\n"))os.Exit(1)}text := inv.Args[0]if upper {text = strings.ToUpper(text)}inv.Stdout.Write([]byte(text))return nil},}err := cmd.Invoke().WithOS().Run()if err != nil {panic(err)}}
Design
This Design section assumes you have a good understanding of howcobra
works.
Options
Serpent is designed for high-configurability. To us, that means providingmany ways to configure the same value (env, YAML, flags, etc.) and keepingthe code clean and testable as you scale the number of options.
Serpent'sOption type looks like:
type Option struct {Name stringFlag stringEnv stringDefault stringValue pflag.Value// ...}
And is used by eachCommand whenpassed as an array to theOptions
field.
More coming...
This README is a stub for now. We'll better explain the design and usageofserpent
in the future.
Documentation¶
Overview¶
Package serpent offers an all-in-one solution for a highly configurable CLIapplication. Within Coder, we use it for all of our subcommands, whichdemands more functionality than cobra/viber offers.
The Command interface is loosely based on the chi middleware pattern andhttp.Handler/HandlerFunc.
Index¶
- Constants
- Variables
- func DefaultCompletionHandler(inv *Invocation) []string
- type Annotations
- type Bool
- type Command
- type CompletionHandlerFunc
- type Duration
- type Enum
- type EnumArray
- type EnvVar
- type Environ
- type Float64
- type Group
- type HandlerFunc
- type HostPort
- func (hp *HostPort) MarshalJSON() ([]byte, error)
- func (hp *HostPort) MarshalYAML() (interface{}, error)
- func (hp *HostPort) Set(v string) error
- func (hp *HostPort) String() string
- func (*HostPort) Type() string
- func (hp *HostPort) UnmarshalJSON(b []byte) error
- func (hp *HostPort) UnmarshalYAML(n *yaml.Node) error
- type Int64
- type Invocation
- func (inv *Invocation) Context() context.Context
- func (inv *Invocation) CurWords() (prev string, cur string)
- func (inv *Invocation) IsCompletionMode() bool
- func (inv *Invocation) ParsedFlags() *pflag.FlagSet
- func (inv *Invocation) Run() (err error)
- func (inv *Invocation) SignalNotifyContext(parent context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc)
- func (inv *Invocation) WithContext(ctx context.Context) *Invocation
- func (inv *Invocation) WithOS() *Invocation
- func (inv *Invocation) WithTestParsedFlags(_ testing.TB, parsedFlags *pflag.FlagSet) *Invocation
- func (inv *Invocation) WithTestSignalNotifyContext(_ testing.TB, ...) *Invocation
- type MiddlewareFunc
- type Net
- type NoOptDefValuer
- type Option
- type OptionSet
- func (optSet *OptionSet) Add(opts ...Option)
- func (optSet OptionSet) ByFlag(flag string) *Option
- func (optSet OptionSet) ByName(name string) *Option
- func (optSet OptionSet) Filter(filter func(opt Option) bool) OptionSet
- func (optSet *OptionSet) FlagSet() *pflag.FlagSet
- func (optSet *OptionSet) MarshalYAML() (any, error)
- func (optSet *OptionSet) ParseEnv(vs []EnvVar) error
- func (optSet *OptionSet) SetDefaults() error
- func (optSet *OptionSet) UnmarshalJSON(data []byte) error
- func (optSet *OptionSet) UnmarshalYAML(rootNode *yaml.Node) error
- type Regexp
- func (r *Regexp) MarshalJSON() ([]byte, error)
- func (r *Regexp) MarshalYAML() (interface{}, error)
- func (r *Regexp) Set(v string) error
- func (r Regexp) String() string
- func (Regexp) Type() string
- func (r *Regexp) UnmarshalJSON(data []byte) error
- func (r *Regexp) UnmarshalYAML(n *yaml.Node) error
- func (r *Regexp) Value() *regexp.Regexp
- type RunCommandError
- type String
- type StringArray
- type Struct
- func (s *Struct[T]) MarshalJSON() ([]byte, error)
- func (s *Struct[T]) MarshalYAML() (interface{}, error)
- func (s *Struct[T]) Set(v string) error
- func (s *Struct[T]) String() string
- func (s *Struct[T]) Type() string
- func (s *Struct[T]) UnmarshalJSON(b []byte) error
- func (s *Struct[T]) UnmarshalYAML(n *yaml.Node) error
- type URL
- type UnknownSubcommandError
- type Validator
- func (i *Validator[T]) MarshalJSON() ([]byte, error)
- func (i *Validator[T]) MarshalYAML() (interface{}, error)
- func (i *Validator[T]) Set(input string) error
- func (i *Validator[T]) String() string
- func (i *Validator[T]) Type() string
- func (i *Validator[T]) Underlying() pflag.Value
- func (i *Validator[T]) UnmarshalJSON(b []byte) error
- func (i *Validator[T]) UnmarshalYAML(n *yaml.Node) error
- type ValueSource
- type YAMLConfigPath
Constants¶
const CompletionModeEnv = "COMPLETION_MODE"
CompletionModeEnv is a special environment variable that isset when the command is being run in completion mode.
Variables¶
var DiscardValue discardValue
DiscardValue does nothing but implements the pflag.Value interface.It's useful in cases where you want to accept an option, but access theunderlying value directly instead of through the Option methods.
Functions¶
funcDefaultCompletionHandler¶added inv0.8.0
func DefaultCompletionHandler(inv *Invocation) []string
DefaultCompletionHandler is a handler that prints all the subcommands, orall the options that haven't been exhaustively set, if the current wordstarts with a dash.
Types¶
typeAnnotations¶
Annotations is an arbitrary key-mapping used to extend the Option and Command types.Its methods won't panic if the map is nil.
func (Annotations)Get¶
func (aAnnotations) Get(keystring) (string,bool)
Get retrieves a key from the map, returning false if the key is not foundor the map is nil.
func (Annotations)IsSet¶
func (aAnnotations) IsSet(keystring)bool
IsSet returns true if the key is set in the annotations map.
func (Annotations)Mark¶
func (aAnnotations) Mark(keystring, valuestring)Annotations
Mark sets a value on the annotations map, creating oneif it doesn't exist. Mark does not mutate the original andreturns a copy. It is suitable for chaining.
typeCommand¶added inv0.5.0
type Command struct {// Parent is the direct parent of the command.//// It is set automatically when an invokation runs.Parent *Command// Children is a list of direct descendants.Children []*Command// Use is provided in form "command [flags] [args...]".Usestring// Aliases is a list of alternative names for the command.Aliases []string// Short is a one-line description of the command.Shortstring// Hidden determines whether the command should be hidden from help.Hiddenbool// Deprecated indicates whether this command is deprecated.// If empty, the command is not deprecated.// If set, the value is used as the deprecation message.Deprecatedstring `json:"deprecated,omitempty"`// RawArgs determines whether the command should receive unparsed arguments.// No flags are parsed when set, and the command is responsible for parsing// its own flags.RawArgsbool// Long is a detailed description of the command,// presented on its help page. It may contain examples.LongstringOptionsOptionSetAnnotationsAnnotations// Middleware is called before the Handler.// Use Chain() to combine multiple middlewares.MiddlewareMiddlewareFuncHandlerHandlerFuncHelpHandlerHandlerFunc// CompletionHandler is called when the command is run in completion// mode. If nil, only the default completion handler is used.//// Flag and option parsing is best-effort in this mode, so even if an Option// is "required" it may not be set.CompletionHandlerCompletionHandlerFunc}
Command describes an executable command.
func (*Command)AddSubcommands¶added inv0.5.0
AddSubcommands adds the given subcommands, setting theirParent field automatically.
func (*Command)FullName¶added inv0.5.0
FullName returns the full invocation name of the command,as seen on the command line.
func (*Command)FullOptions¶added inv0.5.0
FullOptions returns the options of the command and its parents.
func (*Command)FullUsage¶added inv0.5.0
FullName returns usage of the command, precededby the usage of its parents.
func (*Command)Invoke¶added inv0.5.0
func (c *Command) Invoke(args ...string) *Invocation
Invoke creates a new invocation of the command, withstdio discarded.
The returned invocation is not live until Run() is called.
typeCompletionHandlerFunc¶added inv0.8.0
type CompletionHandlerFunc func(i *Invocation) []string
typeEnumArray¶added inv0.8.0
funcEnumArrayOf¶added inv0.8.0
typeEnviron¶
type Environ []EnvVar
funcParseEnviron¶
ParseEnviron returns all environment variables starting withprefix without said prefix.
typeGroup¶
type Group struct {Parent *Group `json:"parent,omitempty"`Namestring `json:"name,omitempty"`YAMLstring `json:"yaml,omitempty"`Descriptionstring `json:"description,omitempty"`}
Group describes a hierarchy of groups that an option or command belongs to.
typeHandlerFunc¶
type HandlerFunc func(i *Invocation)error
HandlerFunc handles an Invocation of a command.
funcDefaultHelpFn¶added inv0.8.0
func DefaultHelpFn()HandlerFunc
DefaultHelpFn returns a function that generates usage (help)output for a given command.
typeHostPort¶
HostPort is a host:port pair.
func (*HostPort)MarshalJSON¶
func (*HostPort)MarshalYAML¶
func (*HostPort)UnmarshalJSON¶
func (*HostPort)UnmarshalYAML¶
typeInvocation¶
type Invocation struct {Command *Command// Args is reduced into the remaining arguments after parsing flags// during Run.Args []string// Environ is a list of environment variables. Use EnvsWithPrefix to parse// os.Environ.EnvironEnvironStdoutio.WriterStderrio.WriterStdinio.Reader// DeprecatedLoggerslog.Logger// DeprecatedNetNet// contains filtered or unexported fields}
Invocation represents an instance of a command being executed.
func (*Invocation)Context¶
func (inv *Invocation) Context()context.Context
func (*Invocation)CurWords¶added inv0.8.0
func (inv *Invocation) CurWords() (prevstring, curstring)
func (*Invocation)IsCompletionMode¶added inv0.8.0
func (inv *Invocation) IsCompletionMode()bool
IsCompletionMode returns true if the command is being run in completion mode.
func (*Invocation)ParsedFlags¶
func (inv *Invocation) ParsedFlags() *pflag.FlagSet
func (*Invocation)Run¶
func (inv *Invocation) Run() (errerror)
Run executes the command.If two command share a flag name, the first command wins.
func (*Invocation)SignalNotifyContext¶
func (inv *Invocation) SignalNotifyContext(parentcontext.Context, signals ...os.Signal) (ctxcontext.Context, stopcontext.CancelFunc)
SignalNotifyContext is equivalent to signal.NotifyContext, but supports being overridden intests.
func (*Invocation)WithContext¶
func (inv *Invocation) WithContext(ctxcontext.Context) *Invocation
WithContext returns a copy of the Invocation with the given context.
func (*Invocation)WithOS¶
func (inv *Invocation) WithOS() *Invocation
WithOS returns the invocation as a main package, filling in the invocation's unsetfields with OS defaults.
func (*Invocation)WithTestParsedFlags¶
func (inv *Invocation) WithTestParsedFlags(_testing.TB,parsedFlags *pflag.FlagSet,) *Invocation
func (*Invocation)WithTestSignalNotifyContext¶
func (inv *Invocation) WithTestSignalNotifyContext(_testing.TB,f func(parentcontext.Context, signals ...os.Signal) (ctxcontext.Context, stopcontext.CancelFunc),) *Invocation
WithTestSignalNotifyContext allows overriding the default implementation of SignalNotifyContext.This should only be used in testing.
typeMiddlewareFunc¶
type MiddlewareFunc func(nextHandlerFunc)HandlerFunc
MiddlewareFunc returns the next handler in the chain,or nil if there are no more.
funcChain¶
func Chain(ms ...MiddlewareFunc)MiddlewareFunc
Chain returns a Handler that first calls middleware in order.
funcRequireNArgs¶
func RequireNArgs(wantint)MiddlewareFunc
funcRequireRangeArgs¶
func RequireRangeArgs(start, endint)MiddlewareFunc
RequireRangeArgs returns a Middleware that requires the number of argumentsto be between start and end (inclusive). If end is -1, then the number ofarguments must be at least start.
typeNet¶
type Net interface {// Listen has the same semantics as `net.Listen` but also supports `udp`Listen(network, addressstring) (net.Listener,error)}
Net abstracts CLI commands interacting with the operating system networking.
At present, it covers opening local listening sockets, since doing thisin testing is a challenge without flakes, since it's hard to pick a port weknow a priori will be free.
typeNoOptDefValuer¶
type NoOptDefValuer interface {NoOptDefValue()string}
NoOptDefValuer describes behavior when nooption is passed into the flag.
This is useful for boolean or otherwise binary flags.
typeOption¶
type Option struct {Namestring `json:"name,omitempty"`Descriptionstring `json:"description,omitempty"`// Required means this value must be set by some means. It requires// `ValueSource != ValueSourceNone`// If `Default` is set, then `Required` is ignored.Requiredbool `json:"required,omitempty"`// Flag is the long name of the flag used to configure this option. If unset,// flag configuring is disabled.Flagstring `json:"flag,omitempty"`// FlagShorthand is the one-character shorthand for the flag. If unset, no// shorthand is used.FlagShorthandstring `json:"flag_shorthand,omitempty"`// Env is the environment variable used to configure this option. If unset,// environment configuring is disabled.Envstring `json:"env,omitempty"`// YAML is the YAML key used to configure this option. If unset, YAML// configuring is disabled.YAMLstring `json:"yaml,omitempty"`// Default is parsed into Value if set.Defaultstring `json:"default,omitempty"`// Value includes the types listed in values.go.Valuepflag.Value `json:"value,omitempty"`// Annotations enable extensions to serpent higher up in the stack. It's useful for// help formatting and documentation generation.AnnotationsAnnotations `json:"annotations,omitempty"`// Group is a group hierarchy that helps organize this option in help, configs// and other documentation.Group *Group `json:"group,omitempty"`// UseInstead is a list of options that should be used instead of this one.// The field is used to generate a deprecation warning.UseInstead []Option `json:"use_instead,omitempty"`Hiddenbool `json:"hidden,omitempty"`ValueSourceValueSource `json:"value_source,omitempty"`CompletionHandlerCompletionHandlerFunc `json:"-"`}
Option is a configuration option for a CLI application.
func (*Option)UnmarshalJSON¶
typeOptionSet¶
type OptionSet []Option
OptionSet is a group of options that can be applied to a command.
func (OptionSet)ByName¶
ByName returns the Option with the given name, or nil if no such optionexists.
func (*OptionSet)MarshalYAML¶
MarshalYAML converts the option set to a YAML node, that can beconverted into bytes via yaml.Marshal.
The node is returned to enable post-processing higher up inthe stack.
It is isomorphic with FromYAML.
func (*OptionSet)ParseEnv¶
ParseEnv parses the given environment variables into the OptionSet.Use EnvsWithPrefix to filter out prefixes.
func (*OptionSet)SetDefaults¶
SetDefaults sets the default values for each Option, skipping valuesthat already have a value source.
func (*OptionSet)UnmarshalJSON¶
UnmarshalJSON implements json.Unmarshaler for OptionSets. Options have aninterface Value type that cannot handle unmarshalling because the types cannotbe inferred. Since it is a slice, instantiating the Options first does nothelp.
However, we typically do instantiate the slice to have the correct types.So this unmarshaller will attempt to find the named option in the existingset, if it cannot, the value is discarded. If the option exists, the valueis unmarshalled into the existing option, and replaces the existing option.
The value is discarded if it's type cannot be inferred. This behavior justfeels "safer", although it should never happen if the correct option setis passed in. The situation where this could occur is if a client and serverare on different versions with different options.
func (*OptionSet)UnmarshalYAML¶
UnmarshalYAML converts the given YAML node into the option set.It is isomorphic with ToYAML.
typeRegexp¶
func (*Regexp)MarshalJSON¶
func (*Regexp)MarshalYAML¶
func (*Regexp)UnmarshalJSON¶
func (*Regexp)UnmarshalYAML¶
typeRunCommandError¶
func (*RunCommandError)Error¶
func (e *RunCommandError) Error()string
func (*RunCommandError)Unwrap¶
func (e *RunCommandError) Unwrap()error
typeStringArray¶
type StringArray []string
StringArray is a slice of strings that implements pflag.Value and pflag.SliceValue.
funcStringArrayOf¶
func StringArrayOf(ss *[]string) *StringArray
func (*StringArray)Append¶
func (s *StringArray) Append(vstring)error
func (*StringArray)GetSlice¶
func (s *StringArray) GetSlice() []string
func (*StringArray)Replace¶
func (s *StringArray) Replace(vals []string)error
func (*StringArray)Set¶
func (s *StringArray) Set(vstring)error
func (StringArray)String¶
func (sStringArray) String()string
func (StringArray)Type¶
func (StringArray) Type()string
func (StringArray)Value¶
func (sStringArray) Value() []string
typeStruct¶
type Struct[Tany] struct {Value T}
Struct is a special value type that encodes an arbitrary struct.It implements the flag.Value interface, but in general these values shouldonly be accepted via config for ergonomics.
The string encoding type is YAML.
typeURL¶
func (*URL)MarshalJSON¶
func (*URL)MarshalYAML¶
func (*URL)UnmarshalJSON¶
func (*URL)UnmarshalYAML¶
typeUnknownSubcommandError¶added inv0.8.0
type UnknownSubcommandError struct {Args []string}
func (*UnknownSubcommandError)Error¶added inv0.8.0
func (e *UnknownSubcommandError) Error()string
typeValidator¶
Validator is a wrapper around a pflag.Value that allows for validationof the value after or before it has been set.
func (*Validator[T])MarshalJSON¶
func (*Validator[T])MarshalYAML¶
func (*Validator[T])Underlying¶
func (*Validator[T])UnmarshalJSON¶
func (*Validator[T])UnmarshalYAML¶
typeValueSource¶
type ValueSourcestring
const (ValueSourceNoneValueSource = ""ValueSourceFlagValueSource = "flag"ValueSourceEnvValueSource = "env"ValueSourceYAMLValueSource = "yaml"ValueSourceDefaultValueSource = "default")
typeYAMLConfigPath¶
type YAMLConfigPathstring
YAMLConfigPath is a special value type that encodes a path to a YAMLconfiguration file where options are read from.
func (*YAMLConfigPath)Set¶
func (p *YAMLConfigPath) Set(vstring)error
func (*YAMLConfigPath)String¶
func (p *YAMLConfigPath) String()string
func (*YAMLConfigPath)Type¶
func (*YAMLConfigPath) Type()string