packages feed

pantry 0.9.2 → 0.9.3

raw patch · 21 files changed

+339/−153 lines, 21 filesdep ~rio-prettyprintPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: rio-prettyprint

API changes (from Hackage documentation)

+ Pantry: LocalNoArchiveFileFound :: !Path Abs File -> PantryException

Files

ChangeLog.md view
@@ -1,5 +1,10 @@ # Changelog for pantry
 
+## v0.9.3
+
+* Add error S-628 (`LocalNoArchiveFileFound`).
+* Depend on `rio-prettyprint-0.1.7.0`.
+
 ## v0.9.2
 
 * `defaultCasaRepoPrefix` references https://casa.stackage.org, instead of
app/test-pretty-exceptions/Main.hs view
@@ -161,6 +161,7 @@   , [ DownloadInvalidSHA256 urlExample (Mismatch sha256Example sha256Example) ]
   , [ DownloadInvalidSize urlExample (Mismatch fileSizeExample fileSizeExample) ]
   , [ DownloadTooLarge urlExample (Mismatch fileSizeExample fileSizeExample) ]
+  , [ LocalNoArchiveFileFound pathAbsFileExample ]
   , [ LocalInvalidSHA256 pathAbsFileExample (Mismatch sha256Example sha256Example) ]
   , [ LocalInvalidSize pathAbsFileExample (Mismatch fileSizeExample fileSizeExample) ]
   , [ UnknownArchiveType archiveLocation
@@ -258,16 +259,15 @@ treeKeyExample = TreeKey blobKeyExample
 
 rawPackageLocationImmutableExamples :: [RawPackageLocationImmutable]
-rawPackageLocationImmutableExamples = concat
-  [ [ RPLIHackage packageIdentifierRevision treeKey
-    | packageIdentifierRevision <- packageIdentifierRevisionExamples
-    , treeKey <- [Nothing, Just treeKeyExample]
-    ]
+rawPackageLocationImmutableExamples =
+     [ RPLIHackage packageIdentifierRevision treeKey
+     | packageIdentifierRevision <- packageIdentifierRevisionExamples
+     , treeKey <- [Nothing, Just treeKeyExample]
+     ]
 --, RPLIArchive
-  , [ RPLIRepo repoExample rawPackageMetadata
-    | rawPackageMetadata <- rawPackageMetadataExamples
-    ]
-  ]
+  <> [ RPLIRepo repoExample rawPackageMetadata
+     | rawPackageMetadata <- rawPackageMetadataExamples
+     ]
 
 safeFilePathExamples :: [SafeFilePath]
 safeFilePathExamples =
@@ -286,8 +286,8 @@   , WCGhcjs versionExample versionExample
   ]
 
-data ExceptionExample
-  = ExceptionExample !Text
+newtype ExceptionExample
+  = ExceptionExample Text
   deriving (Show, Typeable)
 
 instance Exception ExceptionExample where
@@ -400,7 +400,7 @@ 
 duplicatePackageNamesExamples :: [(PackageName, [RawPackageLocationImmutable])]
 duplicatePackageNamesExamples = map
-  ((, rawPackageLocationImmutableExamples))
+  (, rawPackageLocationImmutableExamples)
   packageNameExamples
 
 descriptionExample :: Text
int/Pantry/HPack.hs view
@@ -13,11 +13,17 @@ import qualified Hpack
 import qualified Hpack.Config as Hpack
 import           Pantry.Types
+                   ( HasPantryConfig, HpackExecutable (..), PantryConfig (..)
+                   , Version, pantryConfigL, parseVersionThrowing
+                   )
 import           Path
                    ( Abs, Dir, Path, (</>), filename, parseRelFile, toFilePath )
 import           Path.IO ( doesFileExist )
 import           RIO
 import           RIO.Process
+                   ( HasProcessContext, proc, readProcessStdout_, runProcess_
+                   , withWorkingDir
+                   )
 
 hpackVersion ::
      (HasPantryConfig env, HasLogFunc env, HasProcessContext env)
int/Pantry/SHA256.hs view
@@ -39,15 +39,18 @@   , toRaw
   ) where
 
-import           Conduit
+import           Conduit ( ConduitT )
 import qualified Crypto.Hash as Hash ( Digest, SHA256, hash, hashlazy )
 import qualified Crypto.Hash.Conduit as Hash ( hashFile, sinkHash )
-import           Data.Aeson
+import           Data.Aeson ( FromJSON (..), ToJSON (..), withText )
 import qualified Data.ByteArray
 import qualified Data.ByteArray.Encoding as Mem
 import           Data.StaticBytes
                    ( Bytes32, StaticBytesException, toStaticExact )
-import           Database.Persist.Sql
+import           Database.Persist.Class.PersistField ( PersistField (..) )
+import           Database.Persist.PersistValue ( PersistValue (..) )
+import           Database.Persist.Sql ( PersistFieldSql (..) )
+import           Database.Persist.Types ( SqlType (..) )
 import           RIO
 import qualified RIO.Text as T
 
int/Pantry/Types.hs view
@@ -125,11 +125,13 @@   ) where
 
 import           Casa.Client ( CasaRepoPrefix )
-import           Database.Persist
-import           Database.Persist.Sql
+import           Database.Persist.Class.PersistField ( PersistField (..) )
+import           Database.Persist.PersistValue ( PersistValue (..) )
+import           Database.Persist.Sql ( PersistFieldSql (..), SqlBackend )
 #if MIN_VERSION_persistent(2, 13, 0)
 import           Database.Persist.SqlBackend.Internal ( connRDBMS )
 #endif
