packages feed

load-env 0.1.2 → 0.2.0.0

raw patch · 4 files changed

+129/−28 lines, 4 filesdep +doctestdep +filepathdep +temporary

Dependencies added: doctest, filepath, temporary

Files

+ doctest/Main.hs view
@@ -0,0 +1,6 @@+module Main (main) where++import Test.DocTest++main :: IO ()+main = doctest ["-isrc", "src/"]
load-env.cabal view
@@ -1,11 +1,11 @@--- This file has been generated from package.yaml by hpack version 0.20.0.+-- This file has been generated from package.yaml by hpack version 0.28.2. -- -- see: https://github.com/sol/hpack ----- hash: 9c909afbd1a08e860e60320c5f83c51783e382789593a82de5798a86d1464c45+-- hash: b1f053dbd45894ad7e296e47dd667ef92f771066b17032b259e2f8d68b156898  name:           load-env-version:        0.1.2+version:        0.2.0.0 synopsis:       Load environment variables from a file. description:    Parse a .env file and load any declared variables into the current process's environment. This allows for a .env file to specify development-friendly defaults for configuration values normally set in the deployment environment. category:       Configuration@@ -29,6 +29,7 @@   build-depends:       base >=4.8.0 && <5     , directory+    , filepath     , parsec   exposed-modules:       LoadEnv@@ -37,6 +38,19 @@       Paths_load_env   default-language: Haskell2010 +test-suite doctest+  type: exitcode-stdio-1.0+  main-is: Main.hs+  other-modules:+      Paths_load_env+  hs-source-dirs:+      doctest+  ghc-options: -Wall+  build-depends:+      base >=4.8.0 && <5+    , doctest+  default-language: Haskell2010+ test-suite spec   type: exitcode-stdio-1.0   main-is: Spec.hs@@ -49,6 +63,7 @@     , hspec     , load-env     , parsec+    , temporary   other-modules:       LoadEnv.ParseSpec       LoadEnvSpec
src/LoadEnv.hs view
@@ -1,41 +1,91 @@+-- |+--+-- This is effectively a port of dotenv, whose README explains it best:+--+-- /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 loads variables from a .env file into ENV when the environment is+-- bootstrapped./+--+-- <https://github.com/bkeepers/dotenv>+--+-- This library exposes functions for doing just that.+-- module LoadEnv     ( loadEnv     , loadEnvFrom+    , loadEnvFromAbsolute     ) where --import Control.Monad (when)+import Control.Monad ((<=<))+import Data.Bool (bool)+import Data.Foldable (for_, traverse_)+import Data.List (inits) import LoadEnv.Parse-import System.Directory (doesFileExist)+import System.Directory+    (doesFileExist, findFile, getCurrentDirectory, makeAbsolute) import System.Environment (setEnv)+import System.FilePath (isRelative, joinPath, splitDirectories) import Text.Parsec.String (parseFromFile) --- |+-- | @'loadEnvFrom' \".env\"@+loadEnv :: IO ()+loadEnv = loadEnvFrom ".env"++-- | Parse the given file and set variables in the process's environment ----- Parse @./.env@ for variable declariations. Set those variables in the--- currently running process's environment. Variables can be declared in the--- following form:+-- Variables can be declared in the following form: -- -- > FOO=bar -- > FOO="bar" -- > FOO='bar' ----- Declarations may optionally be preceded by @export@, which will be ignored.--- Trailing whitespace is ignored. Quotes inside quoted values or spaces in--- unquoted values must be escaped with a backlash.------ Invalid lines are silently ignored.+-- Declarations may optionally be preceded by @\"export \"@, which will be+-- ignored. Trailing whitespace is ignored. Quotes inside quoted values or+-- spaces in unquoted values must be escaped with a backlash. Invalid lines are+-- silently ignored. ----- If you wish to specify your own file, use @'loadEnvFrom'@. If you wish to--- pass your own string or work with the parse result directly, use the--- lower-level functions available in @"LoadEnv.Parse"@.+-- __NOTE__: If the file-name is relative, the directory tree will be traversed+-- up to @\/@ looking for the file in each parent. Use @'loadEnvFromAbsolute'@+-- to avoid this. ---loadEnv :: IO ()-loadEnv = loadEnvFrom ".env"- loadEnvFrom :: FilePath -> IO ()-loadEnvFrom fp = do-    e <- doesFileExist fp+loadEnvFrom name = do+    mFile <- if isRelative name+        then flip findFile name . takeDirectories =<< getCurrentDirectory+        else bool Nothing (Just name) <$> doesFileExist name -    when e $ parseFromFile parseEnvironment fp >>=-        either print (mapM_ $ uncurry setEnv)+    for_ mFile $ \file -> do+        result <- parseFromFile parseEnvironment file+        either print (traverse_ $ uncurry setEnv) result++-- | @'loadEnvFrom'@, but don't traverse up the directory tree+loadEnvFromAbsolute :: FilePath -> IO ()+loadEnvFromAbsolute = loadEnvFrom <=< makeAbsolute++-- | Get all directory names of a directory+--+-- Includes itself as the first element of the output.+--+-- >>> takeDirectories "/foo/bar/baz"+-- ["/foo/bar/baz","/foo/bar","/foo","/"]+--+-- Leading path-separator is meaningful, and determines if the root directory is+-- included or not.+--+-- >>> takeDirectories "foo/bar/baz"+-- ["foo/bar/baz","foo/bar","foo"]+--+-- Trailing path-separator is not meaningful.+--+-- >>> takeDirectories "/foo/bar/baz/"+-- ["/foo/bar/baz","/foo/bar","/foo","/"]+--+takeDirectories :: FilePath -> [FilePath]+takeDirectories = map joinPath . reverse . drop 1 . inits . splitDirectories
test/LoadEnvSpec.hs view
@@ -4,12 +4,13 @@  import Control.Monad (when) import LoadEnv-import System.Directory (doesFileExist, removeFile)-import System.Environment (lookupEnv)+import System.Directory+import System.Environment+import System.IO.Temp import Test.Hspec  spec :: Spec-spec = after_ cleanup $+spec = after_ cleanup $ do     describe "loadEnv" $ do         it "loads environment variables from ./.env if present" $ do             writeFile envFile $ unlines@@ -29,8 +30,37 @@              return () +    describe "loadEnvFrom" $ do+        it "traverses up the directory tree" $ do+            inTempDirectory $ do+                writeFile ".env.test" "FOO=\"bar\"\n"+                inNewDirectory "foo/bar/baz" $ do+                    loadEnvFrom ".env.test"++            lookupEnv "FOO" `shouldReturn` Just "bar"++    describe "loadEnvFromAbsolute" $ do+        it "does not traverse up the directory tree" $ do+            inTempDirectory $ do+                writeFile ".env.test" "FOO=\"bar\"\n"+                inNewDirectory "foo/bar/baz" $ do+                    loadEnvFromAbsolute ".env.test"++            lookupEnv "FOO" `shouldReturn` Nothing++inTempDirectory :: IO a -> IO a+inTempDirectory f =+    withSystemTempDirectory "" $ \tmp -> withCurrentDirectory tmp f++inNewDirectory :: FilePath -> IO a -> IO a+inNewDirectory path f = do+    createDirectoryIfMissing True path+    withCurrentDirectory path f+ cleanup :: IO () cleanup = do+    unsetEnv "FOO"+    unsetEnv "BAR"     e <- doesFileExist envFile     when e $ removeFile envFile