diff --git a/README.md b/README.md index 9f2c0b93e..c2d7e5e66 100644 --- a/README.md +++ b/README.md @@ -147,12 +147,12 @@ This project is participating in **Google Summer of Code (GSoC) 2025**! If you'r ### 🛠️ [Scaladex – Support for Compiler Plugins](https://summerofcode.withgoogle.com/programs/2025/projects/D71ZWImy) -* **Contributor**: [Vidisha Gawas](hhttps://github.com/vidishagawas121) +* **Contributor**: [Vidisha Gawas](https://github.com/vidishagawas121) 🔗 [LinkedIn](https://in.linkedin.com/in/vidisha-gawas-146348364) * **Mentors**: * [Adrien Piquerez](https://github.com/adpi2) * [Kannupriya Kalra](https://github.com/kannupriyakalra) -* **Announcement**: [Official Acceptance Post](https://www.linkedin.com/posts/vidisha-gawas-146348364_gsoc-gsoc2025-scalacenter-activity-7326793814331871232-0hGz?utm_source=share&utm_medium=member_android&rcm=ACoAAFp2nsYBNBsrS0fo8jK1QyeRCdtOBCWgUuQ) | [Midterm evaluation post](https://www.linkedin.com/posts/vidisha-gawas-146348364_gsoc2025-scalacenter-scaladex-activity-7352311549400662016-zvpl?utm_source=share&utm_medium=member_android&rcm=ACoAAFp2nsYBNBsrS0fo8jK1QyeRCdtOBCWgUuQ) +* **Announcement**: [Official Acceptance Post](https://www.linkedin.com/posts/vidisha-gawas-146348364_gsoc-gsoc2025-scalacenter-activity-7326793814331871232-0hGz?utm_source=share&utm_medium=member_android&rcm=ACoAAFp2nsYBNBsrS0fo8jK1QyeRCdtOBCWgUuQ) | [Midterm evaluation post](https://www.linkedin.com/posts/vidisha-gawas-146348364_gsoc2025-scalacenter-scaladex-activity-7352311549400662016-zvpl?utm_source=share&utm_medium=member_android&rcm=ACoAAFp2nsYBNBsrS0fo8jK1QyeRCdtOBCWgUuQ) | [GSoC Final Report URL](https://opensourcegirl.hashnode.dev/gsoc-2025-scaladex-project-review) * **Blog**: [Building with GSoC](https://opensourcegirl.hashnode.dev/) * **Work Log**:[GSoC Progress Tracker](https://github.com/users/vidishagawas121/projects/2) * **Technologies**: Scala, SBT, Play Framework, Elasticsearch, GitHub Actions, JavaScript, HTML/CSS diff --git a/doc/assets/GSoC_Lighting_Talk/GSoC-2025-Lightning-Talk.pdf b/doc/assets/GSoC_Lighting_Talk/GSoC-2025-Lightning-Talk.pdf new file mode 100644 index 000000000..a0b2156bf Binary files /dev/null and b/doc/assets/GSoC_Lighting_Talk/GSoC-2025-Lightning-Talk.pdf differ diff --git a/doc/assets/GSoC_Lighting_Talk/GSoC-2025-Lightning-Talk.pptx b/doc/assets/GSoC_Lighting_Talk/GSoC-2025-Lightning-Talk.pptx new file mode 100644 index 000000000..66ff03721 Binary files /dev/null and b/doc/assets/GSoC_Lighting_Talk/GSoC-2025-Lightning-Talk.pptx differ diff --git a/modules/core/shared/src/main/scala/scaladex/core/model/Artifact.scala b/modules/core/shared/src/main/scala/scaladex/core/model/Artifact.scala index 48bc659a8..b39b96f6a 100644 --- a/modules/core/shared/src/main/scala/scaladex/core/model/Artifact.scala +++ b/modules/core/shared/src/main/scala/scaladex/core/model/Artifact.scala @@ -60,6 +60,7 @@ case class Artifact( def sbtInstall: Option[String] = val install = platform match + case CompilerPlugin => throw new UnsupportedOperationException("CompilerPlugin sbtInstall not supported yet") case SbtPlugin(_) => Some(s"""addSbtPlugin("$groupId" % "$name" % "$version")""") case MillPlugin(_) => None case _ if isNonStandardLib => Some(s"""libraryDependencies += "$groupId" % "$artifactId" % "$version"""") @@ -96,6 +97,7 @@ case class Artifact( |interp.resolvers() = interp.resolvers() :+ res""".stripMargin val install = platform match + case CompilerPlugin => throw new UnsupportedOperationException("CompilerPlugin ammInstall not supported yet") case MillPlugin(_) | SbtPlugin(_) | ScalaNative(_) | ScalaJs(_) => None case Jvm => language match @@ -115,6 +117,7 @@ case class Artifact( */ def mavenInstall: Option[String] = platform match + case CompilerPlugin => throw new UnsupportedOperationException("CompilerPlugin mavenInstall not supported yet") case MillPlugin(_) | SbtPlugin(_) | ScalaNative(_) | ScalaJs(_) => None case Jvm => Some( @@ -130,6 +133,7 @@ case class Artifact( */ def gradleInstall: Option[String] = platform match + case CompilerPlugin => throw new UnsupportedOperationException("CompilerPlugin gradleInstall not supported yet") case MillPlugin(_) | SbtPlugin(_) | ScalaNative(_) | ScalaJs(_) => None case Jvm => Some(s"compile group: '$groupId', name: '$artifactId', version: '$version'") @@ -138,6 +142,7 @@ case class Artifact( */ def millInstall: Option[String] = val install = platform match + case CompilerPlugin => throw new UnsupportedOperationException("CompilerPlugin millInstall not supported yet") case MillPlugin(_) => Some(s"import $$ivy.`$groupId::$name::$version`") case SbtPlugin(_) => None case ScalaNative(_) | ScalaJs(_) => Some(s"""ivy"$groupId::$name::$version"""") @@ -159,6 +164,7 @@ case class Artifact( def scalaCliInstall: Option[String] = binaryVersion.platform match + case CompilerPlugin => throw new UnsupportedOperationException("CompilerPlugin scalaCliInstall not supported yet") case MillPlugin(_) | SbtPlugin(_) => None case ScalaNative(_) | ScalaJs(_) => Some(s"""//> using dep "$groupId::$name::$version"""") case Jvm => @@ -170,6 +176,7 @@ case class Artifact( def csLaunch: Option[String] = platform match + case CompilerPlugin => throw new UnsupportedOperationException("CompilerPlugin csLaunch not supported yet") case MillPlugin(_) | SbtPlugin(_) => None case ScalaNative(_) | ScalaJs(_) => Some(s"cs launch $groupId::$name::$version") case Jvm => @@ -190,6 +197,7 @@ case class Artifact( val targetParam = platform match case ScalaJs(_) => Some("t" -> "JS") case Jvm => Some("t" -> "JVM") + case CompilerPlugin => None case _ => None val scalaVersionParam = language match diff --git a/modules/core/shared/src/main/scala/scaladex/core/model/BinaryVersion.scala b/modules/core/shared/src/main/scala/scaladex/core/model/BinaryVersion.scala index 75671ea20..ff4aea387 100644 --- a/modules/core/shared/src/main/scala/scaladex/core/model/BinaryVersion.scala +++ b/modules/core/shared/src/main/scala/scaladex/core/model/BinaryVersion.scala @@ -28,6 +28,7 @@ final case class BinaryVersion(platform: Platform, language: Language): case Jvm => language.toString case p: SbtPlugin => p.toString case p: MillPlugin => p.toString + case CompilerPlugin => s"CompilerPlugin ($language)" case _ => s"$platform ($language)" end BinaryVersion diff --git a/modules/core/shared/src/main/scala/scaladex/core/model/Platform.scala b/modules/core/shared/src/main/scala/scaladex/core/model/Platform.scala index 67f732ef5..2657b0363 100644 --- a/modules/core/shared/src/main/scala/scaladex/core/model/Platform.scala +++ b/modules/core/shared/src/main/scala/scaladex/core/model/Platform.scala @@ -9,6 +9,11 @@ case object Jvm extends Platform: override def value: String = "jvm" override def isValid: Boolean = true +case object CompilerPlugin extends Platform: + override def toString: String = "CompilerPlugin" + override def value: String = "compiler-plugin" + override def isValid: Boolean = true + case class ScalaJs(version: Version) extends Platform: override def toString: String = s"Scala.js $version" override def value: String = s"sjs${version.value}" @@ -80,6 +85,7 @@ object Platform: case ScalaNative(version) => (3, Some(version)) case SbtPlugin(version) => (2, Some(version)) case MillPlugin(version) => (1, Some(version)) + case CompilerPlugin => (0, None) } def parse(input: String): Option[Platform] = @@ -89,5 +95,6 @@ object Platform: case s"native$version" => Version.parseSemantically(version).map(ScalaNative.apply) case s"sbt$version" => Version.parseSemantically(version).map(SbtPlugin.apply) case s"mill$version" => Version.parseSemantically(version).map(MillPlugin.apply) + case "compiler-plugin" => Some(CompilerPlugin) case _ => None end Platform diff --git a/modules/core/shared/src/test/scala/scaladex/core/model/ArtifactTests.scala b/modules/core/shared/src/test/scala/scaladex/core/model/ArtifactTests.scala index 8320f14f7..08bb5649c 100644 --- a/modules/core/shared/src/test/scala/scaladex/core/model/ArtifactTests.scala +++ b/modules/core/shared/src/test/scala/scaladex/core/model/ArtifactTests.scala @@ -196,6 +196,20 @@ class ArtifactTests extends AnyFunSpec with Matchers: ) ) } + + it("should allow creating Artifact with CompilerPlugin platform without exceptions") { + val artifact = createArtifact( + groupId = "org.example", + artifactId = "myplugin_2.13", + version = "1.0.0", + binaryVersion = BinaryVersion(CompilerPlugin, Scala.`2.13`), + artifactName = Some(Name("myplugin")) + ) + + artifact.platform shouldBe CompilerPlugin + artifact.language shouldBe Scala.`2.13` + artifact.binaryVersion.isValid shouldBe true + } } private def createArtifact( diff --git a/modules/core/shared/src/test/scala/scaladex/core/test/Values.scala b/modules/core/shared/src/test/scala/scaladex/core/test/Values.scala index 80e4ea463..a8ba88e41 100644 --- a/modules/core/shared/src/test/scala/scaladex/core/test/Values.scala +++ b/modules/core/shared/src/test/scala/scaladex/core/test/Values.scala @@ -287,4 +287,40 @@ object Values: object Scala3: val organization: Project.Organization = Project.Organization("scala") val reference: Project.Reference = Project.Reference.unsafe("scala/scala3") + + object CompilerPluginProj: + val reference: Project.Reference = Project.Reference.unsafe("org/example-compiler-plugin") + val artifact: Artifact = Artifact( + groupId = GroupId("org"), + artifactId = ArtifactId("example-compiler-plugin_2.13"), + version = Version("1.0.0"), + projectRef = reference, + description = Some("Example compiler plugin"), + binaryVersion = BinaryVersion(CompilerPlugin, Scala.`2.13`), + fullScalaVersion = Some(Scala.`2.13`.version), + developers = Seq.empty, + scaladocUrl = None, + versionScheme = None, + creationDate = now.minus(5, ChronoUnit.MINUTES) + ) + val creationDate: Instant = now.minus(5, ChronoUnit.MINUTES) + val githubInfo: GithubInfo = GithubInfo( + description = Some("Example compiler plugin for testing"), + homepage = None, + logo = None, + stars = 10, + forks = 2, + watchers = 5, + issues = 0, + language = Some("Scala"), + topics = Seq("compiler-plugin", "scala"), + defaultBranch = "main" + ) + val projectDocument: ProjectDocument = + ProjectDocument + .default(reference) + .copy( + languages = Seq(Scala.`2.13`), + platforms = Seq(CompilerPlugin) + ) end Values diff --git a/modules/infra/src/test/scala/scaladex/infra/ElasticsearchEngineTests.scala b/modules/infra/src/test/scala/scaladex/infra/ElasticsearchEngineTests.scala index d19b4c221..fc07188f4 100644 --- a/modules/infra/src/test/scala/scaladex/infra/ElasticsearchEngineTests.scala +++ b/modules/infra/src/test/scala/scaladex/infra/ElasticsearchEngineTests.scala @@ -6,6 +6,7 @@ import scala.concurrent.Future import scala.concurrent.duration.Duration import scaladex.core.model.BinaryVersion +import scaladex.core.model.CompilerPlugin import scaladex.core.model.Jvm import scaladex.core.model.Project import scaladex.core.model.Scala @@ -17,6 +18,7 @@ import scaladex.core.model.search.PageParams import scaladex.core.model.search.ProjectDocument import scaladex.core.model.search.SearchParams import scaladex.core.model.search.Sorting +import scaladex.core.test.Values import scaladex.core.test.Values.* import scaladex.core.util.ScalaExtensions.* import scaladex.infra.config.ElasticsearchConfig @@ -33,7 +35,8 @@ class ElasticsearchEngineTests extends AsyncFreeSpec with Matchers with BeforeAn val searchEngine: ElasticsearchEngine = ElasticsearchEngine.open(config) val pageParams: PageParams = PageParams(1, 20) - val projects: Seq[ProjectDocument] = Seq(Cats.projectDocument, Scalafix.projectDocument) + val projects: Seq[ProjectDocument] = + Seq(Cats.projectDocument, Scalafix.projectDocument, Values.CompilerPluginProj.projectDocument) private def insertAll(projects: Seq[ProjectDocument]): Future[Unit] = for @@ -66,11 +69,27 @@ class ElasticsearchEngineTests extends AsyncFreeSpec with Matchers with BeforeAn byCommitActivity <- searchEngine.find(params.copy(sorting = Sorting.CommitActivity), pageParams) byContributors <- searchEngine.find(params.copy(sorting = Sorting.Contributors), pageParams) yield - (byDependent.items.map(_.document) should contain).theSameElementsInOrderAs(catsFirst) - (byCreated.items.map(_.document) should contain).theSameElementsInOrderAs(scalafixFirst) // todo fix - (byStars.items.map(_.document) should contain).theSameElementsInOrderAs(catsFirst) - (byCommitActivity.items.map(_.document) should contain).theSameElementsInOrderAs(catsFirst) - (byContributors.items.map(_.document) should contain).theSameElementsInOrderAs(catsFirst) + val depDocs = byDependent.items + .map(_.document) + .filter(d => Set(Cats.projectDocument.reference, Scalafix.projectDocument.reference).contains(d.reference)) + val createdDocs = byCreated.items + .map(_.document) + .filter(d => Set(Cats.projectDocument.reference, Scalafix.projectDocument.reference).contains(d.reference)) + val starsDocs = byStars.items + .map(_.document) + .filter(d => Set(Cats.projectDocument.reference, Scalafix.projectDocument.reference).contains(d.reference)) + val commitDocs = byCommitActivity.items + .map(_.document) + .filter(d => Set(Cats.projectDocument.reference, Scalafix.projectDocument.reference).contains(d.reference)) + val contribDocs = byContributors.items + .map(_.document) + .filter(d => Set(Cats.projectDocument.reference, Scalafix.projectDocument.reference).contains(d.reference)) + + (depDocs should contain).theSameElementsInOrderAs(catsFirst) + (createdDocs should contain).theSameElementsInOrderAs(scalafixFirst) // todo fix + (starsDocs should contain).theSameElementsInOrderAs(catsFirst) + (commitDocs should contain).theSameElementsInOrderAs(catsFirst) + (contribDocs should contain).theSameElementsInOrderAs(catsFirst) end for } @@ -125,6 +144,14 @@ class ElasticsearchEngineTests extends AsyncFreeSpec with Matchers with BeforeAn yield (scalaJsVersions should contain).theSameElementsInOrderAs(expected) } + "count by platforms includes compiler plugin" in { + val params = SearchParams(queryString = "*") + for + _ <- insertAll(projects) + platforms <- searchEngine.countByPlatforms(params) + yield platforms should contain(CompilerPlugin -> 1L) + } + "remove missing document should not fail" in { for _ <- searchEngine.delete(Cats.reference) yield succeed diff --git a/modules/server/src/main/scala/scaladex/server/route/FrontPage.scala b/modules/server/src/main/scala/scaladex/server/route/FrontPage.scala index be885b5cf..106e6bf8b 100644 --- a/modules/server/src/main/scala/scaladex/server/route/FrontPage.scala +++ b/modules/server/src/main/scala/scaladex/server/route/FrontPage.scala @@ -80,6 +80,13 @@ class FrontPage(env: Env, database: WebDatabase, searchEngine: SearchEngine)(usi EcosystemVersion(millP.version, count, search = Url(s"search?platform=${millP.value}")) } ) + val compilerPluginEcosystem = EcosystemHighlight( + "Compiler Plugin", + platforms.collect { + case (CompilerPlugin, count) => + EcosystemVersion(Version(1), count, search = Url(s"search?platform=${CompilerPlugin.value}")) + } + ) frontpage( env, @@ -87,6 +94,7 @@ class FrontPage(env: Env, database: WebDatabase, searchEngine: SearchEngine)(usi Seq(scala3Ecosystem, scala2Ecosystem).flatten, Seq(scalajsEcosystem, scalaNativeEcosystem).flatten, Seq(sbtPluginEcosystem, millPluginEcosystem).flatten, + Seq(compilerPluginEcosystem).flatten, latestProjects, mostDependedUpon, userInfo, diff --git a/modules/server/src/test/scala/scaladex/server/route/FrontPageTests.scala b/modules/server/src/test/scala/scaladex/server/route/FrontPageTests.scala new file mode 100644 index 000000000..a96f1f95d --- /dev/null +++ b/modules/server/src/test/scala/scaladex/server/route/FrontPageTests.scala @@ -0,0 +1,69 @@ +package scaladex.server.route + +import scala.concurrent.Await +import scala.concurrent.Future +import scala.concurrent.duration.Duration + +import scaladex.core.model.* +import scaladex.core.test.Values + +import org.apache.pekko.http.scaladsl.model.StatusCodes +import org.apache.pekko.http.scaladsl.server.Directives.* +import org.apache.pekko.http.scaladsl.server.Route +import org.scalatest.BeforeAndAfterEach + +class FrontPageTests extends ControllerBaseSuite with BeforeAndAfterEach: + import Values.* + + override def beforeEach(): Unit = + database.reset() + Await.result(insertCompilerPluginProject(), Duration.Inf) + + private def insertCompilerPluginProject(): Future[Unit] = + for + _ <- artifactService.insertArtifact(CompilerPluginProj.artifact, Seq.empty) + _ <- database.updateProjectCreationDate(CompilerPluginProj.reference, CompilerPluginProj.creationDate) + _ <- database.updateGithubInfoAndStatus(CompilerPluginProj.reference, CompilerPluginProj.githubInfo, ok) + yield () + + val frontPage = new FrontPage(config.env, database, searchEngine) + val route: Route = frontPage.route(None) + + it("should display CompilerPlugin section on front page") { + Get("/") ~> route ~> check { + status shouldBe StatusCodes.OK + val responseBody = responseAs[String] + + // Check that the CompilerPlugin section is present + responseBody should include("Compiler Plugins") + responseBody should include("Compiler Plugin") + responseBody should include("search?platform=compiler-plugin") + } + } + + it("should display CompilerPlugin with correct project count") { + Get("/") ~> route ~> check { + status shouldBe StatusCodes.OK + val responseBody = responseAs[String] + + // Check that the CompilerPlugin section shows the project count + responseBody should include("projects") + } + } + + it("should have CompilerPlugin as a separate section from Build Tool Plugins") { + Get("/") ~> route ~> check { + status shouldBe StatusCodes.OK + val responseBody = responseAs[String] + + // Check that both sections exist + responseBody should include("Build Tool Plugins") + responseBody should include("Compiler Plugins") + + // Check that CompilerPlugin is not mixed with build tool plugins + val buildToolPluginsIndex = responseBody.indexOf("Build Tool Plugins") + val compilerPluginsIndex = responseBody.indexOf("Compiler Plugins") + compilerPluginsIndex should be > buildToolPluginsIndex + } + } +end FrontPageTests diff --git a/modules/template/src/main/twirl/scaladex/view/frontpage.scala.html b/modules/template/src/main/twirl/scaladex/view/frontpage.scala.html index ac02ca191..04e9791b8 100644 --- a/modules/template/src/main/twirl/scaladex/view/frontpage.scala.html +++ b/modules/template/src/main/twirl/scaladex/view/frontpage.scala.html @@ -9,6 +9,7 @@ scalaVersions: Seq[EcosystemHighlight], platforms: Seq[EcosystemHighlight], buildToolPlugins: Seq[EcosystemHighlight], + compilerPlugins: Seq[EcosystemHighlight], latestProjects: Seq[ProjectDocument], mostDependedUpon: Seq[ProjectDocument], user: Option[UserState], @@ -28,6 +29,7 @@