A Go port of Ruby's dotenv library (Loads environment variables from `.env`.)

GoDotEnv CI Go Report Card

A Go (golang) port of the Ruby dotenv project (which loads env vars from a .env file)

From the original Library:

Storing configuration in the environment is one of the tenets of a twelve-factor app. Anything that is likely to change between deployment environments–such as resource handles for databases or credentials for external services–should be extracted from the code into environment variables.

But it is not always practical to set environment variables on development machines or continuous integration servers where multiple projects are run. Dotenv load variables from a .env file into ENV when the environment is bootstrapped.

It can be used as a library (for loading in env for your own daemons etc) or as a bin command.

There is test coverage and CI for both linuxish and windows environments, but I make no guarantees about the bin version working on windows.

Installation

As a library

go get github.com/joho/godotenv

or if you want to use it as a bin command

go get github.com/joho/godotenv/cmd/godotenv

Usage

Add your application configuration to your .env file in the root of your project:

S3_BUCKET=YOURS3BUCKET
SECRET_KEY=YOURSECRETKEYGOESHERE

Then in your Go app you can do something like

package main

import (
    "github.com/joho/godotenv"
    "log"
    "os"
)

func main() {
  err := godotenv.Load()
  if err != nil {
    log.Fatal("Error loading .env file")
  }

  s3Bucket := os.Getenv("S3_BUCKET")
  secretKey := os.Getenv("SECRET_KEY")

  // now do something with s3 or whatever
}

If you're even lazier than that, you can just take advantage of the autoload package which will read in .env on import

import _ "github.com/joho/godotenv/autoload"

While .env in the project root is the default, you don't have to be constrained, both examples below are 100% legit

_ = godotenv.Load("somerandomfile")
_ = godotenv.Load("filenumberone.env", "filenumbertwo.env")

If you want to be really fancy with your env file you can do comments and exports (below is a valid env file)

# I am a comment and that is OK
SOME_VAR=someval
FOO=BAR # comments at line end are OK too
export BAR=BAZ

Or finally you can do YAML(ish) style

FOO: bar
BAR: baz

as a final aside, if you don't want godotenv munging your env you can just get a map back instead

var myEnv map[string]string
myEnv, err := godotenv.Read()

s3Bucket := myEnv["S3_BUCKET"]

... or from an io.Reader instead of a local file

reader := getRemoteFile()
myEnv, err := godotenv.Parse(reader)

... or from a string if you so desire

content := getRemoteFileContent()
myEnv, err := godotenv.Unmarshal(content)

Precedence & Conventions

Existing envs take precedence of envs that are loaded later.

The convention for managing multiple environments (i.e. development, test, production) is to create an env named {YOURAPP}_ENV and load envs in this order:

env := os.Getenv("FOO_ENV")
if "" == env {
  env = "development"
}

godotenv.Load(".env." + env + ".local")
if "test" != env {
  godotenv.Load(".env.local")
}
godotenv.Load(".env." + env)
godotenv.Load() // The Original .env

If you need to, you can also use godotenv.Overload() to defy this convention and overwrite existing envs instead of only supplanting them. Use with caution.

Command Mode

Assuming you've installed the command as above and you've got $GOPATH/bin in your $PATH

godotenv -f /some/path/to/.env some_command with some args

If you don't specify -f it will fall back on the default of loading .env in PWD

Writing Env Files

Godotenv can also write a map representing the environment to a correctly-formatted and escaped file

env, err := godotenv.Unmarshal("KEY=value")
err := godotenv.Write(env, "./.env")

... or to a string

env, err := godotenv.Unmarshal("KEY=value")
content, err := godotenv.Marshal(env)

Contributing

Contributions are most welcome! The parser itself is pretty stupidly naive and I wouldn't be surprised if it breaks with edge cases.

code changes without tests will not be accepted

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Added some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

Releases

Releases should follow Semver though the first couple of releases are v1 and v1.1.

Use annotated tags for all releases. Example git tag -a v1.2.1

CI

Linux: Build Status Windows: Build status

Who?

The original library dotenv was written by Brandon Keepers, and this port was done by John Barton based off the tests/fixtures in the original library.

