Git Product home page Git Product logo

dotnet-env's Introduction

Windows build status License: MIT NuGet version

dotnet-env

A .NET library to load environment variables from .env files. Supports .NET Core and .NET Framework (4.6+).

Installation

Available on NuGet

Visual Studio:

PM> Install-Package DotNetEnv

.NET Core CLI:

dotnet add package DotNetEnv

Usage

Load env file

Load() will automatically look for a .env file in the current directory by default, or any higher parent/ancestor directory if given the option flag via TraversePath()

DotNetEnv.Env.Load();
DotNetEnv.Env.TraversePath().Load();

Or you can specify the path directly to the .env file, and as above, with TraversePath(), it will start looking there and then look in higher dirs from there if not found.

DotNetEnv.Env.Load("./path/to/.env");

It's also possible to load the (text) file as a Stream or string or multiple files in sequence

using (var stream = File.OpenRead("./path/to/.env"))
{
    DotNetEnv.Env.Load(stream);
}

DotNetEnv.Env.LoadContents("OK=GOOD\nTEST=\"more stuff\"");

// will use values in later files over values in earlier files
// NOTE: NoClobber will reverse this, it will use the first value encountered!
DotNetEnv.Env.LoadMulti(new[] {
    ".env",
    ".env2",
});

Accessing environment variables

The variables in the .env can then be accessed through the System.Environment class

System.Environment.GetEnvironmentVariable("IP");

Or through one of the helper methods:

DotNetEnv.Env.GetString("A_STRING");
DotNetEnv.Env.GetBool("A_BOOL");
DotNetEnv.Env.GetInt("AN_INT");
DotNetEnv.Env.GetDouble("A_DOUBLE");

The helper methods also have an optional second argument which specifies what value to return if the variable is not found:

DotNetEnv.Env.GetString("THIS_DOES_NOT_EXIST", "Variable not found");

Additional arguments

You can also pass a LoadOptions object arg to all DotNetEnv.Env.Load variants to affect the Load/Parse behavior:

new DotNetEnv.LoadOptions(
    setEnvVars: true,
    clobberExistingVars: true,
    onlyExactPath: true
)

However the recommended approach is with a fluent syntax for turning flags off such as:

DotNetEnv.Env.NoEnvVars().NoClobber().TraversePath().Load();

All parameters default to true, which means:

  1. setEnvVars, first arg: true in order to actually update env vars. Setting it false allows consumers of this library to process the .env file but use it for things other than updating env vars, as a generic configuration file. The Load methods all return an IEnumerable<KeyValuePair<string,string>> for this, but there is an extension method ToDotEnvDictionary() to get a dict with the last value for each key.
KEY=value
var kvps = DotNetEnv.Env.Load(
    options: new DotNetEnv.Env.LoadOptions(
        setEnvVars: false
    )
)

// or the recommended, cleaner (fluent) approach:
var dict = DotNetEnv.Env.NoEnvVars().Load().ToDotEnvDictionary();

// not "value" from the .env file
null == System.Environment.GetEnvironmentVariable("KEY")
"KEY" == kvps.First().Key
"value" == kvps.First().Value

With CreateDictionaryOption you can change behavior of ToDotEnvDictionary to take either the First value or to throw on duplicates. With the TakeFirst options you can simulate NoClobber-behavior.

  1. clobberExistingVars, second arg: true to always set env vars, false would leave existing env vars alone.
KEY=value
System.Environment.SetEnvironmentVariable("KEY", "really important value, don't overwrite");
DotNetEnv.Env.Load(
    options: new DotNetEnv.Env.LoadOptions(
        clobberExistingVars: false
    )
)

// or the recommended, cleaner (fluent) approach:
DotNetEnv.Env.NoClobber().Load();

// not "value" from the .env file
"really important value, don't overwrite" == System.Environment.GetEnvironmentVariable("KEY")
  1. exactPathOnly, third arg: true to require .env to be in the current directory if not specified, or to match the exact path passed in, false would traverse the parent directories above the current or given path to find the nearest .env file or whatever name was passed in. This option only applies to Env.Load that takes a string path.

See DotNetEnvTraverse.Tests for examples.

