packages feed

atomic-write 0.2.0.5 → 0.2.0.6

raw patch · 15 files changed

+1124/−41 lines, 15 filesdep +atomic-writenew-uploaderPVP: minor bump suggested

API additions: PVP suggests at least a minor version bump

Dependencies added: atomic-write

API changes (from Hackage documentation)

+ System.AtomicWrite.Writer.ByteString: atomicWriteFileWithMode :: FileMode -> FilePath -> ByteString -> IO ()
+ System.AtomicWrite.Writer.ByteStringBuilder: atomicWriteFileWithMode :: FileMode -> FilePath -> Builder -> IO ()
+ System.AtomicWrite.Writer.LazyByteString: atomicWriteFileWithMode :: FileMode -> FilePath -> ByteString -> IO ()
+ System.AtomicWrite.Writer.LazyText: atomicWriteFile :: FilePath -> Text -> IO ()
+ System.AtomicWrite.Writer.LazyText: atomicWriteFileWithMode :: FileMode -> FilePath -> Text -> IO ()
+ System.AtomicWrite.Writer.String: atomicWithFileAndMode :: FileMode -> FilePath -> (Handle -> IO ()) -> IO ()
+ System.AtomicWrite.Writer.String: atomicWriteFileWithMode :: FileMode -> FilePath -> String -> IO ()
+ System.AtomicWrite.Writer.Text: atomicWriteFileWithMode :: FileMode -> FilePath -> Text -> IO ()

Files

LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2015 Justin Leitgeb+Copyright (c) 2015-2018 Stack Builders Inc.  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the
atomic-write.cabal view
@@ -1,5 +1,5 @@ name:                atomic-write-version:             0.2.0.5+version:             0.2.0.6 synopsis:            Atomically write to a file homepage:            https://github.com/stackbuilders/atomic-write description:@@ -35,15 +35,15 @@   .   > import System.AtomicWrite.Writer.ByteString   .-  Then you can use the atomicWrite function that accepts a `FilePath` and a+  Then you can use the atomicWriteFile function that accepts a `FilePath` and a   `ByteString`, e.g.:   .-  > atomicWrite myFilePath myByteString+  > atomicWriteFile myFilePath myByteString license:             MIT license-file:        LICENSE author:              Justin Leitgeb-maintainer:          justin@stackbuilders.com-copyright:           2015 Stack Builders Inc.+maintainer:          support@stackbuilders.com+copyright:           2015-2018 Stack Builders Inc. category:            System build-type:          Simple cabal-version:       >=1.10@@ -55,6 +55,7 @@                        , System.AtomicWrite.Writer.LazyByteString                        , System.AtomicWrite.Writer.String                        , System.AtomicWrite.Writer.Text+                       , System.AtomicWrite.Writer.LazyText    other-modules:       System.AtomicWrite.Internal @@ -70,15 +71,23 @@   default-language:    Haskell2010   ghc-options:         -Wall + test-suite atomic-write-test   type: exitcode-stdio-1.0-  hs-source-dirs: spec, src+  hs-source-dirs: spec   main-is: Spec.hs +  other-modules:       System.AtomicWrite.Writer.ByteStringSpec+                     , System.AtomicWrite.Writer.ByteStringBuilderSpec+                     , System.AtomicWrite.Writer.LazyByteStringSpec+                     , System.AtomicWrite.Writer.StringSpec+                     , System.AtomicWrite.Writer.TextSpec+                     , System.AtomicWrite.Writer.LazyTextSpec+   build-depends:       base >= 4.5 && < 5.0+                     , atomic-write                      , temporary                      , unix-compat-                     , directory                      , filepath                      , text                      , bytestring >= 0.10.4
+ spec/System/AtomicWrite/Writer/ByteStringBuilderSpec.hs view
@@ -0,0 +1,138 @@+module System.AtomicWrite.Writer.ByteStringBuilderSpec (spec) where++import Test.Hspec (it, describe, shouldBe, Spec)++import System.AtomicWrite.Writer.ByteStringBuilder (atomicWriteFile, atomicWriteFileWithMode)++import System.IO.Temp (withSystemTempDirectory)+import System.FilePath.Posix (joinPath)+import System.PosixCompat.Files+  (setFileMode, setFileCreationMask, getFileStatus, fileMode)++import Data.ByteString.Builder (lazyByteString)++import Data.ByteString.Lazy.Char8 (pack)++spec :: Spec+spec = do+  describe "atomicWriteFile" $ do+    it "writes contents to a file" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++        let path = joinPath [ tmpDir, "writeTest.tmp" ]++        atomicWriteFile path $ lazyByteString $ pack "just testing"+        contents <- readFile path++        contents `shouldBe` "just testing"+    it "preserves the permissions of original file, regardless of umask" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let filePath = joinPath [tmpDir, "testFile"]++        writeFile filePath "initial contents"+        setFileMode filePath 0o100644++        newStat <- getFileStatus filePath+        fileMode newStat `shouldBe` 0o100644++        -- New files are created with 100600 perms.+        _ <- setFileCreationMask 0o100066++        -- Create a new file once different mask is set and make sure that mask+        -- is applied.+        writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+        sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+        fileMode sanityCheckStat `shouldBe` 0o100600++        -- Since we move, this makes the new file assume the filemask of 0600+        atomicWriteFile filePath $ lazyByteString $ pack "new contents"++        resultStat <- getFileStatus filePath++        -- reset mask to not break subsequent specs+        _ <- setFileCreationMask 0o100022++        -- Fails when using atomic mv command unless apply perms on initial file+        fileMode resultStat `shouldBe` 0o100644+++    it "creates a new file with permissions based on active umask" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let+          filePath       = joinPath [tmpDir, "testFile"]+          sampleFilePath = joinPath [tmpDir, "sampleFile"]++        -- Set somewhat distinctive defaults for test+        _ <- setFileCreationMask 0o100171++        -- We don't know what the default file permissions are, so create a+        -- file to sample them.+        writeFile sampleFilePath "I'm being written to sample permissions"++        newStat <- getFileStatus sampleFilePath+        fileMode newStat `shouldBe` 0o100606++        atomicWriteFile filePath $ lazyByteString $ pack "new contents"++        resultStat <- getFileStatus filePath++        -- reset mask to not break subsequent specs+        _ <- setFileCreationMask 0o100022++        -- The default tempfile permissions are 0600, so this fails unless we+        -- make sure that the default umask is relied on for creation of the+        -- tempfile.+        fileMode resultStat `shouldBe` 0o100606+  describe "atomicWriteFileWithMode" $ do+    it "writes contents to a file" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++        let path = joinPath [ tmpDir, "writeTest.tmp" ]++        atomicWriteFileWithMode 0o100777 path $ lazyByteString $ pack "just testing"++        contents <- readFile path++        contents `shouldBe` "just testing"+++    it "changes the permissions of a previously created file, regardless of umask" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let filePath = joinPath [tmpDir, "testFile"]++        writeFile filePath "initial contents"+        setFileMode filePath 0o100644++        newStat <- getFileStatus filePath+        fileMode newStat `shouldBe` 0o100644++        -- New files are created with 100600 perms.+        _ <- setFileCreationMask 0o100066++        -- Create a new file once different mask is set and make sure that mask+        -- is applied.+        writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+        sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+        fileMode sanityCheckStat `shouldBe` 0o100600++        -- Since we move, this makes the new file assume the filemask of 0600+        atomicWriteFileWithMode 0o100655 filePath $ lazyByteString $ pack "new contents"++        resultStat <- getFileStatus filePath++        -- reset mask to not break subsequent specs+        _ <- setFileCreationMask 0o100022++        -- Fails when using atomic mv command unless apply perms on initial file+        fileMode resultStat `shouldBe` 0o100655+++    it "creates a new file with specified permissions" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let+          filePath       = joinPath [tmpDir, "testFile"]+        atomicWriteFileWithMode 0o100606 filePath $ lazyByteString $ pack "new contents"++        resultStat <- getFileStatus filePath++        fileMode resultStat `shouldBe` 0o100606
+ spec/System/AtomicWrite/Writer/ByteStringSpec.hs view
@@ -0,0 +1,136 @@+module System.AtomicWrite.Writer.ByteStringSpec (spec) where++import Test.Hspec (it, describe, shouldBe, Spec)++import System.AtomicWrite.Writer.ByteString (atomicWriteFile, atomicWriteFileWithMode)++import System.IO.Temp (withSystemTempDirectory)+import System.FilePath.Posix (joinPath)+import System.PosixCompat.Files+  (setFileMode, setFileCreationMask, getFileStatus, fileMode)++import Data.ByteString.Char8 (pack)++spec :: Spec+spec = do+  describe "atomicWriteFile" $ do+    it "writes contents to a file" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++        let path = joinPath [ tmpDir, "writeTest.tmp" ]++        atomicWriteFile path $ pack "just testing"+        contents <- readFile path++        contents `shouldBe` "just testing"+    it "preserves the permissions of original file, regardless of umask" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let filePath = joinPath [tmpDir, "testFile"]++        writeFile filePath "initial contents"+        setFileMode filePath 0o100644++        newStat <- getFileStatus filePath+        fileMode newStat `shouldBe` 0o100644++        -- New files are created with 100600 perms.+        _ <- setFileCreationMask 0o100066++        -- Create a new file once different mask is set and make sure that mask+        -- is applied.+        writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+        sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+        fileMode sanityCheckStat `shouldBe` 0o100600++        -- Since we move, this makes the new file assume the filemask of 0600+        atomicWriteFile filePath $ pack "new contents"++        resultStat <- getFileStatus filePath++        -- reset mask to not break subsequent specs+        _ <- setFileCreationMask 0o100022++        -- Fails when using atomic mv command unless apply perms on initial file+        fileMode resultStat `shouldBe` 0o100644+++    it "creates a new file with permissions based on active umask" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let+          filePath       = joinPath [tmpDir, "testFile"]+          sampleFilePath = joinPath [tmpDir, "sampleFile"]++        -- Set somewhat distinctive defaults for test+        _ <- setFileCreationMask 0o100171++        -- We don't know what the default file permissions are, so create a+        -- file to sample them.+        writeFile sampleFilePath "I'm being written to sample permissions"++        newStat <- getFileStatus sampleFilePath+        fileMode newStat `shouldBe` 0o100606++        atomicWriteFile filePath $ pack "new contents"++        resultStat <- getFileStatus filePath++        -- reset mask to not break subsequent specs+        _ <- setFileCreationMask 0o100022++        -- The default tempfile permissions are 0600, so this fails unless we+        -- make sure that the default umask is relied on for creation of the+        -- tempfile.+        fileMode resultStat `shouldBe` 0o100606+  describe "atomicWriteFileWithMode" $ do+    it "writes contents to a file" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++        let path = joinPath [ tmpDir, "writeTest.tmp" ]++        atomicWriteFileWithMode 0o100777 path $ pack "just testing"++        contents <- readFile path++        contents `shouldBe` "just testing"+++    it "changes the permissions of a previously created file, regardless of umask" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let filePath = joinPath [tmpDir, "testFile"]++        writeFile filePath "initial contents"+        setFileMode filePath 0o100644++        newStat <- getFileStatus filePath+        fileMode newStat `shouldBe` 0o100644++        -- New files are created with 100600 perms.+        _ <- setFileCreationMask 0o100066++        -- Create a new file once different mask is set and make sure that mask+        -- is applied.+        writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+        sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+        fileMode sanityCheckStat `shouldBe` 0o100600++        -- Since we move, this makes the new file assume the filemask of 0600+        atomicWriteFileWithMode 0o100655 filePath $ pack "new contents"++        resultStat <- getFileStatus filePath++        -- reset mask to not break subsequent specs+        _ <- setFileCreationMask 0o100022++        -- Fails when using atomic mv command unless apply perms on initial file+        fileMode resultStat `shouldBe` 0o100655+++    it "creates a new file with specified permissions" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let+          filePath       = joinPath [tmpDir, "testFile"]+        atomicWriteFileWithMode 0o100606 filePath $ pack "new contents"++        resultStat <- getFileStatus filePath++        fileMode resultStat `shouldBe` 0o100606
+ spec/System/AtomicWrite/Writer/LazyByteStringSpec.hs view
@@ -0,0 +1,136 @@+module System.AtomicWrite.Writer.LazyByteStringSpec (spec) where++import Test.Hspec (it, describe, shouldBe, Spec)++import System.AtomicWrite.Writer.LazyByteString (atomicWriteFile, atomicWriteFileWithMode)++import System.IO.Temp (withSystemTempDirectory)+import System.FilePath.Posix (joinPath)+import System.PosixCompat.Files+  (setFileMode, setFileCreationMask, getFileStatus, fileMode)++import Data.ByteString.Lazy.Char8 (pack)++spec :: Spec+spec = do+  describe "atomicWriteFile" $ do+    it "writes contents to a file" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++        let path = joinPath [ tmpDir, "writeTest.tmp" ]++        atomicWriteFile path $ pack "just testing"+        contents <- readFile path++        contents `shouldBe` "just testing"+    it "preserves the permissions of original file, regardless of umask" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let filePath = joinPath [tmpDir, "testFile"]++        writeFile filePath "initial contents"+        setFileMode filePath 0o100644++        newStat <- getFileStatus filePath+        fileMode newStat `shouldBe` 0o100644++        -- New files are created with 100600 perms.+        _ <- setFileCreationMask 0o100066++        -- Create a new file once different mask is set and make sure that mask+        -- is applied.+        writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+        sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+        fileMode sanityCheckStat `shouldBe` 0o100600++        -- Since we move, this makes the new file assume the filemask of 0600+        atomicWriteFile filePath $ pack "new contents"++        resultStat <- getFileStatus filePath++        -- reset mask to not break subsequent specs+        _ <- setFileCreationMask 0o100022++        -- Fails when using atomic mv command unless apply perms on initial file+        fileMode resultStat `shouldBe` 0o100644+++    it "creates a new file with permissions based on active umask" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let+          filePath       = joinPath [tmpDir, "testFile"]+          sampleFilePath = joinPath [tmpDir, "sampleFile"]++        -- Set somewhat distinctive defaults for test+        _ <- setFileCreationMask 0o100171++        -- We don't know what the default file permissions are, so create a+        -- file to sample them.+        writeFile sampleFilePath "I'm being written to sample permissions"++        newStat <- getFileStatus sampleFilePath+        fileMode newStat `shouldBe` 0o100606++        atomicWriteFile filePath $ pack "new contents"++        resultStat <- getFileStatus filePath++        -- reset mask to not break subsequent specs+        _ <- setFileCreationMask 0o100022++        -- The default tempfile permissions are 0600, so this fails unless we+        -- make sure that the default umask is relied on for creation of the+        -- tempfile.+        fileMode resultStat `shouldBe` 0o100606+  describe "atomicWriteFileWithMode" $ do+    it "writes contents to a file" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++        let path = joinPath [ tmpDir, "writeTest.tmp" ]++        atomicWriteFileWithMode 0o100777 path $ pack "just testing"++        contents <- readFile path++        contents `shouldBe` "just testing"+++    it "changes the permissions of a previously created file, regardless of umask" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let filePath = joinPath [tmpDir, "testFile"]++        writeFile filePath "initial contents"+        setFileMode filePath 0o100644++        newStat <- getFileStatus filePath+        fileMode newStat `shouldBe` 0o100644++        -- New files are created with 100600 perms.+        _ <- setFileCreationMask 0o100066++        -- Create a new file once different mask is set and make sure that mask+        -- is applied.+        writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+        sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+        fileMode sanityCheckStat `shouldBe` 0o100600++        -- Since we move, this makes the new file assume the filemask of 0600+        atomicWriteFileWithMode 0o100655 filePath $ pack "new contents"++        resultStat <- getFileStatus filePath++        -- reset mask to not break subsequent specs+        _ <- setFileCreationMask 0o100022++        -- Fails when using atomic mv command unless apply perms on initial file+        fileMode resultStat `shouldBe` 0o100655+++    it "creates a new file with specified permissions" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let+          filePath       = joinPath [tmpDir, "testFile"]+        atomicWriteFileWithMode 0o100606 filePath $ pack "new contents"++        resultStat <- getFileStatus filePath++        fileMode resultStat `shouldBe` 0o100606
+ spec/System/AtomicWrite/Writer/LazyTextSpec.hs view
@@ -0,0 +1,136 @@+module System.AtomicWrite.Writer.LazyTextSpec (spec) where++import Test.Hspec (it, describe, shouldBe, Spec)++import System.AtomicWrite.Writer.LazyText (atomicWriteFile, atomicWriteFileWithMode)++import System.IO.Temp (withSystemTempDirectory)+import System.FilePath.Posix (joinPath)+import System.PosixCompat.Files+  (setFileMode, setFileCreationMask, getFileStatus, fileMode)++import Data.Text.Lazy (pack)++spec :: Spec+spec = do+  describe "atomicWriteFile" $ do+    it "writes contents to a file" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++        let path = joinPath [ tmpDir, "writeTest.tmp" ]++        atomicWriteFile path $ pack "just testing"+        contents <- readFile path++        contents `shouldBe` "just testing"+    it "preserves the permissions of original file, regardless of umask" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let filePath = joinPath [tmpDir, "testFile"]++        writeFile filePath "initial contents"+        setFileMode filePath 0o100644++        newStat <- getFileStatus filePath+        fileMode newStat `shouldBe` 0o100644++        -- New files are created with 100600 perms.+        _ <- setFileCreationMask 0o100066++        -- Create a new file once different mask is set and make sure that mask+        -- is applied.+        writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+        sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+        fileMode sanityCheckStat `shouldBe` 0o100600++        -- Since we move, this makes the new file assume the filemask of 0600+        atomicWriteFile filePath $ pack "new contents"++        resultStat <- getFileStatus filePath++        -- reset mask to not break subsequent specs+        _ <- setFileCreationMask 0o100022++        -- Fails when using atomic mv command unless apply perms on initial file+        fileMode resultStat `shouldBe` 0o100644+++    it "creates a new file with permissions based on active umask" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let+          filePath       = joinPath [tmpDir, "testFile"]+          sampleFilePath = joinPath [tmpDir, "sampleFile"]++        -- Set somewhat distinctive defaults for test+        _ <- setFileCreationMask 0o100171++        -- We don't know what the default file permissions are, so create a+        -- file to sample them.+        writeFile sampleFilePath "I'm being written to sample permissions"++        newStat <- getFileStatus sampleFilePath+        fileMode newStat `shouldBe` 0o100606++        atomicWriteFile filePath $ pack "new contents"++        resultStat <- getFileStatus filePath++        -- reset mask to not break subsequent specs+        _ <- setFileCreationMask 0o100022++        -- The default tempfile permissions are 0600, so this fails unless we+        -- make sure that the default umask is relied on for creation of the+        -- tempfile.+        fileMode resultStat `shouldBe` 0o100606+  describe "atomicWriteFileWithMode" $ do+    it "writes contents to a file" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++        let path = joinPath [ tmpDir, "writeTest.tmp" ]++        atomicWriteFileWithMode 0o100777 path $ pack "just testing"++        contents <- readFile path++        contents `shouldBe` "just testing"+++    it "changes the permissions of a previously created file, regardless of umask" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let filePath = joinPath [tmpDir, "testFile"]++        writeFile filePath "initial contents"+        setFileMode filePath 0o100644++        newStat <- getFileStatus filePath+        fileMode newStat `shouldBe` 0o100644++        -- New files are created with 100600 perms.+        _ <- setFileCreationMask 0o100066++        -- Create a new file once different mask is set and make sure that mask+        -- is applied.+        writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+        sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+        fileMode sanityCheckStat `shouldBe` 0o100600++        -- Since we move, this makes the new file assume the filemask of 0600+        atomicWriteFileWithMode 0o100655 filePath $ pack "new contents"++        resultStat <- getFileStatus filePath++        -- reset mask to not break subsequent specs+        _ <- setFileCreationMask 0o100022++        -- Fails when using atomic mv command unless apply perms on initial file+        fileMode resultStat `shouldBe` 0o100655+++    it "creates a new file with specified permissions" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let+          filePath       = joinPath [tmpDir, "testFile"]+        atomicWriteFileWithMode 0o100606 filePath $ pack "new contents"++        resultStat <- getFileStatus filePath++        fileMode resultStat `shouldBe` 0o100606
+ spec/System/AtomicWrite/Writer/StringSpec.hs view
@@ -0,0 +1,140 @@+module System.AtomicWrite.Writer.StringSpec (spec) where++import Test.Hspec (it, describe, shouldBe, Spec)++import System.AtomicWrite.Writer.String (atomicWriteFile, atomicWriteFileWithMode)++import System.IO.Temp (withSystemTempDirectory)+import System.FilePath (joinPath)+import System.PosixCompat.Files+  (setFileMode, setFileCreationMask, getFileStatus, fileMode)+++{-# ANN module "HLint: ignore Reduce duplication" #-}++spec :: Spec+spec = do+  describe "atomicWriteFile" $ do+    it "writes contents to a file" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++        let path = joinPath [ tmpDir, "writeTest.tmp" ]++        atomicWriteFile path "just testing"++        contents <- readFile path++        contents `shouldBe` "just testing"+++    it "preserves the permissions of original file, regardless of umask" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let filePath = joinPath [tmpDir, "testFile"]++        writeFile filePath "initial contents"+        setFileMode filePath 0o100644++        newStat <- getFileStatus filePath+        fileMode newStat `shouldBe` 0o100644++        -- New files are created with 100600 perms.+        _ <- setFileCreationMask 0o100066++        -- Create a new file once different mask is set and make sure that mask+        -- is applied.+        writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+        sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+        fileMode sanityCheckStat `shouldBe` 0o100600++        -- Since we move, this makes the new file assume the filemask of 0600+        atomicWriteFile filePath "new contents"++        resultStat <- getFileStatus filePath++        -- reset mask to not break subsequent specs+        _ <- setFileCreationMask 0o100022++        -- Fails when using atomic mv command unless apply perms on initial file+        fileMode resultStat `shouldBe` 0o100644+++    it "creates a new file with permissions based on active umask" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let+          filePath       = joinPath [tmpDir, "testFile"]+          sampleFilePath = joinPath [tmpDir, "sampleFile"]++        -- Set somewhat distinctive defaults for test+        _ <- setFileCreationMask 0o100171++        -- We don't know what the default file permissions are, so create a+        -- file to sample them.+        writeFile sampleFilePath "I'm being written to sample permissions"++        newStat <- getFileStatus sampleFilePath+        fileMode newStat `shouldBe` 0o100606++        atomicWriteFile filePath "new contents"++        resultStat <- getFileStatus filePath++        -- reset mask to not break subsequent specs+        _ <- setFileCreationMask 0o100022++        -- The default tempfile permissions are 0600, so this fails unless we+        -- make sure that the default umask is relied on for creation of the+        -- tempfile.+        fileMode resultStat `shouldBe` 0o100606+  describe "atomicWriteFileWithMode" $ do+    it "writes contents to a file" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++        let path = joinPath [ tmpDir, "writeTest.tmp" ]++        atomicWriteFileWithMode 0o100777 path "just testing"++        contents <- readFile path++        contents `shouldBe` "just testing"+++    it "changes the permissions of a previously created file, regardless of umask" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let filePath = joinPath [tmpDir, "testFile"]++        writeFile filePath "initial contents"+        setFileMode filePath 0o100644++        newStat <- getFileStatus filePath+        fileMode newStat `shouldBe` 0o100644++        -- New files are created with 100600 perms.+        _ <- setFileCreationMask 0o100066++        -- Create a new file once different mask is set and make sure that mask+        -- is applied.+        writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+        sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+        fileMode sanityCheckStat `shouldBe` 0o100600++        -- Since we move, this makes the new file assume the filemask of 0600+        atomicWriteFileWithMode 0o100655 filePath "new contents"++        resultStat <- getFileStatus filePath++        -- reset mask to not break subsequent specs+        _ <- setFileCreationMask 0o100022++        -- Fails when using atomic mv command unless apply perms on initial file+        fileMode resultStat `shouldBe` 0o100655+++    it "creates a new file with specified permissions" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let+          filePath       = joinPath [tmpDir, "testFile"]+        atomicWriteFileWithMode 0o100606 filePath "new contents"++        resultStat <- getFileStatus filePath++        fileMode resultStat `shouldBe` 0o100606
+ spec/System/AtomicWrite/Writer/TextSpec.hs view
@@ -0,0 +1,136 @@+module System.AtomicWrite.Writer.TextSpec (spec) where++import Test.Hspec (it, describe, shouldBe, Spec)++import System.AtomicWrite.Writer.Text (atomicWriteFile, atomicWriteFileWithMode)++import System.IO.Temp (withSystemTempDirectory)+import System.FilePath.Posix (joinPath)+import System.PosixCompat.Files+  (setFileMode, setFileCreationMask, getFileStatus, fileMode)++import Data.Text (pack)++spec :: Spec+spec = do+  describe "atomicWriteFile" $ do+    it "writes contents to a file" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++        let path = joinPath [ tmpDir, "writeTest.tmp" ]++        atomicWriteFile path $ pack "just testing"+        contents <- readFile path++        contents `shouldBe` "just testing"+    it "preserves the permissions of original file, regardless of umask" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let filePath = joinPath [tmpDir, "testFile"]++        writeFile filePath "initial contents"+        setFileMode filePath 0o100644++        newStat <- getFileStatus filePath+        fileMode newStat `shouldBe` 0o100644++        -- New files are created with 100600 perms.+        _ <- setFileCreationMask 0o100066++        -- Create a new file once different mask is set and make sure that mask+        -- is applied.+        writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+        sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+        fileMode sanityCheckStat `shouldBe` 0o100600++        -- Since we move, this makes the new file assume the filemask of 0600+        atomicWriteFile filePath $ pack "new contents"++        resultStat <- getFileStatus filePath++        -- reset mask to not break subsequent specs+        _ <- setFileCreationMask 0o100022++        -- Fails when using atomic mv command unless apply perms on initial file+        fileMode resultStat `shouldBe` 0o100644+++    it "creates a new file with permissions based on active umask" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let+          filePath       = joinPath [tmpDir, "testFile"]+          sampleFilePath = joinPath [tmpDir, "sampleFile"]++        -- Set somewhat distinctive defaults for test+        _ <- setFileCreationMask 0o100171++        -- We don't know what the default file permissions are, so create a+        -- file to sample them.+        writeFile sampleFilePath "I'm being written to sample permissions"++        newStat <- getFileStatus sampleFilePath+        fileMode newStat `shouldBe` 0o100606++        atomicWriteFile filePath $ pack "new contents"++        resultStat <- getFileStatus filePath++        -- reset mask to not break subsequent specs+        _ <- setFileCreationMask 0o100022++        -- The default tempfile permissions are 0600, so this fails unless we+        -- make sure that the default umask is relied on for creation of the+        -- tempfile.+        fileMode resultStat `shouldBe` 0o100606+  describe "atomicWriteFileWithMode" $ do+    it "writes contents to a file" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++        let path = joinPath [ tmpDir, "writeTest.tmp" ]++        atomicWriteFileWithMode 0o100777 path $ pack "just testing"++        contents <- readFile path++        contents `shouldBe` "just testing"+++    it "changes the permissions of a previously created file, regardless of umask" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let filePath = joinPath [tmpDir, "testFile"]++        writeFile filePath "initial contents"+        setFileMode filePath 0o100644++        newStat <- getFileStatus filePath+        fileMode newStat `shouldBe` 0o100644++        -- New files are created with 100600 perms.+        _ <- setFileCreationMask 0o100066++        -- Create a new file once different mask is set and make sure that mask+        -- is applied.+        writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+        sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+        fileMode sanityCheckStat `shouldBe` 0o100600++        -- Since we move, this makes the new file assume the filemask of 0600+        atomicWriteFileWithMode 0o100655 filePath $ pack "new contents"++        resultStat <- getFileStatus filePath++        -- reset mask to not break subsequent specs+        _ <- setFileCreationMask 0o100022++        -- Fails when using atomic mv command unless apply perms on initial file+        fileMode resultStat `shouldBe` 0o100655+++    it "creates a new file with specified permissions" $+      withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+        let+          filePath       = joinPath [tmpDir, "testFile"]+        atomicWriteFileWithMode 0o100606 filePath $ pack "new contents"++        resultStat <- getFileStatus filePath++        fileMode resultStat `shouldBe` 0o100606
src/System/AtomicWrite/Internal.hs view
@@ -1,9 +1,21 @@-module System.AtomicWrite.Internal (closeAndRename, tempFileFor) where+-- |+-- Module      :  Configuration.Dotenv.Parse+-- Copyright   :  © 2015-2017 Stack Builders Inc.+-- License     :  MIT+--+-- Maintainer  :  Stack Builders <hackage@stackbuilders.com>+-- Stability   :  experimental+-- Portability :  portable+--+-- Provides functionality to create a temporary file with correct permissions+-- atomically. +module System.AtomicWrite.Internal (closeAndRename, tempFileFor, maybeSetFileMode) where+ import System.Directory (doesFileExist, renameFile) import System.PosixCompat.Files (setFileMode, getFileStatus, fileMode)+import System.Posix.Types (FileMode) import System.FilePath (takeDirectory)- import System.IO   (hClose, Handle, openTempFile, openTempFileWithDefaultPermissions) @@ -39,3 +51,9 @@ closeAndRename :: Handle -> FilePath -> FilePath -> IO () closeAndRename tmpHandle tempFile destFile =   hClose tmpHandle >> renameFile tempFile destFile++maybeSetFileMode :: FilePath -> Maybe FileMode -> IO ()+maybeSetFileMode path =+  maybe+    ( return () )+    ( \mode -> setFileMode path mode )
src/System/AtomicWrite/Writer/ByteString.hs view
@@ -1,14 +1,48 @@-module System.AtomicWrite.Writer.ByteString (atomicWriteFile) where+-- |+-- Module      :  Configuration.Dotenv.Parse+-- Copyright   :  © 2015-2017 Stack Builders Inc.+-- License     :  MIT+--+-- Maintainer  :  Stack Builders <hackage@stackbuilders.com>+-- Stability   :  experimental+-- Portability :  portable+--+-- Provides functionality to dump the contents of a ByteString+-- to a file. -import System.AtomicWrite.Internal (closeAndRename, tempFileFor)+module System.AtomicWrite.Writer.ByteString (atomicWriteFile, atomicWriteFileWithMode) where +import System.AtomicWrite.Internal (closeAndRename, tempFileFor, maybeSetFileMode)++import System.Posix.Types (FileMode)+ import Data.ByteString (ByteString, hPutStr) --- | Creates a file atomically on POSIX-compliant systems while preserving--- permissions.+-- | Creates or modifies a file atomically on POSIX-compliant+-- systems while preserving permissions. atomicWriteFile ::   FilePath      -- ^ The path where the file will be updated or created   -> ByteString -- ^ The content to write to the file   -> IO ()-atomicWriteFile f txt =-  tempFileFor f >>= \(tmpPath, h) -> hPutStr h txt >> closeAndRename h tmpPath f+atomicWriteFile = atomicWriteFileMaybeMode Nothing++-- | Creates or modifies a file atomically on POSIX-compliant+-- systems and updates permissions.+atomicWriteFileWithMode ::+  FileMode+  -> FilePath      -- ^ The path where the file will be updated or created+  -> ByteString -- ^ The content to write to the file+  -> IO ()+atomicWriteFileWithMode mode =+  atomicWriteFileMaybeMode $ Just mode++-- | Helper function+atomicWriteFileMaybeMode ::+  Maybe FileMode+  -> FilePath      -- ^ The path where the file will be updated or created+  -> ByteString -- ^ The content to write to the file+  -> IO ()+atomicWriteFileMaybeMode mmode path text =+  tempFileFor path >>= \(tmpPath, h) -> hPutStr h text+                    >> closeAndRename h tmpPath path+                    >> maybeSetFileMode path mmode
src/System/AtomicWrite/Writer/ByteStringBuilder.hs view
@@ -1,20 +1,53 @@-module System.AtomicWrite.Writer.ByteStringBuilder (atomicWriteFile) where+-- |+-- Module      :  Configuration.Dotenv.Parse+-- Copyright   :  © 2015-2017 Stack Builders Inc.+-- License     :  MIT+--+-- Maintainer  :  Stack Builders <hackage@stackbuilders.com>+-- Stability   :  experimental+-- Portability :  portable+--+-- Provides functionality to dump the contents of a ByteStringBuilder+-- to a file. -import System.AtomicWrite.Internal (closeAndRename, tempFileFor)+module System.AtomicWrite.Writer.ByteStringBuilder (atomicWriteFile, atomicWriteFileWithMode) where +import System.AtomicWrite.Internal (closeAndRename, tempFileFor, maybeSetFileMode)+ import Data.ByteString.Builder (hPutBuilder, Builder, hPutBuilder)  import GHC.IO.Handle (hSetBinaryMode, hSetBuffering, BufferMode (BlockBuffering)) --- | Creates a file atomically on POSIX-compliant systems while preserving--- permissions.+import System.Posix.Types (FileMode)++-- | Creates or modifies a file atomically on POSIX-compliant+-- systems while preserving permissions. atomicWriteFile ::   FilePath    -- ^ The path where the file will be updated or created   -> Builder  -- ^ The content to write to the file   -> IO ()-atomicWriteFile f builder = do-  (temppath, h) <- tempFileFor f+atomicWriteFile =+  atomicWriteFileMaybeMode Nothing +-- | Creates or modifies a file atomically on POSIX-compliant+-- systems and updates permissions.+atomicWriteFileWithMode ::+  FileMode+  -> FilePath    -- ^ The path where the file will be updated or created+  -> Builder  -- ^ The content to write to the file+  -> IO ()+atomicWriteFileWithMode mode =+  atomicWriteFileMaybeMode $ Just mode++-- Helper function+atomicWriteFileMaybeMode ::+  Maybe FileMode+  -> FilePath    -- ^ The path where the file will be updated or created+  -> Builder  -- ^ The content to write to the file+  -> IO ()+atomicWriteFileMaybeMode mmode path builder = do+  (temppath, h) <- tempFileFor path+   -- Recommendations for binary and buffering are from the   -- Data.ByteString.Builder docs:   -- http://hackage.haskell.org/package/bytestring-0.10.2.0/docs/Data-ByteString-Builder.html#v:hPutBuilder@@ -23,4 +56,7 @@    hPutBuilder h builder -  closeAndRename h temppath f+  closeAndRename h temppath path++  -- set new permissions if a FileMode was provided+  maybeSetFileMode path mmode
src/System/AtomicWrite/Writer/LazyByteString.hs view
@@ -1,14 +1,49 @@-module System.AtomicWrite.Writer.LazyByteString (atomicWriteFile) where+-- |+-- Module      :  Configuration.Dotenv.Parse+-- Copyright   :  © 2015-2017 Stack Builders Inc.+-- License     :  MIT+--+-- Maintainer  :  Stack Builders <hackage@stackbuilders.com>+-- Stability   :  experimental+-- Portability :  portable+--+-- Provides functionality to dump the contents of a Lazy ByteString+-- to a file. -import System.AtomicWrite.Internal (closeAndRename, tempFileFor)+module System.AtomicWrite.Writer.LazyByteString (atomicWriteFile, atomicWriteFileWithMode) where +import System.AtomicWrite.Internal (closeAndRename, tempFileFor, maybeSetFileMode)+ import Data.ByteString.Lazy (ByteString, hPutStr) --- | Creates a file atomically on POSIX-compliant systems while preserving--- permissions.+import System.Posix.Types (FileMode)++-- | Creates or modifies a file atomically on POSIX-compliant+-- systems while preserving permissions. atomicWriteFile ::   FilePath      -- ^ The path where the file will be updated or created   -> ByteString -- ^ The content to write to the file   -> IO ()-atomicWriteFile f txt =-  tempFileFor f >>= \(tmpPath, h) -> hPutStr h txt >> closeAndRename h tmpPath f+atomicWriteFile =+  atomicWriteFileMaybeMode Nothing++-- | Creates or modifies a file atomically on+-- POSIX-compliant systems and updates permissions+atomicWriteFileWithMode ::+  FileMode      -- ^ The mode to set the file to+  -> FilePath   -- ^ The path where the file will be updated or created+  -> ByteString -- ^ The content to write to the file+  -> IO ()+atomicWriteFileWithMode mode =+  atomicWriteFileMaybeMode $ Just mode++-- Helper Function+atomicWriteFileMaybeMode ::+  Maybe FileMode -- ^ The mode to set the file to+  -> FilePath    -- ^ The path where the file will be updated or created+  -> ByteString  -- ^ The content to write to the file+  -> IO ()+atomicWriteFileMaybeMode mmode path text =+  tempFileFor path >>= \(tmpPath, h) -> hPutStr h text+                    >> closeAndRename h tmpPath path+                    >> maybeSetFileMode path mmode
+ src/System/AtomicWrite/Writer/LazyText.hs view
@@ -0,0 +1,51 @@+-- |+-- Module      :  Configuration.Dotenv.Parse+-- Copyright   :  © 2015-2017 Stack Builders Inc.+-- License     :  MIT+--+-- Maintainer  :  Stack Builders <hackage@stackbuilders.com>+-- Stability   :  experimental+-- Portability :  portable+--+-- Provides functionality to dump the contents of a Text+-- to a file.++module System.AtomicWrite.Writer.LazyText (atomicWriteFile, atomicWriteFileWithMode) where++import System.AtomicWrite.Internal (closeAndRename, tempFileFor, maybeSetFileMode)++import Data.Text.Lazy (Text)++import Data.Text.Lazy.IO (hPutStr)++import System.Posix.Types (FileMode)++-- | Creates a file atomically on POSIX-compliant+-- systems while preserving permissions.+atomicWriteFile ::+  FilePath   -- ^ The path where the file will be updated or created+  -> Text    -- ^ The content to write to the file+  -> IO ()+atomicWriteFile =+  atomicWriteFileMaybeMode Nothing++-- | Creates or modifies a file atomically on+-- POSIX-compliant systems and updates permissions+atomicWriteFileWithMode ::+  FileMode      -- ^ The mode to set the file to+  -> FilePath   -- ^ The path where the file will be updated or created+  -> Text       -- ^ The content to write to the file+  -> IO ()+atomicWriteFileWithMode mode =+  atomicWriteFileMaybeMode $ Just mode++-- Helper Function+atomicWriteFileMaybeMode ::+  Maybe FileMode -- ^ The mode to set the file to+  -> FilePath    -- ^ The path where the file will be updated or created+  -> Text        -- ^ The content to write to the file+  -> IO ()+atomicWriteFileMaybeMode mmode path text =+  tempFileFor path >>= \(tmpPath, h) -> hPutStr h text+                    >> closeAndRename h tmpPath path+                    >> maybeSetFileMode path mmode
src/System/AtomicWrite/Writer/String.hs view
@@ -1,18 +1,61 @@-module System.AtomicWrite.Writer.String (atomicWriteFile, atomicWithFile) where+-- |+-- Module      :  Configuration.Dotenv.Parse+-- Copyright   :  © 2015-2017 Stack Builders Inc.+-- License     :  MIT+--+-- Maintainer  :  Stack Builders <hackage@stackbuilders.com>+-- Stability   :  experimental+-- Portability :  portable+--+-- Provides functionality to dump the contents of a String+-- to a file. -import System.AtomicWrite.Internal (closeAndRename, tempFileFor)+module System.AtomicWrite.Writer.String (atomicWriteFile, atomicWithFile, atomicWriteFileWithMode, atomicWithFileAndMode) where +import System.AtomicWrite.Internal (closeAndRename, tempFileFor, maybeSetFileMode)+ import System.IO (Handle, hPutStr) --- | Creates a file atomically on POSIX-compliant systems while preserving--- permissions.+import System.Posix.Types (FileMode)++-- | Creates or modifies a file atomically on POSIX-compliant+-- systems while preserving permissions. atomicWriteFile ::   FilePath   -- ^ The path where the file will be updated or created   -> String  -- ^ The content to write to the file   -> IO () atomicWriteFile = (. flip hPutStr) . atomicWithFile ++-- | Creates or modifies a file atomically on+-- POSIX-compliant systems and updates permissions+atomicWriteFileWithMode ::+  FileMode    -- ^ The mode to set the file to+  -> FilePath -- ^ The path where the file will be updated or created+  -> String   -- ^ The content to write to the file+  -> IO ()+atomicWriteFileWithMode mode = ( . flip hPutStr)+                             . ( atomicWithFileAndMode mode )+ -- | A general version of 'atomicWriteFile' atomicWithFile :: FilePath -> (Handle -> IO ()) -> IO ()-atomicWithFile f action = -  tempFileFor f >>= \(tmpPath, h) -> action h >> closeAndRename h tmpPath f+atomicWithFile f action =+  atomicWithFileAndMaybeMode Nothing f action++-- | A general version of 'atomicWriteFileWithMode'+atomicWithFileAndMode :: FileMode+                      -> FilePath+                      -> (Handle -> IO ())+                      -> IO ()+atomicWithFileAndMode mode path action =+  atomicWithFileAndMaybeMode (Just mode) path action++-- | Helper function+atomicWithFileAndMaybeMode :: Maybe FileMode+                           -> FilePath+                           -> (Handle -> IO ())+                           -> IO ()+atomicWithFileAndMaybeMode mmode path action =+  tempFileFor path >>= \(tmpPath, h) -> action h+                    >> closeAndRename h tmpPath path+                    >> maybeSetFileMode path mmode
src/System/AtomicWrite/Writer/Text.hs view
@@ -1,16 +1,51 @@-module System.AtomicWrite.Writer.Text (atomicWriteFile) where+-- |+-- Module      :  Configuration.Dotenv.Parse+-- Copyright   :  © 2015-2017 Stack Builders Inc.+-- License     :  MIT+--+-- Maintainer  :  Stack Builders <hackage@stackbuilders.com>+-- Stability   :  experimental+-- Portability :  portable+--+-- Provides functionality to dump the contents of a Text+-- to a file. -import System.AtomicWrite.Internal (closeAndRename, tempFileFor)+module System.AtomicWrite.Writer.Text (atomicWriteFile, atomicWriteFileWithMode) where +import System.AtomicWrite.Internal (closeAndRename, tempFileFor, maybeSetFileMode)+ import Data.Text (Text)  import Data.Text.IO (hPutStr) --- | Creates a file atomically on POSIX-compliant systems while preserving--- permissions.+import System.Posix.Types (FileMode)++-- | Creates a file atomically on POSIX-compliant+-- systems while preserving permissions. atomicWriteFile ::   FilePath   -- ^ The path where the file will be updated or created   -> Text    -- ^ The content to write to the file   -> IO ()-atomicWriteFile f txt =-  tempFileFor f >>= \(tmpPath, h) -> hPutStr h txt >> closeAndRename h tmpPath f+atomicWriteFile =+  atomicWriteFileMaybeMode Nothing++-- | Creates or modifies a file atomically on+-- POSIX-compliant systems and updates permissions+atomicWriteFileWithMode ::+  FileMode      -- ^ The mode to set the file to+  -> FilePath   -- ^ The path where the file will be updated or created+  -> Text       -- ^ The content to write to the file+  -> IO ()+atomicWriteFileWithMode mode =+  atomicWriteFileMaybeMode $ Just mode++-- Helper Function+atomicWriteFileMaybeMode ::+  Maybe FileMode -- ^ The mode to set the file to+  -> FilePath    -- ^ The path where the file will be updated or created+  -> Text        -- ^ The content to write to the file+  -> IO ()+atomicWriteFileMaybeMode mmode path text =+  tempFileFor path >>= \(tmpPath, h) -> hPutStr h text+                    >> closeAndRename h tmpPath path+                    >> maybeSetFileMode path mmode