Skip to content

Commit

Permalink
Merge pull request #880 from sidux/main
Browse files Browse the repository at this point in the history
Handle multiple dotenv files
  • Loading branch information
domenkozar authored Dec 8, 2023
2 parents 81143e4 + 5ad1498 commit 2ff5c8b
Show file tree
Hide file tree
Showing 7 changed files with 45 additions and 42 deletions.
2 changes: 2 additions & 0 deletions docs/integrations/dotenv.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ If you have a `.env`, you'll see instructions how to enable integration:
# Optionally, you can choose which filename to load.
#
# dotenv.filename = ".env.production";
# or
# dotenv.filename = [ ".env.production" ".env.development" ]
}
```

Expand Down
6 changes: 2 additions & 4 deletions docs/reference/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,6 @@ Disable the hint that are printed when the dotenv module is not enabled, but .en




*Type:*
boolean

Expand All @@ -541,13 +540,12 @@ boolean

## dotenv.filename

The name of the dotenv file to load.

The name of the dotenv file to load, or a list of dotenv files to load in order of precedence.



*Type:*
string
string or list of string



Expand Down
1 change: 1 addition & 0 deletions examples/dotenv/.env
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
FOO=1
BAR=2
BAZ=3
3 changes: 2 additions & 1 deletion examples/dotenv/.test.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
env | grep FOO=1
env | grep BAR=1
env | grep BAR=1
env | grep BAZ=5
1 change: 1 addition & 0 deletions examples/dotenv/bar.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
BAZ=5
1 change: 1 addition & 0 deletions examples/dotenv/devenv.nix
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{ pkgs, ... }: {
dotenv.enable = true;
dotenv.filename = [ ".env" "bar.env" ];

env.BAR = "1";
}
73 changes: 36 additions & 37 deletions src/modules/integrations/dotenv.nix
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
let
cfg = config.dotenv;

dotenvPath = config.devenv.root + "/" + cfg.filename;
normalizeFilenames = filenames: if lib.isList filenames then filenames else [ filenames ];
dotenvFiles = normalizeFilenames cfg.filename;
dotenvPaths = map (filename: config.devenv.root + "/" + filename) dotenvFiles;

dotenvFound = lib.pathExists dotenvPath;
parseLine = line:
let
parts = builtins.match "(.+) *= *(.+)" line;
Expand All @@ -16,17 +17,30 @@ let
null;

parseEnvFile = content: builtins.listToAttrs (lib.filter (x: !builtins.isNull x) (map parseLine (lib.splitString "\n" content)));

mergeEnvFiles = files: lib.foldl' (acc: file: lib.recursiveUpdate acc (if lib.pathExists file then parseEnvFile (builtins.readFile file) else { })) { } files;

createMissingFileMessage = file:
let
exampleExists = builtins.pathExists (file + ".example");
in
lib.optionalString (!lib.pathExists file) ''
echo "💡 The dotenv file '${file}' was not found."
${lib.optionalString exampleExists ''
echo " To create this file, you can copy the example file:"
echo " $ cp ${file}.example ${file}"
''}
'';

in
{
options.dotenv = {
enable = lib.mkEnableOption ".env integration, doesn't support comments or multiline values.";

filename = lib.mkOption {
type = lib.types.str;
type = lib.types.either lib.types.str (lib.types.listOf lib.types.str);
default = ".env";
description = ''
The name of the dotenv file to load.
'';
description = "The name of the dotenv file to load, or a list of dotenv files to load in order of precedence.";
};

resolved = lib.mkOption {
Expand All @@ -37,46 +51,31 @@ in
disableHint = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Disable the hint that are printed when the dotenv module is not enabled, but .env is present.
'';
description = "Disable the hint that are printed when the dotenv module is not enabled, but .env is present.";
};
};

config = lib.mkMerge [
(lib.mkIf (cfg.enable && builtins.pathExists dotenvPath) {
(lib.mkIf cfg.enable {
env = lib.mapAttrs (name: value: lib.mkDefault value) config.dotenv.resolved;
dotenv.resolved = parseEnvFile (builtins.readFile dotenvPath);
dotenv.resolved = mergeEnvFiles dotenvPaths;
})
(lib.mkIf (cfg.enable && !builtins.pathExists dotenvPath) (
let
exampleExists = builtins.pathExists (dotenvPath + ".example");
in
{
enterShell = ''
echo "💡 A ${cfg.filename} file was not found, while dotenv integration is enabled."
echo
${lib.optionalString exampleExists ''
echo " To create .env, you can copy the example file:"
echo
echo " $ cp ${dotenvPath}.example ${dotenvPath}";
echo
''}
echo " To disable it, add \`dotenv.enable = false;\` to your devenv.nix file.";
(lib.mkIf (cfg.enable) {
enterShell = lib.concatStringsSep "\n" (map createMissingFileMessage dotenvPaths);
})
(lib.mkIf (!cfg.enable && !cfg.disableHint) {
enterShell =
let
dotenvFound = lib.any (file: lib.pathExists file) dotenvPaths;
in
lib.optionalString dotenvFound ''
echo "💡 A dotenv file was found, while dotenv integration is currently not enabled."
echo
echo " To enable it, add \`dotenv.enable = true;\` to your devenv.nix file.";
echo " To disable this hint, add \`dotenv.disableHint = true;\` to your devenv.nix file.";
echo
echo "See https://devenv.sh/integrations/dotenv/ for more information.";
'';
}
))
(lib.mkIf (!cfg.enable && !cfg.disableHint) {
enterShell = lib.optionalString dotenvFound ''
echo "💡 A ${cfg.filename} file found, while dotenv integration is currently not enabled."
echo
echo " To enable it, add \`dotenv.enable = true;\` to your devenv.nix file.";
echo " To disable this hint, add \`dotenv.disableHint = true;\` to your devenv.nix file.";
echo
echo "See https://devenv.sh/integrations/dotenv/ for more information.";
'';
})
];
}

0 comments on commit 2ff5c8b

Please sign in to comment.