hackport-0.4.7: cabal/cabal-install/Distribution/Client/Upload.hs
-- This is a quick hack for uploading packages to Hackage.
-- See http://hackage.haskell.org/trac/hackage/wiki/CabalUpload
module Distribution.Client.Upload (check, upload, report) where
import Distribution.Client.Types (Username(..), Password(..),Repo(..),RemoteRepo(..))
import Distribution.Client.HttpUtils
( isOldHackageURI, HttpTransport(..), remoteRepoTryUpgradeToHttps )
import Distribution.Simple.Utils (notice, warn, info, die)
import Distribution.Verbosity (Verbosity)
import Distribution.Text (display)
import Distribution.Client.Config
import qualified Distribution.Client.BuildReports.Anonymous as BuildReport
import qualified Distribution.Client.BuildReports.Upload as BuildReport
import Network.URI (URI(uriPath), parseURI)
import System.IO (hFlush, stdin, stdout, hGetEcho, hSetEcho)
import Control.Exception (bracket)
import System.FilePath ((</>), takeExtension)
import qualified System.FilePath.Posix as FilePath.Posix ((</>))
import System.Directory
import Control.Monad (forM_, when)
type Auth = Maybe (String, String)
--FIXME: how do we find this path for an arbitrary hackage server?
-- is it always at some fixed location relative to the server root?
legacyUploadURI :: URI
Just legacyUploadURI = parseURI "http://hackage.haskell.org/cgi-bin/hackage-scripts/protected/upload-pkg"
checkURI :: URI
Just checkURI = parseURI "http://hackage.haskell.org/cgi-bin/hackage-scripts/check-pkg"
upload :: HttpTransport -> Verbosity -> [Repo] -> Maybe Username -> Maybe Password -> [FilePath] -> IO ()
upload transport verbosity repos mUsername mPassword paths = do
targetRepo <-
case [ remoteRepo | Left remoteRepo <- map repoKind repos ] of
[] -> die $ "Cannot upload. No remote repositories are configured."
rs -> remoteRepoTryUpgradeToHttps transport (last rs)
let targetRepoURI = remoteRepoURI targetRepo
uploadURI
| isOldHackageURI targetRepoURI
= legacyUploadURI
| otherwise
= targetRepoURI {
uriPath = uriPath targetRepoURI FilePath.Posix.</> "upload"
}
Username username <- maybe promptUsername return mUsername
Password password <- maybe promptPassword return mPassword
let auth = Just (username,password)
flip mapM_ paths $ \path -> do
notice verbosity $ "Uploading " ++ path ++ "... "
handlePackage transport verbosity uploadURI auth path
promptUsername :: IO Username
promptUsername = do
putStr "Hackage username: "
hFlush stdout
fmap Username getLine
promptPassword :: IO Password
promptPassword = do
putStr "Hackage password: "
hFlush stdout
-- save/restore the terminal echoing status
passwd <- bracket (hGetEcho stdin) (hSetEcho stdin) $ \_ -> do
hSetEcho stdin False -- no echoing for entering the password
fmap Password getLine
putStrLn ""
return passwd
report :: Verbosity -> [Repo] -> Maybe Username -> Maybe Password -> IO ()
report verbosity repos mUsername mPassword = do
Username username <- maybe promptUsername return mUsername
Password password <- maybe promptPassword return mPassword
let auth = (username,password)
forM_ repos $ \repo -> case repoKind repo of
Left remoteRepo
-> do dotCabal <- defaultCabalDir
let srcDir = dotCabal </> "reports" </> remoteRepoName remoteRepo
-- We don't want to bomb out just because we haven't built any packages from this repo yet
srcExists <- doesDirectoryExist srcDir
when srcExists $ do
contents <- getDirectoryContents srcDir
forM_ (filter (\c -> takeExtension c == ".log") contents) $ \logFile ->
do inp <- readFile (srcDir </> logFile)
let (reportStr, buildLog) = read inp :: (String,String)
case BuildReport.parse reportStr of
Left errs -> do warn verbosity $ "Errors: " ++ errs -- FIXME
Right report' ->
do info verbosity $ "Uploading report for " ++ display (BuildReport.package report')
BuildReport.uploadReports verbosity auth (remoteRepoURI remoteRepo) [(report', Just buildLog)]
return ()
Right{} -> return ()
check :: HttpTransport -> Verbosity -> [FilePath] -> IO ()
check transport verbosity paths = do
flip mapM_ paths $ \path -> do
notice verbosity $ "Checking " ++ path ++ "... "
handlePackage transport verbosity checkURI Nothing path
handlePackage :: HttpTransport -> Verbosity -> URI -> Auth
-> FilePath -> IO ()
handlePackage transport verbosity uri auth path =
do resp <- postHttpFile transport verbosity uri path auth
case resp of
(200,_) -> do notice verbosity "Ok"
(code,err) -> do notice verbosity $ "Error uploading " ++ path ++ ": "
++ "http code " ++ show code ++ "\n"
++ err