8000 GitHub - UmanaiaForks/ff: Flags-first package for configuration
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

UmanaiaForks/ff

 
 

Repository files navigation

ff go.dev reference Latest Release Build Status

ff is a flags-first approach to configuration.

The basic idea is that myprogram -h should always show the complete configurati 8000 on "surface area" of a program. Therefore, every config parameter should be defined as a flag. This module provides a simple and robust way to define those flags, and to parse them from command-line arguments, environment variables, and/or a config file.

Building a command-line application in the style of kubectl or docker? Command provides a declarative approach that's simpler to write, and easier to maintain, than many common alternatives.

Usage

This module provides a FlagSet that can be used as follows.

fs := ff.NewFlagSet("myprogram")
var (
	listenAddr = fs.StringLong("listen", "localhost:8080", "listen address")
	refresh    = fs.Duration('r', "refresh", 15*time.Second, "refresh interval")
	debug      = fs.Bool('d', "debug", false, "log debug information")
	_          = fs.StringLong("config", "", "config file (optional)")
)

It's also possible to use a standard library flag.FlagSet. Be sure to pass the ContinueOnError error handling strategy, as other options either panic or terminate the program on parse errors -- rude!

fs := flag.NewFlagSet("myprogram", flag.ContinueOnError)
var (
	listenAddr = fs.String("listen", "localhost:8080", "listen address")
	refresh    = fs.Duration("refresh", 15*time.Second, "refresh interval")
	debug      = fs.Bool("debug", "log debug information")
	_          = fs.String("config", "", "config file (optional)")
)

You can also provide your own implementation of the Flags interface.

Call ff.Parse to parse the flag set. Options can be provided to control parsing behavior.

err := ff.Parse(fs, os.Args[1:],
	ff.WithEnvVarPrefix("MY_PROGRAM"),
	ff.WithConfigFileFlag("config"),
	ff.WithConfigFileParser(ff.PlainParser),
)

Here, flags are first set from the provided command-line arguments, from env vars beginning with MY_PROGRAM, and, if the user specifies a config file, from values in that file, as parsed by ff.PlainParser.

Unlike other flag packages, help text is not automatically printed as a side effect of parsing. When a user requests help via e.g. -h or --help, it's reported as a parse error. Callers are responsible for checking parse errors, and printing help text when appropriate. package ffhelp has helpers for producing help text in a standard format, but you can always write your own.

if errors.Is(err, ff.ErrHelp) {
	fmt.Fprint(os.Stderr, ffhelp.Flags(fs))
	os.Exit(0)
} else if err != nil {
	fmt.Fprintf(os.Stderr, "error: %v\n", )
	os.Exit(1)
}

Environment variables

It's possible to take runtime configuration from the environment. The options WithEnvVars and WithEnvVarPrefix enable this feature, and determine how flag names are mapped to environment variable names.

fs := ff.NewFlagSet("myservice")
var (
	port  = fs.Int('p', "port", 8080, "listen port for server (also via PORT)")
	debug = fs.Bool('d', "debug", false, "log debug information (also via DEBUG)")
)
ff.Parse(fs, os.Args[1:], ff.WithEnvVars())
fmt.Printf("port %d, debug %v\n", *port, *debug)
$ env PORT=9090 myservice
port 9090, debug false
$ env PORT=9090 DEBUG=1 myservice --port=1234
port 1234, debug true

Config files

It's possible to take runtime configuration from config files. The options WithConfigFile, WithConfigFileFlag, and WithConfigFileParser control how config files are specified and parsed. This module includes support for JSON, YAML, TOML, and .env config files, as well as the simple PlainParser format.

fs := ff.NewFlagSet("myservice")
var (
	port  = fs.IntLong("port", 8080, "listen port for server")
	debug = fs.BoolLong("debug", false, "log debug information")
	_     = fs.StringLong("config", "", "config file")
)
ff.Parse(fs, os.Args[1:], ff.WithConfigFileFlag("config"), ff.WithConfigFileParser(ff.PlainParser))
fmt.Printf("port %d, debug %v\n", *port, *debug)
$ printf "port 9090\n" >1.conf ; myservice --config=1.conf
port 9090, debug false
$ printf "port 9090\ndebug\n" >2.conf ; myservice --config=2.conf --port=1234
port 1234, debug true

Priority

Command-line args have the highest priority, because they're explicitly given to each running instance of a program by the user. Think of command-line args as the "user" configuration.

Environment variables have the next-highest priority, because they reflect configuration set in the runtime context. Think of env vars as the "session" configuration.

Config files have the lowest priority, because they represent config that's static to the host. Think of config files as the "host" configuration.

Commands

Command is a declarative and lightweight alternative to common CLI frameworks like spf13/cobra, urfave/cli, or alecthomas/kingpin.

Those frameworks have relatively large APIs, in order to support a large number of "table stakes" features. In contrast, the command API is quite small, with the immediate goal of being intuitive and productive, and the long-term goal of producing CLI applications that are substantially easier to understand and maintain.

Commands are concerned only with the core mechanics of defining a command tree, parsing flags, and selecting a command to run. They're not intended to be a one-stop-shop for everything a command-line application may need. Features like tab completion, colorized output, etc. are orthogonal to command tree parsing, and can be easily provided by the consumer.

See the examples directory for some CLI tools built with commands.

About

Flags-first package for configuration

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Go 99.5%
  • Shell 0.5%
0