Merge pull request #92 from fpco/download-handler

Download handler
This commit is contained in:
Michael Snoyman 2015-04-26 08:48:58 +03:00
commit cafa6b0496
10 changed files with 203 additions and 0 deletions

View File

@ -74,6 +74,7 @@ import Handler.BuildVersion
import Handler.PackageCounts import Handler.PackageCounts
import Handler.Sitemap import Handler.Sitemap
import Handler.BuildPlan import Handler.BuildPlan
import Handler.Download
-- 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

41
Data/GhcLinks.hs Normal file
View File

@ -0,0 +1,41 @@
module Data.GhcLinks
( GhcLinks(..)
, readGhcLinks
) where
import ClassyPrelude.Yesod
import Control.Monad.State.Strict (modify, execStateT)
import qualified Data.HashMap.Strict as HashMap
import Filesystem (readTextFile, isFile)
import Types
newtype GhcLinks = GhcLinks
{ ghcLinksMap :: HashMap (SupportedArch, GhcMajorVersion) Text }
-- ^ a map from (arch, ver) to yaml
supportedArches :: [SupportedArch]
supportedArches = [minBound .. maxBound]
supportedGhcMajorVersions :: [GhcMajorVersion]
supportedGhcMajorVersions = ["7.8"]
readGhcLinks :: FilePath -> IO GhcLinks
readGhcLinks dir = do
let opts =
[ (arch, ver)
| arch <- supportedArches
, ver <- supportedGhcMajorVersions
]
hashMap <- flip execStateT HashMap.empty
$ forM_ opts $ \(arch, ver) -> do
let fileName = "ghc-" <> ver <> "-links.yaml"
let path = dir
</> fpFromText (toPathPiece arch)
</> fpFromText fileName
whenM (liftIO $ isFile path) $ do
text <- liftIO $ readTextFile path
modify (HashMap.insert (arch, ver) text)
return $ GhcLinks hashMap

View File

@ -5,12 +5,14 @@ module Data.WebsiteContent
import ClassyPrelude.Yesod import ClassyPrelude.Yesod
import Text.Markdown (markdown, msXssProtect, msAddHeadingId) import Text.Markdown (markdown, msXssProtect, msAddHeadingId)
import Data.GhcLinks
data WebsiteContent = WebsiteContent data WebsiteContent = WebsiteContent
{ wcHomepage :: !Html { wcHomepage :: !Html
, wcAuthors :: !Html , wcAuthors :: !Html
, wcInstall :: !Html , wcInstall :: !Html
, wcOlderReleases :: !Html , wcOlderReleases :: !Html
, wcGhcLinks :: !GhcLinks
} }
loadWebsiteContent :: FilePath -> IO WebsiteContent loadWebsiteContent :: FilePath -> IO WebsiteContent
@ -20,6 +22,7 @@ loadWebsiteContent dir = do
wcInstall <- readMarkdown "install.md" wcInstall <- readMarkdown "install.md"
wcOlderReleases <- readHtml "older-releases.html" `catchIO` wcOlderReleases <- readHtml "older-releases.html" `catchIO`
\_ -> readMarkdown "older-releases.md" \_ -> readMarkdown "older-releases.md"
wcGhcLinks <- readGhcLinks $ dir </> "stackage-cli"
return WebsiteContent {..} return WebsiteContent {..}
where where
readHtml fp = fmap (preEscapedToMarkup . decodeUtf8 :: ByteString -> Html) readHtml fp = fmap (preEscapedToMarkup . decodeUtf8 :: ByteString -> Html)

View File

@ -11,6 +11,7 @@ import Handler.StackageIndex (getStackageIndexR)
import Handler.StackageSdist (getStackageSdistR) import Handler.StackageSdist (getStackageSdistR)
import Handler.Hoogle (getHoogleR, getHoogleDatabaseR) import Handler.Hoogle (getHoogleR, getHoogleDatabaseR)
import Handler.BuildPlan (getBuildPlanR) import Handler.BuildPlan (getBuildPlanR)
import Handler.Download (getGhcMajorVersionR)
handleAliasR :: Slug -> Slug -> [Text] -> Handler () handleAliasR :: Slug -> Slug -> [Text] -> Handler ()
handleAliasR user name pieces = do handleAliasR user name pieces = do
@ -81,4 +82,5 @@ goSid sid pieces = do
HoogleR -> getHoogleR slug >>= sendResponse HoogleR -> getHoogleR slug >>= sendResponse
HoogleDatabaseR -> getHoogleDatabaseR slug >>= sendResponse HoogleDatabaseR -> getHoogleDatabaseR slug >>= sendResponse
BuildPlanR -> getBuildPlanR slug >>= sendResponse BuildPlanR -> getBuildPlanR slug >>= sendResponse
GhcMajorVersionR -> getGhcMajorVersionR slug >>= sendResponse
_ -> notFound _ -> notFound

