Giter VIP home page Giter VIP logo

cleanenv's Introduction

Clean Env

Clean Env

Minimalistic configuration reader

Mentioned in Awesome Go GoDoc Go Report Card Coverage Status Build Status Release License

Overview

This is a simple configuration reading tool. It just does the following:

  • reads and parses configuration structure from the file
  • reads and overwrites configuration structure from environment variables
  • writes a detailed variable list to help output

Content

Installation

To install the package run

go get -u github.com/ilyakaznacheev/cleanenv

Usage

The package is oriented to be simple in use and explicitness.

The main idea is to use a structured configuration variable instead of any sort of dynamic set of configuration fields like some libraries does, to avoid unnecessary type conversions and move the configuration through the program as a simple structure, not as an object with complex behavior.

There are just several actions you can do with this tool and probably only things you want to do with your config if your application is not too complicated.

  • read configuration file
  • read environment variables
  • read some environment variables again

Read Configuration

You can read a configuration file and environment variables in a single function call.

import "github.com/ilyakaznacheev/cleanenv"

type ConfigDatabase struct {
    Port     string `yaml:"port" env:"PORT" env-default:"5432"`
    Host     string `yaml:"host" env:"HOST" env-default:"localhost"`
    Name     string `yaml:"name" env:"NAME" env-default:"postgres"`
    User     string `yaml:"user" env:"USER" env-default:"user"`
    Password string `yaml:"password" env:"PASSWORD"`
}

var cfg ConfigDatabase

err := cleanenv.ReadConfig("config.yml", &cfg)
if err != nil {
    ...
}

This will do the following:

  1. parse configuration file according to YAML format (yaml tag in this case);
  2. reads environment variables and overwrites values from the file with the values which was found in the environment (env tag);
  3. if no value was found on the first two steps, the field will be filled with the default value (env-default tag) if it is set.

Read Environment Variables Only

Sometimes you don't want to use configuration files at all, or you may want to use .env file format instead. Thus, you can limit yourself with only reading environment variables:

import "github.com/ilyakaznacheev/cleanenv"

type ConfigDatabase struct {
    Port     string `env:"PORT" env-default:"5432"`
    Host     string `env:"HOST" env-default:"localhost"`
    Name     string `env:"NAME" env-default:"postgres"`
    User     string `env:"USER" env-default:"user"`
    Password string `env:"PASSWORD"`
}

var cfg ConfigDatabase

err := cleanenv.ReadEnv(&cfg)
if err != nil {
    ...
}

Update Environment Variables

Some environment variables may change during the application run. To get the new values you need to mark these variables as updatable with the tag env-upd and then run the update function:

import "github.com/ilyakaznacheev/cleanenv"

type ConfigRemote struct {
    Port     string `env:"PORT" env-upd`
    Host     string `env:"HOST" env-upd`
    UserName string `env:"USERNAME"`
}

var cfg ConfigRemote

cleanenv.ReadEnv(&cfg)

// ... some actions in-between

err := cleanenv.UpdateEnv(&cfg)
if err != nil {
    ...
}

Here remote host and port may change in a distributed system architecture. Fields cfg.Port and cfg.Host can be updated in the runtime from corresponding environment variables. You can update them before the remote service call. Field cfg.UserName will not be changed after the initial read, though.

Description

You can get descriptions of all environment variables to use them in the help documentation.

import "github.com/ilyakaznacheev/cleanenv"

type ConfigServer struct {
    Port     string `env:"PORT" env-description:"server port"`
    Host     string `env:"HOST" env-description:"server host"`
}

var cfg ConfigRemote

help, err := cleanenv.GetDescription(&cfg, nil)
if err != nil {
    ...
}

You will get the following:

Environment variables:
  PORT  server port
  HOST  server host

Model Format

Library uses tags to configure the model of configuration structure. There are the following tags:

  • env="<name>" - environment variable name (e.g. env="PORT");
  • env-upd - flag to mark a field as updatable. Run UpdateEnv(&cfg) to refresh updatable variables from environment;
  • env-required - flag to mark a field as required. If set will return an error during environment parsing when the flagged as required field is empty (default Go value). Tag env-default is ignored in this case;
  • env-default="<value>" - default value. If the field wasn't filled from the environment variable default value will be used instead;
  • env-separator="<value>" - custom list and map separator. If not set, the default separator , will be used;
  • env-description="<value>" - environment variable description;
  • env-layout="<value>" - parsing layout (for types like time.Time);
  • env-prefix="<value>" - prefix for all fields of nested structure (only for nested structures);

