packages feed

hpack 0.19.3 → 0.20.0

raw patch · 9 files changed

+275/−110 lines, 9 filesdep +cryptonitePVP ok

version bump matches the API change (PVP)

Dependencies added: cryptonite

API changes (from Hackage documentation)

+ Hpack: ExistingCabalFileWasModifiedManually :: Status
+ Hpack: Force :: Force
+ Hpack: NoForce :: Force
+ Hpack: NoVerbose :: Verbose
+ Hpack: Verbose :: Verbose
+ Hpack: data Force
+ Hpack: data Verbose
+ Hpack: instance GHC.Classes.Eq Hpack.Result
+ Hpack: instance GHC.Classes.Eq Hpack.Status
+ Hpack: instance GHC.Show.Show Hpack.Result
+ Hpack: instance GHC.Show.Show Hpack.Status
- Hpack: hpack :: Maybe FilePath -> Bool -> IO ()
+ Hpack: hpack :: Maybe FilePath -> Verbose -> Force -> IO ()
- Hpack: hpackResult :: Maybe FilePath -> IO Result
+ Hpack: hpackResult :: Maybe FilePath -> Force -> IO Result

Files

CHANGELOG.md view
@@ -1,3 +1,6 @@+## Changes in 0.20.0+  - Do not overwrite any existing cabal file if it has been modified manually+ ## Changes in 0.19.3   - Add support for `frameworks` and `extra-frameworks-dirs` 
hpack.cabal view
@@ -1,9 +1,9 @@--- This file has been generated from package.yaml by hpack version 0.19.1.+-- This file has been generated from package.yaml by hpack version 0.19.3. -- -- see: https://github.com/sol/hpack  name:           hpack-version:        0.19.3+version:        0.20.0 synopsis:       An alternative format for Haskell packages description:    See README at <https://github.com/sol/hpack#readme> category:       Development@@ -34,6 +34,7 @@     , base-compat >=0.8     , bytestring     , containers+    , cryptonite     , deepseq     , directory     , filepath@@ -47,6 +48,7 @@       Hpack.Run       Hpack.Yaml   other-modules:+      Hpack.CabalFile       Hpack.Dependency       Hpack.FormattingHints       Hpack.GenericsUtil@@ -70,6 +72,7 @@     , base-compat >=0.8     , bytestring     , containers+    , cryptonite     , deepseq     , directory     , filepath@@ -100,6 +103,7 @@     , base-compat >=0.8     , bytestring     , containers+    , cryptonite     , deepseq     , directory     , filepath@@ -113,6 +117,7 @@     , yaml   other-modules:       Helper+      Hpack.CabalFileSpec       Hpack.ConfigSpec       Hpack.DependencySpec       Hpack.FormattingHintsSpec@@ -124,6 +129,7 @@       Hpack.UtilSpec       HpackSpec       Hpack+      Hpack.CabalFile       Hpack.Config       Hpack.Dependency       Hpack.FormattingHints
src/Hpack.hs view
@@ -4,12 +4,13 @@ , hpackResult , Result(..) , Status(..)+, Verbose(..)+, Force(..) , version , main #ifdef TEST-, hpackWithVersion-, extractVersion-, parseVersion+, header+, hpackWithVersionResult , splitDirectory #endif ) where@@ -19,10 +20,6 @@  import           Control.Monad.Compat import qualified Data.ByteString as B-import           Data.List.Compat-import           Data.Maybe-import qualified Data.Text as T-import           Data.Text.Encoding (encodeUtf8) import           Data.Version (Version) import qualified Data.Version as Version import           System.Environment@@ -30,22 +27,24 @@ import           System.IO import           System.FilePath import           System.Directory-import           Text.ParserCombinators.ReadP  import           Paths_hpack (version) import           Hpack.Options import           Hpack.Config import           Hpack.Run import           Hpack.Util+import           Hpack.CabalFile  programVersion :: Version -> String programVersion v = "hpack version " ++ Version.showVersion v -header :: FilePath -> Version -> String-header p v = unlines [+header :: FilePath -> Version -> Hash -> String+header p v hash = unlines [     "-- This file has been generated from " ++ p ++ " by " ++ programVersion v ++ "."   , "--"   , "-- see: https://github.com/sol/hpack"+  , "--"+  , "-- hash: " ++ hash   , ""   ] @@ -56,8 +55,8 @@     PrintVersion -> putStrLn (programVersion version)     Help -> printHelp     Run options -> case options of-      Options _verbose True dir -> hpackStdOut dir-      Options verbose False dir -> hpack dir verbose+      Options _verbose _force True dir -> hpackStdOut dir+      Options verbose force False dir -> hpack dir verbose force     ParseError -> do       printHelp       exitFailure@@ -65,47 +64,40 @@ printHelp :: IO () printHelp = do   hPutStrLn stderr $ unlines [-      "Usage: hpack [ --silent ] [ dir ] [ - ]"+      "Usage: hpack [ --silent ] [ --force | -f ] [ PATH ] [ - ]"     , "       hpack --version"+    , "       hpack --help"     ] -safeInit :: [a] -> [a]-safeInit [] = []-safeInit xs = init xs--extractVersion :: [String] -> Maybe Version-extractVersion = listToMaybe . mapMaybe (stripPrefix prefix >=> parseVersion . safeInit)-  where-    prefix = "-- This file has been generated from package.yaml by hpack version "--parseVersion :: String -> Maybe Version-parseVersion xs = case [v | (v, "") <- readP_to_S Version.parseVersion xs] of-  [v] -> Just v-  _ -> Nothing--hpack :: Maybe FilePath -> Bool -> IO ()+hpack :: Maybe FilePath -> Verbose -> Force -> IO () hpack = hpackWithVersion version -hpackResult :: Maybe FilePath -> IO Result+hpackResult :: Maybe FilePath -> Force -> IO Result hpackResult = hpackWithVersionResult version  data Result = Result {   resultWarnings :: [String] , resultCabalFile :: String , resultStatus :: Status-}+} deriving (Eq, Show) -data Status = Generated | AlreadyGeneratedByNewerHpack | OutputUnchanged+data Status =+    Generated+  | ExistingCabalFileWasModifiedManually+  | AlreadyGeneratedByNewerHpack+  | OutputUnchanged+  deriving (Eq, Show) -hpackWithVersion :: Version -> Maybe FilePath -> Bool -> IO ()-hpackWithVersion v p verbose = do-    r <- hpackWithVersionResult v p+hpackWithVersion :: Version -> Maybe FilePath -> Verbose -> Force -> IO ()+hpackWithVersion v p verbose force = do+    r <- hpackWithVersionResult v p force     printWarnings (resultWarnings r)-    when verbose $ putStrLn $+    when (verbose == Verbose) $ putStrLn $       case resultStatus r of         Generated -> "generated " ++ resultCabalFile r         OutputUnchanged -> resultCabalFile r ++ " is up-to-date"         AlreadyGeneratedByNewerHpack -> resultCabalFile r ++ " was generated with a newer version of hpack, please upgrade and try again."+        ExistingCabalFileWasModifiedManually -> resultCabalFile r ++ " was modified manually, please use --force to overwrite."  printWarnings :: [String] -> IO () printWarnings warnings = do@@ -122,33 +114,40 @@       dir = takeDirectory p       in (guard (p /= file) >> Just dir, if null file then packageConfig else file) -hpackWithVersionResult :: Version -> Maybe FilePath -> IO Result-hpackWithVersionResult v p = do+mkStatus :: [String] -> Version -> CabalFile -> Status+mkStatus new v (CabalFile mOldVersion mHash old) = case (mOldVersion, mHash) of+  (Nothing, _) -> ExistingCabalFileWasModifiedManually+  (Just oldVersion, _) | oldVersion < makeVersion [0, 20, 0] -> Generated+  (_, Nothing) -> ExistingCabalFileWasModifiedManually+  (Just oldVersion, Just hash)+    | v < oldVersion -> AlreadyGeneratedByNewerHpack+    | sha256 (unlines old) /= hash -> ExistingCabalFileWasModifiedManually+    | old == new -> OutputUnchanged+    | otherwise -> Generated++hpackWithVersionResult :: Version -> Maybe FilePath -> Force -> IO Result+hpackWithVersionResult v p force = do   (dir, file) <- splitDirectory p   (warnings, cabalFile, new) <- run dir file-  old <- fmap splitHeader <$> tryReadFile cabalFile-  let oldVersion = fmap fst old >>= extractVersion-  status <--    if (oldVersion <= Just v) then-      if (fmap snd old == Just (lines new)) then-        return OutputUnchanged-      else do-        B.writeFile cabalFile $ encodeUtf8 $ T.pack $ header file v ++ new-        return Generated-    else-      return AlreadyGeneratedByNewerHpack+  oldCabalFile <- readCabalFile cabalFile+  let+    status = case force of+      Force -> Generated+      NoForce -> maybe Generated (mkStatus (lines new) v) oldCabalFile+  case status of+    Generated -> do+      let hash = sha256 new+      B.writeFile cabalFile . encodeUtf8 $ header file v hash ++ new+    _ -> return ()   return Result     { resultWarnings = warnings     , resultCabalFile = cabalFile     , resultStatus = status     }-  where-    splitHeader :: String -> ([String], [String])-    splitHeader = fmap (dropWhile null) . span ("--" `isPrefixOf`) . lines  hpackStdOut :: Maybe FilePath -> IO () hpackStdOut p = do   (dir, file) <- splitDirectory p   (warnings, _cabalFile, new) <- run dir file-  B.putStr $ encodeUtf8 $ T.pack new+  B.putStr (encodeUtf8 new)   printWarnings warnings
+ src/Hpack/CabalFile.hs view
@@ -0,0 +1,54 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ViewPatterns #-}+module Hpack.CabalFile where++import           Prelude ()+import           Prelude.Compat++import           Control.Monad.Compat+import           Data.List.Compat+import           Data.Maybe+import           Data.Version (Version(..))+import qualified Data.Version as Version+import           Text.ParserCombinators.ReadP++import           Hpack.Util++makeVersion :: [Int] -> Version+makeVersion v = Version v []++data CabalFile = CabalFile {+  cabalFileHpackVersion :: Maybe Version+, cabalFileHash :: Maybe Hash+, cabalFileContents :: [String]+} deriving (Eq, Show)++readCabalFile :: FilePath -> IO (Maybe CabalFile)+readCabalFile cabalFile = fmap parse <$> tryReadFile cabalFile+  where+    parse :: String -> CabalFile+    parse (splitHeader -> (h, c)) = CabalFile (extractVersion h) (extractHash h) c++    splitHeader :: String -> ([String], [String])+    splitHeader = fmap (dropWhile null) . span ("--" `isPrefixOf`) . lines++extractHash :: [String] -> Maybe Hash+extractHash = extract "-- hash: " Just++extractVersion :: [String] -> Maybe Version+extractVersion = extract prefix (parseVersion . safeInit)+  where+    prefix = "-- This file has been generated from package.yaml by hpack version "++extract :: String -> (String -> Maybe a) -> [String] -> Maybe a+extract prefix parse = listToMaybe . mapMaybe (stripPrefix prefix >=> parse)++safeInit :: [a] -> [a]+safeInit [] = []+safeInit xs = init xs++parseVersion :: String -> Maybe Version+parseVersion xs = case [v | (v, "") <- readP_to_S Version.parseVersion xs] of+  [v] -> Just v+  _ -> Nothing
src/Hpack/Options.hs view
@@ -6,8 +6,15 @@ data ParseResult = Help | PrintVersion | Run Options | ParseError   deriving (Eq, Show) +data Verbose = Verbose | NoVerbose+  deriving (Eq, Show)++data Force = Force | NoForce+  deriving (Eq, Show)+ data Options = Options {-  optionsVerbose :: Bool+  optionsVerbose :: Verbose+, optionsForce :: Force , optionsToStdout :: Bool , optionsTarget :: Maybe FilePath } deriving (Eq, Show)@@ -17,12 +24,16 @@   ["--version"] -> PrintVersion   ["--help"] -> Help   _ -> case targets of-    Just (target, toStdout) -> Run (Options verbose toStdout target)+    Just (target, toStdout) -> Run (Options verbose force toStdout target)     Nothing -> ParseError     where       silentFlag = "--silent"-      verbose = not (silentFlag `elem` xs)-      ys = filter (/= silentFlag) xs+      forceFlags = ["--force", "-f"]++      flags = [silentFlag] ++ forceFlags+      verbose = if silentFlag `elem` xs then NoVerbose else Verbose+      force = if any (`elem` xs) forceFlags then Force else NoForce+      ys = filter (`notElem` flags) xs        targets = case ys of         ["-"] -> Just (Nothing, True)
src/Hpack/Util.hs view
@@ -15,6 +15,9 @@ , expandGlobs , sort , lexicographically+, encodeUtf8+, Hash+, sha256 ) where  import           Prelude ()@@ -29,13 +32,14 @@ import           Data.List.Compat hiding (sort) import           Data.Ord import qualified Data.Text as T-import           Data.Text.Encoding (decodeUtf8With)+import qualified Data.Text.Encoding as Encoding import           Data.Text.Encoding.Error (lenientDecode) import           System.IO.Error import           System.Directory import           System.FilePath import qualified System.FilePath.Posix as Posix import           System.FilePath.Glob+import           Crypto.Hash  import           Hpack.Haskell @@ -110,7 +114,7 @@ tryReadFile :: FilePath -> IO (Maybe String) tryReadFile file = do   r <- tryJust (guard . isDoesNotExistError) (B.readFile file)-  return $ either (const Nothing) (Just . T.unpack . decodeUtf8With lenientDecode) r+  return $ either (const Nothing) (Just . T.unpack . Encoding.decodeUtf8With lenientDecode) r  toPosixFilePath :: FilePath -> FilePath toPosixFilePath = Posix.joinPath . splitDirectories@@ -149,3 +153,11 @@       , pathSepInRanges = False       , errorRecovery = True       }++encodeUtf8 :: String -> B.ByteString+encodeUtf8 = Encoding.encodeUtf8 . T.pack++type Hash = String++sha256 :: String -> Hash+sha256 c = show (hash (encodeUtf8 c) :: Digest SHA256)
+ test/Hpack/CabalFileSpec.hs view
@@ -0,0 +1,38 @@+module Hpack.CabalFileSpec (spec) where++import           Helper+import           Test.QuickCheck+import           Data.Version (showVersion)+import           Control.Monad.Compat++import           Paths_hpack (version)++import           Hpack (header)+import           Hpack.CabalFile++spec :: Spec+spec = do+  describe "readCabalFile" $ do+    let+      file = "package.yaml"+      hash = "some-hash"+    it "includes hash" $ do+      inTempDirectory $ do+        writeFile file $ header file version hash+        readCabalFile file `shouldReturn` Just (CabalFile (Just version) (Just hash) [])++  describe "extractVersion" $ do+    it "extracts Hpack version from a cabal file" $ do+      let cabalFile = ["-- This file has been generated from package.yaml by hpack version 0.10.0."]+      extractVersion cabalFile `shouldBe` Just (makeVersion [0, 10, 0])++    it "is total" $ do+      let cabalFile = ["-- This file has been generated from package.yaml by hpack version "]+      extractVersion cabalFile `shouldBe` Nothing++  describe "parseVersion" $ do+    it "is inverse to showVersion" $ do+      let positive = getPositive <$> arbitrary+      forAll (replicateM 3 positive) $ \xs -> do+        let v = makeVersion xs+        parseVersion (showVersion v) `shouldBe` Just v
test/Hpack/OptionsSpec.hs view
@@ -20,22 +20,30 @@      context "by default" $ do       it "returns Run" $ do-        parseOptions [] `shouldBe` Run (Options True False Nothing)+        parseOptions [] `shouldBe` Run (Options Verbose NoForce False Nothing)        it "includes target" $ do-        parseOptions ["foo"] `shouldBe` Run (Options True False (Just "foo"))+        parseOptions ["foo"] `shouldBe` Run (Options Verbose NoForce False (Just "foo"))        context "with superfluous arguments" $ do         it "returns ParseError" $ do           parseOptions ["foo", "bar"] `shouldBe` ParseError        context "with --silent" $ do-        it "sets optionsVerbose to False" $ do-          parseOptions ["--silent"] `shouldBe` Run (Options False False Nothing)+        it "sets optionsVerbose to NoVerbose" $ do+          parseOptions ["--silent"] `shouldBe` Run (Options NoVerbose NoForce False Nothing) +      context "with --force" $ do+        it "sets optionsForce to Force" $ do+          parseOptions ["--force"] `shouldBe` Run (Options Verbose Force False Nothing)++      context "with -f" $ do+        it "sets optionsForce to Force" $ do+          parseOptions ["-f"] `shouldBe` Run (Options Verbose Force False Nothing)+       context "with -" $ do         it "sets optionsToStdout to True" $ do-          parseOptions ["-"] `shouldBe` Run (Options True True Nothing)+          parseOptions ["-"] `shouldBe` Run (Options Verbose NoForce True Nothing)          it "rejects - for target" $ do           parseOptions ["-", "-"] `shouldBe` ParseError
test/HpackSpec.hs view
@@ -3,64 +3,98 @@ import           Helper  import           Prelude ()-import           Prelude.Compat+import           Prelude.Compat hiding (readFile)+import qualified Prelude.Compat as Prelude -import           Control.Monad.Compat import           Control.DeepSeq-import           Data.Version (Version(..), showVersion) -import           Test.QuickCheck--import           Hpack+import           Hpack.Config (packageConfig)+import           Hpack.CabalFile+import           Hpack hiding (hpack) -makeVersion :: [Int] -> Version-makeVersion v = Version v []+readFile :: FilePath -> IO String+readFile name = Prelude.readFile name >>= (return $!!)  spec :: Spec spec = do-  describe "extractVersion" $ do-    it "extracts Hpack version from a cabal file" $ do-      let cabalFile = ["-- This file has been generated from package.yaml by hpack version 0.10.0."]-      extractVersion cabalFile `shouldBe` Just (Version [0, 10, 0] [])+  describe "hpackWithVersionResult" $ do+    context "with existing cabal file" $ around_ inTempDirectory $ before_ (writeFile packageConfig "name: foo") $ do+      let+        file = "foo.cabal" -    it "is total" $ do-      let cabalFile = ["-- This file has been generated from package.yaml by hpack version "]-      extractVersion cabalFile `shouldBe` Nothing+        hpackWithVersion v = hpackWithVersionResult v Nothing NoForce+        hpack = hpackWithVersionResult version Nothing NoForce+        hpackForce = hpackWithVersionResult version Nothing Force -  describe "parseVersion" $ do-    it "is inverse to showVersion" $ do-      let positive = getPositive <$> arbitrary-      forAll (replicateM 3 positive) $ \xs -> do-        let v = Version xs []-        parseVersion (showVersion v) `shouldBe` Just v+        generated = Result [] file Generated+        modifiedManually = Result [] file ExistingCabalFileWasModifiedManually+        outputUnchanged = Result [] file OutputUnchanged+        alreadyGeneratedByNewerHpack = Result [] file AlreadyGeneratedByNewerHpack -  describe "hpackWithVersion" $ do-    context "when only the hpack version in the cabal file header changed" $ do-      it "does not write a new cabal file" $ do-        inTempDirectory $ do-          writeFile "package.yaml" "name: foo"-          hpackWithVersion (makeVersion [0,8,0]) Nothing False-          old <- readFile "foo.cabal" >>= (return $!!)-          hpackWithVersion (makeVersion [0,10,0]) Nothing False-          readFile "foo.cabal" `shouldReturn` old+      context "when cabal file was created manually" $ do+        it "does not overwrite existing cabal file" $ do+          let existing = "some existing cabal file"+          writeFile file existing+          hpack `shouldReturn` modifiedManually+          readFile file `shouldReturn` existing -    context "when exsting cabal file was generated with a newer version of hpack" $ do-      it "does not re-generate" $ do-        inTempDirectory $ do-          writeFile "package.yaml" $ unlines [-              "name: foo"-            , "version: 0.1.0"-            ]-          hpackWithVersion (makeVersion [0,10,0]) Nothing False-          old <- readFile "foo.cabal" >>= (return $!!)+        context "with --force" $ do+          it "overwrites existing cabal file" $ do+            _ <- hpack+            expected <- readFile file+            writeFile file "some existing cabal file"+            hpackForce `shouldReturn` generated+            readFile file `shouldReturn` expected -          writeFile "package.yaml" $ unlines [-              "name: foo"-            , "version: 0.2.0"-            ]+      context "when cabal file was created with hpack < 0.20.0" $ do+        it "overwrites existing cabal file" $ do+          _ <- hpack+          expected <- readFile file+          writeFile file "-- This file has been generated from package.yaml by hpack version 0.19.3."+          hpack `shouldReturn` generated+          readFile file `shouldReturn` expected -          hpackWithVersion (makeVersion [0,8,0]) Nothing False-          readFile "foo.cabal" `shouldReturn` old+      context "when cabal file was created with hpack >= 0.20.0" $ do+        context "when hash is missing" $ do+          it "does not overwrite existing cabal file" $ do+            let existing = "-- This file has been generated from package.yaml by hpack version 0.20.0."+            writeFile file existing+            hpack `shouldReturn` modifiedManually+            readFile file `shouldReturn` existing++        context "when hash is present" $ do+          context "when exsting cabal file was generated with a newer version of hpack" $ do+            it "does not overwrite existing cabal file" $ do+              writeFile packageConfig $ unlines [+                  "name: foo"+                , "version: 0.1.0"+                ]+              _ <- hpackWithVersion (makeVersion [0,22,0])+              old <- readFile file++              writeFile packageConfig $ unlines [+                  "name: foo"+                , "version: 0.2.0"+                ]++              hpackWithVersion (makeVersion [0,20,0]) `shouldReturn` alreadyGeneratedByNewerHpack+              readFile file `shouldReturn` old++          context "when cabal file was modified manually" $ do+            it "does not overwrite existing cabal file" $ do+              _ <- hpack+              old <- readFile file+              let modified = old ++ "foo\n"+              writeFile file modified+              _ <- hpack+              readFile file `shouldReturn` modified++          context "when only the hpack version in the cabal file header changed" $ do+            it "does not overwrite existing cabal file" $ do+              _ <- hpackWithVersion (makeVersion [0,20,0])+              old <- readFile file+              hpack `shouldReturn` outputUnchanged+              readFile file `shouldReturn` old    describe "splitDirectory" $ do     context "when given Nothing" $ do