Last active
August 12, 2024 11:50
-
-
Save rudfoss/d2b547c96d33a0678c891cd08ff06c44 to your computer and use it in GitHub Desktop.
A very simple parser for loading .env files in a c# application. Has no external dependencies and supports basic references.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace DotEnv | |
{ | |
public class DotEnv(ILogger<DotEnv> logger) | |
{ | |
/// <summary> | |
/// Holds a set of all environment variables set from .env files. Listed if logLevel set do debug. | |
/// </summary> | |
private readonly IDictionary<string, string> _finalEnvironmentVariables = new Dictionary<string, string>(); | |
private void LoadAndParse() | |
{ | |
string currentDirectory = Directory.GetCurrentDirectory(); | |
logger.LogDebug("Working directory: {CurrentDirectory}", currentDirectory); | |
var allEnvFiles = FindEnvFilesInDirectoryTree(currentDirectory); | |
foreach (var envFile in allEnvFiles) | |
{ | |
logger.LogDebug("Parsing env file: {EnvFile}", envFile); | |
ParseEnvFile(envFile); | |
} | |
var allEnvVariables = _finalEnvironmentVariables.Select((keyValuePair) => $"{keyValuePair.Key}={keyValuePair.Value}"); | |
logger.LogDebug("Environment variables loaded:\n{EnvironmentVariables}", string.Join('\n', allEnvVariables)); | |
} | |
/// <summary> | |
/// Locates all .env* files in the specified directory and the set number of ancestors up to the root directory of the volume. Orders them so that the most specific files are loaded last. | |
/// </summary> | |
/// <param name="startingDirectory">Where to begin looking for env files</param> | |
/// <param name="nrOfAncestorsToCheck">The number of ancestor directories to check (default = 20) up to the root directory. Set to 0 to only check current startingDirectory</param> | |
/// <returns>A list of absolute file paths to all env files found orderer from least to most specific.</returns> | |
private static List<string> FindEnvFilesInDirectoryTree(string startingDirectory, int nrOfAncestorsToCheck = 20) { | |
string? currentDirectory = startingDirectory; | |
var allFiles = new List<string>(); | |
while (nrOfAncestorsToCheck-- >= 0 && currentDirectory is not null) | |
{ | |
var currentDirectoryFiles = Directory.GetFiles(currentDirectory, ".env*", SearchOption.TopDirectoryOnly); | |
allFiles.AddRange(currentDirectoryFiles); | |
currentDirectory = Directory.GetParent(currentDirectory)?.FullName; | |
} | |
allFiles.Sort(); | |
return allFiles; | |
} | |
private void ParseEnvFile(string filePath) | |
{ | |
foreach (var line in File.ReadAllLines(filePath)) | |
{ | |
var parts = line.Trim().Split("=", StringSplitOptions.RemoveEmptyEntries); | |
if (parts.Length < 2) continue; | |
var name = parts[0]; | |
var value = parts[1]; | |
if (value.StartsWith('$')) | |
{ | |
var referenceEnvironmentVariableName = value[1..]; | |
// This value is a reference to another environment variable that must be resolved. | |
logger.LogDebug("Resolving reference {Value} in {Name}", referenceEnvironmentVariableName, name); | |
value = Environment.GetEnvironmentVariable(referenceEnvironmentVariableName); | |
} | |
logger.LogDebug("Setting environment variable {Name} to {Value}", name, value); | |
_finalEnvironmentVariables[name] = value ?? ""; | |
Environment.SetEnvironmentVariable(name, value); | |
} | |
} | |
/// <summary> | |
/// Set up and load environment variables from any .env file. | |
/// </summary> | |
public static void LoadDotEnv(LogLevel logLevel = LogLevel.Information) | |
{ | |
var logger = LoggerFactory.Create(builder => | |
{ | |
builder.AddConsole(); | |
builder.SetMinimumLevel(logLevel); | |
}).CreateLogger<DotEnv>(); | |
var env = new DotEnv(logger); | |
env.LoadAndParse(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment