This commit is contained in:
Michael Snoyman 2014-12-26 16:13:08 +02:00
parent 7672603fcb
commit ef9e5cc7ce
12 changed files with 165 additions and 75 deletions

View File

@ -68,6 +68,7 @@ import Handler.CompressorStatus
import Handler.Tag import Handler.Tag
import Handler.BannedTags import Handler.BannedTags
import Handler.RefreshDeprecated import Handler.RefreshDeprecated
import Handler.UploadV2
-- This line actually creates our YesodDispatch instance. It is the second half -- This line actually creates our YesodDispatch instance. It is the second half
-- of the call to mkYesodData which occurs in Foundation.hs. Please see the -- of the call to mkYesodData which occurs in Foundation.hs. Please see the
@ -198,6 +199,8 @@ makeFoundation useEcho conf = do
loadWebsiteContent loadWebsiteContent
#endif #endif
snapshotInfoCache' <- newIORef mempty
let logger = Yesod.Core.Types.Logger loggerSet' getter let logger = Yesod.Core.Types.Logger loggerSet' getter
foundation = App foundation = App
{ settings = conf { settings = conf
@ -215,6 +218,7 @@ makeFoundation useEcho conf = do
, widgetCache = widgetCache' , widgetCache = widgetCache'
, compressorStatus = statusRef , compressorStatus = statusRef
, websiteContent = websiteContent' , websiteContent = websiteContent'
, snapshotInfoCache = snapshotInfoCache'
} }
env <- getEnvironment env <- getEnvironment

View File

@ -22,6 +22,8 @@ import Yesod.Core.Types (Logger, GWData)
import Yesod.Default.Config import Yesod.Default.Config
import Yesod.Default.Util (addStaticContentExternal) import Yesod.Default.Util (addStaticContentExternal)
import Yesod.GitRepo import Yesod.GitRepo
import Stackage.ServerBundle (SnapshotType, DocMap)
import Stackage.BuildPlan (BuildPlan)
-- | The site argument for your application. This can be a good place to -- | The site argument for your application. This can be a good place to
-- keep settings and values requiring initialization before your application -- keep settings and values requiring initialization before your application
@ -47,6 +49,13 @@ data App = App
, widgetCache :: !(IORef (HashMap Text (UTCTime, GWData (Route App)))) , widgetCache :: !(IORef (HashMap Text (UTCTime, GWData (Route App))))
, compressorStatus :: !(IORef Text) , compressorStatus :: !(IORef Text)
, websiteContent :: GitRepo WebsiteContent , websiteContent :: GitRepo WebsiteContent
, snapshotInfoCache :: !(IORef (HashMap PackageSetIdent SnapshotInfo))
}
data SnapshotInfo = SnapshotInfo
{ siType :: !SnapshotType
, siPlan :: !BuildPlan
, siDocMap :: !DocMap
} }
type ForceUnpack = Bool type ForceUnpack = Bool
@ -152,6 +161,7 @@ instance Yesod App where
maximumContentLength _ (Just UploadStackageR) = Just 50000000 maximumContentLength _ (Just UploadStackageR) = Just 50000000
maximumContentLength _ (Just UploadHaddockR{}) = Just 100000000 maximumContentLength _ (Just UploadHaddockR{}) = Just 100000000
maximumContentLength _ (Just UploadV2R) = Just 100000000
maximumContentLength _ _ = Just 2000000 maximumContentLength _ _ = Just 2000000
instance ToMarkup (Route App) where instance ToMarkup (Route App) where

View File

@ -202,30 +202,6 @@ gzipHash dirs suffix = do
src = dirRawRoot dirs </> suffix src = dirRawRoot dirs </> suffix
dst = dirGzRoot dirs </> suffix dst = dirGzRoot dirs </> suffix
data Dirs = Dirs
{ dirRawRoot :: !FilePath
, dirGzRoot :: !FilePath
, dirCacheRoot :: !FilePath
}
getDirs :: Handler Dirs
getDirs = mkDirs . haddockRootDir <$> getYesod
mkDirs :: FilePath -> Dirs
mkDirs dir = Dirs
{ dirRawRoot = dir </> "idents-raw"
, dirGzRoot = dir </> "idents-gz"
, dirCacheRoot = dir </> "cachedir"
}
dirGzIdent, dirRawIdent :: Dirs -> PackageSetIdent -> FilePath
dirGzIdent dirs ident = dirGzRoot dirs </> fpFromText (toPathPiece ident)
dirRawIdent dirs ident = dirRawRoot dirs </> fpFromText (toPathPiece ident)
dirGzFp, dirRawFp :: Dirs -> PackageSetIdent -> [Text] -> FilePath
dirGzFp dirs ident rest = dirGzIdent dirs ident </> mconcat (map fpFromText rest)
dirRawFp dirs ident rest = dirRawIdent dirs ident </> mconcat (map fpFromText rest)
dirCacheFp :: Dirs -> Digest SHA1 -> FilePath dirCacheFp :: Dirs -> Digest SHA1 -> FilePath
dirCacheFp dirs digest = dirCacheFp dirs digest =
dirCacheRoot dirs </> fpFromText x </> fpFromText y <.> "gz" dirCacheRoot dirs </> fpFromText x </> fpFromText y <.> "gz"