79
Handler/Download.hs Normal file
View File

@ -0,0 +1,79 @@
module Handler.Download
( getDownloadR
, getDownloadLtsSnapshotsJsonR
, getGhcMajorVersionR
, getDownloadGhcLinksR
) where
import Import
import Data.Slug (SnapSlug)
import Data.GhcLinks
import Yesod.GitRepo (grContent)
executableFor :: SupportedArch -> StackageExecutable
executableFor Win32 = StackageWindowsExecutable
executableFor Win64 = StackageWindowsExecutable
executableFor _ = StackageUnixExecutable
-- TODO: link to s3
executableLink :: SupportedArch -> StackageExecutable -> Text
executableLink arch exe =
"https://s3.amazonaws.com/download.fpcomplete.com/stackage-cli/"
<> toPathPiece arch <> "/" <> toPathPiece exe
downloadCandidates :: [(SupportedArch, StackageExecutable)]
downloadCandidates =
map (\arch -> (arch, executableFor arch))
[minBound .. maxBound]
currentlySupported :: SupportedArch -> Bool
currentlySupported Linux64 = True
currentlySupported _ = False
getDownloadR :: Handler Html
getDownloadR = defaultLayout $ do
$(widgetFile "download")
ltsMajorVersions :: Handler [Lts]
ltsMajorVersions = liftM (map entityVal) $ runDB $ do
mapWhileIsJustM [0..] $ \x -> do
selectFirst [LtsMajor ==. x] [Desc LtsMinor]
mapWhileIsJustM :: Monad m => [a] -> (a -> m (Maybe b)) -> m [b]
mapWhileIsJustM [] _f = return []
mapWhileIsJustM (x:xs) f = f x >>= \case
Nothing -> return []
Just y -> (y:) `liftM` mapWhileIsJustM xs f
getDownloadLtsSnapshotsJsonR :: Handler Value
getDownloadLtsSnapshotsJsonR = liftM reverse ltsMajorVersions >>= \case
[] -> return $ object []
majorVersions@(latest:_) -> return $ object
$ ["lts" .= printLts latest]
++ map toObj majorVersions
where
toObj lts@(Lts major _ _) =
pack ("lts-" ++ show major) .= printLts lts
printLts (Lts major minor _) =
"lts-" ++ show major ++ "." ++ show minor
-- TODO: add this to db
ltsGhcMajorVersion :: Stackage -> Text
ltsGhcMajorVersion _ = "7.8"
getGhcMajorVersionR :: SnapSlug -> Handler Text
getGhcMajorVersionR slug = do
snapshot <- runDB $ getBy404 $ UniqueSnapshot slug
return $ ltsGhcMajorVersion $ entityVal snapshot
getDownloadGhcLinksR :: SupportedArch -> Text -> Handler TypedContent
getDownloadGhcLinksR arch fileName = do
ver <- maybe notFound return
$ stripPrefix "ghc-" >=> stripSuffix "-links.yaml"
$ fileName
ghcLinks <- getYesod >>= fmap wcGhcLinks . liftIO . grContent . websiteContent
case lookup (arch, ver) (ghcLinksMap ghcLinks) of
Just text -> return $ TypedContent yamlMimeType $ toContent text
Nothing -> notFound
where
yamlMimeType = "text/yaml"

View File

@ -13,6 +13,8 @@ getSitemapR = sitemap $ do
priority 1.0 $ HomeR priority 1.0 $ HomeR
priority 0.9 $ LtsR [] priority 0.9 $ LtsR []
-- TODO: uncomment when this is presentable
--priority 0.9 $ DownloadR
priority 0.8 $ NightlyR [] priority 0.8 $ NightlyR []
priority 0.7 $ AllSnapshotsR priority 0.7 $ AllSnapshotsR

View File

