diff --git a/cache/src/main/scala/coursier/Cache.scala b/cache/src/main/scala/coursier/Cache.scala index aa7d2f595..90e815fc4 100644 --- a/cache/src/main/scala/coursier/Cache.scala +++ b/cache/src/main/scala/coursier/Cache.scala @@ -903,9 +903,32 @@ object Cache { val res = if (f.exists()) { if (f.isDirectory) { - if (artifact.url.startsWith("file:")) - Left("Not implemented: listing of local directories") - else { + if (artifact.url.startsWith("file:")) { + + val elements = f.listFiles().map { c => + val name = c.getName + val name0 = if (c.isDirectory) + name + "/" + else + name + + s"""
  • $name0
  • """ + }.mkString + + val page = + s""" + | + | + | + | + | + | + """.stripMargin + + Right(page) + } else { val f0 = new File(f, ".directory") if (f0.exists()) { diff --git a/core/js/src/main/scala/coursier/core/compatibility/package.scala b/core/js/src/main/scala/coursier/core/compatibility/package.scala index eebc41fa1..3b8659b5b 100644 --- a/core/js/src/main/scala/coursier/core/compatibility/package.scala +++ b/core/js/src/main/scala/coursier/core/compatibility/package.scala @@ -93,7 +93,12 @@ package object compatibility { def encodeURIComponent(s: String): String = g.encodeURIComponent(s).asInstanceOf[String] - def listWebPageSubDirectories(page: String): Seq[String] = { + def listWebPageSubDirectories(url: String, page: String): Seq[String] = { + // TODO + ??? + } + + def listWebPageFiles(url: String, page: String): Seq[String] = { // TODO ??? } diff --git a/core/jvm/src/main/scala/coursier/core/compatibility/package.scala b/core/jvm/src/main/scala/coursier/core/compatibility/package.scala index 11f052fe2..d4fbbd74c 100644 --- a/core/jvm/src/main/scala/coursier/core/compatibility/package.scala +++ b/core/jvm/src/main/scala/coursier/core/compatibility/package.scala @@ -56,17 +56,25 @@ package object compatibility { def encodeURIComponent(s: String): String = new java.net.URI(null, null, null, -1, s, null, null) .toASCIIString - def listWebPageSubDirectories(page: String): Seq[String] = + def listWebPageDirectoryElements(url: String, page: String, directories: Boolean): Seq[String] = Jsoup.parse(page) - .select("a[href~=[^/]*/]") + .select("a") .asScala .toVector - .map { elem => - elem - .attr("href") - .stripPrefix(":") // bintray typically prepends these - .stripSuffix("/") + .map(_.attr("href")) + .collect { + case elem if elem.nonEmpty && elem.endsWith("/") == directories => + elem + .stripSuffix("/") + .stripPrefix(url) + .stripPrefix(":") // bintray typically prepends these } - .filter(n => n != "." && n != "..") + .filter(n => !n.contains("/") && n != "." && n != "..") + + def listWebPageSubDirectories(url: String, page: String): Seq[String] = + listWebPageDirectoryElements(url, page, directories = true) + + def listWebPageFiles(url: String, page: String): Seq[String] = + listWebPageDirectoryElements(url, page, directories = false) } diff --git a/core/shared/src/main/scala/coursier/ivy/IvyRepository.scala b/core/shared/src/main/scala/coursier/ivy/IvyRepository.scala index fd3d562b2..d10a9ae7f 100644 --- a/core/shared/src/main/scala/coursier/ivy/IvyRepository.scala +++ b/core/shared/src/main/scala/coursier/ivy/IvyRepository.scala @@ -147,8 +147,8 @@ case class IvyRepository( s"Don't know how to list revisions of ${metadataPattern.string}".left } - def fromWebPage(s: String) = { - val subDirs = coursier.core.compatibility.listWebPageSubDirectories(s) + def fromWebPage(url: String, s: String) = { + val subDirs = coursier.core.compatibility.listWebPageSubDirectories(url, s) val versions = subDirs.map(Parse.version).collect { case Some(v) => v } val versionsInItv = versions.filter(itv.contains) @@ -173,7 +173,7 @@ case class IvyRepository( for { url <- EitherT(F.point(listingUrl)) s <- fetch(artifactFor(url)) - res <- fromWebPage(s) + res <- fromWebPage(url, s) } yield res } } diff --git a/plugin/src/main/scala-2.10/coursier/FallbackDependenciesRepository.scala b/plugin/src/main/scala-2.10/coursier/FallbackDependenciesRepository.scala index b2d495970..08018ffe7 100644 --- a/plugin/src/main/scala-2.10/coursier/FallbackDependenciesRepository.scala +++ b/plugin/src/main/scala-2.10/coursier/FallbackDependenciesRepository.scala @@ -8,7 +8,7 @@ case class FallbackDependenciesRepository( fallbacks: Map[(Module, String), (URL, Boolean)] ) extends Repository { - private val source = new Artifact.Source { + private val source: Artifact.Source = new Artifact.Source { def artifacts( dependency: Dependency, project: Project, @@ -35,22 +35,41 @@ case class FallbackDependenciesRepository( EitherT.left(F.point("No fallback URL found")) case Some((url, _)) => - val proj = Project( - module, - version, - Nil, - Map.empty, - None, - Nil, - Nil, - Nil, - None, - None, - None, - Nil, - Info.empty - ) - EitherT.right(F.point((source, proj))) + val urlStr = url.toExternalForm + val idx = urlStr.lastIndexOf('/') + + if (idx < 0 || urlStr.endsWith("/")) + EitherT.left(F.point(s"$url doesn't point to a file")) + else { + val (dirUrlStr, fileName) = urlStr.splitAt(idx + 1) + + fetch(Artifact(dirUrlStr, Map.empty, Map.empty, Attributes("", ""), changing = true, None)).flatMap { listing => + + val files = coursier.core.compatibility.listWebPageFiles(dirUrlStr, listing) + + if (files.contains(fileName)) { + + val proj = Project( + module, + version, + Nil, + Map.empty, + None, + Nil, + Nil, + Nil, + None, + None, + None, + Nil, + Info.empty + ) + + EitherT.right(F.point((source, proj))) + } else + EitherT.left(F.point(s"$fileName not found under $dirUrlStr")) + } + } } } diff --git a/plugin/src/sbt-test/sbt-coursier/from-wrong-url/build.sbt b/plugin/src/sbt-test/sbt-coursier/from-wrong-url/build.sbt new file mode 100644 index 000000000..fbb442536 --- /dev/null +++ b/plugin/src/sbt-test/sbt-coursier/from-wrong-url/build.sbt @@ -0,0 +1,7 @@ +scalaVersion := "2.11.8" + +// keeping the default cache policies here + +libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.2" from { + "https://repo1.maven.org/maven2/com/chuusai/shapeless_2.11/2.3.242/shapeless_2.11-2.3.242.jar" +} diff --git a/plugin/src/sbt-test/sbt-coursier/from-wrong-url/project/plugins.sbt b/plugin/src/sbt-test/sbt-coursier/from-wrong-url/project/plugins.sbt new file mode 100644 index 000000000..152225a9e --- /dev/null +++ b/plugin/src/sbt-test/sbt-coursier/from-wrong-url/project/plugins.sbt @@ -0,0 +1,11 @@ +{ + val pluginVersion = sys.props.getOrElse( + "plugin.version", + throw new RuntimeException( + """|The system property 'plugin.version' is not defined. + |Specify this property using the scriptedLaunchOpts -D.""".stripMargin + ) + ) + + addSbtPlugin("io.get-coursier" % "sbt-coursier" % pluginVersion) +} diff --git a/plugin/src/sbt-test/sbt-coursier/from-wrong-url/src/main/scala/Main.scala b/plugin/src/sbt-test/sbt-coursier/from-wrong-url/src/main/scala/Main.scala new file mode 100644 index 000000000..c9401ff3e --- /dev/null +++ b/plugin/src/sbt-test/sbt-coursier/from-wrong-url/src/main/scala/Main.scala @@ -0,0 +1,13 @@ +import java.io.File +import java.nio.file.Files + +import shapeless._ + +object Main extends App { + case class CC(s: String) + val cc = CC("OK") + val l = Generic[CC].to(cc) + val msg = l.head + + Files.write(new File("output").toPath, msg.getBytes("UTF-8")) +} diff --git a/plugin/src/sbt-test/sbt-coursier/from-wrong-url/test b/plugin/src/sbt-test/sbt-coursier/from-wrong-url/test new file mode 100644 index 000000000..2182f57b0 --- /dev/null +++ b/plugin/src/sbt-test/sbt-coursier/from-wrong-url/test @@ -0,0 +1,3 @@ +$ delete output +> run +$ exists output