@@ -1,343 +1,26 @@
1−{-# OPTIONS_GHC -Wall #-}
2−{-# LANGUAGE OverloadedStrings #-}
3module Deps.Website
1module Deps.Website
4− ( getElmJson
2+ ( domain
6− , getNewPackages
4+ , metadata
17−import Prelude hiding (zip)
18−import qualified Codec.Archive.Zip as Zip
19−import Control.Monad (void)
20−import qualified Data.Binary as Binary
21−import qualified Data.Binary.Get as Binary
22−import qualified Data.ByteString.Char8 as BS
23−import qualified Data.ByteString.Lazy as LBS
24−import qualified Data.Digest.Pure.SHA as SHA
25−import qualified Data.Map as Map
26−import qualified Data.Utf8 as Utf8
27−import qualified Network.HTTP.Client as Client
28−import qualified Network.HTTP.Client.MultipartFormData as Multi
29−import qualified Network.HTTP.Types as Http
30−import qualified System.Directory as Dir
31−import System.FilePath ((</>), splitFileName)
33import qualified Elm.Package as Pkg
9import qualified Elm.Package as Pkg
34import qualified Elm.Version as V
10import qualified Elm.Version as V
35−import qualified Json.Decode as D
11+import qualified Http
37−import qualified Reporting.Exit.Http as E
38−import qualified Reporting.Progress as Progress
39−import qualified Reporting.Task as Task
40−import qualified Reporting.Task.Http as Http
41−import qualified Stuff.Paths as Path
48−getElmJson :: Pkg.Name -> V.Version -> Task.Task BS.ByteString
49−getElmJson name version =
50− Http.run $ fetchByteString $
51− "packages/" ++ Pkg.toUrl name ++ "/" ++ V.toChars version ++ "/elm.json"
54−getDocs :: Pkg.Name -> V.Version -> Task.Task BS.ByteString
55−getDocs name version =
56− Http.run $ fetchByteString $
57− "packages/" ++ Pkg.toUrl name ++ "/" ++ V.toChars version ++ "/docs.json"
64−getNewPackages :: Int -> Task.Task [(Pkg.Name, V.Version)]
65−getNewPackages index =
66− Http.run $ fetchJson (D.list newPkgDecoder) ("all-packages/since/" ++ show index)
69−newPkgDecoder :: D.Decoder E.BadJson ( Pkg.Name, V.Version )
72− case Utf8.split 0x40 {-@-} str of
74− case Pkg.fromString key of
76− case V.fromString value of
81− D.failure (E.BadNewPkg str)
84− D.failure (E.BadNewPkg str)
87− D.failure (E.BadNewPkg str)
94−getAllPackages :: Task.Task (Map.Map Pkg.Name [V.Version])
96− Http.run $ fetchJson allPkgsDecoder "all-packages"
99−allPkgsDecoder :: D.Decoder E.BadJson (Map.Map Pkg.Name [V.Version])
102− checkKeys pairs pkgs =
107− (key, versions) : rest ->
108− case Pkg.fromString key of
110− checkKeys rest (Map.insert pkg versions pkgs)
113− D.failure (E.BadAllPkg key badName)
115− do pairs <- D.pairs (D.list (D.mapError E.BadAllVsn V.decoder))
116− checkKeys pairs Map.empty
123−fetchByteString :: String -> Http.Fetch BS.ByteString
124−fetchByteString path =
125− Http.package path [] $ \request manager ->
126− do response <- Client.httpLbs request manager
127− return $ Right $ LBS.toStrict $ Client.responseBody response
130−fetchJson :: D.Decoder e a -> String -> Http.Fetch a
131−fetchJson decoder path =
132− Http.package path [] $ \request manager ->
133− do response <- Client.httpLbs request manager
134− let bytes = LBS.toStrict (Client.responseBody response)
135− case D.fromByteString decoder bytes of
137− return $ Right value
140− return $ Left $ E.BadJson path (error "TODO give real HTTP problem" jsonProblem)
147−download :: [(Pkg.Name, V.Version)] -> Task.Task ()
151− Task.report Progress.DownloadSkip
154− do cache <- Task.getPackageCacheDir
156− let start = Progress.DownloadStart packages
157− let toEnd = Progress.DownloadEnd
159− void $ Http.run $ Http.report start toEnd $
160− Http.parallel $ map (downloadHelp cache) packages
163−downloadHelp :: FilePath -> (Pkg.Name, V.Version) -> Http.Fetch ()
164−downloadHelp cache (name, version) =
167− "packages/" ++ Pkg.toUrl name ++ "/" ++ V.toChars version ++ "/endpoint.json"
169− Http.andThen (fetchJson endpointDecoder endpointUrl) $ \(endpoint, hash) ->
171− start = Progress.DownloadPkgStart name version
172− toEnd = Progress.DownloadPkgEnd name version
174− Http.report start toEnd $
175− Http.anything endpoint $ \request manager ->
176− Client.withResponse request manager (downloadArchive cache name version hash)
179−endpointDecoder :: D.Decoder e (String, String)
182− <$> (Utf8.toChars <$> D.field "url" D.string)
183− <*> (Utf8.toChars <$> D.field "hash" D.string)
187−-- DOWNLOAD ZIP ARCHIVE
190−downloadArchive :: FilePath -> Pkg.Name -> V.Version -> String -> Client.Response Client.BodyReader -> IO (Either E.Exit ())
191−downloadArchive cache name version expectedHash response =
192− do result <- readArchive (Client.responseBody response) initialArchiveState
197− Right (sha, archive) ->
198− if expectedHash == SHA.showDigest sha then
199− Right <$> writeArchive archive (cache </> Pkg.toFilePath name) (V.toChars version)
201− return $ Left $ E.BadZipSha expectedHash (SHA.showDigest sha)
211− , _sha :: !(Binary.Decoder SHA.SHA1State)
212− , _zip :: !(Binary.Decoder Zip.Archive)
216−initialArchiveState :: ArchiveState
217−initialArchiveState =
218− AS 0 SHA.sha1Incremental (Binary.runGetIncremental Binary.get)
221−type Sha = SHA.Digest SHA.SHA1State
224−readArchive :: Client.BodyReader -> ArchiveState -> IO (Either E.Exit (Sha, Zip.Archive))
225−readArchive body (AS len sha zip) =
227− Binary.Fail _ _ _ ->
228− return $ Left E.BadZipData
231− do chunk <- Client.brRead body
232− readArchive body $ AS
233− { _len = len + BS.length chunk
234− , _sha = Binary.pushChunk sha chunk
235− , _zip = k (if BS.null chunk then Nothing else Just chunk)
238− Binary.Done _ _ archive ->
239− return $ Right ( SHA.completeSha1Incremental sha len, archive )
246−writeArchive :: Zip.Archive -> FilePath -> FilePath -> IO ()
247−writeArchive archive destination newRoot =
248− do Dir.createDirectoryIfMissing True destination
249− let opts = [Zip.OptDestination destination]
250− mapM_ (Zip.writeEntry opts . replaceRoot newRoot) (Zip.zEntries archive)
253−replaceRoot :: String -> Zip.Entry -> Zip.Entry
254−replaceRoot root entry =
257− dropWhile (/='/') (Zip.eRelativePath entry)
259− entry { Zip.eRelativePath = root ++ rootless }
263−-- FIND TAGGED COMMIT ON GITHUB
266−githubCommit :: Pkg.Name -> V.Version -> Task.Task String
267−githubCommit name version =
270− "https://api.github.com/repos/" ++ Pkg.toUrl name ++ "/git/refs/tags/" ++ V.toChars version
273− [ ( Http.hUserAgent, "elm-cli" )
274− , ( Http.hAccept, "application/json" )
278− D.field "object" (D.field "sha" D.string)
280− Http.run $ Http.anything endpoint $ \request manager ->
281− do response <- Client.httpLbs (request { Client.requestHeaders = headers }) manager
282− let bytes = LBS.toStrict (Client.responseBody response)
283− case D.fromByteString decoder bytes of
285− return $ Right (Utf8.toChars value)
288− return $ Left $ E.BadJson "github.json" jsonProblem
292−-- DOWNLOAD FROM GITHUB
295−githubDownload :: Pkg.Name -> V.Version -> FilePath -> Task.Task Sha
296−githubDownload name version dir =
299− "https://github.com/" ++ Pkg.toUrl name ++ "/zipball/" ++ V.toChars version ++ "/"
301− Http.run $ Http.anything endpoint $ \request manager ->
302− Client.withResponse request manager (githubDownloadHelp dir)
305−githubDownloadHelp :: FilePath -> Client.Response Client.BodyReader -> IO (Either E.Exit Sha)
306−githubDownloadHelp targetDir response =
307− do result <- readArchive (Client.responseBody response) initialArchiveState
312− Right (sha, archive) ->
313− do let (dir, root) = splitFileName targetDir
314− writeArchive archive dir root
16+ "https://package.elm-lang.org"
19+route :: String -> [(String,String)] -> String
21+ Http.toUrl (domain ++ path) params
322−register :: Pkg.Name -> V.Version -> String -> Sha -> Task.Task ()
323−register name version commitHash digest =
326− [ ("name", Pkg.toChars name)
327− , ("version", V.toChars version)
328− , ("commit-hash", commitHash)
331− files =
24+metadata :: Pkg.Name -> V.Version -> String -> String
332− [ Multi.partFileSource "elm.json" "elm.json"
25+metadata name version file =
333− , Multi.partFileSource "docs.json" Path.docs
26+ domain ++ "/packages/" ++ Pkg.toUrl name ++ "/" ++ V.toChars version ++ "/" ++ file
334− , Multi.partFileSource "README.md" "README.md"
335− , Multi.partFileRequestBody "github-hash" "github-hash" $
336− Client.RequestBodyBS (BS.pack (SHA.showDigest digest))
339− Http.run $ Http.package "register" params $ \rawRequest manager ->
340− do requestWithBody <- Multi.formDataBody files rawRequest
341− let request = requestWithBody { Client.responseTimeout = Client.responseTimeoutNone }
342− void $ Client.httpLbs request manager