Comments
  • Won't read from variable when it contains $ character

    Won't read from variable when it contains $ character

    go version go1.9.2 windows/386

    When I am reading from variable in my .env file which has $ in string: MAIL_SECRET=xTvDqw$27

    os.Getenv("MAIL_SECRET") gives xTvDqw7 back, ignoring $2

    why is that, have $ sign some special meaning?

  • Thoughts on publishing binaries to Github Releases?

    Thoughts on publishing binaries to Github Releases?

    Update: Published

    Since I didn't hear anything back, I used the library to create my own command line binary and published:

    Mac, Linux:

    curl -sS https://webinstall.dev/dotenv | bash
    

    Windows 10:

    curl -A MS https://webinstall.dev/dotenv | powershell
    

    I'm also flirting with the idea of using dotenv-rs for a smaller binary, but it's just so much easier to do automated cross-platform builds in Go.

    Original Question

    I've been a fan of godotenv for a while and I'd like to add it to webinstall.dev. Would you be willing to publish builds for this (i.e. Mac, Linux, Windows, x64, arm64) to Github Releases?

    If you are, and you think that this is likely to have new versions in the future, I'd be happy to work with you on getting that to be an automated process.

    +1 if the compiled binary could be called dotenv as was suggested in https://github.com/joho/godotenv/issues/113.

  • Proposal: add an

    Proposal: add an "import" statement to the .env syntax

    Problem: My projects usually have a stack of .env files, all of which are optional. For example, most of my projects have a .env, and allow an option .env.local, which overrides .env. And when I run tests, the tests load .env.local, then .env.test, then .env.

    It would be nice to run the godotenv command with a single file argument, like when running tests: godotenv -f .env.test go test ./..., while still loading the other .env files in the right precedence order.

    Proposal:

    If a .env file contains a comment line like:

    # import: .env.local
    

    When encountered, godotenv would essentially Load() that other file. File not found errors would be ignored. (a "must-import" directive could be added to require the presence of the other file).

    So .env files could put "overrides" up top, and "defaults" at the bottom. For example, the .env.local use case could be handled with a .env file like:

    # load local developer overrides
    # import: .env.local
    key1=val
    key2=val
    

    The .env.test case could be handed by defining a .env.test file like:

    # load local developer overrides
    # import: .env.local
    testkey1=value
    # load defaults
    # import: .env
    

    Or, if a project wanted to have separate local overrides for both the default .env file and the .env.test file, the .env.test file could be:

    # load local developer overrides
    # import: .env.test.local
    testkey1=value
    # load defaults (which in turn loads .env.local overrides)
    # import: .env
    

    Issues: files could be loaded more than once. This shouldn't pose an issue. Circular references could occur, but could be detected by the loading code and error out.

  • Do support opting out for 'value quoteing

    Do support opting out for 'value quoteing"

    Right now, when you open a file with godotenv and manipulate it, write it back, the values are all quoted.

    In fact, that does not work out, since e.g. docker-compose does not allow those values to be quoted. So e.g.

    That will work

    COMPOSE_FILE=docker-compose.yml:docker-compose-dwcm.yml
    

    while

    COMPOSE_FILE="docker-compose.yml:docker-compose-dwcm.yml"
    

    errors with ERROR: .IOError: [Errno 2] No such file or directory: u'./"docker-compose.yml'

    Any way to opt out of "quoting" values? Thanks!

  • Fixes #64 Multiline variables

    Fixes #64 Multiline variables

    Fixes #64

    This PR handles multi-line values by single quote delimiter '.

    The parsing of the whole .env file is pretty naive (as expressed in the README) but does the work. I tried to add as little code as possible and change as little as possible just to support this.

    All tests are passing and 3 new ones were added (1 success and 2 different failing scenarios)

  • Tests failing on Windows

    Tests failing on Windows

    I've hit an issue on windows, that I think is to do with how godotenv handles line endings.

    Given you have a text file at c:\test.txt with a few lines in it and the following go program:

    package main
    
    import (
        "bufio"
        "github.com/joho/godotenv"
        "log"
        "os"
    )
    
    func main() {
        err := godotenv.Load()
        if err != nil {
            log.Fatalf("Error loading .env file %s\n", err.Error())
        }
    
        path := os.Getenv("A_PATH")
    
        log.Printf("path: %v", path)
    
        file, err := os.Open(path)
        if err != nil {
            log.Fatalf("error opening file: %s", err.Error())
        }
    
        defer file.Close()
    
        scanner := bufio.NewScanner(file)
    
        for scanner.Scan() {
            log.Println(scanner.Text())
        }
    
        log.Println("Everything ok")
    }
    

    If the .env file (located in the same folder as the application) contains

    A_PATH=C:\\test.txt
    

    then runs correctly, and the contents of c:\test.txt are printed to the console.

    if however you have multiple lines in the .env file; for instance

    A_PATH=C:\\test.txt
    SOMETHING_ELSE=anothervar
    

    Then on running the program it errors, outputting

    : the filename, directory name, or volume label syntax is incorrect.
    

    I'm just narrowing down the problem - I suspect it may be to do with windows line returns.

    If I can fix it would you be willing to accept a Pull request?

  • Integrate with Kubernetes

    Integrate with Kubernetes

    Very useful, thank you!

    I discovered godotenv through a Go sample that uses it. I'm working on running that solution on Kubernetes. One way to solve this with Kubernetes would be to add the .env file to the container but this seems to defeat the principle of keeping config externalized.

    I used ConfigMaps and am sharing it here for consideration:

    There's a limitation (!) in ConfigMaps which prevents using them to map a source file containing environment variables or other properties directly into container environment variables.

    The workaround is to surface the ConfigMap's contents as a volume in the container but this then requires moving the .env (or similarly named file) to a separate directory.

    1. Move .env. to its own directory and revise the Golang reference. NB for convenience I'm using a relative reference here without "./" and I use an absolute reference in the container spec (step 3)
    err := godotenv.Load("config/.env")
    
    1. Convert an existing .env to a ConfigMap
    kubectl create configmap env --from-file=./config/.env
    
    1. The container spec must then reference the ConfigMap (env) through a volume mount:
    spec:
      containers:
      - image: dazwilkin/configmaptest:rpi3
        imagePullPolicy: Always
        name: configmaptest
        ports:
        - name: http
          containerPort: 8080
          protocol: TCP
        volumeMounts:
        - name: config-volume
          mountPath: /config
      volumes:
        - name: config-volume
          configMap:
            name: configmaptest
    

    This results in a container that will run on Kubernetes deriving its env settings from the pod during deployment. The same image may be used in multiple pods each using a different configuration.

    Thoughts?

  • support for writing envs out in dotenv format

    support for writing envs out in dotenv format

    implements: #31 (give writing out dotenv files a shot) requires: https://github.com/joho/godotenv/pull/34 (fixes for parsing escapes -- \\ in particular)

    A few months ago you said I could give roundtripping dotenv files a whirl. Here's the whirl :)

    It's is useful for systems that create environments for other processes or change their own environments. That way everyone involved can use the same package. It also came in handy for exercising the parser more heavily. The output values are double-quoted and backslash escaped.

    godotenv gets the following new members:

    // Write serializes the given environment and writes it to a file
    func Write(envMap map[string]string, filename string) error {}
    
    // Marshal outputs the given environment as a dotenv-formatted environment file.
    // Each line is in the format: KEY="VALUE" where VALUE is backslash-escaped.
    func Marshal(envMap map[string]string) (string, error) {}
    
    //Unmarshal reads an env file from a string, returning a map of keys and values.
    func Unmarshal(str string) (envMap map[string]string, err error) {}
    
  • Try to get .env filename from env first

    Try to get .env filename from env first

    Inception warning!! 😄

    TL;DR: this patch allows us to do: DOTENV_FILENAME=/directory/.env godotenv /bin/application or configure DOTENV_FILENAME as env var for an app that has autoload enabled.

    Long explanation: We're using drone for CI, and the latest versions are autoloading env vars using godotenv. The problem is that in our setup (mesos/marathon) we cannot put files wherever we want, but we can configure environment variables!

    So with this little change we can run the drone image, put the config file in a specific dir and by using an env variable, make drone load it using godotenv.

    I know you don't want to have much differences from the original dotenv.. If this is much of a problem, I can make a PR for them too. I don't know much about ruby, but don't know much about go as well!

    Regards!

  • Improve error handling

    Improve error handling

    • propagate errors from scanner (e.g.: passing path to directory to Read())
    • propagate line parsing errors when parsing file - currently they are quietly ignored
  • tune README

    tune README

    $ go get github.com/joho/godotenv/cmd/godotenv
    go get: installing executables with 'go get' in module mode is deprecated.
            Use 'go install pkg@version' instead.
            For more information, see https://golang.org/doc/go-get-install-deprecation
            or run 'go help get' or 'go help install'.
    
  • dotenv does not report an error if file does not exist

    dotenv does not report an error if file does not exist

    Hi,

    Is there any particular reason why there is no return err here godotenv.go line 71 in Overload()

    Like that it silently fails if it cannot find a file..

  • expanding command line interface or linking to sister project

    expanding command line interface or linking to sister project

    I think expanding the cli here or linking to a project written in go that uses this library to provide a little more cli utility would be very helpful and more complete.

    I find the cli interface bit limited, for example I don't fine this functionality generally useful:

    godotenv -f /path/to/something/.env,/another/path/.env fortune
    

    something like a generic set/get of key/values like seems more useful:

    godotenv set foo "$bar"  # updates ./.env by default
    

    similar to the python cli dotenv:

    $ dotenv 
    Usage: dotenv [OPTIONS] COMMAND [ARGS]...
    
      This script is used to set, get or unset values from a .env file.
    
    Options:
      -f, --file PATH                 Location of the .env file, defaults to .env
                                      file in current working directory.
    
      -q, --quote [always|never|auto]
                                      Whether to quote or not the variable values.
                                      Default mode is always. This does not affect
                                      parsing.
    
      -e, --export BOOLEAN            Whether to write the dot file as an
                                      executable bash script.
    
      --version                       Show the version and exit.
      --help                          Show this message and exit.
    
    Commands:
      get    Retrieve the value for the given key.
      list   Display all the stored key/value.
      run    Run command with environment variables present.
      set    Store the given key/value.
      unset  Removes the given key.
    
  • Expanding variable value

    Expanding variable value

    Say I have .env like

    MY_USERNAME=${LOCAL_DEVELOPER_USERNAME}
    

    The LOCAL_DEVELOPER_USERNAME is host environment variable, I wish the value of that to be expanded using the host environment variable value with possible overrides if defined locally in the environment file.

    Let me know if you are interested in accepting a pull for the same as I have this logic working https://github.com/kameshsampath/godotenv/blob/issue-189/godotenv.go#L339-L347

  • Bump thatisuday/go-cross-build from 1.0.2 to 1.1.0

    Bump thatisuday/go-cross-build from 1.0.2 to 1.1.0

    Bumps thatisuday/go-cross-build from 1.0.2 to 1.1.0.

    Release notes

    Sourced from thatisuday/go-cross-build's releases.

    v1.1.0 - ldflags support

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
A Go (golang) environment loader (which loads env vars from a .env file)

