From 7f5e509cc5c3c00934c30cea9c8ded38b4ab3bf0 Mon Sep 17 00:00:00 2001 From: Karolina Bogacka Date: Tue, 18 Mar 2025 20:36:46 +0100 Subject: [PATCH 1/4] Configure formatting for the project --- .github/workflows/checks.yml | 33 +++++++++++++++++++ .scalafix.conf | 7 ++++ .scalafmt.conf | 7 ++++ build.sbt | 12 ++++++- project/plugins.sbt | 4 +++ .../scala/eu/neverblink/jelly/cli/App.scala | 5 ++- .../neverblink/jelly/cli/JellyCommand.scala | 27 ++++++++------- .../jelly/cli/command/FoolAround.scala | 9 +++-- .../jelly/cli/command/VersionSpec.scala | 6 ++-- 9 files changed, 86 insertions(+), 24 deletions(-) create mode 100644 .github/workflows/checks.yml create mode 100644 .scalafix.conf create mode 100644 .scalafmt.conf diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml new file mode 100644 index 0000000..cb82408 --- /dev/null +++ b/.github/workflows/checks.yml @@ -0,0 +1,33 @@ +name: Code checks +on: + pull_request: + branches: [ "main" ] +jobs: + scalafmt-lint: + name: Scalafmt lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 1 + - uses: jrouly/scalafmt-native-action@v4 + + scalafix-lint: + name: Scalafix lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Setup JDK + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 21 + cache: sbt + + - name: Setup SBT + uses: sbt/setup-sbt@v1 + + - run: sbt 'scalafixAll --check' \ No newline at end of file diff --git a/.scalafix.conf b/.scalafix.conf new file mode 100644 index 0000000..4235cba --- /dev/null +++ b/.scalafix.conf @@ -0,0 +1,7 @@ +rules = [ + RemoveUnused + ExplicitResultTypes + ProcedureSyntax + RedundantSyntax +] +ExplicitResultTypes.memberVisibility = [Public] \ No newline at end of file diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 0000000..6eb6d6f --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,7 @@ +version = 3.8.6 +runner.dialect = scala36 +maxColumn = 100 +align.preset=none +rewrite.trailingCommas.style = always +newlines.selectChains.style = keep +newlines.selectChains.classicKeepAfterFirstBreak = true \ No newline at end of file diff --git a/build.sbt b/build.sbt index 21d0d67..762b13a 100644 --- a/build.sbt +++ b/build.sbt @@ -1,12 +1,14 @@ ThisBuild / version := "0.1.0-SNAPSHOT" ThisBuild / scalaVersion := "3.6.4" - +ThisBuild / autoAPIMappings := true +ThisBuild / semanticdbEnabled := true resolvers += "Sonatype OSS Snapshots" at "https://s01.oss.sonatype.org/content/repositories/snapshots" lazy val jenaV = "5.3.0" lazy val jellyV = "2.8.0+14-4181e89a-SNAPSHOT" +addCommandAlias("fixAll", "scalafixAll; scalafmtAll") def isDevBuild: Boolean = sys.env.get("DEV_BUILD").exists(s => s != "0" && s != "false") @@ -25,6 +27,14 @@ lazy val root = (project in file(".")) "com.github.alexarchambault" %% "case-app" % "2.1.0-M30", "org.scalatest" %% "scalatest" % "3.2.19" % Test, ), + scalacOptions ++= Seq( + "-Wunused:imports", + "-Werror", + "-feature", + "-deprecation", + "-unchecked", + "-explain", + ), buildInfoKeys := Seq[BuildInfoKey]( version, scalaVersion, diff --git a/project/plugins.sbt b/project/plugins.sbt index 9abd225..412ab3f 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,3 +1,7 @@ addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.13.1") addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.9.3") addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.11.1") + +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.1") +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.14.0") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.4") \ No newline at end of file diff --git a/src/main/scala/eu/neverblink/jelly/cli/App.scala b/src/main/scala/eu/neverblink/jelly/cli/App.scala index 7973cf6..ef394a5 100644 --- a/src/main/scala/eu/neverblink/jelly/cli/App.scala +++ b/src/main/scala/eu/neverblink/jelly/cli/App.scala @@ -3,9 +3,8 @@ package eu.neverblink.jelly.cli import caseapp.* import eu.neverblink.jelly.cli.command.* -/** - * Main entrypoint. - */ +/** Main entrypoint. + */ object App extends CommandsEntryPoint: override def progName: String = "jelly-cli" diff --git a/src/main/scala/eu/neverblink/jelly/cli/JellyCommand.scala b/src/main/scala/eu/neverblink/jelly/cli/JellyCommand.scala index 118b818..9bb527e 100644 --- a/src/main/scala/eu/neverblink/jelly/cli/JellyCommand.scala +++ b/src/main/scala/eu/neverblink/jelly/cli/JellyCommand.scala @@ -9,17 +9,17 @@ import scala.compiletime.uninitialized object JellyCommand: val emptyRemainingArgs: RemainingArgs = RemainingArgs(Seq.empty, Seq.empty) -abstract class JellyCommand[T : {Parser, Help}] extends Command[T]: +abstract class JellyCommand[T: {Parser, Help}] extends Command[T]: private var isTest = false private var out = System.out private var err = System.err private var osOut: ByteArrayOutputStream = uninitialized private var osErr: ByteArrayOutputStream = uninitialized - /** - * Enable the "test mode" which captures stdout, stderr, exit code, and so on. - * @param test true to enable, false to disable - */ + /** Enable the "test mode" which captures stdout, stderr, exit code, and so on. + * @param test + * true to enable, false to disable + */ def testMode(test: Boolean): Unit = this.isTest = test if test then @@ -47,13 +47,16 @@ abstract class JellyCommand[T : {Parser, Help}] extends Command[T]: s else throw new IllegalStateException("Not in test mode") - /** - * Run the command in test mode, capturing stdout and stderr. - * @param options the command options - * @param remainingArgs the remaining arguments - * @throws ExitError if the command exits - * @return (stdout, stderr) - */ + /** Run the command in test mode, capturing stdout and stderr. + * @param options + * the command options + * @param remainingArgs + * the remaining arguments + * @throws ExitError + * if the command exits + * @return + * (stdout, stderr) + */ @throws[ExitError] def runTest(options: T, remainingArgs: RemainingArgs = emptyRemainingArgs): (String, String) = if !isTest then testMode(true) diff --git a/src/main/scala/eu/neverblink/jelly/cli/command/FoolAround.scala b/src/main/scala/eu/neverblink/jelly/cli/command/FoolAround.scala index 8425f8b..9f1b2f2 100644 --- a/src/main/scala/eu/neverblink/jelly/cli/command/FoolAround.scala +++ b/src/main/scala/eu/neverblink/jelly/cli/command/FoolAround.scala @@ -4,13 +4,12 @@ import caseapp.* import eu.neverblink.jelly.cli.JellyCommand case class FoolAroundOptions( - @HelpMessage("What to say") - say: String = "Hello, World!" + @HelpMessage("What to say") + say: String = "Hello, World!", ) -/** - * "foo-bar" kind of command. - */ +/** "foo-bar" kind of command. + */ object FoolAround extends JellyCommand[FoolAroundOptions]: // https://alexarchambault.github.io/case-app/commands/ override def names: List[List[String]] = List( diff --git a/src/test/scala/eu/neverblink/jelly/cli/command/VersionSpec.scala b/src/test/scala/eu/neverblink/jelly/cli/command/VersionSpec.scala index fc4f9f9..3059933 100644 --- a/src/test/scala/eu/neverblink/jelly/cli/command/VersionSpec.scala +++ b/src/test/scala/eu/neverblink/jelly/cli/command/VersionSpec.scala @@ -7,8 +7,8 @@ class VersionSpec extends AnyWordSpec, Matchers: "version command" should { "print something" in { val (out, err) = Version.runTest(VersionOptions()) - out should startWith ("jelly-cli") - out should include ("Jelly-JVM") - out should include ("Apache Jena") + out should startWith("jelly-cli") + out should include("Jelly-JVM") + out should include("Apache Jena") } } From f704002e639939813d77602412f319ee0f653c61 Mon Sep 17 00:00:00 2001 From: Karolina Bogacka Date: Wed, 19 Mar 2025 10:29:47 +0100 Subject: [PATCH 2/4] Add formatting changes --- README.md | 5 ++++ .../jelly/cli/command/FromJellyRDF.scala | 23 +++++++++++++++++++ .../jelly/cli/command/FromJellyRDFSpec.scala | 14 +++++++++++ 3 files changed, 42 insertions(+) create mode 100644 src/main/scala/eu/neverblink/jelly/cli/command/FromJellyRDF.scala create mode 100644 src/test/scala/eu/neverblink/jelly/cli/command/FromJellyRDFSpec.scala diff --git a/README.md b/README.md index 08195d3..e91e46a 100644 --- a/README.md +++ b/README.md @@ -28,3 +28,8 @@ $ ./jelly-cli --help - The binary will be available at `./target/graalvm-native-image/jelly-cli`. Alternatively, you can use the utility with your JVM (no ahead-of-time compilation), by running `sbt run`. + +## Developer notes + +Run `sbt fixAll` before committing. Your code should be formatted and free of warnings. +The CI checks will not pass if this is not the case. diff --git a/src/main/scala/eu/neverblink/jelly/cli/command/FromJellyRDF.scala b/src/main/scala/eu/neverblink/jelly/cli/command/FromJellyRDF.scala new file mode 100644 index 0000000..a731a40 --- /dev/null +++ b/src/main/scala/eu/neverblink/jelly/cli/command/FromJellyRDF.scala @@ -0,0 +1,23 @@ +package eu.neverblink.jelly.cli.command +import caseapp.* +import eu.neverblink.jelly.cli.JellyCommand + +case class FromJellyRDFOptions() + +object FromJellyRDF extends JellyCommand[FromJellyRDFOptions]: + override def names: List[List[String]] = List( + List("from-jelly-rdf"), + ) + + override def run(options: FromJellyRDFOptions, remainingArgs: RemainingArgs): Unit = + println("from-jelly-rdf") + println(options) + println(remainingArgs) + println("from-jelly-rdf") + + /* + * This method will be used to validate the passing RDF stream + */ + def validate(): Unit = + println("from-jelly-rdf validate") + println("from-jelly-rdf validate") diff --git a/src/test/scala/eu/neverblink/jelly/cli/command/FromJellyRDFSpec.scala b/src/test/scala/eu/neverblink/jelly/cli/command/FromJellyRDFSpec.scala new file mode 100644 index 0000000..a82f83b --- /dev/null +++ b/src/test/scala/eu/neverblink/jelly/cli/command/FromJellyRDFSpec.scala @@ -0,0 +1,14 @@ +package eu.neverblink.jelly.cli.command + +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec + +class FromJellyRDFSpec extends AnyWordSpec, Matchers: + "from-jelly-rdf command" should { + "print something" in { + val (out, err) = FromJellyRDF.runTest(FromJellyRDFOptions()) + out should startWith("jelly-cli") + out should include("Jelly-JVM") + out should include("Apache Jena") + } + } From d60d75a9f80e8bee110c91dde3c5845978b1a9c5 Mon Sep 17 00:00:00 2001 From: Karolina Bogacka Date: Wed, 19 Mar 2025 14:30:55 +0100 Subject: [PATCH 3/4] Add test jelly generation --- ...{FromJellyRDF.scala => RDFFromJelly.scala} | 12 ++--- .../jelly/cli/command/FromJellyRDFSpec.scala | 14 ------ .../jelly/cli/command/RDFFromJellySpec.scala | 12 +++++ .../cli/command/helpers/DataGenHelper.scala | 45 +++++++++++++++++++ 4 files changed, 63 insertions(+), 20 deletions(-) rename src/main/scala/eu/neverblink/jelly/cli/command/{FromJellyRDF.scala => RDFFromJelly.scala} (65%) delete mode 100644 src/test/scala/eu/neverblink/jelly/cli/command/FromJellyRDFSpec.scala create mode 100644 src/test/scala/eu/neverblink/jelly/cli/command/RDFFromJellySpec.scala create mode 100644 src/test/scala/eu/neverblink/jelly/cli/command/helpers/DataGenHelper.scala diff --git a/src/main/scala/eu/neverblink/jelly/cli/command/FromJellyRDF.scala b/src/main/scala/eu/neverblink/jelly/cli/command/RDFFromJelly.scala similarity index 65% rename from src/main/scala/eu/neverblink/jelly/cli/command/FromJellyRDF.scala rename to src/main/scala/eu/neverblink/jelly/cli/command/RDFFromJelly.scala index a731a40..78ea3d3 100644 --- a/src/main/scala/eu/neverblink/jelly/cli/command/FromJellyRDF.scala +++ b/src/main/scala/eu/neverblink/jelly/cli/command/RDFFromJelly.scala @@ -4,20 +4,20 @@ import eu.neverblink.jelly.cli.JellyCommand case class FromJellyRDFOptions() -object FromJellyRDF extends JellyCommand[FromJellyRDFOptions]: +object RDFFromJelly extends JellyCommand[FromJellyRDFOptions]: override def names: List[List[String]] = List( - List("from-jelly-rdf"), + List("rdf-from-jelly"), ) override def run(options: FromJellyRDFOptions, remainingArgs: RemainingArgs): Unit = - println("from-jelly-rdf") + println("rdf-from-jelly") println(options) println(remainingArgs) - println("from-jelly-rdf") + println("rdf-from-jelly") /* * This method will be used to validate the passing RDF stream */ def validate(): Unit = - println("from-jelly-rdf validate") - println("from-jelly-rdf validate") + println("rdf-from-jelly validate") + println("rdf-from-jelly validate") diff --git a/src/test/scala/eu/neverblink/jelly/cli/command/FromJellyRDFSpec.scala b/src/test/scala/eu/neverblink/jelly/cli/command/FromJellyRDFSpec.scala deleted file mode 100644 index a82f83b..0000000 --- a/src/test/scala/eu/neverblink/jelly/cli/command/FromJellyRDFSpec.scala +++ /dev/null @@ -1,14 +0,0 @@ -package eu.neverblink.jelly.cli.command - -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpec - -class FromJellyRDFSpec extends AnyWordSpec, Matchers: - "from-jelly-rdf command" should { - "print something" in { - val (out, err) = FromJellyRDF.runTest(FromJellyRDFOptions()) - out should startWith("jelly-cli") - out should include("Jelly-JVM") - out should include("Apache Jena") - } - } diff --git a/src/test/scala/eu/neverblink/jelly/cli/command/RDFFromJellySpec.scala b/src/test/scala/eu/neverblink/jelly/cli/command/RDFFromJellySpec.scala new file mode 100644 index 0000000..4ef9c96 --- /dev/null +++ b/src/test/scala/eu/neverblink/jelly/cli/command/RDFFromJellySpec.scala @@ -0,0 +1,12 @@ +package eu.neverblink.jelly.cli.command + +import eu.neverblink.jelly.cli.command.helpers.DataGenHelper +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec + +class RDFFromJellySpec extends AnyWordSpec, Matchers: + "rdf-from-jelly command" should { + "be able to convert a jelly stream to ntriples" in { + val jellyStream = DataGenHelper.generateJelly(3) + } + } diff --git a/src/test/scala/eu/neverblink/jelly/cli/command/helpers/DataGenHelper.scala b/src/test/scala/eu/neverblink/jelly/cli/command/helpers/DataGenHelper.scala new file mode 100644 index 0000000..6c1e187 --- /dev/null +++ b/src/test/scala/eu/neverblink/jelly/cli/command/helpers/DataGenHelper.scala @@ -0,0 +1,45 @@ +package eu.neverblink.jelly.cli.command.helpers + +import eu.ostrzyciel.jelly.convert.jena.riot.JellyLanguage +import org.apache.commons.io.output.ByteArrayOutputStream +import org.apache.jena.rdf.model.{Model, ModelFactory, ResourceFactory} +import org.apache.jena.riot.RDFDataMgr + +import scala.util.Using + +/* + * This class will be used to generate test data + */ +object DataGenHelper: + + /* + * This method generates a triple model with nTriples + * @param nTriples number of triples to generate + * @return Model + */ + def generateTripleModel(nTriples: Int): Model = + val model = ModelFactory.createDefaultModel() + val subStr = "http://example.org/subject" + val predStr = "http://example.org/predicate" + val objStr = "http://example.org/object" + val tripleList = (1 to nTriples).map { i => + val sub = ResourceFactory.createResource(s"$subStr/$i") + val pred = ResourceFactory.createProperty(s"$predStr/$i") + val obj = ResourceFactory.createResource(s"$objStr/$i") + val stat = ResourceFactory.createStatement(sub, pred, obj) + model.add(stat) + } + model + + /* + * This method generates a Jelly stream with nTriples + * @param nTriples number of triples to generate + */ + def generateJelly(nTriple: Int): ByteArrayOutputStream = + val model = generateTripleModel(nTriple) + val newStream = ByteArrayOutputStream() + // TODO: Add tests for different variants of Jelly (small strict etc) + Using.resource(newStream) { stream => + RDFDataMgr.write(stream, model, JellyLanguage.JELLY) + } + newStream From 5477e8bacbddb1a442b03cba2382531d34eef3d8 Mon Sep 17 00:00:00 2001 From: Karolina Bogacka Date: Wed, 19 Mar 2025 15:12:56 +0100 Subject: [PATCH 4/4] Correct formatting --- src/main/scala/eu/neverblink/jelly/cli/command/Version.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/scala/eu/neverblink/jelly/cli/command/Version.scala b/src/main/scala/eu/neverblink/jelly/cli/command/Version.scala index 5c265ae..0d99d41 100644 --- a/src/main/scala/eu/neverblink/jelly/cli/command/Version.scala +++ b/src/main/scala/eu/neverblink/jelly/cli/command/Version.scala @@ -16,8 +16,7 @@ object Version extends JellyCommand[VersionOptions]: .find(_.startsWith("org.apache.jena:jena-core:")).get.split(":")(2) val jellyV = BuildInfo.libraryDependencies .find(_.startsWith("eu.ostrzyciel.jelly:jelly-jena:")).get.split(":")(2) - printLine( - f"""jelly-cli ${BuildInfo.version} + printLine(f"""jelly-cli ${BuildInfo.version} |---------------------------------------------- |Jelly-JVM $jellyV |Apache Jena $jenaV