Supported types

There are following supported types:

  • int (any kind);
  • float (any kind);
  • string;
  • boolean;
  • slices (of any other supported type);
  • maps (of any other supported type);
  • time.Duration;
  • time.Time (layout by default is RFC3339, may be overridden by env-layout);
  • *time.Location (time zone parsing depends on running machine);
  • any type that implements encoding.TextUnmarshaler;
  • any type implementing cleanenv.Setter interface.

Custom Functions

To enhance package abilities you can use some custom functions.

Custom Value Setter

To make custom type allows to set the value from the environment variable, you need to implement the Setter interface on the field level:

type MyField string

func (f *MyField) SetValue(s string) error  {
    if s == "" {
        return fmt.Errorf("field value can't be empty")
    }
    *f = MyField("my field is: "+ s)
    return nil
}

type Config struct {
    Field MyField `env="MY_VALUE"`
}

SetValue method should implement conversion logic from string to custom type.

Custom Value Update

You may need to execute some custom field update logic, e.g. for remote config load.

Thus, you need to implement the Updater interface on the structure level:

type Config struct {
    Field string
}

func (c *Config) Update() error {
    newField, err := SomeCustomUpdate()
    f.Field = newField
    return err
}

Supported File Formats

There are several most popular config file formats supported:

  • YAML (.yaml, .yml)
  • JSON (.json)
  • TOML (.toml)
  • EDN (.edn)
  • ENV (.env)

Note:

  • while using .env file the library will set corresponding data to process environment variables. It will override existing variables with the same keys in the process environment.

Integration

The package can be used with many other solutions. To make it more useful, we made some helpers.

Flag

You can use the cleanenv help together with Golang flag package.

// create some config structure
var cfg config 

// create flag set using `flag` package
fset := flag.NewFlagSet("Example", flag.ContinueOnError)

// get config usage with wrapped flag usage
fset.Usage = cleanenv.FUsage(fset.Output(), &cfg, nil, fset.Usage)

fset.Parse(os.Args[1:])

Examples

type Config struct {
    Port string `yaml:"port" env:"PORT" env-default:"8080"`
    Host string `yaml:"host" env:"HOST" env-default:"localhost"`
}

var cfg Config

err := ReadConfig("config.yml", &cfg)
if err != nil {
    ...
}

This code will try to read and parse the configuration file config.yml as the structure is described in the Config structure. Then it will overwrite fields from available environment variables (PORT, HOST).

For more details check the example directory.

Version Support Policy

We support the last 7 versions of Golang. E.g. if the current version is 1.19, we test compatibility with all versions from 1.19 to 1.13.

If you use an older version of Golang in your project, please use an older library version.

Contribution

The tool is open-sourced under the MIT license.

If you find some error, want to add something or ask a question - feel free to create an issue and/or make a pull request.

Guidelines for contribution may be found in CONTRIBUTING.md.

Any contribution is welcome.

Thanks

Big thanks to a project kelseyhightower/envconfig for inspiration.

The logo was made by alexchoffy.

Blog Posts

Clean Configuration Management in Golang.

cleanenv's People

Contributors

arunvm123 avatar gsdevme avatar illiafox avatar ilyakaznacheev avatar kha7iq avatar lihmeh avatar marcoderama avatar mikhailv avatar mildc avatar mirecl avatar padawin avatar padremortius avatar pansachin avatar saddit avatar vincentserpoul avatar vinicio avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

cleanenv's Issues

Add environment variables groups

Add groups with prefixes and group descriptions

The group should be defined as a structure with a set of tags, which defines the structure as a group, specifies an environment variable prefix (optional) and a group description, that will be shown in help (optional).

disable github workflow for pull request

Github repo secrets are only shard among contributors, and based on recent changes by GH, workflow does not run automatically for unknown contributors as well.
In this case its better to disable the workflow for notification on pull request, either by deleting pull_request.yml or commenting out the content of file.
Connects to #73

Map of string to array of strings

How can we use map of string to array of strings in ENV?
Suppose we have :
MapStringArrayString map[string][]string env:"TEST_MAPSTRINGARRAYSTRING"``
Is it even possible with current implementation?

