From 33503746d7395b4edc61e8e2822c09ece4bbf46b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20Leuth=C3=A4user?= <1417198+max-leuthaeuser@users.noreply.github.com> Date: Mon, 6 Jul 2026 14:26:35 +0200 Subject: [PATCH] Migrate to SBT 2: fix resource loading and reorganize CI --- .github/workflows/dependency-graph.yml | 4 +- .github/workflows/pr.yml | 48 +++++++++++++++---- .../scroll/internal/ecore/ECoreImporter.scala | 3 +- .../scroll/internal/util/ResourceLoader.scala | 22 +++++++++ project/Dependencies.scala | 1 + project/build.properties | 2 +- project/plugins.sbt | 7 ++- .../scroll/tests/AbstractSCROLLTest.scala | 3 ++ .../scroll/tests/parameterized/CROITest.scala | 2 +- .../parameterized/ECoreInstanceTest.scala | 2 +- 10 files changed, 76 insertions(+), 18 deletions(-) create mode 100644 core/src/main/scala/scroll/internal/util/ResourceLoader.scala diff --git a/.github/workflows/dependency-graph.yml b/.github/workflows/dependency-graph.yml index c6a9eba7..de72cfe9 100644 --- a/.github/workflows/dependency-graph.yml +++ b/.github/workflows/dependency-graph.yml @@ -10,9 +10,9 @@ jobs: name: Update Dependency Graph runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v7 - uses: sbt/setup-sbt@v1 - - uses: scalacenter/sbt-dependency-submission@v2 + - uses: scalacenter/sbt-dependency-submission@v3 with: working-directory: './' diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index c8a60240..9ecbca00 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -1,17 +1,37 @@ name: pr on: pull_request jobs: + format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 1 + - name: Set up JDK + uses: actions/setup-java@v5 + with: + distribution: 'temurin' + java-version: '21' + cache: 'sbt' + - name: Set up SBT + uses: sbt/setup-sbt@v1 + - name: Check formatting + run: sbt "scalafmtCheckAll; scalafmtSbtCheck;" + - run: echo "Previous step failed because code is not formatted. Run 'sbt format'" + if: ${{ failure() }} + test-scroll: + needs: format runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 1 - name: Set up JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: '21' @@ -20,9 +40,21 @@ jobs: uses: sbt/setup-sbt@v1 - name: Compile and run tests run: sbt "clean; test;" - - name: Compile Jmh benchmarks - run: sbt "clean; benchmark/Jmh/compile;" - - name: Check formatting - run: sbt "scalafmtCheckAll; scalafmtSbtCheck;" - - run: echo "Previous step failed because code is not formatted. Run 'sbt format'" - if: ${{ failure() }} + + jmh-compile: + needs: format + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 1 + - name: Set up JDK + uses: actions/setup-java@v5 + with: + distribution: 'temurin' + java-version: '21' + cache: 'sbt' + - name: Set up SBT + uses: sbt/setup-sbt@v1 + - name: Compile JMH benchmarks + run: sbt "benchmark/Jmh/compile;" diff --git a/core/src/main/scala/scroll/internal/ecore/ECoreImporter.scala b/core/src/main/scala/scroll/internal/ecore/ECoreImporter.scala index 62cdfac2..9d425c51 100644 --- a/core/src/main/scala/scroll/internal/ecore/ECoreImporter.scala +++ b/core/src/main/scala/scroll/internal/ecore/ECoreImporter.scala @@ -9,11 +9,12 @@ import org.eclipse.emf.ecore.xmi.XMLResource import org.eclipse.emf.ecore.xmi.impl.EcoreResourceFactoryImpl import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl import scroll.internal.errors.SCROLLErrors.CROMMetaModelLoadFailure +import scroll.internal.util.ResourceLoader /** Trait providing functionality for importing ecore models. */ trait ECoreImporter { - private val META_MODEL_PATH = getClass.getResource("/crom_l1_composed.ecore").getPath + private val META_MODEL_PATH: String = ResourceLoader.resourcePath("/crom_l1_composed.ecore") private def registerMetaModel(rs: ResourceSetImpl): Unit = { Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap diff --git a/core/src/main/scala/scroll/internal/util/ResourceLoader.scala b/core/src/main/scala/scroll/internal/util/ResourceLoader.scala new file mode 100644 index 00000000..eabaffb4 --- /dev/null +++ b/core/src/main/scala/scroll/internal/util/ResourceLoader.scala @@ -0,0 +1,22 @@ +package scroll.internal.util + +object ResourceLoader { + + /** Returns a filesystem path to a classpath resource, extracting it to a temp file if it lives inside a JAR. */ + def resourcePath(name: String, loader: ClassLoader = getClass.getClassLoader): String = { + val url = loader.getResource(name.stripPrefix("/")) + require(url != null, s"Classpath resource not found: $name") + if (url.getProtocol == "file") url.getPath + else { + val suffix = name.substring(name.lastIndexOf('.')) + val tmp = java.io.File.createTempFile("scroll-resource", suffix) + tmp.deleteOnExit() + val in = url.openStream() + val out = new java.io.FileOutputStream(tmp) + try in.transferTo(out) + finally { in.close(); out.close() } + tmp.getAbsolutePath + } + } + +} diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 6f70529a..da905faa 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -1,4 +1,5 @@ import sbt.CrossVersion +import sbt.moduleIDConfigurable import sbt.stringToOrganization object Dependencies extends Dependencies diff --git a/project/build.properties b/project/build.properties index 7c95fc19..312809a9 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.12.13 +sbt.version=2.0.1 diff --git a/project/plugins.sbt b/project/plugins.sbt index 9742333b..7cfcccae 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,4 +1,3 @@ -addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.3.1") -addSbtPlugin("com.thoughtworks.sbt-api-mappings" % "sbt-api-mappings" % "3.0.3") -addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.8") -addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.6.1") +addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.3.1") +addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.8") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.6.1") diff --git a/tests/src/test/scala/scroll/tests/AbstractSCROLLTest.scala b/tests/src/test/scala/scroll/tests/AbstractSCROLLTest.scala index 178ccf58..8a52eda1 100644 --- a/tests/src/test/scala/scroll/tests/AbstractSCROLLTest.scala +++ b/tests/src/test/scala/scroll/tests/AbstractSCROLLTest.scala @@ -3,10 +3,13 @@ package scroll.tests import org.scalatest.matchers.should.Matchers import org.scalatest.ParallelTestExecution import org.scalatest.funsuite.AnyFunSuite +import scroll.internal.util.ResourceLoader abstract class AbstractSCROLLTest extends AnyFunSuite with Matchers with ParallelTestExecution { protected def streamToSeq(in: java.io.ByteArrayOutputStream, splitAt: String = System.lineSeparator()): Seq[String] = in.toString.split(splitAt).toSeq + protected def resourcePath(name: String): String = ResourceLoader.resourcePath(name) + } diff --git a/tests/src/test/scala/scroll/tests/parameterized/CROITest.scala b/tests/src/test/scala/scroll/tests/parameterized/CROITest.scala index 565f7f99..f7834f7c 100644 --- a/tests/src/test/scala/scroll/tests/parameterized/CROITest.scala +++ b/tests/src/test/scala/scroll/tests/parameterized/CROITest.scala @@ -7,7 +7,7 @@ class CROITest extends AbstractParameterizedSCROLLTest { test("CROI is filled manually") { forAll(PARAMS) { (c: Boolean, cc: Boolean) => - val p = getClass.getResource("/Bank.crom").getPath + val p = resourcePath("/Bank.crom") new CompartmentUnderTest(c, cc) with CROI { val n = new CoreA() val r = new RoleA() diff --git a/tests/src/test/scala/scroll/tests/parameterized/ECoreInstanceTest.scala b/tests/src/test/scala/scroll/tests/parameterized/ECoreInstanceTest.scala index 5b629485..5f9e31c0 100644 --- a/tests/src/test/scala/scroll/tests/parameterized/ECoreInstanceTest.scala +++ b/tests/src/test/scala/scroll/tests/parameterized/ECoreInstanceTest.scala @@ -15,7 +15,7 @@ class ECoreInstanceTest extends AbstractParameterizedSCROLLTest { } test("Loading from a valid path containing a valid model") { - val p = getClass.getResource("/Bank.crom").getPath + val p = resourcePath("/Bank.crom") forAll(PARAMS) { (c: Boolean, cc: Boolean) => new CompartmentUnderTest(c, cc) with CROM { wellformed(p) shouldBe true