Quick & dirty scaladex lookup

This commit is contained in:
Alexandre Archambault 2016-11-09 09:28:31 -08:00
parent 3f88fb9449
commit 1380d7a741
4 changed files with 231 additions and 4 deletions

View File

@ -91,7 +91,10 @@ lazy val cli = project
coursierPrefix,
libs ++= {
if (scalaBinaryVersion.value == "2.11")
Seq(Deps.caseApp)
Seq(
Deps.caseApp,
Deps.argonautShapeless
)
else
Seq()
},

View File

@ -6,6 +6,7 @@ import java.net.{ URL, URLClassLoader }
import java.util.jar.{ Manifest => JManifest }
import java.util.concurrent.Executors
import coursier.cli.scaladex.Scaladex
import coursier.cli.typelevel.Typelevel
import coursier.ivy.IvyRepository
import coursier.util.{Print, Parse}
@ -162,11 +163,60 @@ class Helper(
}
val (scaladexRawDependencies, otherRawDependencies) =
rawDependencies.partition(s => s.contains("/") || !s.contains(":"))
val scaladexModuleVersionConfigs = {
val res = scaladexRawDependencies.map { s =>
val deps = Scaladex.dependencies(
s,
"2.11",
if (verbosityLevel >= 0) Console.err.println(_) else _ => ()
)
deps.map { modVers =>
val m = modVers.groupBy(_._2)
if (m.size > 1) {
val (keptVer, modVers0) = m.map {
case (v, l) =>
val ver = coursier.core.Parse.version(v)
.getOrElse(???) // FIXME
ver -> l
}
.maxBy(_._1)
if (verbosityLevel >= 0)
Console.err.println(s"Keeping version ${keptVer.repr}")
modVers0
} else
modVers
}
}
val errors = res.collect { case -\/(err) => err }
prematureExitIf(errors.nonEmpty) {
s"Error getting scaladex infos:\n" + errors.map(" " + _).mkString("\n")
}
res
.collect { case \/-(l) => l }
.flatten
.map { case (mod, ver) => (mod, ver, None) }
}
val (modVerCfgErrors, moduleVersionConfigs) =
Parse.moduleVersionConfigs(rawDependencies, scalaVersion)
Parse.moduleVersionConfigs(otherRawDependencies, scalaVersion)
val (intransitiveModVerCfgErrors, intransitiveModuleVersionConfigs) =
Parse.moduleVersionConfigs(intransitive, scalaVersion)
def allModuleVersionConfigs =
// FIXME Order of the dependencies is not respected here (scaladex ones go first)
scaladexModuleVersionConfigs ++ moduleVersionConfigs
prematureExitIf(modVerCfgErrors.nonEmpty) {
s"Cannot parse dependencies:\n" + modVerCfgErrors.map(" "+_).mkString("\n")
}
@ -244,7 +294,7 @@ class Helper(
(mod.organization, mod.name)
}.toSet
val baseDependencies = moduleVersionConfigs.map {
val baseDependencies = allModuleVersionConfigs.map {
case (module, version, configOpt) =>
Dependency(
module,
@ -692,7 +742,7 @@ class Helper(
} else {
// Trying to get the main class of the first artifact
val mainClassOpt = for {
(module, _, _) <- moduleVersionConfigs.headOption
(module, _, _) <- allModuleVersionConfigs.headOption
mainClass <- mainClasses.collectFirst {
case ((org, name), mainClass)
if org == module.organization && (

View File

@ -0,0 +1,173 @@
package coursier.cli.scaladex
import java.net.HttpURLConnection
import java.nio.charset.StandardCharsets
import argonaut._
import Argonaut._
import ArgonautShapeless._
import coursier.Module
import scalaz.{-\/, \/, \/-}
import scalaz.Scalaz.ToEitherOps
import scalaz.Scalaz.ToEitherOpsFromEither
object Scaladex {
// quick & dirty API for querying scaladex
case class SearchResult(
/** GitHub organization */
organization: String,
/** GitHub repository */
repository: String,
/** Scaladex artifact names */
artifacts: List[String] = Nil
)
def search(name: String, target: String, scalaVersion: String): String \/ Seq[SearchResult] = {
val url = new java.net.URL(
// FIXME Escaping
s"https://index.scala-lang.org/api/scastie/search?q=$name&target=$target&scalaVersion=$scalaVersion"
)
var conn: HttpURLConnection = null
val b = try {
conn = url.openConnection().asInstanceOf[HttpURLConnection]
// FIXME See below
// conn.setRequestProperty("Accept", "application/json")
coursier.Platform.readFullySync(conn.getInputStream)
} finally {
if (conn != null)
conn.disconnect()
}
val s = new String(b, StandardCharsets.UTF_8)
s.decodeEither[List[SearchResult]].disjunction
}
case class ArtifactInfos(
/** Dependency group ID (aka organization) */
groupId: String,
/** Dependency artifact ID (aka name or module name) */
artifactId: String,
/** Dependency version */
version: String
)
/**
*
* @param organization: GitHub organization
* @param repository: GitHub repository name
* @param artifactName: Scaladex artifact name
* @return
*/
def artifactInfos(organization: String, repository: String, artifactName: String): String \/ ArtifactInfos = {
val url = new java.net.URL(
// FIXME Escaping
s"https://index.scala-lang.org/api/scastie/project?organization=$organization&repository=$repository&artifact=$artifactName"
)
var conn: HttpURLConnection = null
val b = try {
conn = url.openConnection().asInstanceOf[HttpURLConnection]
// FIXME See below
// conn.setRequestProperty("Accept", "application/json")
coursier.Platform.readFullySync(conn.getInputStream)
} finally {
if (conn != null)
conn.disconnect()
}
val s = new String(b, StandardCharsets.UTF_8)
s.decodeEither[ArtifactInfos].disjunction
}
/**
*
* @param organization: GitHub organization
* @param repository: GitHub repository name
* @return
*/
def artifactNames(organization: String, repository: String): String \/ Seq[String] = {
val url = new java.net.URL(
// FIXME Escaping
s"https://index.scala-lang.org/api/scastie/project?organization=$organization&repository=$repository"
)
var conn: HttpURLConnection = null
val b = try {
conn = url.openConnection().asInstanceOf[HttpURLConnection]
// FIXME report to scaladex, it should accept that (it currently returns JSON as text/plain)
// conn.setRequestProperty("Accept", "application/json")
coursier.Platform.readFullySync(conn.getInputStream)
} finally {
if (conn != null)
conn.disconnect()
}
val s = new String(b, StandardCharsets.UTF_8)
case class Result(artifacts: List[String])
s.decodeEither[Result].disjunction.map(_.artifacts)
}
/**
* Modules / versions known to the Scaladex
*
* Latest version only.
*/
def dependencies(name: String, scalaVersion: String, logger: String => Unit): String \/ Seq[(Module, String)] = {
val idx = name.indexOf('/')
val orgNameOrError =
if (idx >= 0) {
val org = name.take(idx)
val repo = name.drop(idx + 1)
artifactNames(org, repo).map((org, repo, _))
} else
search(name, "JVM", scalaVersion) // FIXME Don't hardcode
.flatMap {
case Seq(first, _*) =>
logger(s"Using ${first.organization}/${first.repository} for $name")
(first.organization, first.repository, first.artifacts).right
case Seq() =>
s"No project found for $name".left
}
orgNameOrError.flatMap {
case (ghOrg, ghRepo, artifactNames) =>
val moduleVersions = artifactNames.flatMap { artifactName =>
artifactInfos(ghOrg, ghRepo, artifactName) match {
case -\/(err) =>
logger(s"Cannot get infos about artifact $artifactName from $ghOrg/$ghRepo: $err, ignoring it")
Nil
case \/-(infos) =>
logger(s"Found module ${infos.groupId}:${infos.artifactId}:${infos.version}")
Seq(Module(infos.groupId, infos.artifactId) -> infos.version)
}
}
if (moduleVersions.isEmpty)
s"No module found for $ghOrg/$ghRepo".left
else
moduleVersions.right
}
}
}

View File

@ -16,6 +16,7 @@ object Deps {
def okhttpUrlConnection = "com.squareup.okhttp" % "okhttp-urlconnection" % "2.7.5"
def sbtLauncherInterface = "org.scala-sbt" % "launcher-interface" % "1.0.0"
def typesafeConfig = "com.typesafe" % "config" % "1.3.1"
def argonautShapeless = "com.github.alexarchambault" %% "argonaut-shapeless_6.2" % "1.2.0-M4"
def scalaAsync = Def.setting {