Better documentation

Documentation should give a better understanding of how to use the library and what features it has.

Didn't skip unexported field

@ilyakaznacheev

panic: reflect.Value.Interface: cannot return value obtained from unexported field or method

goroutine 1 [running]:
reflect.valueInterface({0x95f8a0?, 0xc00055c248?, 0xabea8f?}, 0xa?)
        E:/software/dev_core/go1.18.1/src/reflect/value.go:1441 +0xd8
reflect.Value.Interface(...)
        E:/software/dev_core/go1.18.1/src/reflect/value.go:1430
github.com/ilyakaznacheev/cleanenv.readStructMetadata({0x9c7900?, 0xc00055c1c0?})

        E:/text/code-project/GolangProject/pkg/mod/github.com/ilyakaznacheev/[email protected]/cleanenv.go:274 +0x771
github.com/ilyakaznacheev/cleanenv.readEnvVars({0x9c7900, 0xc00055c1c0}, 0x0)

        E:/text/code-project/GolangProject/pkg/mod/github.com/ilyakaznacheev/[email protected]/cleanenv.go:333 +0x45
github.com/ilyakaznacheev/cleanenv.ReadConfig({0xace493?, 0xc00041f9a8?}, {0x9c7900, 0xc00055c1c0})


        E:/text/code-project/GolangProject/pkg/mod/github.com/ilyakaznacheev/[email protected]/cleanenv.go:95 +0x48

It doesn't skip unexported field

Originally posted by @838239178 in #68 (comment)

Example

package main

import (
	"github.com/ilyakaznacheev/cleanenv"
)

type Children struct {
	name string `yaml:"name" env:"name"`
}

type Config struct {
	private Children `yaml:"private" env-prefix:"private"`
	Public string `yaml:"public" env:"public"`
}

func main()  {
	var conf Config
	if err := cleanenv.ReadConfig("config.yml", &conf); err != nil {
		panic(err)
	}
}

What is the correct location for the config.yml?

Thank you for this solution. I implemented it and was running in minutes.

Question: I am running on Linux and for this web service project I have found that my app only reads the config.yml if I launch the service from the same directory as the web service (where the config file resides as well). This works fine:
cd \usr\bin\myapp
myapp &

This does not work:
\usr\bin\myapp\myapp &
I am using the recommended code:
err := cleanenv.ReadConfig("config.yml", &cfg)

I'd like to be able to start the service in the background and have it read the config from the execution directory. (Or is there a better method? Should I pass the config file location as a cmd line parameter?) Sorry if this is basic, I am new to Linux development.

Thanks again for your help.

Custom functions are not running for values from files

I've noticed that when reading a config file with a field that is not available in env, its custom function won't run.

The reason seems to be that for these kind of fields, parseValue won't be called and therefore the corresponding custom function.

Custom functions runs only if the field was provided as an env argument or with a zero'ed value:

Close develop branch

It's too much for such a simple library. While there are no CI/CDs or environments bound to each branch, the develop branch just gives unnecessary complexity.

Optional value (ptr) via env

Hello, I have used the lib for building config from env and a file. If I created field as pointer then I thought that it is as optional and it is not bound (nil value). When I set value for pointer field in a config file my logic is gone well, but if I set via env variable then my program throw exception ("unsupported type .")

type Config struct {
  optField *MyField `env:"OPT_VALUE"`
}

I think that 'cleanenv' have to implement same logic with env that a config file and this way not provide an unexpected error.

Custom format for GetDescription

Hi,

i really like your simple approach for env configuration and also the possibility to easily print the env variables.
But i'm not that fully happy with the multiline format for the env variables and currently its pretty hard to adjust it.
So maybe it would be useful to have a possibility to pass an own format for the description lines like

"$1 [$2] - $3 (default $4)"

which would be create an output like this

TEST_ENV [string] - This is a test env variable (default "test")

Another maybe more easier approach to implement would be to add an method which returns the parsed metadata so it could be used to print using an own format.

Thanks

Apply Custom Value Setters to custom struct types

I realized that a SetValue() defined on a custom struct type is never called. Insted, the struct is flattened. This prevents me from parsing e.g., a yaml string from an ENV variable:

type Config struct {
	Complex ComplexYamlConfig `env:"COMPLEX_CONFIG"`
}