@ -2,6 +2,7 @@ module Types where
import ClassyPrelude.Yesod import ClassyPrelude.Yesod
import Data.BlobStore (ToPath (..), BackupToS3 (..)) import Data.BlobStore (ToPath (..), BackupToS3 (..))
import Data.Hashable (hashUsing)
import Text.Blaze (ToMarkup) import Text.Blaze (ToMarkup)
import Database.Persist.Sql (PersistFieldSql (sqlType)) import Database.Persist.Sql (PersistFieldSql (sqlType))
import qualified Data.Text as T import qualified Data.Text as T
@ -102,3 +103,47 @@ instance HasHackageRoot HackageRoot where
data UnpackStatus = USReady data UnpackStatus = USReady
| USBusy | USBusy
| USFailed !Text | USFailed !Text
data StackageExecutable
= StackageWindowsExecutable
| StackageUnixExecutable
deriving (Show, Read, Eq)
instance PathPiece StackageExecutable where
-- TODO: distribute stackage, not just stackage-setup
toPathPiece StackageWindowsExecutable = "stackage-setup.exe"
toPathPiece StackageUnixExecutable = "stackage-setup"
fromPathPiece "stackage-setup" = Just StackageUnixExecutable
fromPathPiece "stackage-setup.exe" = Just StackageWindowsExecutable
fromPathPiece _ = Nothing
type GhcMajorVersion = Text
data SupportedArch
= Win32
| Win64
| Linux32
| Linux64
| Mac32
| Mac64
deriving (Enum, Bounded, Show, Read, Eq)
instance Hashable SupportedArch where
hashWithSalt = hashUsing fromEnum
instance PathPiece SupportedArch where
toPathPiece Win32 = "win32"
toPathPiece Win64 = "win64"
toPathPiece Linux32 = "linux32"
toPathPiece Linux64 = "linux64"
toPathPiece Mac32 = "mac32"
toPathPiece Mac64 = "mac64"
fromPathPiece "win32" = Just Win32
fromPathPiece "win64" = Just Win64
fromPathPiece "linux32" = Just Linux32
fromPathPiece "linux64" = Just Linux64
fromPathPiece "mac32" = Just Mac32
fromPathPiece "mac64" = Just Mac64
fromPathPiece _ = Nothing

View File

@ -28,6 +28,7 @@
/hoogle HoogleR GET /hoogle HoogleR GET
/db.hoo HoogleDatabaseR GET /db.hoo HoogleDatabaseR GET
/build-plan BuildPlanR GET /build-plan BuildPlanR GET
/ghc-major-version GhcMajorVersionR GET
/aliases AliasesR PUT /aliases AliasesR PUT
/alias/#Slug/#Slug/*Texts AliasR /alias/#Slug/#Slug/*Texts AliasR
@ -57,3 +58,7 @@
/upload2 UploadV2R PUT /upload2 UploadV2R PUT
/build-version BuildVersionR GET /build-version BuildVersionR GET
/package-counts PackageCountsR GET /package-counts PackageCountsR GET
/download DownloadR GET
/download/lts-snapshots.json DownloadLtsSnapshotsJsonR GET
/download/#SupportedArch/#Text DownloadGhcLinksR GET

View File

@ -23,6 +23,7 @@ library
Data.Slug Data.Slug
Data.Tag Data.Tag
Data.BlobStore Data.BlobStore
Data.GhcLinks
Data.Hackage Data.Hackage
Data.Hackage.DeprecationInfo Data.Hackage.DeprecationInfo
Data.WebsiteContent Data.WebsiteContent
@ -54,6 +55,7 @@ library
Handler.PackageCounts Handler.PackageCounts
Handler.Sitemap Handler.Sitemap
Handler.BuildPlan Handler.BuildPlan
Handler.Download
if flag(dev) || flag(library-only) if flag(dev) || flag(library-only)
cpp-options: -DDEVELOPMENT cpp-options: -DDEVELOPMENT
@ -90,6 +92,7 @@ library
DeriveFunctor DeriveFunctor
DeriveFoldable DeriveFoldable
DeriveTraversable DeriveTraversable
LambdaCase
build-depends: build-depends:
base >= 4 base >= 4
@ -143,6 +146,7 @@ library
, yesod-static >= 1.2 , yesod-static >= 1.2
, zlib , zlib
, unordered-containers , unordered-containers
, hashable
-- Avoid https://github.com/haskell/cabal/issues/1202 -- Avoid https://github.com/haskell/cabal/issues/1202
, Cabal >= 1.18 , Cabal >= 1.18
, lifted-base , lifted-base

21
templates/download.hamlet Normal file
View File

@ -0,0 +1,21 @@
<h1>Warning: pre-release
<p>The following executable is considered experimental.
<p>More operating systems and architectures will be supported upon official release.
<h1>Download
$forall (arch, exe) <- downloadCandidates
<ul .downloads>
$if currentlySupported arch
<li>
#{toPathPiece arch}:
<a href=#{executableLink arch exe}>
#{toPathPiece exe}
<h1>What is stackage-setup?
<p>The stackage-setup executable is an easy way to download and set up the basic executables needed for developing Haskell.
<p>After running stackage-setup, You can find these executables in:
<code>~/.stackage/environment/$group/$group-$version/bin/
<p>For example, ghc, ghc-pkg, haddock, etc for ghc-7.8.4 are installed here:
<code>~/.stackage/environment/ghc/ghc-7.8.4/bin/