+import           Database.Persist.Types ( SqlType (..) )
 import           Data.Aeson.Encoding.Internal ( unsafeToEncoding )
 import           Data.Aeson.Types
                    ( FromJSON (..), FromJSONKey (..), FromJSONKeyFunction (..)
@@ -188,8 +190,8 @@ import           RIO.Time ( Day, UTCTime, toGregorian )
 import qualified RIO.Map as Map
 import           RIO.PrettyPrint
-                   ( bulletedList, fillSep, flow, hang, line, mkNarrativeList
-                   , parens, string, style
+                   ( blankLine, bulletedList, fillSep, flow, hang, line
+                   , mkNarrativeList, parens, string, style
                    )
 import           RIO.PrettyPrint.Types ( Style (..) )
 import           Text.PrettyPrint.Leijen.Extended ( Pretty (..), StyleDoc )
@@ -1078,6 +1080,7 @@   | DownloadTooLarge !Text !(Mismatch FileSize)
   -- ^ Different from 'DownloadInvalidSize' since 'mismatchActual' is
   -- a lower bound on the size from the server.
+  | LocalNoArchiveFileFound !(Path Abs File)
   | LocalInvalidSHA256 !(Path Abs File) !(Mismatch SHA256)
   | LocalInvalidSize !(Path Abs File) !(Mismatch FileSize)
   | UnknownArchiveType !ArchiveLocation
@@ -1184,8 +1187,9 @@     <> "is not the package name."
   display (NoLocalPackageDirFound dir) =
     "Error: [S-395]\n"
-    <> "Stack looks for packages in the directories configured in\n"
-    <> "the 'packages' and 'extra-deps' fields defined in your stack.yaml\n"
+    <> "Stack looks for packages in the directories configured in the\n"
+    <> "'packages' and 'extra-deps' fields defined in its project-level\n"
+    <> "configuration file (usually stack.yaml)\n"
     <> "The current entry points to "
     <> fromString (toFilePath dir)
     <> ",\nbut no such directory could be found. If, alternatively, a package\n"
@@ -1193,8 +1197,9 @@     <> "specified as an extra-dep."
   display (NoCabalFileFound dir) =
     "Error: [S-636]\n"
-    <> "Stack looks for packages in the directories configured in\n"
-    <> "the 'packages' and 'extra-deps' fields defined in your stack.yaml\n"
+    <> "Stack looks for packages in the directories configured in the\n"
+    <> "'packages' and 'extra-deps' fields defined in its project-level\n"
+    <> "configuration file (usually stack.yaml)\n"
     <> "The current entry points to "
     <> fromString (toFilePath dir)
     <> ",\nbut no .cabal or package.yaml file could be found there."
@@ -1298,6 +1303,14 @@     <> display mismatchExpected
     <> ", stopped after receiving: "
     <> display mismatchActual
+  display (LocalNoArchiveFileFound path) =
+    "Error: [S-628]\n"
+    <> "Stack looks for packages in the archive files configured in the\n"
+    <> "'extra-deps' field defined in its project-level configuration file\n"
+    <> "(usually stack.yaml)\n"
+    <> "An entry points to "
+    <> fromString (toFilePath path)
+    <> ",\nbut no such archive file could be found."
   display (LocalInvalidSHA256 path Mismatch {..}) =
     "Error: [S-834]\n"
     <> "Mismatched SHA256 hash from "
@@ -1539,8 +1552,8 @@          , style Shell "packages"
          , "and"
          , style Shell "extra-deps"
-         , flow "fields defined in your"
-         , style File "stack.yaml" <> "."
+         , flow "fields defined in its project-level configuration file"
+         , parens (fillSep ["usually", style File "stack.yaml"]) <> "."
          , flow "The current entry points to"
          , pretty dir
          , flow "but no such directory could be found. If, alternatively, a"
@@ -1555,8 +1568,8 @@          , style Shell "packages"
          , "and"
          , style Shell "extra-deps"
-         , flow "fields defined in your"
-         , style File "stack.yaml" <> "."
+         , flow "fields defined in its project-level configuration file"
+         , parens (fillSep ["usually", style File "stack.yaml"]) <> "."
          , flow "The current entry points to"
          , pretty dir
          , flow "but no Cabal file or"
@@ -1727,6 +1740,19 @@          , flow "stopped after receiving:"
          , fromString . T.unpack $ textDisplay mismatchActual <> "."
          ]
+  pretty (LocalNoArchiveFileFound path) =
+    "[S-628]"
+    <> line
+    <> fillSep
+         [ flow "Stack looks for packages in the archive files configured in"
+         , "the"
+         , style Shell "extra-deps"
+         , flow "field defined in its project-level configuration file"
+         , parens (fillSep ["usually", style File "stack.yaml"]) <> "."
+         , flow "An entry points to"
+         , pretty path
+         , flow "but no such archive file could be found."
+         ]
   pretty (LocalInvalidSHA256 path Mismatch {..}) =
     "[S-834]"
     <> line
@@ -1961,9 +1987,6 @@          ]
     <> blankLine
     <> string (displayException err)
-
-blankLine :: StyleDoc
-blankLine = line <> line
 
 data FuzzyResults
   = FRNameNotFound ![PackageName]
pantry.cabal view
@@ -1,11 +1,11 @@ cabal-version: 2.0
 
--- This file has been generated from package.yaml by hpack version 0.35.2.
+-- This file has been generated from package.yaml by hpack version 0.36.0.
 --
 -- see: https://github.com/sol/hpack
 
 name:           pantry
-version:        0.9.2
+version:        0.9.3
 synopsis:       Content addressable Haskell package management
 description:    Please see the README on GitHub at <https://github.com/commercialhaskell/pantry#readme>
 category:       Development
@@ -51,7 +51,7 @@       Pantry.SHA256
   hs-source-dirs:
       src/
-  ghc-options: -Wall
+  ghc-options: -fwrite-ide-info -hiedir=.hie -Wall
   build-depends:
       Cabal >=3 && <3.11
     , aeson
@@ -90,7 +90,7 @@     , resourcet
     , rio
     , rio-orphans
-    , rio-prettyprint
+    , rio-prettyprint >=0.1.7.0
     , static-bytes
     , tar-conduit >=0.4.0
     , text
@@ -130,7 +130,7 @@       Paths_pantry
   hs-source-dirs:
       int/