type ComplexYamlConfig struct {
	Left  string `yaml:"myProp"`
}

func (c *ComplexYamlConfig) SetValue(s string) error {
        // will never be called by cleanenv
	return yaml.Unmarshal([]byte(s), c)
}

Is this something that can be added? Or is there a different way of achieving the same thing?

Add config overriding

It would be nice if we have the possibility to override a base config.

Like:

.env.override
.config.yml.override

Add flag helper

Need to add a helper to include a flag package help into the main help to build a one-line help setup.
Example

Not able load the config.yml file

I am new to AWS and Multi Docker Container.
Not able load config.yml and I don't know how to configure Dockerrun.aws.json to make sure the config.yml file is accessible in the docker container.

var err = cleanenv.ReadConfig("config.yml", &cfg)
if err != nil {
	log.Fatal(err)
}

Any plans to move to yaml v3?

I would like to use yaml.Node in my config files but have found they don't work with cleanenv. As an experiment, I changed the import in cleanenv.go to use yaml v3 instead and things with yaml.Node started working. I did no testing otherwise though.

So, I'm curious, any plans to move to yaml v3?

Time layout does not have any effect for yaml

Below given is the configuation file

type Config struct { Order struct { Date time.Time ``yaml:"date" env:"ORDER_DATE" env-layout:"02/01/2006 03:04:05 PM"`` } ``yaml:"order"`` }

Below is the yml file

order: date: 17/01/2022 10:00:00 AM subtime: 5m

I am getting the following error:
config file parsing error: parsing time "Mon Jan 17 10:00:00 2022" as "2006-01-02T15:04:05Z07:00": cannot parse "Mon Jan 17 10:00:00 2022" as "2006"

I also cloned your repo, edited the test eleanenv_test.go to include the following to the TestParseFile function

Time time.Time ``yaml:"time" json:"time" toml:"time" env-layout:"02/01/2006 03:04:05 PM"``

On running the test I got the following errors:

go test github.com/ilyakaznacheev/cleanenv --- FAIL: TestParseFile (0.00s) --- FAIL: TestParseFile/yaml (0.00s) cleanenv_test.go:624: wrong error behavior config file parsing error: parsing time "17/01/2022 10:00:00 AM" as "2006-01-02T15:04:05Z07:00": cannot parse "1/2022 10:00:00 AM" as "2006", wantErr false --- FAIL: TestParseFile/json (0.00s) cleanenv_test.go:624: wrong error behavior config file parsing error: parsing time "\"17/01/2022 10:00:00 AM\"" as "\"2006-01-02T15:04:05Z07:00\"": cannot parse "1/2022 10:00:00 AM\"" as "2006", wantErr false --- FAIL: TestParseFile/toml (0.00s) cleanenv_test.go:624: wrong error behavior config file parsing error: parsing time "17/01/2022 10:00:00 AM" as "2006-01-02T15:04:05Z07:00": cannot parse "1/2022 10:00:00 AM" as "2006", wantErr false FAIL FAIL github.com/ilyakaznacheev/cleanenv 0.004s FAIL

Add more examples

Need to add more examples for different cases to example directory.

Set but empty Environment Variables don't raise an error

Found that even if my struct has the env-required or env-default set for the fields, if the environment variable was set but left empty, no error is rised, default value is not set either if used. Eg:

Lets say this var was set like this:
export MY_VAR=""

If my struct were:

type MyVars struct {
    MyVar string `env:"MY_VAR" env-required:"true"`
}

No error is raised at all, and i get and empty struct. If otherwise my struct had a default:

type MyVars struct {
    MyVar string `env:"MY_VAR" env-default:"something"`
}

Id get an empty struct again rather than one with the default value.

Is this an expected behavior or could it be improved on?

if file parse err no config read at all

Hi,

If file doesn't exists or corrupted - nothing got at all. TOML/ENV can be empty (null) to be ok all others - fail.
Expected to get params from env and/or default env

package main

import (
	"github.com/ilyakaznacheev/cleanenv"
	"github.com/spf13/pflag"
	"github.com/davecgh/go-spew/spew"
)

