forked from commercialhaskell/stack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPackageIndex.hs
More file actions
197 lines (179 loc) · 6.81 KB
/
PackageIndex.hs
File metadata and controls
197 lines (179 loc) · 6.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
module Stack.Types.PackageIndex
( PackageDownload (..)
, HSPackageDownload (..)
, PackageCache (..)
, PackageCacheMap (..)
, OffsetSize (..)
-- ** PackageIndex, IndexName & IndexLocation
, PackageIndex(..)
, IndexName(..)
, indexNameText
, IndexLocation(..)
, SimplifiedIndexLocation (..)
, simplifyIndexLocation
, HttpType (..)
, HackageSecurity (..)
) where
import Control.DeepSeq (NFData)
import Control.Monad (mzero)
import Data.Aeson.Extended
import Data.ByteString (ByteString)
import qualified Data.Foldable as F
import Data.Hashable (Hashable)
import Data.Data (Data, Typeable)
import Data.HashMap.Strict (HashMap)
import Data.Int (Int64)
import Data.Map (Map)
import qualified Data.Map.Strict as Map
import Data.Store (Store)
import Data.Text (Text)
import qualified Data.Text as T
import Data.Text.Encoding (encodeUtf8, decodeUtf8)
import Data.Word (Word64)
import GHC.Generics (Generic)
import Path
import Stack.Types.BuildPlan (GitSHA1)
import Stack.Types.PackageIdentifier
data PackageCache = PackageCache
{ pcOffsetSize :: {-# UNPACK #-}!OffsetSize
, pcDownload :: !(Maybe PackageDownload)
}
deriving (Generic, Eq, Show, Data, Typeable)
instance Store PackageCache
instance NFData PackageCache
-- | offset in bytes into the 01-index.tar file for the .cabal file
-- contents, and size in bytes of the .cabal file
data OffsetSize = OffsetSize !Int64 !Int64
deriving (Generic, Eq, Show, Data, Typeable)
instance Store OffsetSize
instance NFData OffsetSize
data PackageCacheMap = PackageCacheMap
{ pcmIdent :: !(Map PackageIdentifier PackageCache)
-- ^ most recent revision of the package
, pcmSHA :: !(HashMap GitSHA1 OffsetSize)
-- ^ lookup via the GitSHA1 of the cabal file contents
}
deriving (Generic, Eq, Show, Data, Typeable)
instance Store PackageCacheMap
instance NFData PackageCacheMap
data PackageDownload = PackageDownload
{ pdSHA256 :: !ByteString
, pdUrl :: !ByteString
, pdSize :: !Word64
}
deriving (Show, Generic, Eq, Data, Typeable)
instance Store PackageDownload
instance NFData PackageDownload
instance FromJSON PackageDownload where
parseJSON = withObject "PackageDownload" $ \o -> do
hashes <- o .: "package-hashes"
sha256 <- maybe mzero return (Map.lookup ("SHA256" :: Text) hashes)
locs <- o .: "package-locations"
url <-
case reverse locs of
[] -> mzero
x:_ -> return x
size <- o .: "package-size"
return PackageDownload
{ pdSHA256 = encodeUtf8 sha256
, pdUrl = encodeUtf8 url
, pdSize = size
}
-- | Hackage Security provides a different JSON format, we'll have our
-- own JSON parser for it.
newtype HSPackageDownload = HSPackageDownload { unHSPackageDownload :: PackageDownload }
instance FromJSON HSPackageDownload where
parseJSON = withObject "HSPackageDownload" $ \o1 -> do
o2 <- o1 .: "signed"
Object o3 <- o2 .: "targets"
Object o4:_ <- return $ F.toList o3
len <- o4 .: "length"
hashes <- o4 .: "hashes"
sha256 <- hashes .: "sha256"
return $ HSPackageDownload PackageDownload
{ pdSHA256 = encodeUtf8 sha256
, pdSize = len
, pdUrl = ""
}
-- | Unique name for a package index
newtype IndexName = IndexName { unIndexName :: ByteString }
deriving (Show, Eq, Ord, Hashable, Store)
indexNameText :: IndexName -> Text
indexNameText = decodeUtf8 . unIndexName
instance ToJSON IndexName where
toJSON = toJSON . indexNameText
instance FromJSON IndexName where
parseJSON = withText "IndexName" $ \t ->
case parseRelDir (T.unpack t) of
Left e -> fail $ "Invalid index name: " ++ show e
Right _ -> return $ IndexName $ encodeUtf8 t
data HttpType = HTHackageSecurity !HackageSecurity | HTVanilla
deriving (Show, Eq, Ord)
data HackageSecurity = HackageSecurity
{ hsKeyIds :: ![Text]
, hsKeyThreshold :: !Int
}
deriving (Show, Eq, Ord)
instance FromJSON HackageSecurity where
parseJSON = withObject "HackageSecurity" $ \o -> HackageSecurity
<$> o .: "keyids"
<*> o .: "key-threshold"
-- | Location of the package index. This ensures that at least one of Git or
-- HTTP is available.
data IndexLocation
= ILGit !Text
| ILHttp !Text !HttpType
| ILGitHttp !Text !Text !HttpType
deriving (Show, Eq, Ord)
-- | Simplified 'IndexLocation', which will either be a Git repo or HTTP URL.
data SimplifiedIndexLocation = SILGit !Text | SILHttp !Text !HttpType
deriving (Show, Eq, Ord)
simplifyIndexLocation :: IndexLocation -> SimplifiedIndexLocation
simplifyIndexLocation (ILGit t) = SILGit t
simplifyIndexLocation (ILHttp t ht) = SILHttp t ht
-- Prefer HTTP over Git
simplifyIndexLocation (ILGitHttp _ t ht) = SILHttp t ht
-- | Information on a single package index
data PackageIndex = PackageIndex
{ indexName :: !IndexName
, indexLocation :: !IndexLocation
, indexDownloadPrefix :: !Text
-- ^ URL prefix for downloading packages
, indexGpgVerify :: !Bool
-- ^ GPG-verify the package index during download. Only applies to Git
-- repositories for now.
, indexRequireHashes :: !Bool
-- ^ Require that hashes and package size information be available for packages in this index
}
deriving Show
instance FromJSON (WithJSONWarnings PackageIndex) where
parseJSON = withObjectWarnings "PackageIndex" $ \o -> do
name <- o ..: "name"
prefix <- o ..: "download-prefix"
mgit <- o ..:? "git"
mhttp <- o ..:? "http"
mhackageSecurity <- o ..:? "hackage-security"
let httpType = maybe HTVanilla HTHackageSecurity mhackageSecurity
loc <-
case (mgit, mhttp) of
(Nothing, Nothing) -> fail $
"Must provide either Git or HTTP URL for " ++
T.unpack (indexNameText name)
(Just git, Nothing) -> return $ ILGit git
(Nothing, Just http) -> return $ ILHttp http httpType
(Just git, Just http) -> return $ ILGitHttp git http httpType
gpgVerify <- o ..:? "gpg-verify" ..!= False
reqHashes <- o ..:? "require-hashes" ..!= False
return PackageIndex
{ indexName = name
, indexLocation = loc
, indexDownloadPrefix = prefix
, indexGpgVerify = gpgVerify
, indexRequireHashes = reqHashes
}