-  ghc-options: -Wall
+  ghc-options: -fwrite-ide-info -hiedir=.hie -Wall
   build-depends:
       Cabal >=3 && <3.11
     , aeson
@@ -168,7 +168,7 @@     , resourcet
     , rio
     , rio-orphans
-    , rio-prettyprint
+    , rio-prettyprint >=0.1.7.0
     , static-bytes
     , tar-conduit >=0.4.0
     , text
@@ -194,7 +194,7 @@       Paths_pantry
   hs-source-dirs:
       app/test-pretty-exceptions
-  ghc-options: -Wall
+  ghc-options: -fwrite-ide-info -hiedir=.hie -Wall
   build-depends:
       Cabal >=3 && <3.11
     , aeson
@@ -234,7 +234,7 @@     , resourcet
     , rio
     , rio-orphans
-    , rio-prettyprint
+    , rio-prettyprint >=0.1.7.0
     , static-bytes
     , tar-conduit >=0.4.0
     , text
@@ -288,7 +288,7 @@       Paths_pantry
   hs-source-dirs:
       test
-  ghc-options: -Wall
+  ghc-options: -fwrite-ide-info -hiedir=.hie -Wall
   build-tool-depends:
       hspec-discover:hspec-discover
   build-depends:
@@ -335,7 +335,7 @@     , resourcet
     , rio
     , rio-orphans
-    , rio-prettyprint
+    , rio-prettyprint >=0.1.7.0
     , static-bytes
     , tar-conduit >=0.4.0
     , text
src/Hackage/Security/Client/Repository/HttpLib/HttpClient.hs view
@@ -10,15 +10,19 @@   ( httpLib
   ) where
 
-import           Control.Exception
+import           Control.Exception ( handle )
 import           Control.Monad ( void )
 import           Data.ByteString ( ByteString )
 import qualified Data.ByteString as BS
 import qualified Data.ByteString.Char8 as BS.C8
-import           Hackage.Security.Client hiding ( Header )
+import           Hackage.Security.Client ( SomeRemoteError (..) )
 import           Hackage.Security.Client.Repository.HttpLib
+                   ( BodyReader, HttpLib (..), HttpRequestHeader (..)
+                   , HttpResponseHeader (..), HttpStatus (..)
+                   )
 import           Hackage.Security.Util.Checked
-import           Network.URI
+                   ( Throws, handleChecked, throwChecked )
+import           Network.URI ( URI )
 import qualified Pantry.HTTP as HTTP
 
 {-------------------------------------------------------------------------------
src/Pantry.hs view
@@ -196,9 +196,14 @@   ) where
 
 import           Casa.Client ( CasaRepoPrefix, thParserCasaRepo )