type Config struct {
	BindAddr   string `toml:"bindaddr" env:"YAACC_BINDADDR" env-default:":5013"`
	DBPort     int    `toml:"dbport" env:"YAACC_DBPORT" env-default:"5432"`
	DBHost     string `toml:"dbhost" env:"YAACC_DBHOST" env-default:"localhost"`
	DBName     string `toml:"dbname" env:"YAACC_DBNAME" env-default:"postgres"`
	DBUser     string `toml:"dbuser" env:"YAACC_DBUSER" env-default:"cdr"`
	DBPassword string `toml:"dbpassword" env:"YAACC_DBPASSWORD"`
	Logfile    string `toml:"logfile"`
}

func main() {
	cfgfile := pflag.StringP("config", "c", "./yaacc.toml", "File to read config params from")
	logfile := pflag.StringP("logfile", "l", "", "File to write exec time messages.")
	pflag.Lookup("logfile").NoOptDefVal = "./yaacc.log"
	pflag.Parse()

	jww.SetPrefix("yaacc")
	jww.SetLogThreshold(jww.LevelTrace)
	jww.SetStdoutThreshold(jww.LevelInfo)

	err := cleanenv.ReadConfig(*cfgfile, &cfg)
	if err != nil {
		jww.WARN.Printf("Error loading config (%s): %q", *cfgfile, err)
	}
	spew.Dump(cfg)
}

Does this support parsing `map[string]CustomStruct` using the `env` tag?

For example, I have the following Config root

var RootConfig Root

type Root struct {
	Http map[string]HttpConfig `yaml:"http" env:"HTTP"`
}

type HttpConfig struct {
	Host      string `yaml:"host"`
	TimeoutMs int    `yaml:"timeout_ms"`
}

func Load(configPath string) {
	cleanenv.ReadConfig(configPath, &RootConfig)
}

I know I can provide the following to populate http key via the following yaml structure:

http:
  payment:
    host: "http://payment:4000"
    timeout_ms: 2000

But what about via environment variable? What would HTTP env var look like?

Create a version compatibility policy

It's obvious now that we don't want to support very old versions and at the same time, we want to use new stuff released with newer versions of Go.

I really want to make it easier for everyone to use the library even with older projects, so I'll make it formal and describe the number of supported versions and add them to the test version matrix.

Expand environment variables when reading from file

Hi,

it would be cool if the library would support expansion of environment variables using os.ExpandEnv
A use-case for this feature would be if you need to construct a configuration variable based on multiple different ones.
e.g. building an URL wrong multiple environment variables

I've tried to figure out where this must be added but couldn't find the write code part.

Thanks

PS: i'm thinking about something like this
https://mtyurt.net/post/go-using-environment-variables-in-configuration-files.html

env-default bool value

Hi,

I'm experiencing a strange behavior and I'd like to understand if it's the expected behavior or a bug (in that case I can create a PR and fix it).
I do have a bool field in my config and the env-default value is true. If I define the field in my config file to false I would expect the value to be set to false, but I get true.

I looked in the code and the isZero function is considering false as zero (which makes sense in general but probably not in this case).

Could you confirm what would be the expected behavior?
Thanks,
Marco

Default Values on Nested Structs

I am attempting to use cleanenv with a nested struct and the env-default tag is not being applied on the child struct.

Is this something cleanenv could support?

Here's a test case replicating the issue:

func TestNestedStructsParseFileEnv(t *testing.T) {
	type child struct {
		Number        int64 `yaml:"number"`
		DefaultNumber int64 `yaml:"default_number" env-defult:"-1"`
	}

	type parent struct {
		Children map[string]child `yaml:"children"`
	}

	children := make(map[string]child)
	children["first"] = child{1, -1}
	wantConfig := parent{children}

	tests := []struct {
		name    string
		file    string
		ext     string
		want    *parent
		wantErr bool
	}{
		{
			name: "yaml",
			file: `
children: 
  first:
    number: 1`,
			ext:     "yaml",
			want:    &wantConfig,
			wantErr: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			tmpFile, err := ioutil.TempFile(os.TempDir(), fmt.Sprintf("*.%s", tt.ext))
			if err != nil {
				t.Fatal("cannot create temporary file:", err)
			}
			defer os.Remove(tmpFile.Name())

			text := []byte(tt.file)
			if _, err = tmpFile.Write(text); err != nil {
				t.Fatal("failed to write to temporary file:", err)
			}

			var prnt parent
			if err = ReadConfig(tmpFile.Name(), &prnt); (err != nil) != tt.wantErr {
				t.Errorf("wrong error behavior %v, wantErr %v", err, tt.wantErr)
			}
			if err == nil && !reflect.DeepEqual(&prnt, tt.want) {
				t.Errorf("wrong data %v, want %v", &prnt, tt.want)
			}
		})
	}

}

