Factor number Three of the Twelve Factor App is "Config"
They suggest to store the configuration in env vars. I completely agree.
However, it's left to us to define how the env vars are loaded into our app.
In addition to that, some defaults are better stored in a json or yaml, which can be overridden
This project is inspired by
- The legendary
github.com/spf13/viper
, and - backstage.io's
app-config.yaml
(still not implemented)
The idea is to declaratively define a big struct, and pass that struct to this app, and it returns your filled config.
The example looks like this:
package main
//go:generate factor3 generate
import (
"log"
"os"
factor3 "github.com/drornir/factor3/pkg/runtime"
)
//factor3:generate --filename ./example/app/config.yaml --env-prefix EX
type Config struct {
DBConnection string
//factor3:pflag port
Port string
//factor3:pflag some-number n
SomeNumber int
//factor3:pflag some-flag
SomeFlag bool
}
func main() {
var c Config
if err := factor3.Load(&c, os.Args[1:]); err != nil {
log.Fatal(err)
}
// ... c is ready
}
You need to include //go:generate factor3 generate
once, somewhere in the package,
in order to trigger code generation.
After that, you need annotate every struct you want with //factor3:generate
in
order to generate an implementation of Factor3Load()
, which loads the data into
the struct.
For this example, the corresponding yaml is the following (notice it's snakecase)
port: "3001"
db_connection: root:pass@localhost:3306
some_number: 4
some_flag: true
And the env vars are
export EX_PORT=9090
export EX_DB_CONNECTION=root:pass@localhost:3307
export EX_SOME_NUMBER=6
export EX_SOME_FLAG=false
Setting flags is optional (like DBConnection
in the example), but you can
explicitly set them by using the //factor3:pflag
annotation.
It accepts between one and two string arguments. For example,
//factor3:pflag some-number n
will declare a flag called some-number
, with
the short form n
.
Using go install
go install github.com/drornir/factor3
Adding configuration to the code generation is done using one line comments
which start with //factor3:
. Notice there is no space after the //
. We call
these comments "annotations".
"Top level" annotations means comments that come directly above the struct type.
"In Struct" annotations means comments that come directly above a field in the struct.
//factor3:generate [--filename <FILE_PATH>] [--env-prefix <PREFIX>]
This is required to trigger the code generation for a certain struct type.
filename
is a path to where you place the json or yaml file with values for this structenv-prefix
is a string that will be prepended to all env vars lookups.⚠️ Note that an underscore will also be prepended between your prefix and the rest of the env var name (for--env-prefix=PRE
and env varVAR
, the result will bePRE_VAR
).
//factor3:pflag <LONG_FLAG_NAME> [SHORT_FLAG_NAME]
Will add flag support for this field. At least one argument is required,
which is the long form of the flag(LONG_FLAG_NAME
). Optionally, you can set
SHORT_FLAG_NAME
, which must be a one character string representing the short flag letter.
The underlying package in use is not the standard library flag
, but
github.com/spf13/pflag
, the POSIX compliant alternative.