// the recommended, cleaner (fluent) approach:
DotNetEnv.Env.TraversePath().Load();

Using .NET Configuration provider

Integrating with the usual ConfigurationBuilder used in .NET is simple!

var configuration = new ConfigurationBuilder()
    .AddDotNetEnv(".env", LoadOptions.TraversePath()) // Simply add the DotNetEnv configuration source!
    .Build();

The configuration provider will map __ as : to allow sections!

.env file structure

All lines must be valid assignments or empty lines (with optional comments).

A minimal valid assignment looks like:

KEY=value

There can optionally be one of a few export or equivalent keywords at the beginning and there can be a comment at the end, values can be quoted to include whitespace, and interpolated references can be included (unquoted values as well as double quoted, with optional braces in both cases -- but often more useful in unquoted), like:

export KEY="extra $ENVVAR value" # comment
set KEY2=extra${ENVVAR}value # comment

The options for the export keyword are:

export  # bash
set     # windows cmd
SET     # windows cmd
set -x  # fish

This allows the .env file itself to be source-d like . .env to load the env vars into a terminal session directly.

The options for quoting values are:

  1. "" double: can have everything: interpolated variables, plus whitespace, escaped chars, and byte code chars
  2. '' single: can have whitespace, but no interpolation, no escaped chars, no byte code chars -- notably not even escaped single quotes inside -- single quoted values are for when you want truly raw values
  3. unquoted: can have interpolated variables, but only inline whitespace, and no quote chars, no escaped chars, nor byte code chars

As these are the options bash recognizes. However, while bash does have special meaning for each of these, in this library, they are all the same, other than that you do not need to escape single quote chars inside a double quoted value, nor double quotes inside single quotes.

As a special note: if a value is unquoted, it can still include a # char, which might look like it is starting a comment, like:

KEY=value#notcomment #actualcomment

This is how bash works as well:

export TEST=value#notcomment #actualcomment
env | grep TEST
# TEST=value#notcomment

However, unlike bash, a # directly after the = will be recognized as a comment:

KEY=#yesacomment

This is because whitespaces between = and the value are allowed by this library, which is not allowed in bash. This prevents confusion between KEY=#comment and KEY= #comment, which is expected to give the same result when leading whitespaces before the value are allowed.

Also unlike bash, inline whitespace is allowed so you can do:

KEY=value#notcomment more	words here # yes comment

"value#notcomment more	words here" == System.Environment.GetEnvironmentVariable("KEY")

You can also declare unicode chars as byte codes in double quoted values:

UTF8 btes: "\xF0\x9F\x9A\x80" # rocket ๐Ÿš€
UTF16 bytes: "\uae" # registered ยฎ
UTF32 bytes: "\U1F680" # rocket ๐Ÿš€

Capitalization on the hex chars is irrelevant, and leading zeroes are optional.