Error after reading time.Duration fields in 1.2.6

Hello, I used v1.2.5 and alway gone all right. But problems with time fields have appeared in 1.2.6.
I have this error pattern:
line N: cannot unmarshal !!int `M` into time.Duration.
I have config same as:

type Config {
...
Timeout time.Duration `yaml:"timeout"`
...
} 

Can not overwrite variable

I'm having hard time overrriding a value from enviroment variable. Here is what I want. I want to read config from a enviroment file. Then read enviroment varialbes and overwrite them even If they exist in the config file. The following code does not overwrite value from enviroment varialbe as it supposed to:

type ConfigDatabase struct {
		Port     string `env:"PORT" env-default:"5432"`
		Host     string `env:"HOST" env-default:"localhost"`
		Name     string `env:"NAME" env-default:"postgres"`
		User     string `env:"USER" env-default:"user"`
		Password string `env:"PASSWORD"`
	}

	var cfg ConfigDatabase
	err := cleanenv.ReadConfig("config.env", &cfg)
	if err != nil {
		log.Println(err)
	}

	err1 := cleanenv.ReadEnv(&cfg)
	if err1 != nil {
		log.Println(err1)
	}
	fmt.Println(cfg)

Even witih cleanenv.UpdateEnv It still does not work. To my understanding, Each Read* function should overwrite the previous values.

Also the env-update struct tag is not working either.

Password string `env:"PASSWORD" env-upd`

I get an warning in VSCode
struct field tag env:"PASSWORD" env-upd not compatible with reflect.StructTag.Get: bad syntax for struct tag pair

Add a custom error that would provide a meaningful feedback about the parsing issues.

For the following example, if I provide a non-valid PORT=asd. Then in the error message, I would see "strconv.ParseInt: parsing "asd": invalid syntax". I think it would be great to have a custom error that would collect and provide some details on the type of error that happened to be able to write it to the log and give corresponding feedback to the runner of the application.

type Config struct {
	Web struct {
		Port int `env:"PORT" env-default:"8000"`
	}
}
if err := cleanenv.ReadEnv(&config); err != nil {
    fmt.Printf("Error: %v", err)
}

The message can have the following format: PORT must be a valid number or DB_HOST is a required variable.

I'm not a Golang developer but have written some code in Go, so if you give your opinion on this and some hints I might be able to implement it and send a PR.

BUG: env-default overwrites value from yml file

Wehnever env-default tag is defined, it's value overwrites value read from yml file (which is not intended behaviour, according to https://github.com/ilyakaznacheev/cleanenv#read-configuration )

100% reproducibility, observed while running go app in vagrant box (ubuntu/bionic).

configs/config.yml

# server config
server:
  host: "localhost"
  port: 3000

# database config
db:
  type: "mongodb"
  mongoConfig:
            # full endpoint: address + port
    URL: "mongodb://127.0.0.1:27017"
    ProjectsDBName: "Projects"
    SchemasDBName: "Schemas"

config.go

package common
package common

import (
  "github.com/ilyakaznacheev/cleanenv"
  "fmt"
)

const YmlConfFile string = "./configs/config.yml"

type AppConfig struct {
	ServerConfig struct {
		PORT string `yaml:"port" env:"MYAPP_PORT" env-default:"3000" env-upd`
		HOST string `yaml:"host" env:"MYAPP_HOST" env-default:"localhost" env-upd`
	} `yaml:"server"`
	DBConfig struct {
		DBType string `yaml:"type" env:"MYAPP_DB_SRV" env-default:"mongodb"`
		MongoConfig struct {
			URL string			`yaml:"URL" env:"MYAPP_DB_URL" env-default:"mongodb://localhost:27017" env-upd`
			ProjectsDB string	`yaml:"ProjectsDBName" env:"MYAPP_PROJECTS_DB" env-default:"TProjects" env-upd`
			SchemasDB string	`yaml:"SchemasDBName"  env:"MYAPP_SCHEMAS_DB" env-default:"TSchemas" env-upd`
		} `yaml:"mongoConfig"`
	} `yaml:"db"`
}

