packages feed

haven (empty) → 0.1.0.0

raw patch · 4 files changed

+204/−0 lines, 4 filesdep +SHAdep +basedep +bytestringsetup-changed

Dependencies added: SHA, base, bytestring, containers, http-conduit, http-types, mtl, xml

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2017, Ali Abrar, Will Fancher++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Ali Abrar, Will Fancher nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ haven.cabal view
@@ -0,0 +1,28 @@+name:                haven+synopsis:            Recursively retrieve maven dependencies+description:         A haskell project that retrieves maven package dependencies recursively given a starting set of packages. The primary output format is a list of nix sets describing the maven packages.+category:            Java+version:             0.1.0.0+license:             BSD3+license-file:        LICENSE+author:              Ali Abrar, Will Fancher+maintainer:          maintenance@obsidian.systems+build-type:          Simple+cabal-version:       >=1.10++executable haven+  main-is:             Main.hs+  build-depends: base >=4.9 && <4.10+               , bytestring+               , containers+               , http-conduit+               , http-types+               , mtl+               , SHA+               , xml+  hs-source-dirs:      src+  default-language:    Haskell2010++source-repository head+  type: git+  location: https://github.com/obsidiansystems/haven
+ src/Main.hs view
@@ -0,0 +1,144 @@+{-# LANGUAGE ScopedTypeVariables #-}+module Main where++import Control.Monad.State+import Data.Digest.Pure.SHA+import Data.List+import Data.Maybe+import Data.Semigroup+import Data.Set (Set)+import qualified Data.Set as Set+import Data.Traversable+import Network.HTTP.Conduit+import Network.HTTP.Types.Status+import System.Environment+import Text.XML.Light++-- | Takes multiple maven package descriptions as command line arguments+-- and finds the dependencies of those maven packages.+-- Package descriptions should be of the form @groupid:artifactid:version@+main :: IO ()+main = do+  mavens <- getArgs+  mgr <- newManager tlsManagerSettings+  (_, deps) <- flip runStateT Set.empty $ traverse (recurseDependencies mgr) $ parseMaven <$> mavens+  putStrLn $ "[" <> (concatMap toNix deps)  <> "]"+++data Maven = Maven+  { _maven_groupId :: String+  , _maven_artifactId :: String+  , _maven_version :: String+  }+  deriving (Show, Read, Eq, Ord)++data MavenNix = MavenNix+  { _mavenNix_maven :: Maven+  , _mavenNix_jarSha256 :: Maybe (Digest SHA256State)+  , _mavenNix_pomSha256 :: Maybe (Digest SHA256State)+  }+  deriving (Show, Eq, Ord)++-- | Parses strings of the form @groupid:artifactid:version@,+-- e.g., @com.android.build.tools:gradle:2.3.0@+parseMaven :: String -> Maven+parseMaven s =+  let (groupId, _:rest) = break (==':') s+      (artifactId, _:version) = break (==':') rest+  in Maven groupId artifactId version++-- | Create a nix record for a hashed maven package+toNix :: MavenNix -> String+toNix m =+  let mvn = _mavenNix_maven m+      showHash h = fromMaybe "null" $ (\x -> "\"" <> x <> "\"") . showDigest <$> h+  in unlines+      [ "  { artifactId = \"" <> _maven_artifactId mvn <> "\";"+      , "    groupId = \"" <> _maven_groupId mvn <> "\";"+      , "    version = \"" <> _maven_version mvn <> "\";"+      , "    jarSha256 = " <> showHash (_mavenNix_jarSha256 m) <> ";"+      , "    pomSha256 = " <> showHash (_mavenNix_pomSha256 m) <> "; }"+      ]++-- | Maven repositories in which to look for packages+mirrors :: [String]+mirrors =+  [ "https://repo1.maven.org/maven2/"+  , "https://jcenter.bintray.com/"+  , "http://central.maven.org/maven2/"+  ]++-- | Hash a particular maven package's .pom and .jar files and parse the .pom file as xml+fetch :: [String] -> Manager -> Maven -> IO (MavenNix, Element)+fetch (mirror:fallbacks) mgr mvn = do+  let version = takeWhile (/= ',') $ dropWhile (\c -> c == '[' || c == '(') $ _maven_version mvn+      url ext = mconcat+        [ mirror+        , (\x -> if x == '.' then '/' else x) <$> _maven_groupId mvn+        , "/"+        , _maven_artifactId mvn+        , "/"+        , version+        , "/"+        , _maven_artifactId mvn+        , "-"+        , version+        , ext+        ]+      hash rsp = if responseStatus rsp == status200+                   then Just $ sha256 $ responseBody rsp+                   else Nothing+  reqPom <- parseRequest $ url ".pom"+  reqJar <- parseRequest $ url ".jar"+  pom <- httpLbs reqPom mgr+  jar <- httpLbs reqJar mgr+  if responseStatus jar /= status200 && responseStatus pom /= status200+    then fetch fallbacks mgr mvn+    else return $+      let Just e = parseXMLDoc $ responseBody pom+          mvnNix = MavenNix+            { _mavenNix_maven = mvn+            , _mavenNix_jarSha256 = hash jar+            , _mavenNix_pomSha256 = hash pom+            }+      in (mvnNix, e)+fetch [] _ mvn = error $ mconcat+  [ "Error: Could not find "+  , _maven_groupId mvn+  , ":"+  , _maven_artifactId mvn+  , ":"+  , _maven_version mvn+  , " in any mirror."+  ]++-- | Extract the dependencies from a package's pom xml+getDepsFor :: Element -> [Maven]+getDepsFor x = do+  deps <- findChildrenByTagName "dependencies" x+  dep <- findChildrenByTagName "dependency" deps+  groupId <- findChildrenByTagName "groupId" dep+  artifactId <- findChildrenByTagName "artifactId" dep+  version <- findChildrenByTagName "version" dep+  let optional = case findChildrenByTagName "optional" dep of+        [] -> False+        xs -> any ((=="true") . strContent) xs+  guard (not ("$" `isPrefixOf` strContent version) && not optional)+  return $ Maven+    { _maven_groupId = strContent groupId+    , _maven_artifactId = strContent artifactId+    , _maven_version = strContent version+    }++-- | Given a starting maven package, retrieve its dependencies recursively+recurseDependencies :: Manager -> Maven -> StateT (Set MavenNix) IO ()+recurseDependencies mgr mvn = do+  s <- get+  when (not $ any (\mvnNix -> _mavenNix_maven mvnNix == mvn) s) $ do+    (mvnNix, e) <- liftIO $ fetch mirrors mgr mvn+    modify $ Set.insert mvnNix+    void $ traverse (recurseDependencies mgr) $ getDepsFor e++-- | Retrieve an XML Element's children by tag name+findChildrenByTagName :: String -> Element -> [Element]+findChildrenByTagName n = filterChildren (\a -> qName (elName a) == n)