-import           Conduit
+import           Conduit ( (.|), mapC, mapMC, runConduitRes, sinkList, sumC )
+import           Control.Applicative ( empty )
 import           Control.Arrow ( right )
 import           Control.Monad.State.Strict ( State, execState, get, modify' )
+import           Control.Monad.Trans.Maybe ( MaybeT (..) )
+#if MIN_VERSION_transformers(0,6,0)
+import           Control.Monad.Trans.Maybe ( hoistMaybe )
+#endif
 import           Data.Aeson.Types ( Value, parseEither )
 import           Data.Aeson.WarningParser ( WithJSONWarnings (..) )
 #if !MIN_VERSION_rio(0,1,17)
@@ -209,7 +214,7 @@ import           Data.Time ( diffUTCTime, getCurrentTime )
 import qualified Data.Yaml as Yaml
 import           Data.Yaml.Include ( decodeFileWithWarnings )
-import           Database.Persist ( entityKey )
+import           Database.Persist.Class.PersistEntity ( entityKey )
 import           Distribution.PackageDescription
                    ( FlagName, GenericPackageDescription )
 import qualified Distribution.PackageDescription as D
@@ -218,17 +223,66 @@ import qualified Hpack.Config as Hpack
 import           Hpack.Error ( formatHpackError )
 import           Hpack.Yaml ( formatWarning )
-import           Network.HTTP.Download
+import           Network.HTTP.Download ( download, redownload )
 import           Pantry.Archive
-import           Pantry.Casa
-import           Pantry.HTTP
+                   ( fetchArchives, findCabalOrHpackFile, getArchive
+                   , getArchiveKey, getArchivePackage
+                   )
+import           Pantry.Casa ( casaBlobSource, casaLookupKey, casaLookupTree )
+import           Pantry.HTTP ( httpSinkChecked, parseRequest )
 import           Pantry.Hackage
+                   ( DidUpdateOccur (..), RequireHackageIndex (..)
+                   , UsePreferredVersions (..), getHackageCabalFile
+                   , getHackagePackageVersionRevisions
+                   , getHackagePackageVersions, getHackageTarball
+                   , getHackageTarballKey, getHackageTypoCorrections
+                   , hackageIndexTarballL, htrPackage, updateHackageIndex
+                   )
 import           Pantry.Repo
+                   ( fetchRepos, fetchReposRaw, getRepo, getRepoKey, withRepo )
 import qualified Pantry.SHA256 as SHA256
-import           Pantry.Storage hiding
-                   ( TreeEntry, PackageName, Version, findOrGenerateCabalFile )
-import           Pantry.Tree
+import           Pantry.Storage
+                   ( getSnapshotCacheByHash, getSnapshotCacheId, getTreeForKey
+                   , initStorage, loadBlob, loadCachedTree
+                   , loadExposedModulePackages, loadPackageById, loadURLBlob
+                   , storeSnapshotModuleCache, storeTree, storeURLBlob
+                   , withStorage
+                   )
+import           Pantry.Tree ( rawParseGPD, unpackTree )
 import           Pantry.Types as P
+                   ( Archive (..), ArchiveLocation (..), BlobKey (..)
+                   , CabalFileInfo (..), CabalString (..), FileSize (..)
+                   , FuzzyResults (..), HackageSecurityConfig (..)
+                   , HasPantryConfig (..), HpackExecutable (..), Mismatch (..)
+                   , ModuleName, Package (..), PackageCabal (..)
+                   , PackageIdentifier (..), PackageIdentifierRevision (..)
+                   , PackageIndexConfig (..), PackageLocation (..)
+                   , PackageLocationImmutable (..), PackageMetadata (..)
+                   , PackageName, PantryConfig (..), PantryException (..)
+                   , PHpack (..), PrintWarnings (..), RawArchive (..)
+                   , RawPackageLocation (..), RawPackageLocationImmutable (..)
+                   , RawPackageMetadata (..), RawSnapshot (..)
+                   , RawSnapshotLayer (..), RawSnapshotLocation (..)
+                   , RawSnapshotPackage (..), RelFilePath (..), Repo (..)
+                   , RepoType (..), ResolvedPath (..), Revision (..)
+                   , SafeFilePath, SHA256, SimpleRepo (..), SnapName (..)
+                   , Snapshot (..), SnapshotCacheHash (..), SnapshotLayer (..)
+                   , SnapshotLocation (..), SnapshotPackage (..), Tree (..)
+                   , TreeEntry (..), TreeKey (..), Unresolved, Version
+                   , WantedCompiler (..), bsToBlobKey, cabalFileName
+                   , defaultHackageSecurityConfig, defaultSnapshotLocation
+                   , flagNameString, getGlobalHintsFile, mkSafeFilePath
+                   , moduleNameString, packageIdentifierString
+                   , packageNameString, parseFlagName, parseHackageText
+                   , parsePackageIdentifier, parsePackageIdentifierRevision
+                   , parsePackageName, parsePackageNameThrowing
+                   , parseRawSnapshotLocation, parseSnapName, parseTreeM
+                   , parseVersion, parseVersionThrowing, parseWantedCompiler
+                   , pirForHash, resolvePaths, snapshotLocation
+                   , toCabalStringMap, toRawPL, toRawPLI, toRawPM, toRawSL
+                   , toRawSnapshotLayer, unCabalStringMap, unSafeFilePath
+                   , versionString, warnMissingCabalFile
+                   )
 import           Path
                    ( Abs, Dir, File, Path, (</>), filename, parent, parseAbsDir
                    , parseRelFile, toFilePath
@@ -240,14 +294,22 @@ import qualified RIO.FilePath as FilePath
 import qualified RIO.List as List
 import qualified RIO.Map as Map
-import           RIO.PrettyPrint
+import           RIO.PrettyPrint ( HasTerm (..) )
 import           RIO.PrettyPrint.StylesUpdate
+                   ( HasStylesUpdate (..), StylesUpdate )
 import           RIO.Process
+                   ( HasProcessContext (..), proc, runProcess_, withWorkingDir )
 import qualified RIO.Set as Set
 import           RIO.Text ( unpack )
 import qualified RIO.Text as T
 import           System.IO.Error ( isDoesNotExistError )
 
+#if !MIN_VERSION_transformers(0,6,0)
+-- | Convert a 'Maybe' computation to 'MaybeT'.
+hoistMaybe :: (Applicative m) => Maybe b -> MaybeT m b
+hoistMaybe = MaybeT . pure
+#endif
+
 decodeYaml :: FilePath -> IO (Either String ([String], Value))
 decodeYaml file = do
   bimap displayException (first formatWarnings) <$> decodeFileWithWarnings file
@@ -956,23 +1018,18 @@   => RawPackageLocationImmutable
   -> TreeKey
   -> RIO env (Maybe Package)
-tryLoadPackageRawViaDbOrCasa rpli treeKey' = do
-  mviaDb <- tryLoadPackageRawViaLocalDb rpli treeKey'
-  case mviaDb of
-    Just package -> do
-      logDebug ("Loaded package from Pantry: " <> display rpli)
-      pure (Just package)
-    Nothing -> do
-      mCasaConfig <- view $ pantryConfigL . to pcCasaConfig
-      case mCasaConfig of
-        Nothing -> pure Nothing
-        Just _ -> do
-          mviaCasa <- tryLoadPackageRawViaCasa rpli treeKey'
-          case mviaCasa of
-            Just package -> do
-              logDebug ("Loaded package from Casa: " <> display rpli)
-              pure (Just package)
-            Nothing -> pure Nothing
+tryLoadPackageRawViaDbOrCasa rpli treeKey' = runMaybeT $
+  tryViaLocalDb <|> tryCasa
+ where
+  tryViaLocalDb = do
+    package <- MaybeT $ tryLoadPackageRawViaLocalDb rpli treeKey'
+    lift $ logDebug ("Loaded package from Pantry: " <> display rpli)
+    pure package
+  tryCasa = do
+    void $ MaybeT $ view $ pantryConfigL . to pcCasaConfig
+    package <- MaybeT $ tryLoadPackageRawViaCasa rpli treeKey'
+    lift $ logDebug ("Loaded package from Casa: " <> display rpli)
+    pure package
 
 -- | Maybe load the package from Casa.
 tryLoadPackageRawViaCasa ::
@@ -980,23 +1037,20 @@   => RawPackageLocationImmutable
   -> TreeKey
   -> RIO env (Maybe Package)
-tryLoadPackageRawViaCasa rlpi treeKey' = do
-  mtreePair <- casaLookupTree treeKey'
-  case mtreePair of
-    Nothing -> pure Nothing
-    Just (treeKey'', _tree) -> do
-      fetchTreeKeys [rlpi]
-      mdb <- tryLoadPackageRawViaLocalDb rlpi treeKey''
-      case mdb of
-        Nothing -> do
-          logWarn
-            ("Did not find tree key in DB after pulling it from Casa: " <>
-             display treeKey'' <>
-             " (for " <>
-             display rlpi <>
-             ")")
-          pure Nothing
-        Just package -> pure (Just package)
+tryLoadPackageRawViaCasa rlpi treeKey' = runMaybeT $ do
+  (treeKey'', _) <- MaybeT $ casaLookupTree treeKey'
+  lift $ fetchTreeKeys [rlpi]
+  tryViaLocalDb treeKey'' <|> warn treeKey''
+ where
+  tryViaLocalDb = MaybeT . tryLoadPackageRawViaLocalDb rlpi
+  warn treeKey'' = do
+    lift $ logWarn $
+         "Did not find tree key in DB after pulling it from Casa: "
+      <> display treeKey''
+      <> " (for "
+      <> display rlpi
+      <> ")"
+    empty
 
 -- | Maybe load the package from the local database.
 tryLoadPackageRawViaLocalDb ::
@@ -1004,12 +1058,9 @@   => RawPackageLocationImmutable
   -> TreeKey
   -> RIO env (Maybe Package)
-tryLoadPackageRawViaLocalDb rlpi treeKey' = do
-  mtreeEntity <- withStorage (getTreeForKey treeKey')
-  case mtreeEntity of
-    Nothing -> pure Nothing
-    Just treeId ->
-      fmap Just (withStorage (loadPackageById rlpi (entityKey treeId)))
+tryLoadPackageRawViaLocalDb rlpi treeKey' = runMaybeT $ do
+  treeId <- MaybeT $ withStorage (getTreeForKey treeKey')
+  lift $ withStorage (loadPackageById rlpi (entityKey treeId))
 
 -- | Complete package location, plus whether the package has a cabal file. This
 -- is relevant to reproducibility, see
@@ -1483,13 +1534,13 @@   => Map RawPackageLocationImmutable PackageLocationImmutable
   -> RawPackageLocationImmutable
   -> RIO env (Maybe PackageLocationImmutable)
-cachedSnapshotCompletePackageLocation cachePackages rpli = do
-  let xs = Map.lookup rpli cachePackages
-  case xs of
-    Nothing -> do
-      cpl <- completePackageLocation rpli
-      pure $ if cplHasCabalFile cpl then Just (cplComplete cpl) else Nothing
-    Just x -> pure $ Just x
+cachedSnapshotCompletePackageLocation cachePackages rpli = runMaybeT $
+  tryCache <|> tryCpl
+ where
+  tryCache = hoistMaybe $ Map.lookup rpli cachePackages
+  tryCpl = do
+    cpl <- lift $ completePackageLocation rpli
+    if cplHasCabalFile cpl then pure (cplComplete cpl) else empty
 
 -- | Add more packages to a snapshot completing their locations if needed
 --
src/Pantry/Archive.hs view
@@ -15,6 +15,9 @@ 
 import qualified Codec.Archive.Zip as Zip
 import           Conduit
+                   ( ConduitT, (.|), runConduit, sinkHandle, sinkList
+                   , sourceHandle, sourceLazy, withSourceFile
+                   )
 import           Data.Bits ( (.&.), shiftR )
 import qualified Data.Conduit.Tar as Tar
 import           Data.Conduit.Zlib ( ungzip )
@@ -22,19 +25,34 @@ import           Distribution.PackageDescription ( package, packageDescription )
 import qualified Hpack.Config as Hpack
 import           Pantry.HPack ( hpackVersion )
-import           Pantry.HTTP
+import           Pantry.HTTP ( httpSinkChecked )
 import           Pantry.Internal ( makeTarRelative, normalizeParents )
 import qualified Pantry.SHA256 as SHA256
-import           Pantry.Storage hiding
-                   ( Tree, TreeEntry, findOrGenerateCabalFile )
-import           Pantry.Tree
+import           Pantry.Storage
+                   ( BlobId, CachedTree (..), TreeId, hpackToCabal
+                   , loadArchiveCache, loadBlob, loadCabalBlobKey
+                   , loadCachedTree, loadPackageById, storeArchiveCache
+                   , storeBlob, storeHPack, storeTree, unCachedTree, withStorage
+                   )
+import           Pantry.Tree ( rawParseGPD )
 import           Pantry.Types
+                   ( Archive, ArchiveLocation (..), BlobKey, BuildFile (..)
+                   , FileSize (..), FileType (..), HasPantryConfig
+                   , Mismatch (..), Package (..), PackageCabal (..)
+                   , PackageIdentifier (..), PackageMetadata (..)
+                   , PantryException (..), PHpack (..), RawArchive (..)
+                   , RawPackageLocationImmutable (..), RawPackageMetadata (..)
+                   , ResolvedPath (..), SHA256, Tree (..), TreeEntry (..)
+                   , TreeKey, cabalFileName, hpackSafeFilePath, mkSafeFilePath
+                   , toRawArchive, toRawPM, unSafeFilePath
+                   )
 import           Path ( toFilePath )
+import           Path.IO ( doesFileExist )
 import           RIO
 import qualified RIO.ByteString.Lazy as BL
 import qualified RIO.List as List
 import qualified RIO.Map as Map
-import           RIO.Process
+import           RIO.Process ( HasProcessContext )
 import qualified RIO.Set as Set
 import qualified RIO.Text as T
 import qualified RIO.Text.Partial as T
@@ -219,7 +237,8 @@    in if and tests then Right pa else Left err
 
 -- | Provide a local file with the contents of the archive, regardless of where
--- it comes from. Perform SHA256 and file size validation if downloading.
+-- it comes from. If not downloading, checks that the archive file exists.
+-- Performs SHA256 and file size validation.
 withArchiveLoc ::
      HasLogFunc env
   => RawArchive
@@ -228,6 +247,9 @@ withArchiveLoc (RawArchive (ALFilePath resolved) msha msize _subdir) f = do
   let abs' = resolvedAbsolute resolved
       fp = toFilePath abs'
+  archiveExists <- doesFileExist abs'
+  unless archiveExists $
+    throwIO $ LocalNoArchiveFileFound abs'
   (sha, size) <- withBinaryFile fp ReadMode $ \h -> do
     size <- FileSize . fromIntegral <$> hFileSize h
     for_ msize $ \size' ->
src/Pantry/Casa.hs view
@@ -4,13 +4,19 @@ 
 module Pantry.Casa where
 
+import           Database.Persist.Sql ( SqlBackend )
 import qualified Casa.Client as Casa
 import qualified Casa.Types as Casa
 import           Conduit
+                   ( ConduitT, ResourceT, (.|), await, mapMC, runConduitRes )
 import qualified Data.HashMap.Strict as HM
 import qualified Pantry.SHA256 as SHA256
-import           Pantry.Storage hiding ( findOrGenerateCabalFile )
+import           Pantry.Storage ( storeBlob, withStorage )
 import           Pantry.Types as P
+                   ( BlobKey (..), FileSize (..), HasPantryConfig (..)
+                   , PantryConfig (..), PantryException (..), Tree, TreeKey (..)
+                   , parseTreeM
+                   )
 import           RIO
 import qualified RIO.ByteString as B
 
src/Pantry/HTTP.hs view
@@ -8,7 +8,7 @@   , httpSinkChecked
   ) where
 
-import           Conduit
+import           Conduit ( ConduitT, ZipSink (..), await, getZipSink )
 import           Network.HTTP.Client as Export
                    ( BodyReader, HttpExceptionContent (StatusCodeException)
                    , parseRequest, parseUrlThrow
@@ -28,6 +28,8 @@                    )
 import qualified Pantry.SHA256 as SHA256
 import           Pantry.Types
+                   ( FileSize (..), Mismatch (..), PantryException (..), SHA256
+                   )
 import           RIO
 import qualified RIO.ByteString as B
 import qualified RIO.Text as T
@@ -35,8 +37,8 @@ setUserAgent :: Request -> Request
 setUserAgent = setRequestHeader "User-Agent" ["Haskell pantry package"]
 
-withResponse
-  :: MonadUnliftIO m
+withResponse ::
+     MonadUnliftIO m
   => HTTP.Request
   -> (Response BodyReader -> m a)
   -> m a
@@ -44,15 +46,15 @@   manager <- getGlobalManager
   HTTP.withResponse (setUserAgent req) manager (run . inner)
 
-httpSink
-  :: MonadUnliftIO m
+httpSink ::
+     MonadUnliftIO m
   => Request
   -> (Response () -> ConduitT ByteString Void m a)
   -> m a
 httpSink req = HTTP.httpSink (setUserAgent req)
 
-httpSinkChecked
-  :: MonadUnliftIO m
+httpSinkChecked ::
+     MonadUnliftIO m
   => Text
   -> Maybe SHA256
   -> Maybe FileSize
src/Pantry/Hackage.hs view
@@ -22,12 +22,20 @@   ) where
 
 import           Conduit
+                   ( ZipSink (..), (.|), getZipSink, runConduit, sinkLazy
+                   , sinkList, sourceHandle, takeC, takeCE
+                   )
 import           Data.Aeson
+                   ( FromJSON (..), Value (..),  (.:), eitherDecode'
+                   , withObject
+                   )
 import           Data.Conduit.Tar
+                   ( FileInfo (..), FileType (..), untar )
 import qualified Data.List.NonEmpty as NE
 import           Data.Text.Metrics (damerauLevenshtein)
 import           Data.Text.Unsafe ( unsafeTail )
 import           Data.Time ( getCurrentTime )
+import           Database.Persist.Sql ( SqlBackend )
 import           Distribution.PackageDescription ( GenericPackageDescription )
 import qualified Distribution.PackageDescription as Cabal
 import qualified Distribution.Text
@@ -40,13 +48,34 @@ import qualified Hackage.Security.Util.Path as HS
 import qualified Hackage.Security.Util.Pretty as HS
 import           Network.URI ( parseURI )
-import           Pantry.Archive
-import           Pantry.Casa
+import           Pantry.Archive ( getArchive )
+import           Pantry.Casa ( casaLookupKey )
 import qualified Pantry.SHA256 as SHA256
-import           Pantry.Storage hiding
-                   ( PackageName, TreeEntry, Version, findOrGenerateCabalFile )
-import           Pantry.Tree
-import           Pantry.Types hiding ( FileType (..) )
+import           Pantry.Storage
+                   ( CachedTree (..), TreeId, BlobId, clearHackageRevisions
+                   , countHackageCabals, getBlobKey, loadBlobById, loadBlobBySHA
+                   , loadHackagePackageVersion, loadHackagePackageVersions
+                   , loadHackageTarballInfo, loadHackageTree, loadHackageTreeKey
+                   , loadLatestCacheUpdate, loadPreferredVersion
+                   , sinkHackagePackageNames, storeBlob, storeCacheUpdate
+                   , storeHackageRevision, storeHackageTarballInfo
+                   , storeHackageTree, storePreferredVersion, storeTree
+                   , unCachedTree, withStorage
+                   )
+import           Pantry.Tree ( rawParseGPD )
+import           Pantry.Types
+                   ( ArchiveLocation (..), BlobKey (..), BuildFile (..)
+                   , CabalFileInfo (..), FileSize (..), FuzzyResults (..)
+                   , HackageSecurityConfig (..), HasPantryConfig (..)
+                   , Mismatch (..), Package (..), PackageCabal (..)
+                   , PackageIdentifier (..), PackageIdentifierRevision (..)
+                   , PackageIndexConfig (..), PackageName, PantryConfig (..)
+                   , PantryException (..), RawArchive (..)
+                   , RawPackageLocationImmutable (..), RawPackageMetadata (..)
+                   , Revision, SHA256, Storage (..), TreeEntry (..), TreeKey
+                   , Version, cabalFileName, packageNameString, parsePackageName
+                   , unSafeFilePath
+                   )
 import           Path
                    ( Abs, Dir, File, Path, Rel, (</>), parseRelDir, parseRelFile
                    , toFilePath
@@ -55,7 +84,7 @@ import qualified RIO.ByteString as B
 import qualified RIO.ByteString.Lazy as BL
 import qualified RIO.Map as Map
-import           RIO.Process
+import           RIO.Process ( HasProcessContext )
 import qualified RIO.Text as T
 #if !MIN_VERSION_rio(0,1,16)
 -- Now provided by RIO from the rio package. Resolvers before lts-15.16
@@ -102,8 +131,8 @@ -- sanity. See the return value to find out if it happened.
 --
 -- @since 0.1.0.0
-updateHackageIndex
-  :: (HasPantryConfig env, HasLogFunc env)
+updateHackageIndex ::
+     (HasPantryConfig env, HasLogFunc env)
   => Maybe Utf8Builder -- ^ reason for updating, if any
   -> RIO env DidUpdateOccur
 updateHackageIndex = updateHackageIndexInternal False
@@ -113,15 +142,15 @@ -- sure the database is in sync with the locally downloaded tarball
 --
 -- @since 0.1.0.0
-forceUpdateHackageIndex
-  :: (HasPantryConfig env, HasLogFunc env)
+forceUpdateHackageIndex ::
+     (HasPantryConfig env, HasLogFunc env)
   => Maybe Utf8Builder
   -> RIO env DidUpdateOccur
 forceUpdateHackageIndex = updateHackageIndexInternal True
 
 
-updateHackageIndexInternal
-  :: (HasPantryConfig env, HasLogFunc env)
+updateHackageIndexInternal ::
+     (HasPantryConfig env, HasLogFunc env)
   => Bool -- ^ Force the database update.
   -> Maybe Utf8Builder -- ^ reason for updating, if any
   -> RIO env DidUpdateOccur
src/Pantry/Repo.hs view
@@ -15,10 +15,21 @@   , withRepo
   ) where
 
-import           Database.Persist ( Entity (..) )
-import           Pantry.Archive
-import           Pantry.Storage hiding ( findOrGenerateCabalFile )
+import           Database.Persist.Class.PersistEntity ( Entity (..) )
+import           Pantry.Archive ( getArchivePackage )
+import           Pantry.Storage
+                   ( getTreeForKey, loadPackageById, loadRepoCache
+                   , storeRepoCache, withStorage
+                   )
 import           Pantry.Types
+                   ( AggregateRepo (..), ArchiveLocation (..), HasPantryConfig
+                   , Package (..), PackageMetadata (..), PantryException (..)
+                   , RawArchive (..), RawPackageLocationImmutable (..)
+                   , RawPackageMetadata (..), RelFilePath (..), Repo (..)
+                   , RepoType (..), ResolvedPath (..), SimpleRepo (..)
+                   , TreeKey (..), arToSimpleRepo, rToSimpleRepo
+                   , toAggregateRepos, toRawPM
+                   )
 import           Path.IO ( resolveFile' )
 import           RIO
 import           RIO.ByteString ( isInfixOf )
@@ -27,6 +38,10 @@ import           RIO.FilePath ( (</>) )
 import qualified RIO.Map as Map
 import           RIO.Process
+                   ( ExitCodeException (..), HasProcessContext, proc
+                   , readProcess, readProcess_, withModifyEnvVars
+                   , withWorkingDir
+                   )
 import qualified RIO.Text as T
 import           System.Console.ANSI ( hSupportsANSIWithoutEmulation )
 import           System.IsWindows ( osIsWindows )
src/Pantry/SQLite.hs view
@@ -10,9 +10,14 @@ 
 import           Control.Concurrent.Companion
                    ( Companion, onCompanionDone, withCompanion )
+import           Database.Persist.Sql ( runSqlConn )
+import           Database.Persist.Sql.Migration
+                   ( Migration, runMigrationSilent )
 import           Database.Persist.Sqlite
-import           Pantry.Types
-                   ( PantryException (MigrationFailure), Storage (..) )
+                   ( extraPragmas, fkEnabled, mkSqliteConnectionInfo
+                   , walEnabled, withSqliteConnInfo
+                   )
+import           Pantry.Types ( PantryException (..), Storage (..) )
 import           Path ( Abs, File, Path, parent, toFilePath )
 import           Path.IO ( ensureDir )
 import           RIO hiding ( FilePath )
src/Pantry/Storage.hs view
@@ -102,11 +102,26 @@   , unCachedTree
   ) where
 
-import           Conduit
+import           Conduit ( ConduitT, (.|), concatMapMC, mapC, runConduit )
 import           Data.Acquire ( with )
-import           Database.Persist
-import           Database.Persist.Sqlite
+import           Database.Persist ( ( !=.), (=.), (==.), (>.) )
+import           Database.Persist.Class.PersistEntity
+                   ( Entity (..), EntityField, Filter (..), Key, SelectOpt (..)
+                   , Unique
+                   )
+import           Database.Persist.Class.PersistField ( PersistField (..) )
+import           Database.Persist.Class.PersistQuery
+                   ( count, deleteWhere, selectFirst, selectKeysList, selectList
+                   , selectSource, selectSourceRes, updateWhere
+                   )
+import           Database.Persist.Class.PersistStore
+                   ( get, getJust, insert, insert_, update,  )
+import           Database.Persist.Class.PersistUnique ( getBy, insertBy )
+import           Database.Persist.Sql ( Single (..), rawExecute, rawSql )
+import           Database.Persist.SqlBackend ( SqlBackend )
 import           Database.Persist.TH
+                   ( mkMigrate, mkPersist, persistLowerCase, share, sqlSettings
+                   )
 import           Pantry.HPack ( hpack, hpackVersion )
 import qualified Pantry.SHA256 as SHA256
 import qualified Pantry.SQLite as SQLite
@@ -133,7 +148,7 @@ import qualified RIO.List as List
 import qualified RIO.Map as Map
 import           RIO.Orphans ( HasResourceMap )
-import           RIO.Process
+import           RIO.Process ( HasProcessContext )
 import qualified RIO.Text as T
 import           RIO.Time ( UTCTime, getCurrentTime )
 
src/Pantry/Tree.hs view
@@ -8,10 +8,14 @@ 
 import           Distribution.PackageDescription ( GenericPackageDescription )
 import           Distribution.PackageDescription.Parsec
+                   ( parseGenericPackageDescription, runParseResult )
 import           Distribution.Parsec ( PWarning (..) )
-import           Pantry.Storage hiding
-                   ( Tree, TreeEntry, findOrGenerateCabalFile )
+import           Pantry.Storage ( loadBlob, withStorage )
 import           Pantry.Types
+                   ( FileType (..), HasPantryConfig, PantryException (..)
+                   , RawPackageLocationImmutable, Tree (..), TreeEntry (..)
+                   , unSafeFilePath
+                   )
 import           Path ( Abs, Dir, File, Path, toFilePath )
 import           RIO
 import qualified RIO.ByteString as B
test/Pantry/BuildPlanSpec.hs view
@@ -3,10 +3,8 @@ 
 module Pantry.BuildPlanSpec where
 
-import           Control.Monad.Catch ( MonadThrow )
 import           Data.Aeson.WarningParser ( WithJSONWarnings(..) )
 import qualified Data.ByteString.Char8 as S8
-import           Data.List.NonEmpty ( NonEmpty )
 import           Data.Yaml ( decodeThrow )
 import           Pantry
 import           RIO
test/Pantry/CabalSpec.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE LambdaCase        #-}
 {-# LANGUAGE OverloadedStrings #-}
 
 module Pantry.CabalSpec
@@ -32,8 +33,7 @@         version2 = mkVersion [0, 2]
         version3 = mkVersion [0, 3]
         size = FileSize 597
-    go `shouldThrow'` \e ->
-      case e of
+    go `shouldThrow'` \case
         MismatchedPackageMetadata rpli' rpm _tree ident ->
           rpli == rpli' &&
           rpm == RawPackageMetadata
@@ -64,8 +64,7 @@         go = loadCabalFileRawImmutable rpli
         acmeMissiles = mkPackageName "acme-missiles"
         version2 = mkVersion [0, 2]
-    go `shouldThrow'` \e ->
-      case e of
+    go `shouldThrow'` \case
         MismatchedPackageMetadata rpli' rpm' _treeKey ident ->
           rpli == rpli' &&
           rpm == rpm' &&
@@ -94,8 +93,7 @@         yesodAuth = mkPackageName "yesod-auth"
         version = mkVersion [1, 6, 4, 1]
         badVersion = mkVersion [1, 6, 4, 0]
-    go `shouldThrow'` \e ->
-      case e of
+    go `shouldThrow'` \case
         MismatchedPackageMetadata rpli' rpm' _treeKey ident ->
           rpli == rpli' &&
           rpm == rpm' &&
test/Pantry/CasaSpec.hs view
@@ -66,11 +66,11 @@ 
 unliftio_0_2_12 :: Args
 unliftio_0_2_12 =
-  let cabalHash = (either
+  let cabalHash = either
                      (error . show)
                      id
                      (fromHexText
-                        "b089fbc2ff2628a963c2c4b12143f2020874e3e5144ffd6c62b25639a0ca1483"))
+                        "b089fbc2ff2628a963c2c4b12143f2020874e3e5144ffd6c62b25639a0ca1483")
       cabalLen = FileSize 3325
       cabalFileHash =
         CFIHash
test/Pantry/HackageSpec.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE LambdaCase        #-}
 {-# LANGUAGE OverloadedStrings #-}
 
 module Pantry.HackageSpec
@@ -16,8 +17,7 @@   it "fuzzy lookup kicks in" $ do
     let pir = PackageIdentifierRevision "thisisnot-tobe-foundon-hackage-please" (mkVersion [1..3]) CFILatest
     runPantryApp (loadPackageRaw (RPLIHackage pir Nothing))
-      `shouldThrow` \e ->
-        case e of
+      `shouldThrow` \case
           UnknownHackagePackage pir' _  -> pir == pir'
           _ -> False
   -- Flaky test, can be broken by new packages on Hackage.
test/Pantry/TypesSpec.hs view
@@ -34,7 +34,7 @@   unless result $ throwString "Hedgehog property failed" :: IO ()
 
 genBlobKey :: Gen BlobKey
-genBlobKey = BlobKey <$> genSha256 <*> (FileSize <$> (Gen.word (Range.linear 1 10000)))
+genBlobKey = BlobKey <$> genSha256 <*> (FileSize <$> Gen.word (Range.linear 1 10000))
 
 genSha256 :: Gen SHA256
 genSha256 = SHA256.hashBytes <$> Gen.bytes (Range.linear 1 500)
@@ -109,13 +109,13 @@       RawSnapshotLayer{..} <- parseSl $
         "name: 'test'\n" ++
         "resolver: lts-2.10\n"
-      rslParent `shouldBe` (RSLSynonym $ LTS 2 10)
+      rslParent `shouldBe` RSLSynonym (LTS 2 10)
 
     it "parses snapshot using 'snapshot'" $ do
       RawSnapshotLayer{..} <- parseSl $
         "name: 'test'\n" ++
         "snapshot: lts-2.10\n"
-      rslParent `shouldBe` (RSLSynonym $ LTS 2 10)
+      rslParent `shouldBe` RSLSynonym (LTS 2 10)
 
     it "throws if both 'resolver' and 'snapshot' are present" $ do
       let go = parseSl $
@@ -190,7 +190,7 @@         Right sha' -> pure sha'
         _ -> fail "parseHackagetext: failed decoding the sha256"
       let Right (pkgIdentifier, blobKey) = parseHackageText txt
-      blobKey `shouldBe` (BlobKey sha (FileSize 5058))
+      blobKey `shouldBe` BlobKey sha (FileSize 5058)
       pkgIdentifier `shouldBe`
           PackageIdentifier
               (mkPackageName "persistent")