func NewAppConfig(confFilePath string) *AppConfig {
	var AppC AppConfig
    err := cleanenv.ReadConfig(confFilePath, &AppC)
    if err != nil { panic(err) }
	fmt.Println("AppC.DBConfig.MongoConfig.URL %v",AppC.DBConfig.MongoConfig.URL)
	return &AppC
}

main.go

package main
import "myappp/common"

func main() {
  appConfig := common.NewAppConfig(common.YmlConfFile)
}

it prints:
AppC.DBConfig.MongoConfig.URL %v mongodb://localhost:27017 (as in env-default)

when I remove respective env-default tag with value, e.g.

it prints

AppC.DBConfig.MongoConfig.URL %v mongodb://127.0.0.1:27017 (as taken from file)

Aside from report above, let me mention that's a great effort to conveniently handle different configuration sources. Thank you!

Value containing `.` always clipped

Trying to read a value that contains . and it doesn't matter what I set the env-separator to. It gets clipped anyway.

Am I using it wrong or this a bug?

Add default env variable binding

Hi,

i'm currently migrating from a combination of godotenv, uber-config and envconfig to your library because AFAIK your library is doing a good job integrating this loading mechanism into a single library without hastle.

But as i have already a decent config struct based on envconfig i see thats its pretty hard to add for every property an env tag (AFAIK this is needed).
So i would like to request some kind of default naming schema like envconfig has already. Further i'm missing the usage of prefixes but i've seen that there is already an issue addressing this.

For so the default naming here is short example:

type Config struct {
	Server   ServerConfig
}

type ServerConfig struct {
	Address          string
	Port                 int        `env-default:"1328"`
        FriendlyName string
}

Currently i would to add everywhere an env tag with the name.. which is mostly the same as the property.
So it would be cool if this config would be sufficient to have the env config working like this:

SERVER_ADDRESS=cleanenv.is.great
SERVER_PORT=1337
SERVER_FRIENDLYNAME="Yeah cleanenv is great"

What do you think? Could this be an future feature?

Thanks

Problem with bool variable in YAML file

yaml file

isdebug: false
rule:
  cachesize: 10485760000

golang struct

type AppConfig struct {
	IsDebug bool `env:"RE_IS_DEBUG" env-required:"true"`
	Rule struct {
		CacheSize          int    `env:RULE_CACHE_SIZE" env-required:"true"`
        }

i got an error field \"IsDebug\" is required but the value is not provided", but if set isdebug: true - all ok, config parse without any errors.

read config from io.Reader

I want to use embed config files. But ReadConfig() only use file path.
If I can use io.Reader for parseFile() like below, it would be nice. What do you think?

Or, If there is any way to use embed files, please let me know.

func ReadConfigFromReader(r io.Reader, cfg interface{}) {
	err := parseFile(r, cfg)
	if err != nil {
		return err
	}

	return readEnvVars(cfg, false)
}

func ReadConfig(path string, cfg interface{}) {
	f, err := os.OpenFile(path, os.O_RDONLY|os.O_SYNC, 0)
	if err != nil {
		return err
	}
	defer f.Close()

	err := parseFile(f, cfg)
	if err != nil {
		return err
	}

	return readEnvVars(cfg, false)
}

Move CI to Github Actions

For now, I see that Actions works a bit faster than Travis CI. and it would be easier to set up all in one place.

Add time.Time support

Need to check if it is possible to parse time.Time and add the possibility to set parsing layouts.

False value in json file

I'm trying to fill a required boolean field in the JSON file, and when the value is false, the following error raises:
field "boolVarName" is required but the value is not provided.
But when I change the value to true, everything goes well.

I think the issue is in the condition of line 316 of cleanenv.go, or the condition of line 329 where you check meta.isFieldValueZero().

Can't read config file in Amazon EC2 server

This is how I'm simply reading the config file:

func Read(path string) {
	readError := cleanenv.ReadConfig(path, &App)
	if readError != nil {
		log.Panic("Failed to read yaml file", readError)
	}
}

This is working on a my local machine (Mac) and in Docker container running locally.
But when I run the app on EC2 (built as RPM package and ran as a service), I get this error:

Process: 20421 ExecStart=/usr/bin/store-service -f /etc/store-service/config.yml (code=exited, status=2)

The very same code runs well when I parse the config using a library like jessevdk/go-flags.
Any idea?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.