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

Overview

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.

Issues
  • 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?

    opened by dkeza 10
  • 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.

    opened by coolaj86 9
  • 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.

    opened by ansel1 9
  • 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!

    opened by EugenMayer 7
  • 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)

    opened by esttorhe 6
  • 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?

    opened by Bochenski 6
  • 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) {}
    
    opened by alexquick 5
  • 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!

    opened by cyberplant 5
  • 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
    opened by mmilata 5
  • 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 [email protected]' instead.
            For more information, see https://golang.org/doc/go-get-install-deprecation
            or run 'go help get' or 'go help install'.
    
    opened by bikbah 4
  • Consider renaming this repository

    Consider renaming this repository

    The first 5 letters of this repository spell "Godot", which is the name of a popular open source game engine. This means that the name of this repository can be confusing at first glance, and it shows up in searches for Godot.

    Perhaps go-dotenv would be a good name. It separates go from dotenv while still being mostly the same name.

    If you rename the repository, the old link becomes a redirect and existing URLs and scripts will continue to work.

    CC @joho

    opened by aaronfranke 4
  • 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.
    
    opened by mandric 0
  • 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

    opened by kameshsampath 0
  • 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)
    dependencies github_actions 
    opened by dependabot[bot] 0
  • Issue 175: Command does not load dotenv files if some do not exist [2]

    Issue 175: Command does not load dotenv files if some do not exist [2]

    Hi, The following PR is intended to resolve issue-175 .

    When the any of the passed fileNames do not exist, it just silently stops the process of 'load env files' and start to execute the command provided.

    I believe that behaviour might lead to unexpected results and assumptions which might affect the correctness of the system that utilizes the environment variables using godotenv command And decided to make small PR.

    Current behaviour: Silently stopping the process of 'load env files' and continuing to execute the command provided.

    $ cat .env
    FOO="Using .env"
    
    $ godotenv -f ".env,.env.doesnotexist" env | grep FOO
    FOO=Using .env
    
    $ godotenv -f ".env.doesnotexist,.env" env | grep FOO
    # FOO does not get set
    

    Considered behaviour: When some of the fileNames do not exist, then print the error message: "File: ${fileName} not found" to stderr and continue to load the remaining fileNames. After the load process has been finished, continue to execute command provided.

    $ cat .env
    FOO="Using .env"
    
    $ godotenv -f ".env,.env.doesnotexist" env | grep FOO
    FOO=Using .env
    
    $ godotenv -f ".env.doesnotexist,.env" env | grep FOO
    File: `.env.doesnotexist` not found
    FOO=Using .env
    
    opened by egasimov 0
  • godotenv is

    godotenv is "complete" and not accepting feature requests

    As of June 2022 I'm declaring godotenv roughly complete and will not be accepting feature requests, nor pull requests adding new functionality or breaking API changes.

    Exceptions will be made to:

    • fix bugs
    • keep up with the Go ecosystem
    • maintain compatibility with env file formats of other mainstream "dotenv" libraries, primarily the OG Ruby dotenv and the Node.js dotenv

    I have two main reasons for wrapping up active development of this library.

    The first is that I'm no longer an enthusiastic supporter of the philosophy of the 12 factor app. I've been working with microservices for quite a long while now, and I find critiques such as Environment Variables Considered Harmful for Your Secrets and Why you shouldn't use ENV variables for secret data very compelling.

    This library's purpose was always to make it easier to do active development of microservices that are deployed to 12 factor environments. The library as it stands today does a reasonable job of that, but I see feature requests/PRs come in that expand the scope to better support the packaging and distribution of env files as general purpose config, which I see as a footgun waiting to go off with someone's secrets.

    The second is that I'm no longer as active a library maintainer as I once was. I've now got two kids, the timing of the first one coincides with the general decline in my review/comment rate on this repo.

    I'm not seeking a new maintainer because:

    • the library does its current job totally fine and doesn't need to do anything else
    • I cannot be assed doing the due diligence on possible future maintainers to avoid being found as the "root cause" on some blog post about a baroque supply chain attack in the future

    I'm on annual leave right now and am spending some of that time clearing the backlog of pull requests and issues.

    If after this month I close an issue or PR, it will likely be because it doesn't meet my criteria above.

    opened by joho 0
Releases(v1.4.0)
Genv is a library for Go (golang) that makes it easy to read and use environment variables in your projects. It also allows environment variables to be loaded from the .env file.

genv Genv is a library for Go (golang) that makes it easy to read and use environment variables in your projects. It also allows environment variables

Şakir Şensoy 28 Jul 9, 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

akuma 0 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.

krako 1 May 30, 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

Vincent Rischmann 222 May 18, 2022
🛠 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() {

Josh Betz 212 Jul 22, 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

Kelsey Hightower 4.2k Aug 3, 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

Seth Vargo 655 Aug 4, 2022
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

null 3 Feb 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

potpro 3 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

Carlos Alexandro Becker 2.6k Aug 7, 2022
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

Tomaž Kovačič 98 May 4, 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

Anthony HAMON 95 Apr 14, 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

Go Sidekick 0 May 30, 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

Ariel Mashraki 524 Aug 10, 2022
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

Panos Petropoulos 3 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

Tv 2 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

moznion 3 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

Sergey Prokhorov 0 Dec 30, 2021