A Go (golang) environment loader (which loads env vars from a .env file)

Feb 8, 2022
formicidate is a small tool for Go application can update the value of environment variables in a .env file with code

formicidae Update .env files in Go with code. What is fomicidae? formicidate is a small tool for Go application. You can update the value of environme

Jan 23, 2022
Tmpl - A tool to apply variables from cli, env, JSON/TOML/YAML files to templates

tmpl allows to apply variables from JSON/TOML/YAML files, environment variables or CLI arguments to template files using Golang text/template and functions from the Sprig project.

Nov 14, 2022
Small library to read your configuration from environment variables

envconfig envconfig is a library which allows you to parse your configuration from environment variables and fill an arbitrary struct. See the example

Nov 3, 2022
🛠 A configuration library for Go that parses environment variables, JSON files, and reloads automatically on SIGHUP
🛠 A configuration library for Go that parses environment variables, JSON files, and reloads automatically on SIGHUP

config A small configuration library for Go that parses environment variables, JSON files, and reloads automatically on SIGHUP. Example func main() {

Dec 11, 2022
Golang library for managing configuration data from environment variables

envconfig import "github.com/kelseyhightower/envconfig" Documentation See godoc Usage Set some environment variables: export MYAPP_DEBUG=false export

Dec 26, 2022
A Go library for parsing struct tags from environment variables.

Envconfig Envconfig populates struct field values based on environment variables or arbitrary lookup functions. It supports pre-setting mutations, whi

Jan 2, 2023
Golang library for reading properties from configuration files in JSON and YAML format or from environment variables.

go-config Golang library for reading properties from configuration files in JSON and YAML format or from environment variables. Usage Create config in

Aug 22, 2022
Automatically generate Go Code from dotenv files.

goenvgen Automatically generate Go Code from dotenv files. This cli automatically determines the type information from the dotenv information and gene

Dec 3, 2021
Simple lib to parse environment variables to structs

env Simple lib to parse envs to structs in Go. Example A very basic example: package main import ( "fmt" "time" // if using go modules "github.c

Jan 9, 2023
Un-marshaling environment variables to Go structs

envcfg Un-marshaling environment variables to Go structs Getting Started Let's set a bunch of environment variables and then run your go app #!/usr/bi

Sep 26, 2022
Go helpers to manage environment variables

Envh This library is made up of two parts : Env object : it wraps your environments variables in an object and provides convenient helpers. Env tree o

Sep 26, 2022
goconfig uses a struct as input and populates the fields of this struct with parameters from command line, environment variables and configuration file.

goconfig goconfig uses a struct as input and populates the fields of this struct with parameters from command line, environment variables and configur

Dec 15, 2022
Environment variables substitution for Go

envsubst Environment variables substitution for Go. see docs below Installation: From binaries Latest stable envsubst prebuilt binaries for 64-bit Lin

Jan 1, 2023
Quickly read variables from environment files

go-quick-env Quickly read variables from environment files The best way to import environment variables to your code, is by using .env files. This lib

May 11, 2021
Read files into environment variables and execute command

read-file-to-env -- Read files into environment variables and execute command Example use: read-file-to-env -one-line=HOST=/etc/hostname sh -c 'echo h

Nov 12, 2021
A mapper of ENVironment variables to Structure for Go

envs a mapper of ENVironment variables to a Structure for Go. This library maps the environment variables to the struct according to the fields' types

Dec 3, 2021
Environment variables configuration package for Go microservices.

gocfg Environment variables configuration package for Go microservices. It helps validate environment variable values and set default values if needed

Dec 30, 2021