mirror of
https://github.com/commercialhaskell/stackage.git
synced 2026-01-24 13:11:57 +01:00
WIP new upload
This commit is contained in:
parent
4377decd33
commit
982bcfa2ad
@ -1,3 +1,7 @@
|
|||||||
|
## 0.4.0
|
||||||
|
|
||||||
|
* Upload bundle V2 stuff
|
||||||
|
|
||||||
## 0.3.1
|
## 0.3.1
|
||||||
|
|
||||||
* Added `justCheck` and `stackage check` command line.
|
* Added `justCheck` and `stackage check` command line.
|
||||||
|
|||||||
@ -412,16 +412,6 @@ singleBuild pb@PerformBuild {..} SingleBuild {..} =
|
|||||||
renameOrCopy :: FilePath -> FilePath -> IO ()
|
renameOrCopy :: FilePath -> FilePath -> IO ()
|
||||||
renameOrCopy src dest = rename src dest `catchIO` \_ -> copyDir src dest
|
renameOrCopy src dest = rename src dest `catchIO` \_ -> copyDir src dest
|
||||||
|
|
||||||
copyDir :: FilePath -> FilePath -> IO ()
|
|
||||||
copyDir src dest =
|
|
||||||
runResourceT $ sourceDirectoryDeep False src $$ mapM_C go
|
|
||||||
where
|
|
||||||
src' = src </> ""
|
|
||||||
go fp = forM_ (F.stripPrefix src' fp) $ \suffix -> do
|
|
||||||
let dest' = dest </> suffix
|
|
||||||
liftIO $ createTree $ parent dest'
|
|
||||||
sourceFile fp $$ (sinkFile dest' :: Sink ByteString (ResourceT IO) ())
|
|
||||||
|
|
||||||
copyBuiltInHaddocks :: FilePath -> IO ()
|
copyBuiltInHaddocks :: FilePath -> IO ()
|
||||||
copyBuiltInHaddocks docdir = do
|
copyBuiltInHaddocks docdir = do
|
||||||
mghc <- findExecutable "ghc"
|
mghc <- findExecutable "ghc"
|
||||||
|
|||||||
@ -20,6 +20,9 @@ import Distribution.Version as X (Version (..),
|
|||||||
VersionRange)
|
VersionRange)
|
||||||
import Distribution.Version as X (withinRange)
|
import Distribution.Version as X (withinRange)
|
||||||
import qualified Distribution.Version as C
|
import qualified Distribution.Version as C
|
||||||
|
import Filesystem (createTree)
|
||||||
|
import Filesystem.Path (parent)
|
||||||
|
import qualified Filesystem.Path as F
|
||||||
|
|
||||||
unPackageName :: PackageName -> Text
|
unPackageName :: PackageName -> Text
|
||||||
unPackageName (PackageName str) = pack str
|
unPackageName (PackageName str) = pack str
|
||||||
@ -101,3 +104,13 @@ topologicalSort toFinal toDeps =
|
|||||||
data TopologicalSortException key = NoEmptyDeps (Map key (Set key))
|
data TopologicalSortException key = NoEmptyDeps (Map key (Set key))
|
||||||
deriving (Show, Typeable)
|
deriving (Show, Typeable)
|
||||||
instance (Show key, Typeable key) => Exception (TopologicalSortException key)
|
instance (Show key, Typeable key) => Exception (TopologicalSortException key)
|
||||||
|
|
||||||
|
copyDir :: FilePath -> FilePath -> IO ()
|
||||||
|
copyDir src dest =
|
||||||
|
runResourceT $ sourceDirectoryDeep False src $$ mapM_C go
|
||||||
|
where
|
||||||
|
src' = src </> ""
|
||||||
|
go fp = forM_ (F.stripPrefix src' fp) $ \suffix -> do
|
||||||
|
let dest' = dest </> suffix
|
||||||
|
liftIO $ createTree $ parent dest'
|
||||||
|
sourceFile fp $$ (sinkFile dest' :: Sink ByteString (ResourceT IO) ())
|
||||||
|
|||||||
@ -7,21 +7,31 @@ module Stackage.ServerBundle
|
|||||||
, epochTime
|
, epochTime
|
||||||
, bpAllPackages
|
, bpAllPackages
|
||||||
, docsListing
|
, docsListing
|
||||||
|
, createBundleV2
|
||||||
|
, CreateBundleV2 (..)
|
||||||
|
, SnapshotType (..)
|
||||||
|
, writeIndexStyle
|
||||||
|
, DocMap
|
||||||
|
, PackageDocs (..)
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import qualified Codec.Archive.Tar as Tar
|
import qualified Codec.Archive.Tar as Tar
|
||||||
import qualified Codec.Archive.Tar.Entry as Tar
|
import qualified Codec.Archive.Tar.Entry as Tar
|
||||||
import qualified Codec.Compression.GZip as GZip
|
import qualified Codec.Compression.GZip as GZip
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
|
import Data.Aeson (ToJSON (..), (.=), object, FromJSON (..), (.:), withObject)
|
||||||
|
import System.IO.Temp (withTempDirectory)
|
||||||
import qualified Data.Yaml as Y
|
import qualified Data.Yaml as Y
|
||||||
import Filesystem (isFile)
|
import Filesystem (isFile, getWorkingDirectory, listDirectory, isDirectory, canonicalizePath)
|
||||||
import Foreign.C.Types (CTime (CTime))
|
import Foreign.C.Types (CTime (CTime))
|
||||||
import Stackage.BuildConstraints
|
import Stackage.BuildConstraints
|
||||||
import Stackage.BuildPlan
|
import Stackage.BuildPlan
|
||||||
import Stackage.Prelude
|
import Stackage.Prelude
|
||||||
|
import System.IO.Temp (withTempDirectory)
|
||||||
import qualified System.PosixCompat.Time as PC
|
import qualified System.PosixCompat.Time as PC
|
||||||
import qualified Text.XML as X
|
import qualified Text.XML as X
|
||||||
import Text.XML.Cursor
|
import Text.XML.Cursor
|
||||||
|
import System.PosixCompat.Files (createSymbolicLink)
|
||||||
|
|
||||||
-- | Get current time
|
-- | Get current time
|
||||||
epochTime :: IO Tar.EpochTime
|
epochTime :: IO Tar.EpochTime
|
||||||
@ -73,13 +83,30 @@ serverBundle time title slug bp@BuildPlan {..} = GZip.compress $ Tar.write
|
|||||||
map (\(PackageName name) -> name)
|
map (\(PackageName name) -> name)
|
||||||
(M.keys $ siCorePackages bpSystemInfo)
|
(M.keys $ siCorePackages bpSystemInfo)
|
||||||
|
|
||||||
|
-- | Package name is key
|
||||||
|
type DocMap = Map Text PackageDocs
|
||||||
|
data PackageDocs = PackageDocs
|
||||||
|
{ pdVersion :: Text
|
||||||
|
, pdModules :: Map Text [Text]
|
||||||
|
-- ^ module name, path
|
||||||
|
}
|
||||||
|
instance ToJSON PackageDocs where
|
||||||
|
toJSON PackageDocs {..} = object
|
||||||
|
[ "version" .= pdVersion
|
||||||
|
, "modules" .= pdModules
|
||||||
|
]
|
||||||
|
instance FromJSON PackageDocs where
|
||||||
|
parseJSON = withObject "PackageDocs" $ \o -> PackageDocs
|
||||||
|
<$> o .: "version"
|
||||||
|
<*> o .: "modules"
|
||||||
|
|
||||||
docsListing :: BuildPlan
|
docsListing :: BuildPlan
|
||||||
-> FilePath -- ^ docs directory
|
-> FilePath -- ^ docs directory
|
||||||
-> IO ByteString
|
-> IO DocMap
|
||||||
docsListing bp docsDir =
|
docsListing bp docsDir =
|
||||||
fmap (Y.encode . fold) $ mapM go $ mapToList $ bpAllPackages bp
|
fmap fold $ mapM go $ mapToList $ bpAllPackages bp
|
||||||
where
|
where
|
||||||
go :: (PackageName, Version) -> IO (Map Text Y.Value)
|
go :: (PackageName, Version) -> IO DocMap
|
||||||
go (package, version) = do -- handleAny (const $ return mempty) $ do
|
go (package, version) = do -- handleAny (const $ return mempty) $ do
|
||||||
let dirname = fpFromText (concat
|
let dirname = fpFromText (concat
|
||||||
[ display package
|
[ display package
|
||||||
@ -107,8 +134,138 @@ docsListing bp docsDir =
|
|||||||
return $ if e
|
return $ if e
|
||||||
then asMap $ singletonMap name [fpToText dirname, href]
|
then asMap $ singletonMap name [fpToText dirname, href]
|
||||||
else mempty
|
else mempty
|
||||||
return $ singletonMap (display package) $ Y.object
|
return $ singletonMap (display package) $ PackageDocs
|
||||||
[ "version" Y..= display version
|
{ pdVersion = display version
|
||||||
, "modules" Y..= m
|
, pdModules = m
|
||||||
]
|
}
|
||||||
else return mempty
|
else return mempty
|
||||||
|
|
||||||
|
data SnapshotType = STNightly
|
||||||
|
| STLTS !Int !Int -- ^ major, minor
|
||||||
|
deriving (Show, Read, Eq, Ord)
|
||||||
|
|
||||||
|
instance ToJSON SnapshotType where
|
||||||
|
toJSON STNightly = object
|
||||||
|
[ "type" .= asText "nightly"
|
||||||
|
]
|
||||||
|
toJSON (STLTS major minor) = object
|
||||||
|
[ "type" .= asText "lts"
|
||||||
|
, "major" .= major
|
||||||
|
, "minor" .= minor
|
||||||
|
]
|
||||||
|
instance FromJSON SnapshotType where
|
||||||
|
parseJSON = withObject "SnapshotType" $ \o -> do
|
||||||
|
t <- o .: "type"
|
||||||
|
case asText t of
|
||||||
|
"nightly" -> return STNightly
|
||||||
|
"lts" -> STLTS
|
||||||
|
<$> o .: "major"
|
||||||
|
<*> o .: "minor"
|
||||||
|
_ -> fail $ "Unknown type for SnapshotType: " ++ unpack t
|
||||||
|
|
||||||
|
data CreateBundleV2 = CreateBundleV2
|
||||||
|
{ cb2Plan :: BuildPlan
|
||||||
|
, cb2Type :: SnapshotType
|
||||||
|
, cb2DocsDir :: FilePath
|
||||||
|
, cb2Dest :: FilePath
|
||||||
|
}
|
||||||
|
|
||||||
|
-- | Create a V2 bundle, which contains the build plan, metadata, docs, and doc
|
||||||
|
-- map.
|
||||||
|
createBundleV2 :: CreateBundleV2 -> IO ()
|
||||||
|
createBundleV2 CreateBundleV2 {..} = do
|
||||||
|
docsDir <- canonicalizePath cb2DocsDir
|
||||||
|
docMap <- docsListing cb2Plan cb2DocsDir
|
||||||
|
|
||||||
|
Y.encodeFile (fpToString $ docsDir </> "build-plan.yaml") cb2Plan
|
||||||
|
Y.encodeFile (fpToString $ docsDir </> "build-type.yaml") cb2Type
|
||||||
|
Y.encodeFile (fpToString $ docsDir </> "docs-map.yaml") docMap
|
||||||
|
void $ writeIndexStyle Nothing cb2DocsDir
|
||||||
|
|
||||||
|
currentDir <- getWorkingDirectory
|
||||||
|
files <- listDirectory docsDir
|
||||||
|
|
||||||
|
let args = "cfJ"
|
||||||
|
: fpToString (currentDir </> cb2Dest)
|
||||||
|
: "--dereference"
|
||||||
|
: map (fpToString . filename) files
|
||||||
|
cp = (proc "tar" args) { cwd = Just $ fpToString docsDir }
|
||||||
|
withCheckedProcess cp $ \ClosedStream Inherited Inherited -> return ()
|
||||||
|
|
||||||
|
writeIndexStyle :: Maybe Text -- ^ snapshot id
|
||||||
|
-> FilePath -- ^ docs dir
|
||||||
|
-> IO [String]
|
||||||
|
writeIndexStyle msnapid dir = do
|
||||||
|
dirs <- fmap sort
|
||||||
|
$ runResourceT
|
||||||
|
$ sourceDirectory dir
|
||||||
|
$$ filterMC (liftIO . isDirectory)
|
||||||
|
=$ mapC (fpToString . filename)
|
||||||
|
=$ sinkList
|
||||||
|
writeFile (dir </> "index.html") $ mkIndex
|
||||||
|
(unpack <$> msnapid)
|
||||||
|
dirs
|
||||||
|
writeFile (dir </> "style.css") styleCss
|
||||||
|
return dirs
|
||||||
|
|
||||||
|
mkIndex :: Maybe String -> [String] -> String
|
||||||
|
mkIndex msnapid dirs = concat
|
||||||
|
[ "<!DOCTYPE html>\n<html lang='en'><head><title>Haddocks index</title>"
|
||||||
|
, "<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css'>"
|
||||||
|
, "<link rel='stylesheet' href='style.css'>"
|
||||||
|
, "<link rel='shortcut icon' href='http://www.stackage.org/static/img/favicon.ico' />"
|
||||||
|
, "</head>"
|
||||||
|
, "<body><div class='container'>"
|
||||||
|
, "<div class='row'><div class='span12 col-md-12'>"
|
||||||
|
, "<h1>Haddock documentation index</h1>"
|
||||||
|
, flip foldMap msnapid $ \snapid -> concat
|
||||||
|
[ "<p class='return'><a href=\"http://www.stackage.org/stackage/"
|
||||||
|
, snapid
|
||||||
|
, "\">Return to snapshot</a></p>"
|
||||||
|
]
|
||||||
|
, "<ul>"
|
||||||
|
, concatMap toLI dirs
|
||||||
|
, "</ul></div></div></div></body></html>"
|
||||||
|
]
|
||||||
|
where
|
||||||
|
toLI name = concat
|
||||||
|
[ "<li><a href='"
|
||||||
|
, name
|
||||||
|
, "/index.html'>"
|
||||||
|
, name
|
||||||
|
, "</a></li>"
|
||||||
|
]
|
||||||
|
|
||||||
|
styleCss :: String
|
||||||
|
styleCss = concat
|
||||||
|
[ "@media (min-width: 530px) {"
|
||||||
|
, "ul { -webkit-column-count: 2; -moz-column-count: 2; column-count: 2 }"
|
||||||
|
, "}"
|
||||||
|
, "@media (min-width: 760px) {"
|
||||||
|
, "ul { -webkit-column-count: 3; -moz-column-count: 3; column-count: 3 }"
|
||||||
|
, "}"
|
||||||
|
, "ul {"
|
||||||
|
, " margin-left: 0;"
|
||||||
|
, " padding-left: 0;"
|
||||||
|
, " list-style-type: none;"
|
||||||
|
, "}"
|
||||||
|
, "body {"
|
||||||
|
, " background: #f0f0f0;"
|
||||||
|
, " font-family: 'Lato', sans-serif;"
|
||||||
|
, " text-shadow: 1px 1px 1px #ffffff;"
|
||||||
|
, " font-size: 20px;"
|
||||||
|
, " line-height: 30px;"
|
||||||
|
, " padding-bottom: 5em;"
|
||||||
|
, "}"
|
||||||
|
, "h1 {"
|
||||||
|
, " font-weight: normal;"
|
||||||
|
, " color: #06537d;"
|
||||||
|
, " font-size: 45px;"
|
||||||
|
, "}"
|
||||||
|
, ".return a {"
|
||||||
|
, " color: #06537d;"
|
||||||
|
, " font-style: italic;"
|
||||||
|
, "}"
|
||||||
|
, ".return {"
|
||||||
|
, " margin-bottom: 1em;"
|
||||||
|
, "}"]
|
||||||
|
|||||||
@ -13,17 +13,23 @@ module Stackage.Upload
|
|||||||
, uploadHackageDistro
|
, uploadHackageDistro
|
||||||
, UploadDocMap (..)
|
, UploadDocMap (..)
|
||||||
, uploadDocMap
|
, uploadDocMap
|
||||||
|
, uploadBundleV2
|
||||||
|
, UploadBundleV2 (..)
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Monad.Writer.Strict (execWriter, tell)
|
import Control.Monad.Writer.Strict (execWriter, tell)
|
||||||
import Data.Default.Class (Default (..))
|
import Data.Default.Class (Default (..))
|
||||||
|
import Data.Function (fix)
|
||||||
import Filesystem (isDirectory, isFile)
|
import Filesystem (isDirectory, isFile)
|
||||||
import Network.HTTP.Client
|
import Network.HTTP.Client
|
||||||
|
import qualified Network.HTTP.Client.Conduit as HCC
|
||||||
import Network.HTTP.Client.MultipartFormData
|
import Network.HTTP.Client.MultipartFormData
|
||||||
import Stackage.BuildPlan (BuildPlan)
|
import Stackage.BuildPlan (BuildPlan)
|
||||||
import Stackage.Prelude
|
import Stackage.Prelude
|
||||||
import Stackage.ServerBundle (bpAllPackages, docsListing)
|
import Stackage.ServerBundle (bpAllPackages, docsListing, writeIndexStyle)
|
||||||
import System.IO.Temp (withSystemTempFile)
|
import System.IO.Temp (withSystemTempFile)
|
||||||
|
import qualified System.IO as IO
|
||||||
|
import qualified Data.Yaml as Y
|
||||||
|
|
||||||
newtype StackageServer = StackageServer { unStackageServer :: Text }
|
newtype StackageServer = StackageServer { unStackageServer :: Text }
|
||||||
deriving (Show, Eq, Ord, Hashable, IsString)
|
deriving (Show, Eq, Ord, Hashable, IsString)
|
||||||
@ -106,17 +112,7 @@ uploadDocs (UploadDocs (StackageServer host) fp0 token ident) man = do
|
|||||||
where
|
where
|
||||||
uploadDocsDir = withSystemTempFile "haddocks.tar.xz" $ \fp h -> do
|
uploadDocsDir = withSystemTempFile "haddocks.tar.xz" $ \fp h -> do
|
||||||
hClose h
|
hClose h
|
||||||
dirs <- fmap sort
|
dirs <- writeIndexStyle (Just $ unSnapshotIdent ident) fp0
|
||||||
$ runResourceT
|
|
||||||
$ sourceDirectory fp0
|
|
||||||
$$ filterMC (liftIO . isDirectory)
|
|
||||||
=$ mapC (fpToString . filename)
|
|
||||||
=$ sinkList
|
|
||||||
writeFile (fp0 </> "index.html") $ mkIndex
|
|
||||||
(unpack $ unSnapshotIdent ident)
|
|
||||||
dirs
|
|
||||||
writeFile (fp0 </> "style.css") styleCss
|
|
||||||
-- FIXME write index.html, style.css
|
|
||||||
let cp = (proc "tar" $ "cJf" : fp : "index.html" : "style.css" : dirs)
|
let cp = (proc "tar" $ "cJf" : fp : "index.html" : "style.css" : dirs)
|
||||||
{ cwd = Just $ fpToString fp0
|
{ cwd = Just $ fpToString fp0
|
||||||
}
|
}
|
||||||
@ -187,7 +183,7 @@ uploadDocMap :: UploadDocMap -> Manager -> IO (Response LByteString)
|
|||||||
uploadDocMap UploadDocMap {..} man = do
|
uploadDocMap UploadDocMap {..} man = do
|
||||||
docmap <- docsListing udmPlan udmDocDir
|
docmap <- docsListing udmPlan udmDocDir
|
||||||
req1 <- parseUrl $ unpack $ unStackageServer udmServer ++ "/upload-doc-map"
|
req1 <- parseUrl $ unpack $ unStackageServer udmServer ++ "/upload-doc-map"
|
||||||
req2 <- formDataBody (formData docmap) req1
|
req2 <- formDataBody (formData $ Y.encode docmap) req1
|
||||||
let req3 = req2
|
let req3 = req2
|
||||||
{ method = "PUT"
|
{ method = "PUT"
|
||||||
, requestHeaders =
|
, requestHeaders =
|
||||||
@ -205,61 +201,54 @@ uploadDocMap UploadDocMap {..} man = do
|
|||||||
, partFileRequestBody "docmap" "docmap" $ RequestBodyBS docmap
|
, partFileRequestBody "docmap" "docmap" $ RequestBodyBS docmap
|
||||||
]
|
]
|
||||||
|
|
||||||
mkIndex :: String -> [String] -> String
|
data UploadBundleV2 = UploadBundleV2
|
||||||
mkIndex snapid dirs = concat
|
{ ub2Server :: StackageServer
|
||||||
[ "<!DOCTYPE html>\n<html lang='en'><head><title>Haddocks index</title>"
|
, ub2AuthToken :: Text
|
||||||
, "<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css'>"
|
, ub2Bundle :: FilePath
|
||||||
, "<link rel='stylesheet' href='style.css'>"
|
}
|
||||||
, "<link rel='shortcut icon' href='http://www.stackage.org/static/img/favicon.ico' />"
|
|
||||||
, "</head>"
|
|
||||||
, "<body><div class='container'>"
|
|
||||||
, "<div class='row'><div class='span12 col-md-12'>"
|
|
||||||
, "<h1>Haddock documentation index</h1>"
|
|
||||||
, "<p class='return'><a href=\"http://www.stackage.org/stackage/"
|
|
||||||
, snapid
|
|
||||||
, "\">Return to snapshot</a></p><ul>"
|
|
||||||
, concatMap toLI dirs
|
|
||||||
, "</ul></div></div></div></body></html>"
|
|
||||||
]
|
|
||||||
where
|
|
||||||
toLI name = concat
|
|
||||||
[ "<li><a href='"
|
|
||||||
, name
|
|
||||||
, "/index.html'>"
|
|
||||||
, name
|
|
||||||
, "</a></li>"
|
|
||||||
]
|
|
||||||
|
|
||||||
styleCss :: String
|
uploadBundleV2 :: UploadBundleV2 -> Manager -> IO Text
|
||||||
styleCss = concat
|
uploadBundleV2 UploadBundleV2 {..} man = IO.withBinaryFile (fpToString ub2Bundle) IO.ReadMode $ \h -> do
|
||||||
[ "@media (min-width: 530px) {"
|
size <- IO.hFileSize h
|
||||||
, "ul { -webkit-column-count: 2; -moz-column-count: 2; column-count: 2 }"
|
req1 <- parseUrl $ unpack $ unStackageServer ub2Server ++ "/upload2"
|
||||||
, "}"
|
let req2 = req1
|
||||||
, "@media (min-width: 760px) {"
|
{ method = "PUT"
|
||||||
, "ul { -webkit-column-count: 3; -moz-column-count: 3; column-count: 3 }"
|
, requestHeaders =
|
||||||
, "}"
|
[ ("Authorization", encodeUtf8 ub2AuthToken)
|
||||||
, "ul {"
|
, ("Accept", "application/json")
|
||||||
, " margin-left: 0;"
|
, ("Content-Type", "application/x-tar")
|
||||||
, " padding-left: 0;"
|
]
|
||||||
, " list-style-type: none;"
|
, requestBody = HCC.requestBodySource (fromIntegral size)
|
||||||
, "}"
|
$ sourceHandle h $= printProgress size
|
||||||
, "body {"
|
}
|
||||||
, " background: #f0f0f0;"
|
sink = decodeUtf8C =$ fix (\loop -> do
|
||||||
, " font-family: 'Lato', sans-serif;"
|
mx <- peekC
|
||||||
, " text-shadow: 1px 1px 1px #ffffff;"
|
case mx of
|
||||||
, " font-size: 20px;"
|
Nothing -> error $ "uploadBundleV2: premature end of stream"
|
||||||
, " line-height: 30px;"
|
Just _ -> do
|
||||||
, " padding-bottom: 5em;"
|
l <- lineC $ takeCE 4096 =$ foldC
|
||||||
, "}"
|
let (cmd, msg') = break (== ':') l
|
||||||
, "h1 {"
|
msg = dropWhile (== ' ') $ dropWhile (== ':') msg'
|
||||||
, " font-weight: normal;"
|
case cmd of
|
||||||
, " color: #06537d;"
|
"CONT" -> do
|
||||||
, " font-size: 45px;"
|
putStrLn msg
|
||||||
, "}"
|
loop
|
||||||
, ".return a {"
|
"FAILURE" -> error $ "uploadBundleV2 failed: " ++ unpack msg
|
||||||
, " color: #06537d;"
|
"SUCCESS" -> return msg
|
||||||
, " font-style: italic;"
|
_ -> error $ "uploadBundleV2: unknown command " ++ unpack cmd
|
||||||
, "}"
|
)
|
||||||
, ".return {"
|
withResponse req2 man $ \res -> HCC.bodyReaderSource (responseBody res) $$ sink
|
||||||
, " margin-bottom: 1em;"
|
where
|
||||||
, "}"]
|
printProgress total =
|
||||||
|
loop 0 0
|
||||||
|
where
|
||||||
|
loop sent lastPercent =
|
||||||
|
await >>= maybe (putStrLn "Upload complete") go
|
||||||
|
where
|
||||||
|
go bs = do
|
||||||
|
yield bs
|
||||||
|
let sent' = sent + fromIntegral (length bs)
|
||||||
|
percent = sent' * 100 `div` total
|
||||||
|
when (percent /= lastPercent)
|
||||||
|
$ putStrLn $ "Upload progress: " ++ tshow percent ++ "%"
|
||||||
|
loop sent' percent
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
name: stackage
|
name: stackage
|
||||||
version: 0.3.1
|
version: 0.4.0
|
||||||
synopsis: "Stable Hackage," tools for creating a vetted set of packages from Hackage.
|
synopsis: "Stable Hackage," tools for creating a vetted set of packages from Hackage.
|
||||||
description: Please see <http://www.stackage.org/package/stackage> for a description and documentation.
|
description: Please see <http://www.stackage.org/package/stackage> for a description and documentation.
|
||||||
homepage: https://github.com/fpco/stackage
|
homepage: https://github.com/fpco/stackage
|
||||||
@ -52,6 +52,7 @@ library
|
|||||||
, yaml
|
, yaml
|
||||||
, unix-compat
|
, unix-compat
|
||||||
, http-client
|
, http-client
|
||||||
|
, http-conduit
|
||||||
, http-client-tls
|
, http-client-tls
|
||||||
, temporary
|
, temporary
|
||||||
, data-default-class
|
, data-default-class
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user