And standard escaped chars like \t, \\``, \n`, etc are also recognized -- though quoted strings can also be multi line, e.g.:

KEY="value
and more"
OTHER='#not_comment
line2'

Loaded gives:

"value\nand more" == System.Environment.GetEnvironmentVariable("KEY")
"#not_comment\nline2" == System.Environment.GetEnvironmentVariable("OTHER")

You can also include whitespace before and after the equals sign in assignments, between the name/identifier, and the value, quoted or unquoted. Note that the pre/trailing and post/leading whitespace will be ignored. If you want leading whitepace on your values, quote them with whitespace.

WHITE_BOTH = value
WHITE_QUOTED=" value "

Loaded gives:

"value" == System.Environment.GetEnvironmentVariable("WHITE_BOTH")
" value " == System.Environment.GetEnvironmentVariable("WHITE_QUOTED")

Note that bash env vars do not allow white space pre or post equals, so this is a convenience feature that will break sourcing .env files. But then, not all of this is 100% compatible anyway, and that's ok.

Note that other .env parsing libraries also might have slightly different rules -- no consistent rules have arisen industry wide yet.

A Note about Production and the Purpose of This Library

You should not be using a .env file in production. The purpose of this library is to enable easy local development.

Your dev team should have a .env with localdev testing credentials/etc stored in some secure storage -- 1pass/lastpass or s3 bucket or something like that.

Then every developer gets a copy of that file as part of onboarding that they save into their project dir that uses DotNetEnv to get env vars for configuration.

When the application is deployed into production, actual env vars should be used, not a static .env file!

This does mean that env vars, and thus this library, are only useful for load time configuration -- not anything that changes during the lifetime of an application's run. (You should load env var values during startup or on first access and not look them up more than once during the application's lifetime.)

Admittedly, this is best practices advice, and if you want to use .env files in production, that's up to you. But at least I have told you so. :)

Issue Reporting

If you have found a bug or if you have a feature request, please report them at this repository issues section.

Contributing

Run dotnet test to run all tests.

Or some more specific test examples:

dotnet test --filter "FullyQualifiedName~DotNetEnv.Tests.EnvTests.BadSyntaxTest"
dotnet test --filter "FullyQualifiedName~DotNetEnv.Tests.ParserTests.ParseAssignment"

src/DotNetEnvEnv/Env.cs is the entry point for all behavior.

src/DotNetEnvEnv/Parsers.cs defines all the Sprache parsers.

The DotNetEnvTraverse.Tests project tests loading .env files in parent (or higher) directories from the executable.

Open a PR on Github if you have some changes, or an issue if you want to discuss some proposed changes before creating a PR for them.

License

This project is licensed under the MIT license. See the LICENSE file for more info.

dotnet-env's People

Contributors

atifaziz avatar cdroulers avatar cmc13 avatar forhot2000 avatar jonstodle avatar mrdave1999 avatar philipp-binder avatar pkunze avatar rogusdev avatar tonerdo 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

dotnet-env's Issues

Not Able to read env variables for project type .NET Framework 4.7.2

I have created .env file in the root of the asp.net mvc application and is trying to load using below code

DotNetEnv.Env.Load();
var value = System.Environment.GetEnvironmentVariable("HELLO");

value is always Null. I tried using below code aswell but no luck. Are you sure this works with .NET Framework 4.6 + ??
DotNetEnv.Env.TraversePath().Load();

Encryption

How do I encrypt this file on windows server?

Environment variables for .NET Framework 4.7.2 set on a run basis rather than on a project root folder basis

I'm not using NET Core, I'm developing using NET Framework 4.7.2.

The project is using a function called Directory.GetCurrentDirectory() to find the root folder.

NET Framework, the function finds the location where the project was launched, not the root folder of the project.

Because of this, the path changes depending on the execution environment, and when you build with IIS Express in Visual Studio 2022, the root folder is applied as "C:\Program Files\IIS Express" rather than the project folder.

Because of this, putting ".env" in the project root folder doesn't work.

This issue was in the past "dotnet/aspnetcore#4206" and was fixed in Net Core but not in Net Framework.

According to what I checked on https://stackoverflow.com/a/53655514, in NET Framework, you should use System.AppContext.BaseDirectory instead of System.IO.Directory.GetCurrentDirectory() to work properly. At this time, I confirmed that it normally finds the project root folder.

Make change to README file

When reading this section of the README: A Note about Production and the Purpose of This Library. This caught my attention:

When the application is deployed into production, actual env vars should be used, not a static .env file!

Here a "good practice" is implied when in reality it is a bad practice. In production you never use environment variables to store sensitive data such as passwords in plain text. The environment variables of a running process can be easily inspected by programs like mogrify or any cracker could create his own malicious software for such an action. Using a static ".env" is just as insecure as using "actual env vars". In addition, environment variables may remain visible in the shell history (although there is a way around this but new users may overlook it).

In production what is used are secret files, where sensitive data is stored but encrypted! or you can also use a secrets manager provider such as AWS.

support parsing .env and returning a Dictionary<string,string> instead of modifying environment

in Microsoft.Extensions.Configuration, ConfigurationBuilder has the ability to load configuration from a dictionary.

Because of that, it would be nice to support using DotNetEnv to parse the .env file (and all the support it has there, including the trimming, quoted values, etc) and return it as a dictionary then allow the caller to use that dictionary.

Currently because it doesn't support returning a dictionary instead of modifying the environment, a .net core test project wanting to use it must switch their <Project Sdk= value from Microsoft.NET.Sdk to Microsoft.NET.Sdk.Web so they can use AddEnvironmentVariables

Not compatible with Kubernetes .env

First of all thank you for the library. It's helping me a lot and I use it across all my projects. Now here is a problem.

When I use .env file with the library, I have to use quotes for some config values e.g.:

ConnectionString="Server=tcp:xyz;Password....."

Then if I want to use same file to create configmap in Kubernetes I run the command

kubectl create configmap my-config --from-env-file .env

But I need to remove all quotes, e.g.:

ConnectionString=Server=tcp:xyz;Password.....

It seems like it's a new issue introduced in v2.0.0.

how to use .env file in production?

In code I have used TraversePath().Load() and it's working good when testing, but when I publish the application as standalone it's not using the .env file. How can I use .env file?

Loading Configuration from File - Parsed it to an object

Hi,

I am trying to load the environment variables into the environment along with parsing the object from it. I was wondering if I am doing it the right way.

I am not able to see the updated values from the file loaded into the object though.

  var configuration = new ConfigurationBuilder()
            .AddDotNetEnv(".env", DotNetEnv.LoadOptions.NoEnvVars().TraversePath().NoClobber()) // Simply add the DotNetEnv configuration source!
            .Build();
            
     services.AddSingleton<IConfiguration>(configuration);       

Then I am trying to bind these settings in my object

var appSettings = new VeneliteConfiguration();
            Configuration.Bind(appSettings);

But It is still loading settings from the appsettings.Json and not from the environment variables. Any help would be appreciated. Thank you.

[question] How can I set empty string value to the variable?

I have the .env file with this content:

SOME_VAR=

and a string template, something like this: begin of %SOME_VAR%.
After ExpandEnvironmentVariables I want to see a string 'begin of '. But now I see 'begin of %SOME_VAR%' as a result. As I can understand, SOME_VAR= means to delete the variable.
Is there any way to assign an empty string to the environment variable?

Denial of Service (DoS) - v2.5.0

Info from snyk.io

Introduced through: project@* โ€บ [email protected] โ€บ [email protected] โ€บ [email protected]
Fix: No remediation path available.
Security information
Factors contributing to the scoring:
Snyk: CVSS 7.5 - High Severity

NVD: CVSS 7.5 - High Severity
Why are the scores different? Learn how Snyk evaluates vulnerability scores
Overview
System.Net.Http is an Provides a programming interface for modern HTTP applications, including HTTP client components that allow applications to consume web services over HTTP and HTTP components that can be used by both clients and servers for parsing HTTP headers.

Affected versions of this package are vulnerable to Denial of Service (DoS) as ASP.NET Core fails to properly validate web requests.

NOTE: Microsoft has not commented on third-party claims that the issue is that the TextEncoder.EncodeCore function in the System.Text.Encodings.Web package in ASP.NET Core Mvc before 1.0.4 and 1.1.x before 1.1.3 allows remote attackers to cause a denial of service by leveraging failure to properly calculate the length of 4-byte characters in the Unicode Non-Character range.

Fails to parse when environment file has dash (-)

I want to thank you for this library. It seems to be the most complete dotnet libraries out there supporting env file parsing. I did run into one issue regarding environment variables with a dash in them:

Example file:

test-dash=value

Result: Sprache.ParseException : Parsing failure: unexpected '-'; expected = (Line 1, Column 5); recently consumed: test

This is a perfectly legal environment variable name. In fact, it appears that both Windows & Linux support all characters but equals character (=) and NUL in environment variable names. It looks like this library is basically supporting the pattern "[a-zA-Z_]+[a-zA-Z0-9_]*". I'm wondering if you'd be open to at least allowing the dash? I'd be willing to contribute a PR - it seems like it'd be easy to support.

Env file not loading

Description

After running DotNetEnv.Env.Load(); no environment variables are added.

Specifications

I am running EndeavourOS Linux.
My code is running on .NET Core 6.

Code

DotNetEnv.Env.Load();
Console.WriteLine("token: " + Environment.GetEnvironmentVariable("TOKEN"));

...with the output of token:

My .env

Located at root/MyProject/.env, where root is where the solution file is located and MyProject is where the project file is located.

TOKEN=token

Parse ${ENV_VAR} in env_file

I have Env like this:

BASENAME=${BASENAME}

and ref in tye.yaml

env_file: 
 - .env

Tye will not replace ${BASENAME} with value of currently set env var in process.

Related to #377, but this one is for env_file, 377 is for env. Feel free to combine issues.

azsdke2e
azsdke2e2

Suggestion: Defaults Are Unexpected

My .env had vars with empty values. I wish variables like this would not clobber on default, but the behavior was not as bad as I originally expected. I'll just remove my blank .env file.

Is there support for hierarchical settings?

Not sure if I am maybe doing this wrong but I have the following settings in my .env file, which I have been using with my docker-compose script and it has worked fine but when trying to use your library for local dev I get an error.

Values:FUNCTIONS_WORKER_RUNTIME=dotnet-isolated
Values:AZURITE_TABLESTORAGE_CONNECTIONSTRING=DefaultEndPoint...

And the error I get is:

Unhandled exception. Sprache.ParseException: Parsing failure: unexpected ':'; expected = (Line 1, Column 7); recently consumed: Values

From what I understand this is a valid way to do config, in JSON it would be:

{
   "Values": {
                 "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
                 "AZURITE_TABLESTORAGE_CONNECTIONSTRING"="DefaultEndPoint..."
    }
}

Failure to parse on $

After upgrading from 1.4 to 2.1, this line of my .env file started to fail to parse:

GROUP_FILTER_REGEX=^((?!Everyone).)*$

Stack Trace:

Sprache.ParseException: Parsing failure: unexpected 'T'; expected end of input (Line 27, Column 1); recently consumed: IDER=okta

   at Sprache.ParserExtensions.Parse[T](Parser`1 parser, String input)
   at DotNetEnv.Parsers.ParseDotenvFile(String contents, Func`2 tranform)
   at DotNetEnv.Env.LoadContents(String contents, LoadOptions options)
   at DotNetEnv.Env.Load(String path, LoadOptions options)

The next line starts with T so I think that is what the error is talking about.

Removing the $ allows it to load but as that it is a necessary part of the value, I can't remove it permanently.

.env.local support

Hi. I've started using this library recently, but I noticed there's no option to pass a list of files to be read (it's just .env, always .env), not even .env.local (which is a standard feature while using Symfony (pretty popular PHP framework))
Would it be possible to add the following features?

  • A better, custom list of files instead of 1 file to load (.env that is later overriden by .env.local)
  • A way to override said list of files (So if I get some options I can add .env.prod as well)

Very nice and clean lib, I'd love if those features could be added.

Enhancement - Recursively search directories for .env

I have a project that has a configuration class that loads keys from the environment. If no directory is specified then .Load() uses the CurrentDirectory, which is likely to be /someDir/MyProject/bin/Debug/netcoreapp2.1/.

It would be better if .Load() could traverse the directory upwards, looking for a .env file to load.

I am currently using this function which does a rudimentary traverse of the directory.

private string FindDotEnv(string currentDirectory)
{
    try
    {
        var envFile = Directory.GetFiles(currentDirectory, ".env");
        if (envFile.Length == 0)
        {
            var parentDirectory = Directory.GetParent(currentDirectory);
            return FindDotEnv(parentDirectory.FullName);
        }

        Console.WriteLine($"found .env in {envFile[0]}");
        return envFile[0];
    }
    catch (UnauthorizedAccessException)
    {
        Console.WriteLine("no .env found in directory");
        return null;
    }
}

public MyConfigClass()
{
    var envDirectory = FindDotEnv(Directory.GetCurrentDirectory());
    DotNetEnv.Env.Load(envDirectory);
}

It would be great if .Load() could do this out of the box.

Interpolation stops working with `SetEnvVars == false`

Interpolation is not properly working without SetEnvVars.

TestCase to show that:
Add LoadOptions.NoEnvVars() in EnvConfigurationTests-->AddSourceToBuilderAndParseInterpolatedTest:

this.configuration = new ConfigurationBuilder()
                .AddDotNetEnv("./.env_embedded", LoadOptions.NoEnvVars())
                .Build();

The test fails now with the first interpolation as shown here:
grafik

Originally posted by @Philipp-Binder in #90 (comment)

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.