Repository: scala-steward-org/scala-steward (scala)

Issue:
Release notes when scmUrl or homepage are set in parent pom
When scm->url or homepage are set in parent pom rather than in the child pom, release notes/changelog links don't get generated

Pull request:
fixes #1157

Patch:
diff --git a/modules/core/src/main/scala/org/scalasteward/core/coursier/CoursierAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/coursier/CoursierAlg.scala
index 4d811a7c0c..f6a9231cee 100644
--- a/modules/core/src/main/scala/org/scalasteward/core/coursier/CoursierAlg.scala
+++ b/modules/core/src/main/scala/org/scalasteward/core/coursier/CoursierAlg.scala
@@ -16,9 +16,11 @@
 
 package org.scalasteward.core.coursier
 
+import cats.data.OptionT
 import cats.effect._
 import cats.implicits._
 import cats.{Applicative, Parallel}
+import coursier.core.Project
 import coursier.interop.cats._
 import coursier.util.StringInterpolators.SafeIvyRepository
 import coursier.{Info, Module, ModuleName, Organization}
@@ -59,9 +61,11 @@ object CoursierAlg {
     val versions = coursier.Versions[F](cache).addRepositories(sbtPluginReleases)
 
     new CoursierAlg[F] {
-      override def getArtifactUrl(dependency: Dependency): F[Option[Uri]] = {
-        val coursierDependency = toCoursierDependency(dependency)
-        for {
+      override def getArtifactUrl(dependency: Dependency): F[Option[Uri]] =
+        getArtifactUrlImpl(toCoursierDependency(dependency))
+
+      private def getArtifactUrlImpl(coursierDependency: coursier.Dependency): F[Option[Uri]] =
+        (for {
           maybeFetchResult <- fetch
             .addDependencies(coursierDependency)
             .addArtifactTypes(coursier.Type.pom, coursier.Type.ivy)
@@ -71,14 +75,23 @@ object CoursierAlg {
               logger.debug(throwable)(s"Failed to fetch artifacts of $coursierDependency").as(None)
             }
         } yield {
-          for {
-            result <- maybeFetchResult
+          (for {
+            result <- maybeFetchResult.toOptionT[F]
             moduleVersion = (coursierDependency.module, coursierDependency.version)
-            (_, project) <- result.resolution.projectCache.get(moduleVersion)
+            (_, project) <- result.resolution.projectCache.get(moduleVersion).toOptionT[F]
             url <- getScmUrlOrHomePage(project.info)
-          } yield url
+              .toOptionT[F]
+              .orElse(OptionT(getParentArtifactUrl(project)))
+          } yield url).value
+        }).flatten
+
+      private def getParentArtifactUrl(project: Project): F[Option[Uri]] =
+        project.parent match {
+          case None => F.pure(none[Uri])
+          case Some((module, version)) =>
+            val parentDep = coursier.Dependency(module, version).withTransitive(false)
+            getArtifactUrlImpl(parentDep)
         }
-      }
 
       override def getVersions(dependency: Dependency): F[List[Version]] =
         versions
@@ -100,7 +113,7 @@ object CoursierAlg {
 
   private def getScmUrlOrHomePage(info: Info): Option[Uri] =
     (info.scm.flatMap(_.url).toList :+ info.homePage)
-      .filterNot(url => url.isEmpty || url.startsWith("git@"))
+      .filterNot(url => url.isEmpty || url.startsWith("git@") || url.startsWith("git:"))
       .flatMap(Uri.fromString(_).toList.filter(_.scheme.isDefined))
       .headOption
 }


Test patch:
diff --git a/modules/core/src/test/scala/org/scalasteward/core/coursier/CoursierAlgTest.scala b/modules/core/src/test/scala/org/scalasteward/core/coursier/CoursierAlgTest.scala
index fcea16bafb..dbf64302ec 100644
--- a/modules/core/src/test/scala/org/scalasteward/core/coursier/CoursierAlgTest.scala
+++ b/modules/core/src/test/scala/org/scalasteward/core/coursier/CoursierAlgTest.scala
@@ -34,18 +34,58 @@ class CoursierAlgTest extends AnyFunSuite with Matchers {
     result shouldBe Some(uri"https://github.com/playframework/play-ws")
   }
 
-  test("getArtifactUrl: URL without a scheme") {
-    val dep = Dependency(
+  test("getArtifactUrl: URL with no or invalid scheme") {
+    val dep1 = Dependency(
       GroupId("org.msgpack"),
       ArtifactId("msgpack-core"),
       "0.8.20"
     )
+    val (state1, result1) = coursierAlg
+      .getArtifactUrl(dep1)
+      .run(MockState.empty)
+      .unsafeRunSync()
+    state1 shouldBe MockState.empty
+    result1 shouldBe Some(uri"http://msgpack.org/")
+
+    val dep2 = Dependency(
+      GroupId("org.xhtmlrenderer"),
+      ArtifactId("flying-saucer-parent"),
+      "9.0.1"
+    )
+    val (state2, result2) = coursierAlg
+      .getArtifactUrl(dep2)
+      .run(MockState.empty)
+      .unsafeRunSync()
+    state2 shouldBe MockState.empty
+    result2 shouldBe Some(uri"http://code.google.com/p/flying-saucer/")
+  }
+
+  test("getArtifactUrl: from parent") {
+    val dep = Dependency(
+      GroupId("net.bytebuddy"),
+      ArtifactId("byte-buddy"),
+      "1.10.5"
+    )
     val (state, result) = coursierAlg
       .getArtifactUrl(dep)
       .run(MockState.empty)
       .unsafeRunSync()
     state shouldBe MockState.empty
-    result shouldBe Some(uri"http://msgpack.org/")
+    result shouldBe Some(uri"https://bytebuddy.net")
+  }
+
+  test("getArtifactUrl: minimal pom") {
+    val dep = Dependency(
+      GroupId("altrmi"),
+      ArtifactId("altrmi-common"),
+      "0.9.6"
+    )
+    val (state, result) = coursierAlg
+      .getArtifactUrl(dep)
+      .run(MockState.empty)
+      .unsafeRunSync()
+    state shouldBe MockState.empty
+    result shouldBe None
   }
 
   test("getArtifactUrl: sbt plugin on Maven Central") {
@@ -72,7 +112,8 @@ class CoursierAlgTest extends AnyFunSuite with Matchers {
       Some(SbtVersion("1.0")),
       Some(ScalaVersion("2.12"))
     )
-    val result = coursierAlg.getArtifactUrl(dep).runA(MockState.empty).unsafeRunSync()
+    val (state, result) = coursierAlg.getArtifactUrl(dep).run(MockState.empty).unsafeRunSync()
+    state shouldBe MockState.empty
     result shouldBe Some(uri"https://github.com/sbt/sbt-release")
   }