1-- | Upload to Stackage and Hackage
2{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3{-# LANGUAGE NoImplicitPrelude #-}
4{-# LANGUAGE OverloadedStrings #-}
5{-# LANGUAGE RecordWildCards #-}
6{-# LANGUAGE TupleSections #-}
18import Control.Monad.Writer.Strict (execWriter, tell)
19import Data.Default.Class (Default (..))
20import Filesystem (isDirectory, isFile)
21import Network.HTTP.Client
22import Network.HTTP.Client.MultipartFormData
23import Stackage.BuildPlan (BuildPlan)
24import Stackage.Prelude
25import Stackage.ServerBundle (bpAllPackages, docsListing)
26import System.IO.Temp (withSystemTempFile)
28newtype StackageServer = StackageServer { unStackageServer :: Text }
29 deriving (Show, Eq, Ord, Hashable, IsString)
30instance Default StackageServer where
31 def = "http://www.stackage.org"
33data UploadBundle = UploadBundle
34 { ubServer :: StackageServer
35 , ubContents :: LByteString
36 , ubAlias :: Maybe Text
37 , ubNightly :: Maybe Text -- ^ should be GHC version
38 , ubLTS :: Maybe Text -- ^ e.g. 2.3
41instance Default UploadBundle where
48 , ubAuthToken = "no-auth-token-provided"
51newtype SnapshotIdent = SnapshotIdent { unSnapshotIdent :: Text }
52 deriving (Show, Eq, Ord, Hashable, IsString)
54uploadBundle :: UploadBundle -> Manager -> IO SnapshotIdent
55uploadBundle UploadBundle {..} man = do
56 req1 <- parseUrl $ unpack $ unStackageServer ubServer ++ "/upload"
57 req2 <- formDataBody formData req1
61 [ ("Authorization", encodeUtf8 ubAuthToken)
62 , ("Accept", "application/json")
63 ] ++ requestHeaders req2
65 , checkStatus = \_ _ _ -> Nothing
66 , responseTimeout = Just 300000000
68 res <- httpLbs req3 man
69 case lookup "x-stackage-ident" $ responseHeaders res of
70 Just snapid -> return $ SnapshotIdent $ decodeUtf8 snapid
71 Nothing -> error $ "An error occurred: " ++ show res
73 params = mapMaybe (\(x, y) -> (x, ) <$> y)
75 , ("nightly", ubNightly)
78 formData = ($ []) $ execWriter $ do
79 forM_ params $ \(key, value) ->
80 tell' $ partBS key $ encodeUtf8 value
81 tell' $ partFileRequestBody "stackage" "stackage"
82 $ RequestBodyLBS ubContents
86data UploadDocs = UploadDocs
87 { udServer :: StackageServer
88 , udDocs :: FilePath -- ^ may be a directory or a tarball
90 , udSnapshot :: SnapshotIdent
93uploadDocs (UploadDocs (StackageServer host) fp0 token ident) man = do
96 then uploadDocsFile $ fpToString fp0
101 else error $ "Path not found: " ++ fpToString fp0
103 uploadDocsDir = withSystemTempFile "haddocks.tar.xz" $ \fp h -> do
107 $ sourceDirectory fp0
108 $$ filterMC (liftIO . isDirectory)
109 =$ mapC (fpToString . filename)
111 writeFile (fp0 </> "index.html") $ mkIndex
112 (unpack $ unSnapshotIdent ident)
114 writeFile (fp0 </> "style.css") styleCss
115 -- FIXME write index.html, style.css
116 let cp = (proc "tar" $ "cJf" : fp : "index.html" : "style.css" : dirs)
117 { cwd = Just $ fpToString fp0
119 withCheckedProcess cp $ \Inherited Inherited Inherited -> return ()
121 uploadDocsFile fp = do
122 req1 <- parseUrl $ unpack $ concat
125 , unSnapshotIdent ident
128 [ partFileSource "tarball" fp
130 req2 <- formDataBody formData req1
134 [ ("Authorization", encodeUtf8 token)
135 , ("Accept", "application/json")
136 ] ++ requestHeaders req2
138 , checkStatus = \_ _ _ -> Nothing
139 , responseTimeout = Just 300000000
143uploadHackageDistro :: BuildPlan
144 -> ByteString -- ^ Hackage username
145 -> ByteString -- ^ Hackage password
147 -> IO (Response LByteString)
148uploadHackageDistro bp username password =
149 httpLbs (applyBasicAuth username password req)
160 (toBuilder $ display name) ++
162 (toBuilder $ display version) ++
163 "\",\"http://www.stackage.org/package/" ++
164 (toBuilder $ display name) ++
167 req = "http://hackage.haskell.org/distro/Stackage/packages.csv"
168 { requestHeaders = [("Content-Type", "text/csv")]
169 , requestBody = RequestBodyLBS csv
170 , checkStatus = \_ _ _ -> Nothing
174data UploadDocMap = UploadDocMap
175 { udmServer :: StackageServer
176 , udmAuthToken :: Text
177 , udmSnapshot :: SnapshotIdent
178 , udmDocDir :: FilePath
179 , udmPlan :: BuildPlan
182uploadDocMap UploadDocMap {..} man = do
183 docmap <- docsListing udmPlan udmDocDir
184 req1 <- parseUrl $ unpack $ unStackageServer udmServer ++ "/upload-doc-map"
185 req2 <- formDataBody (formData docmap) req1
189 [ ("Authorization", encodeUtf8 udmAuthToken)
190 , ("Accept", "application/json")
191 ] ++ requestHeaders req2
193 , checkStatus = \_ _ _ -> Nothing
194 , responseTimeout = Just 300000000
199 [ partBS "snapshot" (encodeUtf8 $ unSnapshotIdent udmSnapshot)
200 , partFileRequestBody "docmap" "docmap" $ RequestBodyBS docmap
203mkIndex :: String -> [String] -> String
204mkIndex snapid dirs = concat
205 [ "<!DOCTYPE html>\n<html lang='en'><head><title>Haddocks index</title>"
206 , "<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css'>"
207 , "<link rel='stylesheet' href='style.css'>"
208 , "<link rel='shortcut icon' href='http://www.stackage.org/static/img/favicon.ico' />"
210 , "<body><div class='container'>"
211 , "<div class='row'><div class='span12 col-md-12'>"
212 , "<h1>Haddock documentation index</h1>"
213 , "<p class='return'><a href=\"http://www.stackage.org/stackage/"
215 , "\">Return to snapshot</a></p><ul>"
216 , concatMap toLI dirs
217 , "</ul></div></div></div></body></html>"
230 [ "@media (min-width: 530px) {"
231 , "ul { -webkit-column-count: 2; -moz-column-count: 2; column-count: 2 }"
233 , "@media (min-width: 760px) {"
234 , "ul { -webkit-column-count: 3; -moz-column-count: 3; column-count: 3 }"
238 , " padding-left: 0;"
239 , " list-style-type: none;"
242 , " background: #f0f0f0;"
243 , " font-family: 'Lato', sans-serif;"
244 , " text-shadow: 1px 1px 1px #ffffff;"
245 , " font-size: 20px;"
246 , " line-height: 30px;"
247 , " padding-bottom: 5em;"
250 , " font-weight: normal;"
252 , " font-size: 45px;"
256 , " font-style: italic;"
259 , " margin-bottom: 1em;"
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list