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 -> Manager -> IO (Response LByteString)
94uploadDocs (UploadDocs (StackageServer host) fp0 token ident) man = do
97 then uploadDocsFile $ fpToString fp0
102 else error $ "Path not found: " ++ fpToString fp0
104 uploadDocsDir = withSystemTempFile "haddocks.tar.xz" $ \fp h -> do
108 $ sourceDirectory fp0
109 $$ filterMC (liftIO . isDirectory)
110 =$ mapC (fpToString . filename)
112 writeFile (fp0 </> "index.html") $ mkIndex
113 (unpack $ unSnapshotIdent ident)
115 writeFile (fp0 </> "style.css") styleCss
116 -- FIXME write index.html, style.css
117 let cp = (proc "tar" $ "cJf" : fp : "index.html" : "style.css" : dirs)
118 { cwd = Just $ fpToString fp0
120 withCheckedProcess cp $ \Inherited Inherited Inherited -> return ()
122 uploadDocsFile fp = do
123 req1 <- parseUrl $ unpack $ concat
126 , unSnapshotIdent ident
129 [ partFileSource "tarball" fp
131 req2 <- formDataBody formData req1
135 [ ("Authorization", encodeUtf8 token)
136 , ("Accept", "application/json")
137 ] ++ requestHeaders req2
139 , checkStatus = \_ _ _ -> Nothing
140 , responseTimeout = Just 300000000
144uploadHackageDistro :: BuildPlan
145 -> ByteString -- ^ Hackage username
146 -> ByteString -- ^ Hackage password
148 -> IO (Response LByteString)
149uploadHackageDistro bp username password =
150 httpLbs (applyBasicAuth username password req)
161 (toBuilder $ display name) ++
163 (toBuilder $ display version) ++
164 "\",\"http://www.stackage.org/package/" ++
165 (toBuilder $ display name) ++
168 req = "http://hackage.haskell.org/distro/Stackage/packages.csv"
169 { requestHeaders = [("Content-Type", "text/csv")]
170 , requestBody = RequestBodyLBS csv
171 , checkStatus = \_ _ _ -> Nothing
175data UploadDocMap = UploadDocMap
176 { udmServer :: StackageServer
177 , udmAuthToken :: Text
178 , udmSnapshot :: SnapshotIdent
179 , udmDocDir :: FilePath
180 , udmPlan :: BuildPlan
183uploadDocMap :: UploadDocMap -> Manager -> IO (Response LByteString)
184uploadDocMap UploadDocMap {..} man = do
185 docmap <- docsListing udmPlan udmDocDir
186 req1 <- parseUrl $ unpack $ unStackageServer udmServer ++ "/upload-doc-map"
187 req2 <- formDataBody (formData docmap) req1
191 [ ("Authorization", encodeUtf8 udmAuthToken)
192 , ("Accept", "application/json")
193 ] ++ requestHeaders req2
195 , checkStatus = \_ _ _ -> Nothing
196 , responseTimeout = Just 300000000
201 [ partBS "snapshot" (encodeUtf8 $ unSnapshotIdent udmSnapshot)
202 , partFileRequestBody "docmap" "docmap" $ RequestBodyBS docmap
205mkIndex :: String -> [String] -> String
206mkIndex snapid dirs = concat
207 [ "<!DOCTYPE html>\n<html lang='en'><head><title>Haddocks index</title>"
208 , "<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css'>"
209 , "<link rel='stylesheet' href='style.css'>"
210 , "<link rel='shortcut icon' href='http://www.stackage.org/static/img/favicon.ico' />"
212 , "<body><div class='container'>"
213 , "<div class='row'><div class='span12 col-md-12'>"
214 , "<h1>Haddock documentation index</h1>"
215 , "<p class='return'><a href=\"http://www.stackage.org/stackage/"
217 , "\">Return to snapshot</a></p><ul>"
218 , concatMap toLI dirs
219 , "</ul></div></div></div></body></html>"
232 [ "@media (min-width: 530px) {"
233 , "ul { -webkit-column-count: 2; -moz-column-count: 2; column-count: 2 }"
235 , "@media (min-width: 760px) {"
236 , "ul { -webkit-column-count: 3; -moz-column-count: 3; column-count: 3 }"
240 , " padding-left: 0;"
241 , " list-style-type: none;"
244 , " background: #f0f0f0;"
245 , " font-family: 'Lato', sans-serif;"
246 , " text-shadow: 1px 1px 1px #ffffff;"
247 , " font-size: 20px;"
248 , " line-height: 30px;"
249 , " padding-bottom: 5em;"
252 , " font-weight: normal;"
254 , " font-size: 45px;"
258 , " font-style: italic;"
261 , " 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