View File

@ -6,12 +6,15 @@ import Data.Time (FormatTime)
import Data.Slug (SnapSlug) import Data.Slug (SnapSlug)
import qualified Database.Esqueleto as E import qualified Database.Esqueleto as E
import Handler.PackageList (cachedWidget) import Handler.PackageList (cachedWidget)
import Stackage.ServerBundle (PackageDocs (..))
import Control.Monad.Writer.Strict (tell, execWriter)
import Stackage.BuildPlan (bpSystemInfo, bpPackages, ppVersion)
import Stackage.BuildConstraints (siCorePackages)
import Stackage.Prelude (display)
getStackageHomeR :: SnapSlug -> Handler Html getStackageHomeR :: SnapSlug -> Handler Html
getStackageHomeR slug = do getStackageHomeR slug = do
stackage <- runDB $ do (Entity sid stackage, msi) <- getStackage slug
Entity _ stackage <- getBy404 $ UniqueSnapshot slug
return stackage
hasBundle <- storeExists $ SnapshotBundle $ stackageIdent stackage hasBundle <- storeExists $ SnapshotBundle $ stackageIdent stackage
let minclusive = let minclusive =
@ -21,7 +24,7 @@ getStackageHomeR slug = do
then Just False then Just False
else Nothing else Nothing
base = maybe 0 (const 1) minclusive :: Int base = maybe 0 (const 1) minclusive :: Int
Entity sid _stackage <- runDB $ getBy404 $ UniqueSnapshot slug
defaultLayout $ do defaultLayout $ do
setTitle $ toHtml $ stackageTitle stackage setTitle $ toHtml $ stackageTitle stackage
cachedWidget (20 * 60) ("package-list-" ++ toPathPiece slug) $ do cachedWidget (20 * 60) ("package-list-" ++ toPathPiece slug) $ do
@ -70,7 +73,7 @@ getStackageHomeR slug = do
getStackageMetadataR :: SnapSlug -> Handler TypedContent getStackageMetadataR :: SnapSlug -> Handler TypedContent
getStackageMetadataR slug = do getStackageMetadataR slug = do
Entity sid _ <- runDB $ getBy404 $ UniqueSnapshot slug (Entity sid _, msi) <- getStackage slug
respondSourceDB typePlain $ do respondSourceDB typePlain $ do
sendChunkBS "Override packages\n" sendChunkBS "Override packages\n"
sendChunkBS "=================\n" sendChunkBS "=================\n"
@ -97,7 +100,7 @@ getStackageMetadataR slug = do
getStackageCabalConfigR :: SnapSlug -> Handler TypedContent getStackageCabalConfigR :: SnapSlug -> Handler TypedContent
getStackageCabalConfigR slug = do getStackageCabalConfigR slug = do
Entity sid _ <- runDB $ getBy404 $ UniqueSnapshot slug (Entity sid _, msi) <- getStackage slug
render <- getUrlRender render <- getUrlRender
mdownload <- lookupGetParam "download" mdownload <- lookupGetParam "download"
@ -107,15 +110,30 @@ getStackageCabalConfigR slug = do
mglobal <- lookupGetParam "global" mglobal <- lookupGetParam "global"
let isGlobal = mglobal == Just "true" let isGlobal = mglobal == Just "true"
respondSourceDB typePlain $ stream isGlobal render sid respondSourceDB typePlain $
stream (maybe (Left sid) Right msi) $=
(if isGlobal then conduitGlobal else conduitLocal) render
where where
stream isGlobal render sid = stream (Left sid) =
selectSource selectSource
[ PackageStackage ==. sid [ PackageStackage ==. sid
] ]
[ Asc PackageName' [ Asc PackageName'
, Asc PackageVersion , Asc PackageVersion
] $= (if isGlobal then conduitGlobal else conduitLocal) render ] $= mapC (\(Entity _ p) ->
( toPathPiece $ packageName' p
, case packageCore p of
Just True -> Nothing
_ -> Just $ toPathPiece $ packageVersion p
))
stream (Right SnapshotInfo {..}) = forM_ (mapToList m) $ \(name, mversion) ->
yield ( display name
, display <$> mversion
)
where
core = fmap (const Nothing) $ siCorePackages $ bpSystemInfo siPlan
noncore = fmap (Just . ppVersion) $ bpPackages siPlan
m = core ++ noncore
conduitGlobal render = do conduitGlobal render = do
headerGlobal render headerGlobal render
@ -145,28 +163,28 @@ getStackageCabalConfigR slug = do
toBuilder (render $ SnapshotR slug StackageHomeR) ++ toBuilder (render $ SnapshotR slug StackageHomeR) ++
toBuilder '\n' toBuilder '\n'
constraint p constraint Nothing = toBuilder $ asText " installed"
| Just True <- packageCore p = toBuilder $ asText " installed" constraint (Just version) =
| otherwise = toBuilder (asText " ==") ++ toBuilder (asText " ==") ++
toBuilder (toPathPiece $ packageVersion p) toBuilder (toPathPiece version)
showPackageGlobal (Entity _ p) = showPackageGlobal (name, mversion) =
toBuilder (asText "constraint: ") ++ toBuilder (asText "constraint: ") ++
toBuilder (toPathPiece $ packageName' p) ++ toBuilder (toPathPiece name) ++
constraint p ++ constraint mversion ++
toBuilder '\n' toBuilder '\n'
goFirst = do goFirst = do
mx <- await mx <- await
forM_ mx $ \(Entity _ p) -> yield $ Chunk $ forM_ mx $ \(name, mversion) -> yield $ Chunk $
toBuilder (asText "constraints: ") ++ toBuilder (asText "constraints: ") ++
toBuilder (toPathPiece $ packageName' p) ++ toBuilder (toPathPiece name) ++
constraint p constraint mversion
showPackageLocal (Entity _ p) = showPackageLocal (name, mversion) =
toBuilder (asText ",\n ") ++ toBuilder (asText ",\n ") ++
toBuilder (toPathPiece $ packageName' p) ++ toBuilder (toPathPiece name) ++
constraint p constraint mversion
yearMonthDay :: FormatTime t => t -> String yearMonthDay :: FormatTime t => t -> String
yearMonthDay = formatTime defaultTimeLocale "%Y-%m-%d" yearMonthDay = formatTime defaultTimeLocale "%Y-%m-%d"
@ -180,7 +198,7 @@ getOldStackageR ident pieces = do
getSnapshotPackagesR :: SnapSlug -> Handler Html getSnapshotPackagesR :: SnapSlug -> Handler Html
getSnapshotPackagesR slug = do getSnapshotPackagesR slug = do
Entity sid _stackage <- runDB $ getBy404 $ UniqueSnapshot slug (Entity sid _stackage, msi) <- getStackage slug
defaultLayout $ do defaultLayout $ do
setTitle $ toHtml $ "Package list for " ++ toPathPiece slug setTitle $ toHtml $ "Package list for " ++ toPathPiece slug
cachedWidget (20 * 60) ("package-list-" ++ toPathPiece slug) $ do cachedWidget (20 * 60) ("package-list-" ++ toPathPiece slug) $ do
@ -223,27 +241,44 @@ getSnapshotPackagesR slug = do
getDocsR :: SnapSlug -> Handler Html getDocsR :: SnapSlug -> Handler Html
getDocsR slug = do getDocsR slug = do
Entity sid _stackage <- runDB $ getBy404 $ UniqueSnapshot slug (Entity sid _stackage, msi) <- getStackage slug
defaultLayout $ do defaultLayout $ do
setTitle $ toHtml $ "Module list for " ++ toPathPiece slug setTitle $ toHtml $ "Module list for " ++ toPathPiece slug
cachedWidget (20 * 60) ("module-list-" ++ toPathPiece slug) $ do cachedWidget (20 * 60) ("module-list-" ++ toPathPiece slug) $ do
modules' <- handlerToWidget $ runDB $ E.select $ E.from $ \(d,m) -> do modules <- handlerToWidget $ maybe (getFromDB sid) convertYaml msi
E.where_ $
(d E.^. DocsSnapshot E.==. E.val (Just sid)) E.&&.
(d E.^. DocsId E.==. m E.^. ModuleDocs)
E.orderBy [ E.asc $ m E.^. ModuleName
, E.asc $ d E.^. DocsName
]
return
( m E.^. ModuleName
, m E.^. ModuleUrl
, d E.^. DocsName
, d E.^. DocsVersion
)
let modules = flip map modules' $ \(name, url, package, version) ->
( E.unValue name
, E.unValue url
, E.unValue package
, E.unValue version
)
$(widgetFile "doc-list") $(widgetFile "doc-list")
where
getFromDB sid = do
modules' <- runDB $ E.select $ E.from $ \(d,m) -> do
E.where_ $
(d E.^. DocsSnapshot E.==. E.val (Just sid)) E.&&.
(d E.^. DocsId E.==. m E.^. ModuleDocs)
E.orderBy [ E.asc $ m E.^. ModuleName
, E.asc $ d E.^. DocsName
]
return
( m E.^. ModuleName
, m E.^. ModuleUrl
, d E.^. DocsName
, d E.^. DocsVersion
)
return $ flip map modules' $ \(name, url, package, version) ->
( E.unValue name
, E.unValue url
, E.unValue package
, E.unValue version
)
convertYaml :: SnapshotInfo -> Handler [(Text, Text, PackageName, Version)]
convertYaml SnapshotInfo {..} = do
render <- getUrlRender
return $ sortBy comp $ ($ []) $ execWriter $ do
forM_ (mapToList siDocMap) $ \(PackageName -> package, pd) -> do
let version = Version $ pdVersion pd
forM_ (mapToList $ pdModules pd) $ \(modname, path) -> do
let url = render $ HaddockR
slug
path
tell ((modname, url, package, version):)
where
comp (a, _, x, _) (b, _, y, _) = compare (a, x) (b, y)

View File

@ -122,6 +122,7 @@ putUploadStackageR = do
, stackageDesc = "No description provided" , stackageDesc = "No description provided"
, stackageHasHaddocks = False , stackageHasHaddocks = False
, stackageSlug = baseSlug , stackageSlug = baseSlug
, stackageYaml = False
} }
-- Evil lazy I/O thanks to tar package -- Evil lazy I/O thanks to tar package

View File

@ -13,6 +13,11 @@ import Yesod.Auth as Import
import Data.Slug (mkSlug) import Data.Slug (mkSlug)
import Data.WebsiteContent as Import (WebsiteContent (..)) import Data.WebsiteContent as Import (WebsiteContent (..))
import Data.Text.Read (decimal) import Data.Text.Read (decimal)
import Data.Conduit.Zlib (ungzip)
import System.IO (openBinaryFile, IOMode (ReadMode))
import Data.Yaml (decodeEither')
import Control.Monad.Trans.Resource (allocate)
import Data.Slug (SnapSlug)
requireAuthIdOrToken :: Handler UserId requireAuthIdOrToken :: Handler UserId
requireAuthIdOrToken = do requireAuthIdOrToken = do
@ -34,3 +39,60 @@ parseLtsPair t1 = do
t3 <- stripPrefix "." t2 t3 <- stripPrefix "." t2
(y, "") <- either (const Nothing) Just $ decimal t3 (y, "") <- either (const Nothing) Just $ decimal t3
Just (x, y) Just (x, y)
getStackage :: SnapSlug -> Handler (Entity Stackage, Maybe SnapshotInfo)
getStackage slug = do
ent@(Entity _ stackage) <- runDB $ getBy404 $ UniqueSnapshot slug
msi <-
if stackageYaml stackage
then Just <$> getSnapshotInfoByIdent (stackageIdent stackage)
else return Nothing
return (ent, msi)
getSnapshotInfoByIdent :: PackageSetIdent -> Handler SnapshotInfo
getSnapshotInfoByIdent ident = do
dirs <- getDirs
let sourceDocFile rest = do
let rawfp = fpToString $ dirRawFp dirs ident rest
gzfp = fpToString $ dirGzFp dirs ident rest
eres <- liftResourceT $ tryIO $ allocate (openBinaryFile rawfp ReadMode) hClose
case eres of
Left _ -> do
(_, h) <- allocate (openBinaryFile gzfp ReadMode) hClose
sourceHandle h $= ungzip
Right (_, h) -> sourceHandle h
let maxFileSize = 1024 * 1024 * 5
yaml :: FromJSON a => Text -> Handler a
yaml name = do
bs <- sourceDocFile [name] $$ takeCE maxFileSize =$ foldC
either throwM return $ decodeEither' bs
siType <- yaml "build-type.yaml"
siPlan <- yaml "build-plan.yaml"
siDocMap <- yaml "docs-map.yaml"
return SnapshotInfo {..}
data Dirs = Dirs
{ dirRawRoot :: !FilePath
, dirGzRoot :: !FilePath
, dirCacheRoot :: !FilePath
}
getDirs :: Handler Dirs
getDirs = mkDirs . haddockRootDir <$> getYesod
mkDirs :: FilePath -> Dirs
mkDirs dir = Dirs
{ dirRawRoot = dir </> "idents-raw"
, dirGzRoot = dir </> "idents-gz"
, dirCacheRoot = dir </> "cachedir"
}
dirGzIdent, dirRawIdent :: Dirs -> PackageSetIdent -> FilePath
dirGzIdent dirs ident = dirGzRoot dirs </> fpFromText (toPathPiece ident)
dirRawIdent dirs ident = dirRawRoot dirs </> fpFromText (toPathPiece ident)
dirGzFp, dirRawFp :: Dirs -> PackageSetIdent -> [Text] -> FilePath
dirGzFp dirs ident rest = dirGzIdent dirs ident </> mconcat (map fpFromText rest)
dirRawFp dirs ident rest = dirRawIdent dirs ident </> mconcat (map fpFromText rest)

View File

@ -669,7 +669,6 @@ constraints: abstract-deque ==0.3,
Spock ==0.7.5.1, Spock ==0.7.5.1,
spoon ==0.3.1, spoon ==0.3.1,
sqlite-simple ==0.4.8.0, sqlite-simple ==0.4.8.0,
stackage ==0.2.1.3,
stateref ==0.3, stateref ==0.3,
statestack ==0.2.0.3, statestack ==0.2.0.3,
statistics ==0.13.2.1, statistics ==0.13.2.1,

View File

@ -23,6 +23,7 @@ Stackage
title Text title Text
desc Text desc Text
hasHaddocks Bool default=false hasHaddocks Bool default=false
yaml Bool default=false
UniqueStackage ident UniqueStackage ident
UniqueSnapshot slug UniqueSnapshot slug

View File

@ -53,3 +53,4 @@
/older-releases OlderReleasesR GET /older-releases OlderReleasesR GET
/refresh-deprecated RefreshDeprecatedR GET /refresh-deprecated RefreshDeprecatedR GET
/upload2 UploadV2R PUT

View File

@ -1,4 +1,3 @@
-- Stackage snapshot: http://www.stackage.org/stackage/aecbf72b568a63e86a971311fee5475f076043cc
name: stackage-server name: stackage-server
version: 0.0.0 version: 0.0.0
cabal-version: >= 1.8 cabal-version: >= 1.8
@ -51,6 +50,7 @@ library
Handler.Tag Handler.Tag
Handler.BannedTags Handler.BannedTags
Handler.RefreshDeprecated Handler.RefreshDeprecated
Handler.UploadV2
if flag(dev) || flag(library-only) if flag(dev) || flag(library-only)
cpp-options: -DDEVELOPMENT cpp-options: -DDEVELOPMENT
@ -150,6 +150,8 @@ library
, blaze-html , blaze-html
, haddock-library , haddock-library
, yesod-gitrepo , yesod-gitrepo
, async
, stackage >= 0.4
executable stackage-server executable stackage-server
if flag(library-only) if flag(library-only)

View File

@ -1,6 +1,6 @@
<h1>Module listing for #{toPathPiece slug} <h1>Module listing for #{toPathPiece slug}
<p> <p>
<a href=@{SnapshotR slug DocsR}>Return to snapshot <a href=@{SnapshotR slug StackageHomeR}>Return to snapshot
<ul> <ul>
$forall (name, url, package, version) <- modules $forall (name, url, package, version) <- modules
<li> <li>

View File

@ -4,11 +4,10 @@ $newline never
#{stackageTitle stackage} #{stackageTitle stackage}
<p> <p>
Published on #{yearMonthDay (stackageUploaded stackage)} Published on #{yearMonthDay (stackageUploaded stackage)}
$if hasBundle <span .separator>
<span .separator> <span>
<span> <a href=@{SnapshotR slug StackageCabalConfigR}>
<a href=@{SnapshotR slug StackageCabalConfigR}> \cabal.config
\cabal.config
<h3>Setup guide <h3>Setup guide
<div class="accordion" id="accordion2"> <div class="accordion" id="accordion2">