mirror of
https://github.com/commercialhaskell/stackage-server.git
synced 2026-01-26 19:01:56 +01:00
Merge pull request #283 from lehins/external-cabal-files
External cabal files
This commit is contained in:
commit
eb46df2050
@ -39,6 +39,7 @@ dependencies:
|
|||||||
- persistent-template
|
- persistent-template
|
||||||
- resourcet
|
- resourcet
|
||||||
- rio
|
- rio
|
||||||
|
- semialign
|
||||||
- shakespeare
|
- shakespeare
|
||||||
- tar-conduit
|
- tar-conduit
|
||||||
- template-haskell
|
- template-haskell
|
||||||
|
|||||||
@ -11,7 +11,6 @@ module Data.WebsiteContent
|
|||||||
|
|
||||||
import ClassyPrelude.Yesod
|
import ClassyPrelude.Yesod
|
||||||
import CMarkGFM
|
import CMarkGFM
|
||||||
import Data.Aeson (withObject)
|
|
||||||
import Data.GhcLinks
|
import Data.GhcLinks
|
||||||
import Data.Yaml
|
import Data.Yaml
|
||||||
import System.FilePath (takeFileName)
|
import System.FilePath (takeFileName)
|
||||||
|
|||||||
@ -86,4 +86,5 @@ getBlogFeedR = do
|
|||||||
, feedEntryTitle = postTitle post
|
, feedEntryTitle = postTitle post
|
||||||
, feedEntryContent = postBody post
|
, feedEntryContent = postBody post
|
||||||
, feedEntryEnclosure = Nothing
|
, feedEntryEnclosure = Nothing
|
||||||
|
, feedEntryCategories = []
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,6 @@ import Import
|
|||||||
import Data.GhcLinks
|
import Data.GhcLinks
|
||||||
import Yesod.GitRepo (grContent)
|
import Yesod.GitRepo (grContent)
|
||||||
import Stackage.Database
|
import Stackage.Database
|
||||||
import Stackage.Database.Types (ghcVersion)
|
|
||||||
|
|
||||||
getDownloadR :: Handler Html
|
getDownloadR :: Handler Html
|
||||||
getDownloadR = track "Hoogle.Download.getDownloadR" $
|
getDownloadR = track "Hoogle.Download.getDownloadR" $
|
||||||
|
|||||||
@ -12,7 +12,6 @@ import RIO.Time (getCurrentTime)
|
|||||||
import Stackage.Database
|
import Stackage.Database
|
||||||
import Stackage.Snapshot.Diff
|
import Stackage.Snapshot.Diff
|
||||||
import Text.Blaze (text)
|
import Text.Blaze (text)
|
||||||
import Yesod.Core.Handler (lookupGetParam)
|
|
||||||
|
|
||||||
getFeedR :: Handler TypedContent
|
getFeedR :: Handler TypedContent
|
||||||
getFeedR = track "Handler.Feed.getBranchFeedR" $ getBranchFeed Nothing
|
getFeedR = track "Handler.Feed.getBranchFeedR" $ getBranchFeed Nothing
|
||||||
@ -38,6 +37,7 @@ mkFeed mBranch snaps = do
|
|||||||
, feedEntryTitle = snapshotTitle snap
|
, feedEntryTitle = snapshotTitle snap
|
||||||
, feedEntryContent = content
|
, feedEntryContent = content
|
||||||
, feedEntryEnclosure = Nothing
|
, feedEntryEnclosure = Nothing
|
||||||
|
, feedEntryCategories = []
|
||||||
}
|
}
|
||||||
updated <-
|
updated <-
|
||||||
case entries of
|
case entries of
|
||||||
|
|||||||
@ -7,7 +7,6 @@ module Handler.Haddock
|
|||||||
import Import
|
import Import
|
||||||
import qualified Data.Text as T (takeEnd)
|
import qualified Data.Text as T (takeEnd)
|
||||||
import Stackage.Database
|
import Stackage.Database
|
||||||
import Stackage.Database.Types (haddockBucketName)
|
|
||||||
|
|
||||||
makeURL :: SnapName -> [Text] -> Text
|
makeURL :: SnapName -> [Text] -> Text
|
||||||
makeURL snapName rest = concat
|
makeURL snapName rest = concat
|
||||||
|
|||||||
@ -8,7 +8,6 @@
|
|||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
module Handler.Hoogle where
|
module Handler.Hoogle where
|
||||||
|
|
||||||
import Control.DeepSeq (NFData(..))
|
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import Data.Text.Read (decimal)
|
import Data.Text.Read (decimal)
|
||||||
import qualified Hoogle
|
import qualified Hoogle
|
||||||
|
|||||||
@ -8,7 +8,7 @@ module Handler.MirrorStatus
|
|||||||
import Import
|
import Import
|
||||||
import Control.AutoUpdate
|
import Control.AutoUpdate
|
||||||
import Network.HTTP.Simple
|
import Network.HTTP.Simple
|
||||||
import RIO.Time (parseTimeM, diffUTCTime, addUTCTime, getCurrentTime)
|
import RIO.Time (diffUTCTime, addUTCTime, getCurrentTime)
|
||||||
import Text.XML.Stream.Parse
|
import Text.XML.Stream.Parse
|
||||||
import Data.XML.Types (Event (EventContent), Content (ContentText))
|
import Data.XML.Types (Event (EventContent), Content (ContentText))
|
||||||
import qualified Prelude
|
import qualified Prelude
|
||||||
|
|||||||
@ -27,8 +27,6 @@ import Graphics.Badge.Barrier
|
|||||||
import Import
|
import Import
|
||||||
import Stackage.Database
|
import Stackage.Database
|
||||||
import Stackage.Database.PackageInfo (PackageInfo(..), Identifier(..), renderEmail)
|
import Stackage.Database.PackageInfo (PackageInfo(..), Identifier(..), renderEmail)
|
||||||
import Stackage.Database.Types (HackageCabalInfo(..), LatestInfo(..),
|
|
||||||
ModuleListingInfo(..))
|
|
||||||
import qualified Text.Blaze.Html.Renderer.Text as LT
|
import qualified Text.Blaze.Html.Renderer.Text as LT
|
||||||
import Yesod.GitRepo
|
import Yesod.GitRepo
|
||||||
|
|
||||||
@ -75,9 +73,7 @@ checkSpam pname inner = do
|
|||||||
$(widgetFile "spam-package")
|
$(widgetFile "spam-package")
|
||||||
else inner
|
else inner
|
||||||
|
|
||||||
packagePage :: Maybe SnapshotPackageInfo
|
packagePage :: Maybe SnapshotPackageInfo -> PackageNameP -> Handler Html
|
||||||
-> PackageNameP
|
|
||||||
-> Handler Html
|
|
||||||
packagePage mspi pname =
|
packagePage mspi pname =
|
||||||
track "Handler.Package.packagePage" $
|
track "Handler.Package.packagePage" $
|
||||||
checkSpam pname $
|
checkSpam pname $
|
||||||
@ -88,8 +84,6 @@ packagePage mspi pname =
|
|||||||
Just spi -> handlePackage $ Right spi
|
Just spi -> handlePackage $ Right spi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
handlePackage :: Either HackageCabalInfo SnapshotPackageInfo -> Handler Html
|
handlePackage :: Either HackageCabalInfo SnapshotPackageInfo -> Handler Html
|
||||||
handlePackage epi = do
|
handlePackage epi = do
|
||||||
(isDeprecated, inFavourOf) <- getDeprecated pname
|
(isDeprecated, inFavourOf) <- getDeprecated pname
|
||||||
|
|||||||
@ -9,9 +9,7 @@ module Handler.PackageDeps
|
|||||||
|
|
||||||
import Handler.StackageSdist (pnvToSnapshotPackageInfo)
|
import Handler.StackageSdist (pnvToSnapshotPackageInfo)
|
||||||
import Import
|
import Import
|
||||||
import Types (PackageVersionRev(..))
|
|
||||||
import Stackage.Database
|
import Stackage.Database
|
||||||
import Stackage.Database.Types (SnapshotPackageInfo(..))
|
|
||||||
|
|
||||||
getPackageDepsR :: PackageNameP -> Handler Html
|
getPackageDepsR :: PackageNameP -> Handler Html
|
||||||
getPackageDepsR pname = do
|
getPackageDepsR pname = do
|
||||||
|
|||||||
@ -17,7 +17,6 @@ import Data.These
|
|||||||
import RIO.Time (FormatTime)
|
import RIO.Time (FormatTime)
|
||||||
import Import
|
import Import
|
||||||
import Stackage.Database
|
import Stackage.Database
|
||||||
import Stackage.Database.Types (PackageListingInfo(..), isLts)
|
|
||||||
import Stackage.Snapshot.Diff
|
import Stackage.Snapshot.Diff
|
||||||
|
|
||||||
getStackageHomeR :: SnapName -> Handler TypedContent
|
getStackageHomeR :: SnapName -> Handler TypedContent
|
||||||
|
|||||||
@ -6,7 +6,6 @@ module Handler.StackageSdist
|
|||||||
|
|
||||||
import Import
|
import Import
|
||||||
import Stackage.Database
|
import Stackage.Database
|
||||||
import Stackage.Database.Types (SnapshotPackageInfo(..))
|
|
||||||
import Handler.Package (packagePage)
|
import Handler.Package (packagePage)
|
||||||
|
|
||||||
handlePNVTarball :: PackageNameP -> VersionP -> Handler TypedContent
|
handlePNVTarball :: PackageNameP -> VersionP -> Handler TypedContent
|
||||||
|
|||||||
@ -3,19 +3,16 @@ module Import
|
|||||||
( module Import
|
( module Import
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Monad.Trans.Class (lift)
|
|
||||||
import ClassyPrelude.Yesod as Import hiding (getCurrentTime)
|
import ClassyPrelude.Yesod as Import hiding (getCurrentTime)
|
||||||
import Foundation as Import
|
import Foundation as Import
|
||||||
import Settings as Import
|
import Settings as Import
|
||||||
import Settings.StaticFiles as Import
|
import Settings.StaticFiles as Import
|
||||||
import Types as Import
|
import Types as Import
|
||||||
import Yesod.Auth as Import
|
import Yesod.Auth as Import
|
||||||
import Yesod.Core.Handler (getYesod)
|
|
||||||
import Data.WebsiteContent as Import (WebsiteContent (..))
|
import Data.WebsiteContent as Import (WebsiteContent (..))
|
||||||
import Data.Text.Read (decimal)
|
import Data.Text.Read (decimal)
|
||||||
import RIO.Time (diffUTCTime)
|
import RIO.Time (diffUTCTime)
|
||||||
--import qualified Prometheus as P
|
--import qualified Prometheus as P
|
||||||
import Stackage.Database (SnapName)
|
|
||||||
import Stackage.Database.Types (ModuleListingInfo(..))
|
import Stackage.Database.Types (ModuleListingInfo(..))
|
||||||
import Formatting (format)
|
import Formatting (format)
|
||||||
import Formatting.Time (diff)
|
import Formatting.Time (diff)
|
||||||
|
|||||||
@ -28,27 +28,25 @@ import Data.Streaming.Network (bindPortTCP)
|
|||||||
import Data.Yaml (decodeFileEither)
|
import Data.Yaml (decodeFileEither)
|
||||||
import Database.Persist
|
import Database.Persist
|
||||||
import Database.Persist.Postgresql
|
import Database.Persist.Postgresql
|
||||||
import Distribution.PackageDescription (GenericPackageDescription)
|
|
||||||
import qualified Hoogle
|
import qualified Hoogle
|
||||||
import Network.AWS hiding (Request, Response)
|
import Network.AWS hiding (Request, Response)
|
||||||
import Network.AWS.Data.Body (toBody)
|
|
||||||
import Network.AWS.Data.Text (toText)
|
import Network.AWS.Data.Text (toText)
|
||||||
import Network.AWS.S3
|
import Network.AWS.S3
|
||||||
import Network.HTTP.Client
|
import Network.HTTP.Client
|
||||||
import Network.HTTP.Client.Conduit (bodyReaderSource)
|
import Network.HTTP.Client.Conduit (bodyReaderSource)
|
||||||
import Network.HTTP.Simple (getResponseBody, httpJSONEither, parseRequest)
|
import Network.HTTP.Simple (getResponseBody, httpJSONEither)
|
||||||
import Network.HTTP.Types (status200, status404)
|
import Network.HTTP.Types (status200, status404)
|
||||||
import Pantry (CabalFileInfo(..), DidUpdateOccur(..),
|
import Pantry (CabalFileInfo(..), DidUpdateOccur(..),
|
||||||
HpackExecutable(HpackBundled), PackageIdentifierRevision(..),
|
HpackExecutable(HpackBundled), PackageIdentifierRevision(..),
|
||||||
defaultHackageSecurityConfig)
|
defaultHackageSecurityConfig, defaultCasaRepoPrefix, defaultCasaMaxPerRequest)
|
||||||
import Pantry.Internal.Stackage (HackageCabalId, HackageTarballResult(..),
|
import Pantry.Internal.Stackage (HackageTarballResult(..),
|
||||||
PantryConfig(..), Storage(..),
|
PantryConfig(..), Storage(..),
|
||||||
forceUpdateHackageIndex, getHackageTarball,
|
forceUpdateHackageIndex, getHackageTarball,
|
||||||
getTreeForKey, loadBlobById, packageTreeKey,
|
packageTreeKey)
|
||||||
treeCabal)
|
|
||||||
import Path (parseAbsDir, toFilePath)
|
import Path (parseAbsDir, toFilePath)
|
||||||
import RIO
|
import RIO
|
||||||
import RIO.Directory
|
import RIO.Directory
|
||||||
|
import RIO.File
|
||||||
import RIO.FilePath
|
import RIO.FilePath
|
||||||
import RIO.List as L
|
import RIO.List as L
|
||||||
import qualified RIO.Map as Map
|
import qualified RIO.Map as Map
|
||||||
@ -118,7 +116,6 @@ newHoogleLocker env man = mkSingleRun hoogleLocker
|
|||||||
hoogleLocker name =
|
hoogleLocker name =
|
||||||
runRIO env $ do
|
runRIO env $ do
|
||||||
let fp = T.unpack $ hoogleKey name
|
let fp = T.unpack $ hoogleKey name
|
||||||
fptmp = fp <.> "tmp"
|
|
||||||
exists <- doesFileExist fp
|
exists <- doesFileExist fp
|
||||||
if exists
|
if exists
|
||||||
then return $ Just fp
|
then return $ Just fp
|
||||||
@ -129,24 +126,17 @@ newHoogleLocker env man = mkSingleRun hoogleLocker
|
|||||||
case responseStatus res of
|
case responseStatus res of
|
||||||
status
|
status
|
||||||
| status == status200 -> do
|
| status == status200 -> do
|
||||||
createDirectoryIfMissing True $ takeDirectory fptmp
|
createDirectoryIfMissing True $ takeDirectory fp
|
||||||
-- TODO: https://github.com/commercialhaskell/rio/issues/160
|
withBinaryFileDurableAtomic fp WriteMode $ \h ->
|
||||||
-- withBinaryFileDurableAtomic fp WriteMode $ \h ->
|
runConduitRes $
|
||||||
-- runConduitRes $
|
|
||||||
-- bodyReaderSource (responseBody res) .| ungzip .|
|
|
||||||
-- sinkHandle h
|
|
||||||
runConduitRes $
|
|
||||||
bodyReaderSource (responseBody res) .| ungzip .|
|
bodyReaderSource (responseBody res) .| ungzip .|
|
||||||
sinkFile fptmp
|
sinkHandle h
|
||||||
renamePath fptmp fp
|
|
||||||
return $ Just fp
|
return $ Just fp
|
||||||
| status == status404 -> do
|
| status == status404 -> do
|
||||||
logDebug $ "NotFound: " <> display (hoogleUrl name)
|
logDebug $ "NotFound: " <> display (hoogleUrl name)
|
||||||
return Nothing
|
return Nothing
|
||||||
| otherwise -> do
|
| otherwise -> do
|
||||||
body <- liftIO $ brConsume $ responseBody res
|
body <- liftIO $ brConsume $ responseBody res
|
||||||
-- TODO: ideally only consume the body when log level set to
|
|
||||||
-- LevelDebug, will require a way to get LogLevel from LogFunc
|
|
||||||
mapM_ (logDebug . displayBytesUtf8) body
|
mapM_ (logDebug . displayBytesUtf8) body
|
||||||
return Nothing
|
return Nothing
|
||||||
|
|
||||||
@ -192,6 +182,8 @@ stackageServerCron StackageCronOptions {..} = do
|
|||||||
, pcParsedCabalFilesRawImmutable = cabalImmutable
|
, pcParsedCabalFilesRawImmutable = cabalImmutable
|
||||||
, pcParsedCabalFilesMutable = cabalMutable
|
, pcParsedCabalFilesMutable = cabalMutable
|
||||||
, pcConnectionCount = connectionCount
|
, pcConnectionCount = connectionCount
|
||||||
|
, pcCasaRepoPrefix = defaultCasaRepoPrefix
|
||||||
|
, pcCasaMaxPerRequest = defaultCasaMaxPerRequest
|
||||||
}
|
}
|
||||||
stackage =
|
stackage =
|
||||||
StackageCron
|
StackageCron
|
||||||
@ -239,31 +231,64 @@ makeCorePackageGetters ::
|
|||||||
makeCorePackageGetters = do
|
makeCorePackageGetters = do
|
||||||
rootDir <- scStackageRoot <$> ask
|
rootDir <- scStackageRoot <$> ask
|
||||||
contentDir <- getStackageContentDir rootDir
|
contentDir <- getStackageContentDir rootDir
|
||||||
|
coreCabalFiles <- getCoreCabalFiles rootDir
|
||||||
liftIO (decodeFileEither (contentDir </> "stack" </> "global-hints.yaml")) >>= \case
|
liftIO (decodeFileEither (contentDir </> "stack" </> "global-hints.yaml")) >>= \case
|
||||||
Right (hints :: Map CompilerP (Map PackageNameP VersionP)) ->
|
Right (hints :: Map CompilerP (Map PackageNameP VersionP)) ->
|
||||||
Map.traverseWithKey
|
Map.traverseWithKey
|
||||||
(\compiler ->
|
(\compiler ->
|
||||||
fmap Map.elems . Map.traverseMaybeWithKey (makeCorePackageGetter compiler))
|
fmap Map.elems .
|
||||||
|
Map.traverseMaybeWithKey (makeCorePackageGetter compiler coreCabalFiles))
|
||||||
hints
|
hints
|
||||||
Left exc -> do
|
Left exc -> do
|
||||||
logError $
|
logError $
|
||||||
"Error parsing 'global-hints.yaml' file: " <> fromString (displayException exc)
|
"Error parsing 'global-hints.yaml' file: " <> fromString (displayException exc)
|
||||||
return mempty
|
return mempty
|
||||||
|
|
||||||
|
getCoreCabalFiles ::
|
||||||
|
FilePath
|
||||||
|
-> RIO StackageCron (Map PackageIdentifierP (GenericPackageDescription, CabalFileIds))
|
||||||
|
getCoreCabalFiles rootDir = do
|
||||||
|
coreCabalFilesDir <- getCoreCabalFilesDir rootDir
|
||||||
|
cabalFileNames <- getDirectoryContents coreCabalFilesDir
|
||||||
|
cabalFiles <-
|
||||||
|
forM (filter (isExtensionOf ".cabal") cabalFileNames) $ \cabalFileName ->
|
||||||
|
let pidTxt = T.pack (dropExtension (takeFileName cabalFileName))
|
||||||
|
in case fromPathPiece pidTxt of
|
||||||
|
Nothing -> do
|
||||||
|
logError $ "Invalid package identifier: " <> fromString cabalFileName
|
||||||
|
pure Nothing
|
||||||
|
Just pid -> do
|
||||||
|
cabalBlob <- readFileBinary (coreCabalFilesDir </> cabalFileName)
|
||||||
|
mCabalInfo <- run $ addCabalFile pid cabalBlob
|
||||||
|
pure ((,) pid <$> mCabalInfo)
|
||||||
|
pure $ Map.fromList $ catMaybes cabalFiles
|
||||||
|
|
||||||
-- | Core package info rarely changes between the snapshots, therefore it would be wasteful to
|
-- | Core package info rarely changes between the snapshots, therefore it would be wasteful to
|
||||||
-- load, parse and update all packages from gloabl-hints for each snapshot, instead we produce
|
-- load, parse and update all packages from gloabl-hints for each snapshot, instead we produce
|
||||||
-- a memoized version that will do it once initiall and then return information aboat a
|
-- a memoized version that will do it once initiall and then return information aboat a
|
||||||
-- package on subsequent invocations.
|
-- package on subsequent invocations.
|
||||||
makeCorePackageGetter ::
|
makeCorePackageGetter ::
|
||||||
CompilerP -> PackageNameP -> VersionP -> RIO StackageCron (Maybe CorePackageGetter)
|
CompilerP
|
||||||
makeCorePackageGetter _compiler pname ver =
|
-> Map PackageIdentifierP (GenericPackageDescription, CabalFileIds)
|
||||||
|
-> PackageNameP
|
||||||
|
-> VersionP
|
||||||
|
-> RIO StackageCron (Maybe CorePackageGetter)
|
||||||
|
makeCorePackageGetter _compiler fallbackCabalFileMap pname ver =
|
||||||
run (getHackageCabalByRev0 pid) >>= \case
|
run (getHackageCabalByRev0 pid) >>= \case
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
logWarn $
|
logWarn $
|
||||||
"Core package from global-hints: '" <> display pid <> "' was not found in pantry."
|
"Core package from global-hints: '" <> display pid <> "' was not found in pantry."
|
||||||
pure Nothing
|
forM (Map.lookup pid fallbackCabalFileMap) $ \(gpd, cabalFileIds) -> do
|
||||||
|
logInfo $
|
||||||
|
"Falling back on '" <> display pid <>
|
||||||
|
".cabal' file from the commercialhaskell/core-cabal-files repo"
|
||||||
|
pure $ pure (Left cabalFileIds, Nothing, pid, gpd)
|
||||||
Just (hackageCabalId, blobId, _) -> do
|
Just (hackageCabalId, blobId, _) -> do
|
||||||
pkgInfoRef <- newIORef Nothing -- use for caching of pkgInfo
|
pkgInfoRef <- newIORef Nothing -- use for caching of pkgInfo
|
||||||
|
let getCabalFileIdsTree gpd =
|
||||||
|
\case
|
||||||
|
Just tree -> pure $ Right tree
|
||||||
|
Nothing -> Left <$> getCabalFileIds blobId gpd
|
||||||
let getMemoPackageInfo =
|
let getMemoPackageInfo =
|
||||||
readIORef pkgInfoRef >>= \case
|
readIORef pkgInfoRef >>= \case
|
||||||
Just pkgInfo -> return pkgInfo
|
Just pkgInfo -> return pkgInfo
|
||||||
@ -273,17 +298,21 @@ makeCorePackageGetter _compiler pname ver =
|
|||||||
htr <- getHackageTarball pir Nothing
|
htr <- getHackageTarball pir Nothing
|
||||||
case htrFreshPackageInfo htr of
|
case htrFreshPackageInfo htr of
|
||||||
Just (gpd, treeId) -> do
|
Just (gpd, treeId) -> do
|
||||||
mTree <- run $ getEntity treeId
|
eTree <-
|
||||||
let pkgInfo = (mTree, Just hackageCabalId, pid, gpd)
|
run $ do
|
||||||
|
mTree <- getEntity treeId
|
||||||
|
getCabalFileIdsTree gpd mTree
|
||||||
|
let pkgInfo = (eTree, Just hackageCabalId, pid, gpd)
|
||||||
gpd `deepseq` writeIORef pkgInfoRef $ Just pkgInfo
|
gpd `deepseq` writeIORef pkgInfoRef $ Just pkgInfo
|
||||||
pure pkgInfo
|
pure pkgInfo
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
(cabalBlob, mTree) <-
|
(gpd, eCabalTree) <-
|
||||||
run
|
run $ do
|
||||||
((,) <$> loadBlobById blobId <*>
|
cabalBlob <- loadBlobById blobId
|
||||||
getTreeForKey (packageTreeKey (htrPackage htr)))
|
let gpd = parseCabalBlob cabalBlob
|
||||||
let gpd = parseCabalBlob cabalBlob
|
mTree <- getTreeForKey (packageTreeKey (htrPackage htr))
|
||||||
pkgInfo = (mTree, Just hackageCabalId, pid, gpd)
|
(,) gpd <$> getCabalFileIdsTree gpd mTree
|
||||||
|
let pkgInfo = (eCabalTree, Just hackageCabalId, pid, gpd)
|
||||||
gpd `deepseq` writeIORef pkgInfoRef $ Just pkgInfo
|
gpd `deepseq` writeIORef pkgInfoRef $ Just pkgInfo
|
||||||
pure pkgInfo
|
pure pkgInfo
|
||||||
pure $ Just getMemoPackageInfo
|
pure $ Just getMemoPackageInfo
|
||||||
@ -325,11 +354,12 @@ addPantryPackage sid compiler isHidden flags (PantryPackage pc treeKey) = do
|
|||||||
, tid /= treeId -> do
|
, tid /= treeId -> do
|
||||||
lift $ logError $ "Pantry Tree Key mismatch for: " <> display pc
|
lift $ logError $ "Pantry Tree Key mismatch for: " <> display pc
|
||||||
pure False
|
pure False
|
||||||
mTree@(Just (Entity _ Tree {treeCabal}))
|
Just tree@(Entity _ Tree {treeCabal})
|
||||||
| Just treeCabal' <- treeCabal -> do
|
| Just treeCabal' <- treeCabal -> do
|
||||||
gpd <- getCachedGPD treeCabal' mgpd
|
gpd <- getCachedGPD treeCabal' mgpd
|
||||||
let mhcid = Just hcid
|
let mhcid = Just hcid
|
||||||
addSnapshotPackage sid compiler Hackage mTree mhcid isHidden flags pid gpd
|
eTree = Right tree
|
||||||
|
addSnapshotPackage sid compiler Hackage eTree mhcid isHidden flags pid gpd
|
||||||
pure True
|
pure True
|
||||||
_ -> do
|
_ -> do
|
||||||
lift $ logError $ "Pantry is missing the source tree for " <> display pc
|
lift $ logError $ "Pantry is missing the source tree for " <> display pc
|
||||||
@ -363,9 +393,7 @@ checkForDocs snapshotId snapName = do
|
|||||||
runConduit $
|
runConduit $
|
||||||
AWS.paginate (req bucketName) .| concatMapC (^. lovrsContents) .|
|
AWS.paginate (req bucketName) .| concatMapC (^. lovrsContents) .|
|
||||||
mapC (\obj -> toText (obj ^. oKey)) .|
|
mapC (\obj -> toText (obj ^. oKey)) .|
|
||||||
concatMapC (T.stripSuffix ".html") .|
|
concatMapC (T.stripSuffix ".html" >=> T.stripPrefix prefix >=> pathToPackageModule) .|
|
||||||
concatMapC (T.stripPrefix prefix) .|
|
|
||||||
concatMapC pathToPackageModule .|
|
|
||||||
sinkList
|
sinkList
|
||||||
-- it is faster to download all modules in this snapshot, than process them with a conduit all
|
-- it is faster to download all modules in this snapshot, than process them with a conduit all
|
||||||
-- the way to the database.
|
-- the way to the database.
|
||||||
@ -429,19 +457,15 @@ sourceSnapshots = do
|
|||||||
"Error parsing snapshot file: " <> fromString fp <> "\n" <>
|
"Error parsing snapshot file: " <> fromString fp <> "\n" <>
|
||||||
fromString (displayException exc)
|
fromString (displayException exc)
|
||||||
return Nothing
|
return Nothing
|
||||||
lastGitFileUpdate gitDir fp >>= \case
|
mUpdatedOn <- lastGitFileUpdate gitDir fp
|
||||||
Left err -> do
|
forM mUpdatedOn $ \updatedOn -> do
|
||||||
logError $ "Error parsing git commit date: " <> fromString err
|
env <- lift ask
|
||||||
return Nothing
|
return $
|
||||||
Right updatedOn -> do
|
SnapshotFileInfo
|
||||||
env <- lift ask
|
{ sfiSnapName = snapName
|
||||||
return $
|
, sfiUpdatedOn = updatedOn
|
||||||
Just
|
, sfiSnapshotFileGetter = runRIO env (parseSnapshot updatedOn)
|
||||||
SnapshotFileInfo
|
}
|
||||||
{ sfiSnapName = snapName
|
|
||||||
, sfiUpdatedOn = updatedOn
|
|
||||||
, sfiSnapshotFileGetter = runRIO env (parseSnapshot updatedOn)
|
|
||||||
}
|
|
||||||
getLtsParser gitDir fp =
|
getLtsParser gitDir fp =
|
||||||
case mapM (BS8.readInt . BS8.pack) $ take 2 $ reverse (splitPath fp) of
|
case mapM (BS8.readInt . BS8.pack) $ take 2 $ reverse (splitPath fp) of
|
||||||
Just [(minor, ".yaml"), (major, "/")] ->
|
Just [(minor, ".yaml"), (major, "/")] ->
|
||||||
@ -496,7 +520,7 @@ decideOnSnapshotUpdate SnapshotFileInfo {sfiSnapName, sfiUpdatedOn, sfiSnapshotF
|
|||||||
_ -> return Nothing
|
_ -> return Nothing
|
||||||
|
|
||||||
type CorePackageGetter
|
type CorePackageGetter
|
||||||
= RIO StackageCron ( Maybe (Entity Tree)
|
= RIO StackageCron ( Either CabalFileIds (Entity Tree)
|
||||||
, Maybe HackageCabalId
|
, Maybe HackageCabalId
|
||||||
, PackageIdentifierP
|
, PackageIdentifierP
|
||||||
, GenericPackageDescription)
|
, GenericPackageDescription)
|
||||||
@ -598,8 +622,8 @@ updateSnapshot corePackageGetters snapshotId snapName updatedOn SnapshotFile {..
|
|||||||
]
|
]
|
||||||
Just compilerCorePackages ->
|
Just compilerCorePackages ->
|
||||||
forM_ compilerCorePackages $ \getCorePackageInfo -> do
|
forM_ compilerCorePackages $ \getCorePackageInfo -> do
|
||||||
(mTree, mhcid, pid, gpd) <- getCorePackageInfo
|
(eTree, mhcid, pid, gpd) <- getCorePackageInfo
|
||||||
run $ addSnapshotPackage snapshotId sfCompiler Core mTree mhcid False mempty pid gpd
|
run $ addSnapshotPackage snapshotId sfCompiler Core eTree mhcid False mempty pid gpd
|
||||||
return $ do
|
return $ do
|
||||||
checkForDocsSucceeded <-
|
checkForDocsSucceeded <-
|
||||||
tryAny (checkForDocs snapshotId snapName) >>= \case
|
tryAny (checkForDocs snapshotId snapName) >>= \case
|
||||||
@ -707,12 +731,8 @@ createHoogleDB snapshotId snapName =
|
|||||||
withResponseUnliftIO req {decompress = const True} man $ \res -> do
|
withResponseUnliftIO req {decompress = const True} man $ \res -> do
|
||||||
throwErrorStatusCodes req res
|
throwErrorStatusCodes req res
|
||||||
createDirectoryIfMissing True $ takeDirectory tarFP
|
createDirectoryIfMissing True $ takeDirectory tarFP
|
||||||
--withBinaryFileDurableAtomic tarFP WriteMode $ \tarHandle ->
|
withBinaryFileDurableAtomic tarFP WriteMode $ \tarHandle ->
|
||||||
--FIXME: https://github.com/commercialhaskell/rio/issues/160
|
|
||||||
let tmpTarFP = tarFP <.> "tmp"
|
|
||||||
withBinaryFile tmpTarFP WriteMode $ \tarHandle ->
|
|
||||||
runConduitRes $ bodyReaderSource (responseBody res) .| sinkHandle tarHandle
|
runConduitRes $ bodyReaderSource (responseBody res) .| sinkHandle tarHandle
|
||||||
renameFile tmpTarFP tarFP
|
|
||||||
void $ tryIO $ removeDirectoryRecursive bindir
|
void $ tryIO $ removeDirectoryRecursive bindir
|
||||||
void $ tryIO $ removeFile outname
|
void $ tryIO $ removeFile outname
|
||||||
createDirectoryIfMissing True bindir
|
createDirectoryIfMissing True bindir
|
||||||
|
|||||||
@ -4,6 +4,7 @@ module Stackage.Database.Github
|
|||||||
( cloneOrUpdate
|
( cloneOrUpdate
|
||||||
, lastGitFileUpdate
|
, lastGitFileUpdate
|
||||||
, getStackageContentDir
|
, getStackageContentDir
|
||||||
|
, getCoreCabalFilesDir
|
||||||
, GithubRepo(..)
|
, GithubRepo(..)
|
||||||
) where
|
) where
|
||||||
|
|
||||||
@ -14,6 +15,7 @@ import RIO.FilePath
|
|||||||
import RIO.Process
|
import RIO.Process
|
||||||
import RIO.Time
|
import RIO.Time
|
||||||
|
|
||||||
|
|
||||||
data GithubRepo = GithubRepo
|
data GithubRepo = GithubRepo
|
||||||
{ grAccount :: !String
|
{ grAccount :: !String
|
||||||
, grName :: !String
|
, grName :: !String
|
||||||
@ -32,17 +34,22 @@ lastGitFileUpdate ::
|
|||||||
(MonadReader env m, HasLogFunc env, HasProcessContext env, MonadUnliftIO m)
|
(MonadReader env m, HasLogFunc env, HasProcessContext env, MonadUnliftIO m)
|
||||||
=> FilePath -- ^ Root dir of the repository
|
=> FilePath -- ^ Root dir of the repository
|
||||||
-> FilePath -- ^ Relative path of the file
|
-> FilePath -- ^ Relative path of the file
|
||||||
-> m (Either String UTCTime)
|
-> m (Maybe UTCTime)
|
||||||
lastGitFileUpdate gitDir filePath = do
|
lastGitFileUpdate gitDir filePath = do
|
||||||
lastCommitTimestamps <- gitLog gitDir filePath ["-1", "--format=%cD"]
|
lastCommitTimestamps <- gitLog gitDir filePath ["-1", "--format=%cD"]
|
||||||
parseGitDate rfc822DateFormat lastCommitTimestamps
|
parseGitDate rfc822DateFormat lastCommitTimestamps
|
||||||
where
|
where
|
||||||
parseGitDate fmt dates =
|
parseGitDate fmt dates =
|
||||||
case listToMaybe $ LBS8.lines dates of
|
case listToMaybe $ LBS8.lines dates of
|
||||||
Nothing -> return $ Left "Git log is empty for the file"
|
Nothing -> do
|
||||||
Just lbsDate ->
|
logError "Git log is empty for the file"
|
||||||
mapLeft (displayException :: SomeException -> String) <$>
|
return Nothing
|
||||||
try (parseTimeM False defaultTimeLocale fmt (LBS8.unpack lbsDate))
|
Just lbsDate -> do
|
||||||
|
let parseDateTime = parseTimeM False defaultTimeLocale fmt (LBS8.unpack lbsDate)
|
||||||
|
catchAny (Just <$> liftIO parseDateTime) $ \exc -> do
|
||||||
|
logError $
|
||||||
|
"Error parsing git commit date: " <> fromString (displayException exc)
|
||||||
|
pure Nothing
|
||||||
|
|
||||||
-- | Clone a repository locally. In case when repository is already present sync it up with
|
-- | Clone a repository locally. In case when repository is already present sync it up with
|
||||||
-- remote. Returns the full path where repository was cloned into.
|
-- remote. Returns the full path where repository was cloned into.
|
||||||
@ -72,3 +79,11 @@ getStackageContentDir ::
|
|||||||
-> m FilePath
|
-> m FilePath
|
||||||
getStackageContentDir rootDir =
|
getStackageContentDir rootDir =
|
||||||
cloneOrUpdate rootDir (GithubRepo "commercialhaskell" "stackage-content")
|
cloneOrUpdate rootDir (GithubRepo "commercialhaskell" "stackage-content")
|
||||||
|
|
||||||
|
-- | Use backup location with cabal files, hackage doesn't have all of them.
|
||||||
|
getCoreCabalFilesDir ::
|
||||||
|
(MonadReader env m, HasLogFunc env, HasProcessContext env, MonadIO m)
|
||||||
|
=> FilePath
|
||||||
|
-> m FilePath
|
||||||
|
getCoreCabalFilesDir rootDir =
|
||||||
|
cloneOrUpdate rootDir (GithubRepo "commercialhaskell" "core-cabal-files")
|
||||||
|
|||||||
@ -38,9 +38,7 @@ hToHtml =
|
|||||||
H.dt (go x) ++ H.dd (go y)
|
H.dt (go x) ++ H.dd (go y)
|
||||||
go (DocCodeBlock x) = H.pre $ H.code $ go x
|
go (DocCodeBlock x) = H.pre $ H.code $ go x
|
||||||
go (DocHyperlink (Hyperlink url mlabel)) =
|
go (DocHyperlink (Hyperlink url mlabel)) =
|
||||||
H.a H.! A.href (H.toValue url) $ toHtml label
|
H.a H.! A.href (H.toValue url) $ maybe (toHtml url) (toHtml . go) mlabel
|
||||||
where
|
|
||||||
label = fromMaybe url mlabel
|
|
||||||
go (DocPic (Picture url mtitle)) =
|
go (DocPic (Picture url mtitle)) =
|
||||||
H.img H.! A.src (H.toValue url) H.! A.title (H.toValue $ fromMaybe mempty mtitle)
|
H.img H.! A.src (H.toValue url) H.! A.title (H.toValue $ fromMaybe mempty mtitle)
|
||||||
go (DocAName s) = H.div H.! A.id (H.toValue s) $ mempty
|
go (DocAName s) = H.div H.! A.id (H.toValue s) $ mempty
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{-# LANGUAGE ViewPatterns #-}
|
|
||||||
{-# LANGUAGE NoImplicitPrelude #-}
|
{-# LANGUAGE NoImplicitPrelude #-}
|
||||||
|
{-# LANGUAGE ViewPatterns #-}
|
||||||
module Stackage.Database.PackageInfo
|
module Stackage.Database.PackageInfo
|
||||||
( PackageInfo(..)
|
( PackageInfo(..)
|
||||||
, Identifier(..)
|
, Identifier(..)
|
||||||
@ -14,12 +14,11 @@ module Stackage.Database.PackageInfo
|
|||||||
) where
|
) where
|
||||||
|
|
||||||
import CMarkGFM
|
import CMarkGFM
|
||||||
import Data.Coerce
|
|
||||||
import Data.Char (isSpace)
|
import Data.Char (isSpace)
|
||||||
|
import Data.Coerce
|
||||||
import Data.Map.Merge.Strict as Map
|
import Data.Map.Merge.Strict as Map
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import Data.Text.Encoding (decodeUtf8With)
|
import qualified Data.Text.Encoding as T
|
||||||
import Data.Text.Encoding.Error (lenientDecode)
|
|
||||||
import Distribution.Compiler (CompilerFlavor(GHC))
|
import Distribution.Compiler (CompilerFlavor(GHC))
|
||||||
import Distribution.Package (Dependency(..))
|
import Distribution.Package (Dependency(..))
|
||||||
import Distribution.PackageDescription (CondTree(..), Condition(..),
|
import Distribution.PackageDescription (CondTree(..), Condition(..),
|
||||||
@ -28,28 +27,29 @@ import Distribution.PackageDescription (CondTree(..), Condition(..),
|
|||||||
GenericPackageDescription, author,
|
GenericPackageDescription, author,
|
||||||
condExecutables, condLibrary,
|
condExecutables, condLibrary,
|
||||||
description, genPackageFlags, homepage,
|
description, genPackageFlags, homepage,
|
||||||
license, maintainer,
|
license, maintainer, packageDescription,
|
||||||
packageDescription, synopsis)
|
synopsis)
|
||||||
import Distribution.PackageDescription.Parsec (parseGenericPackageDescription,
|
import Distribution.PackageDescription.Parsec (parseGenericPackageDescription,
|
||||||
runParseResult)
|
runParseResult)
|
||||||
import Distribution.Pretty (prettyShow)
|
import Distribution.Pretty (prettyShow)
|
||||||
import Distribution.System (Arch(X86_64), OS(Linux))
|
import Distribution.System (Arch(X86_64), OS(Linux))
|
||||||
import Distribution.Types.CondTree (CondBranch(..))
|
import Distribution.Types.CondTree (CondBranch(..))
|
||||||
import Distribution.Types.Library (exposedModules)
|
import Distribution.Types.Library (exposedModules)
|
||||||
|
import Distribution.Types.PackageDescription (PackageDescription(package))
|
||||||
import Distribution.Types.VersionRange (VersionRange, intersectVersionRanges,
|
import Distribution.Types.VersionRange (VersionRange, intersectVersionRanges,
|
||||||
normaliseVersionRange, withinRange)
|
normaliseVersionRange, withinRange)
|
||||||
import Distribution.Version (simplifyVersionRange)
|
import Distribution.Version (simplifyVersionRange)
|
||||||
import qualified Data.Text.Encoding as T
|
|
||||||
import RIO
|
import RIO
|
||||||
import qualified RIO.Map as Map
|
import qualified RIO.Map as Map
|
||||||
import qualified RIO.Map.Unchecked as Map (mapKeysMonotonic)
|
import qualified RIO.Map.Unchecked as Map (mapKeysMonotonic)
|
||||||
import Stackage.Database.Haddock (renderHaddock)
|
import Stackage.Database.Haddock (renderHaddock)
|
||||||
import Stackage.Database.Types (Changelog(..), Readme(..))
|
import Stackage.Database.Types (Changelog(..), Readme(..))
|
||||||
import Text.Blaze.Html (Html, preEscapedToHtml, toHtml)
|
import Text.Blaze.Html (Html, preEscapedToHtml, toHtml)
|
||||||
import Types (CompilerP(..), FlagNameP(..), ModuleNameP(..), PackageNameP(..),
|
|
||||||
SafeFilePath, VersionP(..), VersionRangeP(..), unSafeFilePath)
|
|
||||||
import Yesod.Form.Fields (Textarea(..))
|
|
||||||
import Text.Email.Validate
|
import Text.Email.Validate
|
||||||
|
import Types (CompilerP(..), FlagNameP(..), ModuleNameP(..), PackageIdentifierP,
|
||||||
|
PackageNameP(..), SafeFilePath, VersionP(..), VersionRangeP(..),
|
||||||
|
unSafeFilePath, dtDisplay)
|
||||||
|
import Yesod.Form.Fields (Textarea(..))
|
||||||
|
|
||||||
|
|
||||||
data PackageInfo = PackageInfo
|
data PackageInfo = PackageInfo
|
||||||
@ -81,7 +81,7 @@ toPackageInfo gpd mreadme mchangelog =
|
|||||||
, piHomepage =
|
, piHomepage =
|
||||||
case T.strip $ T.pack $ homepage pd of
|
case T.strip $ T.pack $ homepage pd of
|
||||||
"" -> Nothing
|
"" -> Nothing
|
||||||
x -> Just x
|
x -> Just x
|
||||||
, piLicenseName = T.pack $ prettyShow $ license pd
|
, piLicenseName = T.pack $ prettyShow $ license pd
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
@ -127,17 +127,23 @@ parseCabalBlob cabalBlob =
|
|||||||
|
|
||||||
parseCabalBlobMaybe ::
|
parseCabalBlobMaybe ::
|
||||||
(MonadIO m, MonadReader env m, HasLogFunc env)
|
(MonadIO m, MonadReader env m, HasLogFunc env)
|
||||||
=> PackageNameP
|
=> PackageIdentifierP
|
||||||
-> ByteString
|
-> ByteString
|
||||||
-> m (Maybe GenericPackageDescription)
|
-> m (Maybe GenericPackageDescription)
|
||||||
parseCabalBlobMaybe packageName cabalBlob =
|
parseCabalBlobMaybe pidp cabalBlob =
|
||||||
case snd $ runParseResult $ parseGenericPackageDescription cabalBlob of
|
case snd $ runParseResult $ parseGenericPackageDescription cabalBlob of
|
||||||
Left err ->
|
Left err ->
|
||||||
Nothing <$
|
Nothing <$
|
||||||
logError
|
logError
|
||||||
("Problem parsing cabal blob for '" <> display packageName <> "': " <>
|
("Problem parsing cabal blob for '" <> display pidp <> "': " <> displayShow err)
|
||||||
displayShow err)
|
Right gpd -> do
|
||||||
Right pgd -> pure $ Just pgd
|
let pid = package (packageDescription gpd)
|
||||||
|
unless (textDisplay (dtDisplay pid :: Utf8Builder) == textDisplay pidp) $
|
||||||
|
logError $
|
||||||
|
"Supplied package identifier: '" <> display pidp <>
|
||||||
|
"' does not match the one in cabal file: '" <>
|
||||||
|
dtDisplay pid
|
||||||
|
pure $ Just gpd
|
||||||
|
|
||||||
getCheckCond ::
|
getCheckCond ::
|
||||||
CompilerP -> Map FlagName Bool -> GenericPackageDescription -> Condition ConfVar -> Bool
|
CompilerP -> Map FlagName Bool -> GenericPackageDescription -> Condition ConfVar -> Bool
|
||||||
@ -172,7 +178,7 @@ getDeps checkCond = goTree
|
|||||||
where
|
where
|
||||||
goTree (CondNode _data deps comps) =
|
goTree (CondNode _data deps comps) =
|
||||||
combineDeps $
|
combineDeps $
|
||||||
map (\(Dependency name range) -> Map.singleton (PackageNameP name) range) deps ++
|
map (\(Dependency name range _) -> Map.singleton (PackageNameP name) range) deps ++
|
||||||
map goComp comps
|
map goComp comps
|
||||||
goComp (CondBranch cond yes no)
|
goComp (CondBranch cond yes no)
|
||||||
| checkCond cond = goTree yes
|
| checkCond cond = goTree yes
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
{-# LANGUAGE LambdaCase #-}
|
{-# LANGUAGE LambdaCase #-}
|
||||||
{-# LANGUAGE NamedFieldPuns #-}
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
{-# LANGUAGE ScopedTypeVariables #-}
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
{-# LANGUAGE TupleSections #-}
|
|
||||||
module Stackage.Database.Query
|
module Stackage.Database.Query
|
||||||
(
|
(
|
||||||
-- * Snapshot
|
-- * Snapshot
|
||||||
@ -53,13 +53,15 @@ module Stackage.Database.Query
|
|||||||
, getTreeForKey
|
, getTreeForKey
|
||||||
, treeCabal
|
, treeCabal
|
||||||
-- ** Stackage server
|
-- ** Stackage server
|
||||||
|
, CabalFileIds
|
||||||
|
, addCabalFile
|
||||||
|
, getCabalFileIds
|
||||||
, addSnapshotPackage
|
, addSnapshotPackage
|
||||||
, getHackageCabalByRev0
|
, getHackageCabalByRev0
|
||||||
, getHackageCabalByKey
|
, getHackageCabalByKey
|
||||||
, snapshotMarkUpdated
|
, snapshotMarkUpdated
|
||||||
, insertSnapshotName
|
, insertSnapshotName
|
||||||
, markModuleHasDocs
|
, markModuleHasDocs
|
||||||
, insertSnapshotPackageModules
|
|
||||||
, insertDeps
|
, insertDeps
|
||||||
-- ** For Hoogle db creation
|
-- ** For Hoogle db creation
|
||||||
, lastLtsNightly
|
, lastLtsNightly
|
||||||
@ -72,11 +74,14 @@ import qualified Data.List as L
|
|||||||
import Database.Esqueleto
|
import Database.Esqueleto
|
||||||
import Database.Esqueleto.Internal.Language (FromPreprocess)
|
import Database.Esqueleto.Internal.Language (FromPreprocess)
|
||||||
import Database.Esqueleto.Internal.Sql
|
import Database.Esqueleto.Internal.Sql
|
||||||
|
import Distribution.Types.PackageId (PackageIdentifier(PackageIdentifier))
|
||||||
|
import Distribution.PackageDescription (packageDescription)
|
||||||
|
import Distribution.Types.PackageDescription (PackageDescription(package))
|
||||||
import qualified Database.Persist as P
|
import qualified Database.Persist as P
|
||||||
import Pantry.Internal.Stackage (EntityField(..), PackageName, Unique(..),
|
import Pantry.Internal.Stackage (EntityField(..), PackageName,
|
||||||
Version, getBlobKey, getPackageNameById,
|
Version, getBlobKey, getPackageNameById,
|
||||||
getPackageNameId, getTreeForKey, getVersionId,
|
getPackageNameId, getTreeForKey, getVersionId,
|
||||||
loadBlobById, mkSafeFilePath, treeCabal)
|
loadBlobById, storeBlob, mkSafeFilePath)
|
||||||
import RIO hiding (on, (^.))
|
import RIO hiding (on, (^.))
|
||||||
import qualified RIO.Map as Map
|
import qualified RIO.Map as Map
|
||||||
import qualified RIO.Set as Set
|
import qualified RIO.Set as Set
|
||||||
@ -365,7 +370,7 @@ getPackageVersionForSnapshot snapshotId pname =
|
|||||||
pure (v ^. VersionVersion))
|
pure (v ^. VersionVersion))
|
||||||
|
|
||||||
getLatest ::
|
getLatest ::
|
||||||
FromPreprocess SqlQuery SqlExpr SqlBackend t
|
FromPreprocess t
|
||||||
=> PackageNameP
|
=> PackageNameP
|
||||||
-> (t -> SqlExpr (Value SnapshotId))
|
-> (t -> SqlExpr (Value SnapshotId))
|
||||||
-> (t -> SqlQuery ())
|
-> (t -> SqlQuery ())
|
||||||
@ -777,6 +782,75 @@ insertDeps pid snapshotPackageId dependencies =
|
|||||||
display dep
|
display dep
|
||||||
return $ Just dep
|
return $ Just dep
|
||||||
|
|
||||||
|
data CabalFileIds = CabalFileIds
|
||||||
|
{ cfiPackageNameId :: !PackageNameId
|
||||||
|
, cfiVersionId :: !VersionId
|
||||||
|
, cfiCabalBlobId :: !(Maybe BlobId)
|
||||||
|
, cfiModuleNameIds :: ![ModuleNameId]
|
||||||
|
}
|
||||||
|
|
||||||
|
getCabalFileIds ::
|
||||||
|
HasLogFunc env
|
||||||
|
=> BlobId
|
||||||
|
-> GenericPackageDescription
|
||||||
|
-> ReaderT SqlBackend (RIO env) CabalFileIds
|
||||||
|
getCabalFileIds cabalBlobId gpd = do
|
||||||
|
let PackageIdentifier name ver = package (packageDescription gpd)
|
||||||
|
packageNameId <- getPackageNameId name
|
||||||
|
versionId <- getVersionId ver
|
||||||
|
moduleNameIds <- mapM insertModuleSafe (extractModuleNames gpd)
|
||||||
|
pure
|
||||||
|
CabalFileIds
|
||||||
|
{ cfiPackageNameId = packageNameId
|
||||||
|
, cfiVersionId = versionId
|
||||||
|
, cfiCabalBlobId = Just cabalBlobId
|
||||||
|
, cfiModuleNameIds = moduleNameIds
|
||||||
|
}
|
||||||
|
|
||||||
|
addCabalFile ::
|
||||||
|
HasLogFunc env
|
||||||
|
=> PackageIdentifierP
|
||||||
|
-> ByteString
|
||||||
|
-> ReaderT SqlBackend (RIO env) (Maybe (GenericPackageDescription, CabalFileIds))
|
||||||
|
addCabalFile pid cabalBlob = do
|
||||||
|
mgpd <- lift $ parseCabalBlobMaybe pid cabalBlob
|
||||||
|
forM mgpd $ \gpd -> do
|
||||||
|
(cabalBlobId, _) <- storeBlob cabalBlob
|
||||||
|
cabalIds <- getCabalFileIds cabalBlobId gpd
|
||||||
|
pure (gpd, cabalIds)
|
||||||
|
|
||||||
|
getPackageIds ::
|
||||||
|
GenericPackageDescription
|
||||||
|
-> Either CabalFileIds (Entity Tree)
|
||||||
|
-> ReaderT SqlBackend (RIO env) (CabalFileIds, Maybe (TreeId, BlobId))
|
||||||
|
getPackageIds gpd =
|
||||||
|
\case
|
||||||
|
Left cabalFileIds -> pure (cabalFileIds, Nothing)
|
||||||
|
Right (Entity treeId tree)
|
||||||
|
-- -- TODO: Remove Maybe from cfiCabalBlobId and
|
||||||
|
-- -- Generate cabal file from package.yaml:
|
||||||
|
-- case treeCabal tree of
|
||||||
|
-- Just cabalBlobId -> pure cabalBlobId
|
||||||
|
-- Nothing -> do
|
||||||
|
-- let rawMetaData = RawPackageMetadata {
|
||||||
|
-- rpmName = Just pname
|
||||||
|
-- , rpmVersion = Just pver
|
||||||
|
-- , rpmTreeKey = treeKey tree
|
||||||
|
-- }
|
||||||
|
-- rpli = ... get
|
||||||
|
-- generateHPack (RPLIArchive / RPLIRepo ..) treeId treeVersion tree
|
||||||
|
-- ...
|
||||||
|
-> do
|
||||||
|
moduleNameIds <- mapM insertModuleSafe (extractModuleNames gpd)
|
||||||
|
let cabalFileIds =
|
||||||
|
CabalFileIds
|
||||||
|
{ cfiPackageNameId = treeName tree
|
||||||
|
, cfiVersionId = treeVersion tree
|
||||||
|
, cfiCabalBlobId = treeCabal tree
|
||||||
|
, cfiModuleNameIds = moduleNameIds
|
||||||
|
}
|
||||||
|
pure (cabalFileIds, Just (treeId, treeKey tree))
|
||||||
|
|
||||||
-- TODO: Optimize, whenever package is already in one snapshot only create the modules and new
|
-- TODO: Optimize, whenever package is already in one snapshot only create the modules and new
|
||||||
-- SnapshotPackage
|
-- SnapshotPackage
|
||||||
addSnapshotPackage ::
|
addSnapshotPackage ::
|
||||||
@ -784,30 +858,27 @@ addSnapshotPackage ::
|
|||||||
=> SnapshotId
|
=> SnapshotId
|
||||||
-> CompilerP
|
-> CompilerP
|
||||||
-> Origin
|
-> Origin
|
||||||
-> Maybe (Entity Tree)
|
-> Either CabalFileIds (Entity Tree)
|
||||||
-> Maybe HackageCabalId
|
-> Maybe HackageCabalId
|
||||||
-> Bool
|
-> Bool
|
||||||
-> Map FlagNameP Bool
|
-> Map FlagNameP Bool
|
||||||
-> PackageIdentifierP
|
-> PackageIdentifierP
|
||||||
-> GenericPackageDescription
|
-> GenericPackageDescription
|
||||||
-> ReaderT SqlBackend (RIO env) ()
|
-> ReaderT SqlBackend (RIO env) ()
|
||||||
addSnapshotPackage snapshotId compiler origin mTree mHackageCabalId isHidden flags pid gpd = do
|
addSnapshotPackage snapshotId compiler origin eCabalTree mHackageCabalId isHidden flags pid gpd = do
|
||||||
let PackageIdentifierP pname pver = pid
|
(CabalFileIds{..}, mTree) <- getPackageIds gpd eCabalTree
|
||||||
mTreeId = entityKey <$> mTree
|
let mTreeId = fst <$> mTree
|
||||||
packageNameId <-
|
|
||||||
maybe (getPackageNameId (unPackageNameP pname)) (pure . treeName . entityVal) mTree
|
|
||||||
versionId <- maybe (getVersionId (unVersionP pver)) (pure . treeVersion . entityVal) mTree
|
|
||||||
mrevision <- maybe (pure Nothing) getHackageRevision mHackageCabalId
|
mrevision <- maybe (pure Nothing) getHackageRevision mHackageCabalId
|
||||||
mreadme <- fromMaybe (pure Nothing) $ getContentTreeEntryId <$> mTreeId <*> mreadmeQuery
|
mreadme <- fromMaybe (pure Nothing) $ getContentTreeEntryId <$> mTreeId <*> mreadmeQuery
|
||||||
mchangelog <- fromMaybe (pure Nothing) $ getContentTreeEntryId <$> mTreeId <*> mchangelogQuery
|
mchangelog <- fromMaybe (pure Nothing) $ getContentTreeEntryId <$> mTreeId <*> mchangelogQuery
|
||||||
let snapshotPackage =
|
let snapshotPackage =
|
||||||
SnapshotPackage
|
SnapshotPackage
|
||||||
{ snapshotPackageSnapshot = snapshotId
|
{ snapshotPackageSnapshot = snapshotId
|
||||||
, snapshotPackagePackageName = packageNameId
|
, snapshotPackagePackageName = cfiPackageNameId
|
||||||
, snapshotPackageVersion = versionId
|
, snapshotPackageVersion = cfiVersionId
|
||||||
, snapshotPackageRevision = mrevision
|
, snapshotPackageRevision = mrevision
|
||||||
, snapshotPackageCabal = treeCabal =<< entityVal <$> mTree
|
, snapshotPackageCabal = cfiCabalBlobId
|
||||||
, snapshotPackageTreeBlob = treeKey . entityVal <$> mTree
|
, snapshotPackageTreeBlob = snd <$> mTree
|
||||||
, snapshotPackageOrigin = origin
|
, snapshotPackageOrigin = origin
|
||||||
, snapshotPackageOriginUrl = "" -- TODO: add
|
, snapshotPackageOriginUrl = "" -- TODO: add
|
||||||
, snapshotPackageSynopsis = getSynopsis gpd
|
, snapshotPackageSynopsis = getSynopsis gpd
|
||||||
@ -832,7 +903,8 @@ addSnapshotPackage snapshotId compiler origin mTree mHackageCabalId isHidden fla
|
|||||||
forM_ msnapshotPackageId $ \snapshotPackageId -> do
|
forM_ msnapshotPackageId $ \snapshotPackageId -> do
|
||||||
_ <- insertDeps pid snapshotPackageId (extractDependencies compiler flags gpd)
|
_ <- insertDeps pid snapshotPackageId (extractDependencies compiler flags gpd)
|
||||||
-- TODO: collect all missing dependencies and make a report
|
-- TODO: collect all missing dependencies and make a report
|
||||||
insertSnapshotPackageModules snapshotPackageId (extractModuleNames gpd)
|
forM_ cfiModuleNameIds $ \modNameId -> do
|
||||||
|
void $ P.insertBy (SnapshotPackageModule snapshotPackageId modNameId False)
|
||||||
|
|
||||||
getContentTreeEntryId ::
|
getContentTreeEntryId ::
|
||||||
TreeId
|
TreeId
|
||||||
@ -979,16 +1051,6 @@ getSnapshotPackageCabalBlob snapshotId pname =
|
|||||||
(pn ^. PackageNameName ==. val pname))
|
(pn ^. PackageNameName ==. val pname))
|
||||||
return (blob ^. BlobContents)
|
return (blob ^. BlobContents)
|
||||||
|
|
||||||
|
|
||||||
-- | Add all modules available for the package in a particular snapshot. Initially they are marked
|
|
||||||
-- as without available documentation.
|
|
||||||
insertSnapshotPackageModules ::
|
|
||||||
SnapshotPackageId -> [ModuleNameP] -> ReaderT SqlBackend (RIO env) ()
|
|
||||||
insertSnapshotPackageModules snapshotPackageId =
|
|
||||||
mapM_ $ \modName -> do
|
|
||||||
moduleId <- insertModuleSafe modName
|
|
||||||
void $ P.insertBy (SnapshotPackageModule snapshotPackageId moduleId False)
|
|
||||||
|
|
||||||
-- | Idempotent and thread safe way of adding a new module.
|
-- | Idempotent and thread safe way of adding a new module.
|
||||||
insertModuleSafe :: ModuleNameP -> ReaderT SqlBackend (RIO env) ModuleNameId
|
insertModuleSafe :: ModuleNameP -> ReaderT SqlBackend (RIO env) ModuleNameId
|
||||||
insertModuleSafe modName = do
|
insertModuleSafe modName = do
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
{-# LANGUAGE DerivingStrategies #-}
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
{-# LANGUAGE FunctionalDependencies #-}
|
{-# LANGUAGE FunctionalDependencies #-}
|
||||||
@ -8,8 +9,10 @@
|
|||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
{-# LANGUAGE RankNTypes #-}
|
{-# LANGUAGE RankNTypes #-}
|
||||||
{-# LANGUAGE RecordWildCards #-}
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
|
{-# LANGUAGE StandaloneDeriving #-}
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
{-# LANGUAGE TypeFamilies #-}
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
{-# LANGUAGE UndecidableInstances #-}
|
||||||
module Stackage.Database.Schema
|
module Stackage.Database.Schema
|
||||||
( -- * Database
|
( -- * Database
|
||||||
run
|
run
|
||||||
@ -47,7 +50,7 @@ import Database.Persist.Postgresql
|
|||||||
import Database.Persist.TH
|
import Database.Persist.TH
|
||||||
import Pantry (HasPantryConfig(..), Revision)
|
import Pantry (HasPantryConfig(..), Revision)
|
||||||
import Pantry.Internal.Stackage as PS (BlobId, HackageCabalId, ModuleNameId,
|
import Pantry.Internal.Stackage as PS (BlobId, HackageCabalId, ModuleNameId,
|
||||||
PackageNameId, Tree(..), TreeEntry(..),
|
PackageNameId, Tree(..),
|
||||||
TreeEntryId, TreeId, Unique(..),
|
TreeEntryId, TreeId, Unique(..),
|
||||||
VersionId, unBlobKey)
|
VersionId, unBlobKey)
|
||||||
import Pantry.Internal.Stackage (PantryConfig(..), Storage(..))
|
import Pantry.Internal.Stackage (PantryConfig(..), Storage(..))
|
||||||
|
|||||||
@ -51,9 +51,7 @@ import qualified Data.Text as T
|
|||||||
import Data.Text.Read (decimal)
|
import Data.Text.Read (decimal)
|
||||||
import Network.AWS (Env, HasEnv(..))
|
import Network.AWS (Env, HasEnv(..))
|
||||||
import Pantry (BlobKey(..), CabalFileInfo(..), FileSize(..),
|
import Pantry (BlobKey(..), CabalFileInfo(..), FileSize(..),
|
||||||
HasPantryConfig(..), PackageIdentifierRevision(..), TreeKey(..))
|
HasPantryConfig(..), PantryConfig, PackageIdentifierRevision(..), TreeKey(..))
|
||||||
import Pantry.Internal.Stackage as Pantry (PackageNameP(..), PantryConfig,
|
|
||||||
VersionP(..))
|
|
||||||
import Pantry.SHA256 (fromHexText)
|
import Pantry.SHA256 (fromHexText)
|
||||||
import RIO
|
import RIO
|
||||||
import RIO.Process (HasProcessContext(..), ProcessContext)
|
import RIO.Process (HasProcessContext(..), ProcessContext)
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import Data.These
|
|||||||
import RIO
|
import RIO
|
||||||
import Stackage.Database (GetStackageDatabase, SnapshotId,
|
import Stackage.Database (GetStackageDatabase, SnapshotId,
|
||||||
getPackagesForSnapshot)
|
getPackagesForSnapshot)
|
||||||
import Stackage.Database.Types (PackageListingInfo(..), SnapName)
|
import Stackage.Database.Types (PackageListingInfo(..))
|
||||||
import Types
|
import Types
|
||||||
import Web.PathPieces
|
import Web.PathPieces
|
||||||
|
|
||||||
|
|||||||
19
src/Types.hs
19
src/Types.hs
@ -49,7 +49,6 @@ module Types
|
|||||||
) where
|
) where
|
||||||
|
|
||||||
import ClassyPrelude.Yesod (ToBuilder(..))
|
import ClassyPrelude.Yesod (ToBuilder(..))
|
||||||
import Control.Monad.Catch (MonadThrow, throwM)
|
|
||||||
import Data.Aeson
|
import Data.Aeson
|
||||||
import Data.Bifunctor (bimap)
|
import Data.Bifunctor (bimap)
|
||||||
import Data.Char (ord)
|
import Data.Char (ord)
|
||||||
@ -62,16 +61,16 @@ import Database.Persist
|
|||||||
import Database.Persist.Sql (PersistFieldSql(sqlType))
|
import Database.Persist.Sql (PersistFieldSql(sqlType))
|
||||||
import qualified Distribution.ModuleName as DT (components, fromComponents,
|
import qualified Distribution.ModuleName as DT (components, fromComponents,
|
||||||
validModuleComponent)
|
validModuleComponent)
|
||||||
import Distribution.PackageDescription (FlagName, GenericPackageDescription)
|
import Distribution.PackageDescription (GenericPackageDescription)
|
||||||
import qualified Distribution.Text as DT (Text, display, simpleParse)
|
import Distribution.Parsec as DT (Parsec)
|
||||||
|
import Distribution.Pretty as DT (Pretty)
|
||||||
|
import qualified Distribution.Text as DT (display, simpleParse)
|
||||||
import Distribution.Types.VersionRange (VersionRange)
|
import Distribution.Types.VersionRange (VersionRange)
|
||||||
import Distribution.Version (mkVersion, versionNumbers)
|
import Distribution.Version (mkVersion, versionNumbers)
|
||||||
import Pantry (Revision(..))
|
import Pantry (FlagName, Revision(..), packageNameString, parsePackageName,
|
||||||
|
parseVersionThrowing, parseVersion, versionString)
|
||||||
import Pantry.Internal.Stackage (ModuleNameP(..), PackageNameP(..),
|
import Pantry.Internal.Stackage (ModuleNameP(..), PackageNameP(..),
|
||||||
SafeFilePath, VersionP(..), packageNameString,
|
SafeFilePath, VersionP(..), unSafeFilePath)
|
||||||
parsePackageName, parseVersion,
|
|
||||||
parseVersionThrowing, unSafeFilePath,
|
|
||||||
versionString)
|
|
||||||
import RIO
|
import RIO
|
||||||
import qualified RIO.Map as Map
|
import qualified RIO.Map as Map
|
||||||
import RIO.Time (Day)
|
import RIO.Time (Day)
|
||||||
@ -84,14 +83,14 @@ instance Exception ParseFailedException where
|
|||||||
displayException (ParseFailedException tyRep origString) =
|
displayException (ParseFailedException tyRep origString) =
|
||||||
"Was unable to parse " ++ showsTypeRep tyRep ": " ++ origString
|
"Was unable to parse " ++ showsTypeRep tyRep ": " ++ origString
|
||||||
|
|
||||||
dtParse :: forall a m. (Typeable a, DT.Text a, MonadThrow m) => Text -> m a
|
dtParse :: forall a m. (Typeable a, DT.Parsec a, MonadThrow m) => Text -> m a
|
||||||
dtParse txt =
|
dtParse txt =
|
||||||
let str = T.unpack txt
|
let str = T.unpack txt
|
||||||
in case DT.simpleParse str of
|
in case DT.simpleParse str of
|
||||||
Nothing -> throwM $ ParseFailedException (typeRep (Proxy :: Proxy a)) str
|
Nothing -> throwM $ ParseFailedException (typeRep (Proxy :: Proxy a)) str
|
||||||
Just dt -> pure dt
|
Just dt -> pure dt
|
||||||
|
|
||||||
dtDisplay :: (DT.Text a, IsString b) => a -> b
|
dtDisplay :: (DT.Pretty a, IsString b) => a -> b
|
||||||
dtDisplay = fromString . DT.display
|
dtDisplay = fromString . DT.display
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
19
stack.yaml
19
stack.yaml
@ -1,10 +1,17 @@
|
|||||||
resolver: lts-13.16
|
resolver: nightly-2020-02-08
|
||||||
packages:
|
packages:
|
||||||
- '.'
|
- '.'
|
||||||
extra-deps:
|
extra-deps:
|
||||||
- git: https://github.com/commercialhaskell/stack
|
- barrier-0.1.1@sha256:2021f84c3aba67bb635d72825d3bc0371942444dc014bc307b875071e29eea98,3931
|
||||||
commit: dfbf85ad7e8af5b01cf7b51367290870ffc2c90e
|
- hackage-security-0.6.0.0@sha256:69987d46e7b55fe5f0fc537021c3873c5f6f44a6665d349ee6995fd593df8147,11976
|
||||||
|
- hoogle-5.0.17.14@sha256:a35eab4f833cd131f1abc79360e3bdbc5aecd7526b9a530ac606580e18691e2b,3173
|
||||||
|
- hpack-0.33.0@sha256:ca82f630abe0fba199aa05dcc9942ee8bf137e1425049a7a9ac8458c82d9dcc9,4406
|
||||||
|
- yesod-gitrepo-0.3.0@sha256:7aad996935065726ce615c395d735cc01dcef3993b1788f670f6bfc866085e02,1191
|
||||||
|
- lukko-0.1.1.1@sha256:5c674bdd8a06b926ba55d872abe254155ed49a58df202b4d842b643e5ed6bcc9,4289
|
||||||
|
- github: commercialhaskell/pantry
|
||||||
|
commit: ed48bebc30e539280ad7e13680480be2b87b97ea
|
||||||
|
- github: fpco/casa
|
||||||
|
commit: fc0ed26858bfc4f2966ed2dfb2871bae9266dda6
|
||||||
subdirs:
|
subdirs:
|
||||||
- subs/http-download
|
- casa-client
|
||||||
- subs/pantry
|
- casa-types
|
||||||
- subs/rio-prettyprint
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user