module Stackage.Upload
( uploadHackageDistro
, uploadBundleV2
, UploadBundleV2 (..)
, def
, StackageServer
, unStackageServer
) where
import Data.Default.Class (Default (..))
import Data.Function (fix)
import Network.HTTP.Client
import qualified Network.HTTP.Client.Conduit as HCC
import Stackage.Prelude
import Stackage.ServerBundle (bpAllPackages)
import qualified System.IO as IO
newtype StackageServer = StackageServer { unStackageServer :: Text }
deriving (Show, Eq, Ord, Hashable, IsString)
instance Default StackageServer where
def = "https://www.stackage.org"
uploadHackageDistro
:: Text
-> BuildPlan
-> ByteString
-> ByteString
-> Manager
-> IO (Response LByteString)
uploadHackageDistro name bp username password manager = do
req1 <- parseRequest $ concat
[ "https://hackage.haskell.org/distro/"
, unpack name
, "/packages.csv"
]
let req2 = req1
{ requestHeaders = [("Content-Type", "text/csv")]
, requestBody = RequestBodyLBS csv
, method = "PUT"
}
httpLbs (applyBasicAuth username password req2) manager
where
csv = encodeUtf8
$ builderToLazy
$ mconcat
$ intersperse "\n"
$ map go
$ mapToList
$ bpAllPackages bp
go (name', version) =
"\"" ++
(toBuilder $ display name') ++
"\",\"" ++
(toBuilder $ display version) ++
"\",\"https://www.stackage.org/package/" ++
(toBuilder $ display name') ++
"\""
data UploadBundleV2 = UploadBundleV2
{ ub2Server :: StackageServer
, ub2AuthToken :: Text
, ub2Bundle :: FilePath
}
uploadBundleV2 :: UploadBundleV2 -> Manager -> IO Text
uploadBundleV2 UploadBundleV2 {..} man = IO.withBinaryFile ub2Bundle IO.ReadMode $ \h -> do
size <- IO.hFileSize h
putStrLn $ "Bundle size: " ++ tshow size
req1 <- parseUrlThrow $ unpack $ unStackageServer ub2Server ++ "/upload2"
let req2 = req1
{ method = "PUT"
, requestHeaders =
[ ("Authorization", encodeUtf8 ub2AuthToken)
, ("Accept", "application/json")
, ("Content-Type", "application/x-tar")
]
, requestBody = HCC.requestBodySource (fromIntegral size)
$ sourceHandle h $= printProgress size
}
sink = decodeUtf8C =$ fix (\loop -> do
mx <- peekC
case mx of
Nothing -> error $ "uploadBundleV2: premature end of stream"
Just _ -> do
l <- lineC $ takeCE 4096 =$ foldC
let (cmd, msg') = break (== ':') l
msg = dropWhile (== ' ') $ dropWhile (== ':') msg'
case cmd of
"CONT" -> do
putStrLn msg
loop
"FAILURE" -> error $ "uploadBundleV2 failed: " ++ unpack msg
"SUCCESS" -> return msg
_ -> error $ "uploadBundleV2: unknown command " ++ unpack cmd
)
withResponse req2 man $ \res -> HCC.bodyReaderSource (responseBody res) $$ sink
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