Skip to content

Instantly share code, notes, and snippets.

@xeoncross
Last active July 10, 2021 14:28
Show Gist options
  • Save xeoncross/707ee4a6a7fff8b73baa74a1071c382e to your computer and use it in GitHub Desktop.
Save xeoncross/707ee4a6a7fff8b73baa74a1071c382e to your computer and use it in GitHub Desktop.
Flags, environment variables, and .env file config loading https://play.golang.org/p/pautLXl7yMW
MYSQL_HOST=127.0.0.1
MYSQL_USER=hello
MYSQL_PASS=world
MYSQL_NAME=myapp
MYSQL_PORT=3306
package main
import (
"bufio"
"flag"
"fmt"
"io"
"os"
"strings"
"github.com/peterbourgon/ff"
)
func main() {
_, err := New()
if err != nil {
fmt.Println(err)
}
}
// Config for the root command, including flags and types that should be
// available to each subcommand.
type Config struct {
MySQL mysqlConfig
Verbose bool
}
type mysqlConfig struct {
User string
Pass string
Host string
Port string
Name string
}
func New() (*Config, error) {
c := &Config{}
fs := flag.NewFlagSet("app", flag.ExitOnError)
fs.StringVar(&c.MySQL.Host, "MYSQL_HOST", "", "MySQL host")
fs.StringVar(&c.MySQL.Port, "MYSQL_POST", "", "MySQL port")
fs.StringVar(&c.MySQL.User, "MYSQL_USER", "", "MySQL username")
fs.StringVar(&c.MySQL.Pass, "MYSQL_PASS", "", "MySQL password")
fs.StringVar(&c.MySQL.Name, "MYSQL_NAME", "", "MySQL database name")
fs.BoolVar(&c.Verbose, "v", false, "log verbose output")
return c, ff.Parse(fs,
os.Args[1:],
ff.WithEnvVarNoPrefix(),
ff.WithConfigFileParser(EnvParser),
// ff.WithConfigFileParser(ff.JSONParser),
// ff.WithConfigFileParser(ff.PlainParser),
ff.WithConfigFileFlag("config-file"),
)
}
// EnvParser is a parser for config files in an extremely simple format. Each
// line is tokenized as a single key/value pair. The first whitespace-delimited
// token in the line is interpreted as the flag name, and all remaining tokens
// are interpreted as the value. Any leading hyphens on the flag name are
// ignored.
func EnvParser(r io.Reader, set func(name, value string) error) error {
s := bufio.NewScanner(r)
for s.Scan() {
line := strings.TrimSpace(s.Text())
if line == "" {
continue // skip empties
}
if line[0] == '#' {
continue // skip comments
}
var (
name string
value string
index = strings.IndexRune(line, '=')
)
if index < 0 {
name, value = line, "true" // boolean option
} else {
name, value = line[:index], strings.TrimSpace(line[index+1:])
}
fmt.Println("env:", name, value, index)
// remove ending comment
if i := strings.Index(value, " #"); i >= 0 {
value = strings.TrimSpace(value[:i])
}
if err := set(name, value); err != nil {
return err
}
}
return nil
}
// or you can just wrap what already exists
func EnvTransformParser(r io.Reader, set func(name, value string) error) error {
b, err := ioutil.ReadAll(r)
if err != nil {
return err
}
r = bytes.NewReader(bytes.ReplaceAll(b, []byte("="), []byte(" ")))
return ff.PlainParser(r, set)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment