I'm having trouble getting an SBT task to run migrations with Flyway; I get an exception when I run the task. Any ideas how I could fix it?
org.flywaydb.core.api.FlywayException: Unable to instantiate JDBC driver: org.postgresql.Driver => Check whether the jar file is present
The following code works, when I run it in BeforeAll, in my tests (ScalaTest), but does not work when I move it into an SBT task.
val flyway = Flyway
.configure()
.locations("filesystem:./**/resources/db/migrations/")
.dataSource("jdbc:postgresql://localhost:5432/my_database", "my_user", "secret")
.load()
flyway.clean()
flyway.migrate()
My /build.sbt file looks like this:
import org.flywaydb.core.Flyway
lazy val migrate = taskKey[Unit]("Migrate database")
lazy val migrateTask = Def.task {
println("Migrate")
val flyway = Flyway
.configure()
.locations("filesystem:./**/resources/db/migrations/")
.dataSource("jdbc:postgresql://localhost:5432/my_database", "my_user", "secret")
.load()
flyway.clean()
flyway.migrate()
}
val IntegrationTest = config("integration") extend Test
lazy val integrationTestSettings = inConfig(IntegrationTest)(Defaults.testSettings) ++ List(
IntegrationTest / fork := false,
IntegrationTest / parallelExecution := false,
IntegrationTest / sourceDirectory := baseDirectory.value / "src/test/integration",
IntegrationTest / test := {
(IntegrationTest / test) dependsOn migrateTask
}.value
)
lazy val root = Project(id = "hello", base = file("."))
.configs(Configs.all: _*)
.settings(
integrationTestSettings,
libraryDependencies += "org.scalatest" %% "scalatest" % "3.1.4",
)
And my /project/build.sbt looks like this:
libraryDependencies ++= List(
"org.flywaydb" % "flyway-core" % "7.6.0",
"org.postgresql" % "postgresql" % "42.2.19",
)
The versions I'm using are:
SBT: 1.4.5
Scala: 2.13.4
Flyway: 7.6.0
Does anyone have any ideas why I'm getting that error, and how I can fix it?
Any help would be greatly appreciated. Thanks :)
Searching on the Flyway repo, the error message is coming from here - https://github.com/flyway/flyway/blob/9033185ab8bfa56b0dae9136c04763cdccc50081/flyway-core/src/main/java/org/flywaydb/core/internal/jdbc/DriverDataSource.java#L165-L182 where it's trying load the database driver from the classloader. These ClassLoader techniques sometimes clash with the sbt set up layered ClassLoader to run sbt itself. That's my speculation on what's happening.
How do we work around this?
You said that running it as part the test worked, so maybe you could create a subproject for this purpose?
ThisBuild / scalaVersion := "2.13.4"
lazy val migrate = taskKey[Unit]("Migrate database")
lazy val root = (project in file("."))
.settings(
name := "hello",
migrate := (migrateProj / run).toTask("").value
)
// utility project to run database migration
lazy val migrateProj = (project in file("migrate"))
.settings(
libraryDependencies ++= List(
"org.flywaydb" % "flyway-core" % "7.6.0",
"org.postgresql" % "postgresql" % "42.2.19",
),
Compile / run / fork := true,
publish / skip := true,
)
migrate/Migrate.scala
object Migrate extends App {
println("migrate")
// rest of the code here...
}
Now you can run
sbt:flyway> migrate
[info] running (fork) Migrate
[info] migrate
[success] Total time: 4 s, completed Mar 6, 2021 9:03:07 PM
Details about layered ClassLoader
ClassLoader techniques sometimes clash with the sbt set up layered ClassLoader to run sbt itself. sbt-the-Bash-script allows users to choose the sbt version using project/build.properties, and Scala version using build.sbt. Both of these make sbt build declarative and repeatable, and generally a good thing. But how can sbt launcher written using Scala 2.10 launch sbt 1.4.x written using Scala 2.12, which then launch your Scala 2.13 application? Each of these boundary cross is done by creating a layered ClassLoader, like the movie Inception.
Related
I'm trying to add sbt-docker to my sbt build of my play website but I'm running into an issue. For some reason none of the docker related stuff on the bottom can resolve.
project/plugins.sbt
logLevel := Level.Warn
resolvers ++= Seq(
"Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"
)
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.5.9")
build.sbt
name := "personal_site"
version := "1.1"
lazy val `personal_site` = (project in file(".")).enablePlugins(PlayScala,DockerPlugin)
scalaVersion := "2.11.7"
libraryDependencies ++= Seq( jdbc , cache , ws , specs2 % Test )
unmanagedResourceDirectories in Test <+= baseDirectory ( _ /"target/web/public/test" )
resolvers += "scalaz-bintray" at "https://dl.bintray.com/scalaz/releases"
dockerfile in docker := {
val targetDir = "/usr/src"
new Dockerfile {
from("flurdy/activator")
//More goes here
}
}
imageNames in docker := Seq(
// Sets the latest tag
ImageName(s"${name.value}:latest"),
// Sets a name with a tag that contains the project version
ImageName(
namespace = None,
repository = name.value,
tag = Some("v" + version.value)
)
)
Here's an image of what it looks like in IntelliJ
I've also tried adding addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "1.4.0") to my project/plugins.sbt but I get this error about DockerPlugin being imported twice.
~/Sync/Projects/Programming/Personal_Site (master ✘)✹ ᐅ sbt clean
[info] Loading project definition from /home/ryan/Sync/Projects/Programming/Personal_Site/project
/home/ryan/Sync/Projects/Programming/Personal_Site/build.sbt:5: error: reference to DockerPlugin is ambiguous;
it is imported twice in the same scope by
import _root_.sbtdocker.DockerPlugin
and import _root_.com.typesafe.sbt.packager.docker.DockerPlugin
lazy val `personal_site` = (project in file(".")).enablePlugins(PlayScala,DockerPlugin)
Try changing your build.sbt config to this.
lazy val root = (project in file(".")).enablePlugins(sbtdocker.DockerPlugin, PlayScala)
It removes the ambiguity by using the full name to DockerPlugin, since sbt-native-packager uses the same name for its Docker plugin I believe.
Maybe worth raising a Github issue with the author's repo so they can document it in the project docs.
I have a multi module project in IntelliJ, as in this screen capture shows, contexProcessor module depends on contextSummary module.
IntelliJ takes care of everything once I setup the dependencies in Project Structure.
However, when I run sbt test with the following setup in build.sbt, I got an error complaining that it can't find the packages in contextSummary module.
name := "contextProcessor"
version := "1.0"
scalaVersion := "2.11.7"
libraryDependencies += "org.scalatest" % "scalatest_2.11" % "2.2.2" % "test"
How to teach sbt that the missing modules are found?
I could use the build.sbt file in the main root directory.
lazy val root = (project in file(".")).aggregate(contextSummary, contextProcessor)
lazy val contextSummary = project
lazy val contextProcessor = project.dependsOn(contextSummary)
Reference: http://www.scala-sbt.org/0.13.5/docs/Getting-Started/Multi-Project.html
For testing only one project, I can use project command in sbt.
> sbt
[info] Set current project to root (in build file:/Users/smcho/Desktop/code/ContextSharingSimulation/)
> project contextProcessor
[info] Set current project to contextProcessor (in build file:/Users/smcho/Desktop/code/ContextSharingSimulation/)
> test
For batch mode as in How to pass command line args to program in SBT 0.13.1?
sbt "project contextProcessor" test
I think a simple build.sbt might not be enough for that.
You would need to create a more sophisticated project/Build.scala like that:
import sbt._
import sbt.Keys._
object Build extends Build {
lazy val root = Project(
id = "root",
base = file("."),
aggregate = Seq(module1, module2)
)
lazy val module1 = Project(
id = "module1",
base = file("module1-folder"),
settings = Seq(
name := "Module 1",
version := "1.0",
scalaVersion := "2.11.7",
libraryDependencies += "org.scalatest" % "scalatest_2.11" % "2.2.2" % "test"
lazy val module2 = Project(
id = "module2",
base = file("module2-folder"),
dependencies = Seq(module1),
settings = Seq(
name := "Module 2",
version := "1.0",
scalaVersion := "2.11.7",
libraryDependencies += "org.scalatest" % "scalatest_2.11" % "2.2.2" % "test"
}
This is my project/Build.scala:
package sutils
import sbt._
import Keys._
object SutilsBuild extends Build {
scalaVersion in ThisBuild := "2.10.0"
val scalazVersion = "7.0.6"
lazy val sutils = Project(
id = "sutils",
base = file(".")
).settings(
test := { },
publish := { }, // skip publishing for this root project.
publishLocal := { }
).aggregate(
core
)
lazy val core = Project(
id = "sutils-core",
base = file("sutils-core")
).settings(
libraryDependencies += "org.scalaz" % "scalaz-core_2.10" % scalazVersion
)
}
This seems to be compiling my project just fine, but when I go into the console, I can't import any of the code that just got compiled?!
$ sbt console
scala> import com.github.dcapwell.sutils.validate.Validation._
<console>:7: error: object github is not a member of package com
import com.github.dcapwell.sutils.validate.Validation._
What am I doing wrong here? Trying to look at the usage, I don't see a way to say which subproject to load while in the console
$ sbt about
[info] Loading project definition from /src/sutils/project
[info] Set current project to sutils (in build file:/src/sutils/)
[info] This is sbt 0.13.1
[info] The current project is {file:/src/sutils/}sutils 0.1-SNAPSHOT
[info] The current project is built against Scala 2.10.3
[info] Available Plugins: org.sbtidea.SbtIdeaPlugin
[info] sbt, sbt plugins, and build definitions are using Scala 2.10.3
There's the solution from #Alexey-Romanov to start the console task in the project the classes to import are in.
sbt sutils/console
There's however another solution that makes the root sutils project depend on the other core. Use the following snippet to set up the project - note dependsOn core that will bring the classes from the core project to sutils's namespace.
lazy val sutils = Project(
id = "sutils",
base = file(".")
).settings(
test := { },
publish := { }, // skip publishing for this root project.
publishLocal := { }
).aggregate(
core
).dependsOn core
BTW, you should really use a simpler build.sbt for your use case as follows:
scalaVersion in ThisBuild := "2.10.0"
val scalazVersion = "7.0.6"
lazy val sutils = project.in(file(".")).settings(
test := {},
publish := {}, // skip publishing for this root project.
publishLocal := {}
).aggregate(core).dependsOn(core)
lazy val core = Project(
id = "sutils-core",
base = file("sutils-core")
).settings(
libraryDependencies += "org.scalaz" %% "scalaz-core" % scalazVersion
)
You could make it even easier when you'd split the build to two build.sbts, each for the projects.
My goal is to integrate jacoco into my play 2.2.0 project.
Different guides on the web I tried to follow mostly added to confusion not to closing in on the goal.
Adding to confusion
Most guides assume the existance of an build.sbt
which as it seems as been superseded by an build.scala with a different
There is a jacoco4sbt and a regular jacoco
which one is most appropiate for use with scala play framework 2
Current state
in plugins.sbt added
addSbtPlugin("de.johoop" % "jacoco4sbt" % "2.1.2")
in build.scala added
import de.johoop.jacoco4sbt.JacocoPlugin._
lazy val jacoco_settings = Defaults.defaultSettings ++ Seq(jacoco.settings: _*)
With these changes i don't get an "jacoco" task in sbt nor in the play console.
What are the appropriate steps to get this working?
Update
As requested the content of the build.scala
import com.typesafe.sbt.SbtNativePackager._
import com.typesafe.sbt.SbtScalariform._
import play.Project._
import sbt.Keys._
import sbt._
import sbtbuildinfo.Plugin._
import de.johoop.jacoco4sbt.JacocoPlugin._
object BuildSettings {
val buildOrganization = "XXXXX"
val buildVersion = "0.1"
val buildScalaVersion = "2.10.2"
val envConfig = "-Dsbt.log.format=false -Dconfig.file=" + Option(System.getProperty("env.config")).getOrElse("local.application")
scalacOptions ++= Seq("-encoding", "UTF-8", "-deprecation", "-unchecked", "-feature")
javaOptions += envConfig
val buildSettings = Defaults.defaultSettings ++ Seq (
organization := buildOrganization,
version := buildVersion,
scalaVersion := buildScalaVersion
)
}
object Resolvers {
val remoteRepoUrl = "XXXXXXXXXXXX" at "http://nexus.cXXXXX/content/repositories/snapshots/"
val publishRepoUrl = "XXXXXXXXXXXX" at "http://nexus.ciXXXXXXXXXXXXXXXX/content/repositories/snapshots/"
}
object Dependencies {
val ods = "XXXXXXXXX" % "XXXXXX-ws" % "2.2.1-SNAPSHOT"
val scalatest = "org.scalatest" %% "scalatest" % "2.0.M8" % "test"
val mockito = "org.mockito" % "mockito-all" % "1.9.5" % "test"
}
object ApplicationBuild extends Build {
import BuildSettings._
import Dependencies._
import Resolvers._
// Sub-project specific dependencies
val commonDeps = Seq(
ods,
scalatest,
mockito
)
//val bN = settingKey[Int]("current build Number")
val gitHeadCommitSha = settingKey[String]("current git commit SHA")
val release = settingKey[Boolean]("Release")
lazy val jacoco_settings = Defaults.defaultSettings ++ Seq(jacoco.settings: _*)
lazy val nemo = play.Project(
"nemo",
path = file("."),
settings = Defaults.defaultSettings ++ buildSettings ++
Seq(libraryDependencies ++= commonDeps) ++
Seq(scalariformSettings: _*) ++
Seq(playScalaSettings: _*) ++
buildInfoSettings ++
jacoco_settings ++
Seq(
sourceGenerators in Compile <+= buildInfo,
buildInfoKeys ++= Seq[BuildInfoKey](
resolvers,
libraryDependencies in Test,
buildInfoBuildNumber,
BuildInfoKey.map(name) { case (k, v) => "project" + k.capitalize -> v.capitalize },
"envConfig" -> envConfig, // computed at project load time
BuildInfoKey.action("buildTime") {
System.currentTimeMillis
} // re-computed each time at compile
),
buildInfoPackage := "com.springer.nemo"
) ++
Seq(resolvers += remoteRepoUrl) ++
Seq(mappings in Universal ++= Seq(
file("ops/rpm/start-server.sh") -> "start-server.sh",
file("ops/rpm/stop-server.sh") -> "stop-server.sh"
))
).settings(version <<= version in ThisBuild)
lazy val nemoPackaging = Project(
"nemoPackaging",
file("nemoPackaging"),
settings = Defaults.defaultSettings ++Seq(Packaging.settings:_*)
)
def publishSettings =
Seq(
publishTo := Option(publishRepoUrl),
credentials += Credentials(
"Repo", "http://mycompany.com/repo", "admin", "admin123"))
}
Note: jacoco is running with this but does not pick up our tests. Output:
jacoco:cover
[info] Compiling 1 Scala source to /home/schl14/work/nemo/target/scala-2.10/classes...
[info] ScalaTest
[info] Run completed in 13 milliseconds.
[info] Total number of tests run: 0
[info] Suites: completed 0, aborted 0
[info] Tests: succeeded 0, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[info] Passed: Total 0, Failed 0, Errors 0, Passed 0
[info] No tests to run for nemo/jacoco:test
I solved it by doing this.
Add the following to plugins.sbt
addSbtPlugin("de.johoop" % "jacoco4sbt" % "2.1.2")
In build.scala i added a new import
import de.johoop.jacoco4sbt.JacocoPlugin._
and added jacoco to the config section like this
lazy val xyz = play.Project(
"xyz",
path = file("."),
settings = Defaults.defaultSettings
jacoco.settings ++ //this is the important part.
).settings(parallelExecution in jacoco.Config := false) //not mandatory but needed in `most cases as most test can not be run in parallel`
After these steps jacoco:cover was available in the sbt and play console and also discovers our tests.
A better option for Scala code coverage is Scoverage which gives statement line coverage.
https://github.com/scoverage/scalac-scoverage-plugin
Add to project/plugins.sbt:
addSbtPlugin("com.sksamuel.scoverage" % "sbt-scoverage" % "1.0.1")
Then run SBT with
sbt clean coverage test
To measure the code coverage of Java code jacoco4sbt is the best fit.
Add to project/plugins.sbt:
addSbtPlugin("de.johoop" % "jacoco4sbt" % "2.1.2")
Add at the end of build.sbt:
jacoco.settings
Then run in the terminal:
//or just the sbt command and then use your browser
sbt jacoco:cover && /usr/bin/x-www-browser target/scala-2.10/jacoco/html/index.html
Scala code coverage can also be determined by SCCT.
Add to project/plugins.sbt:
addSbtPlugin("com.github.scct" % "sbt-scct" % "0.2.1")
Add at the end of build.sbt:
ScctPlugin.instrumentSettings
And then to see the coverage:
sbt scct:test && /usr/bin/x-www-browser target/scala_2.10/coverage-report/index.html
Maybe you get the error
Please either restart the browser with --allow-file-access-from-files
or use a different browser.
Maybe you use chrome and the security settings forbid dynamic actions on local files. You can open the page with firefox or use python -m SimpleHTTPServer 8000 to bind it to the http protokoll and open http://localhost:8000/target/scala-2.10/coverage-report/ .
Inspiration which generated classes should be excluded from the report can you find on the mailing list.
My test projects are on GitHub.
I have a project foo with two children foo-core and foo-cli, foo-cli depends on foo-core
(I come from Java/Maven and tried to transpose the parent module with 2 submodules architecture).
Following https://github.com/harrah/xsbt/wiki/Full-Configuration, I wrote my project/Build.scala this way:
import sbt._
import Keys._
object MyBuild extends Build {
//Dependencies
val slf4s = "com.weiglewilczek.slf4s" %% "slf4s" % "1.0.6"
val slf4j = "org.slf4j" %% "slf4j-simple" % "1.5.6"
val grizzled = "org.clapper" %% "grizzled-slf4j" % "0.5"
val junit = "junit" % "junit" % "4.8" % "test"
//End dependencies
lazy val root : Project = Project("root", file(".")) aggregate(cli) settings(
mainClass:= Some("Main")
)
lazy val core : Project = Project("core", file("core"), delegates = root :: Nil) settings(
name := "foo-core",
libraryDependencies ++= Seq(grizzled)
)
lazy val cli: Project = Project("cli", file("cli")) dependsOn(core) settings(
name := "foo-cli",
libraryDependencies ++= Seq(grizzled)
)
}
This configuration does not work: grizzled library is not dowloaded when I run sbt reload;sbt +update (as indicated in http://software.clapper.org/grizzled-slf4j/) and thus the "import grizzli._" fail in my core and cli projects when I sbt compile.
Since I'm new to scala/sbt I imagine I'm doing something awful but can't figure why since I'm confused with all sbt 0.7/sbt0.10 conflicting configurations that were suggested
(like Subproject dependencies in SBT).
Any idea? Hint that could help me?
Thanks in advance
That's grizzled, not grizzli you are using as dependency. The import is:
import grizzled._
This works here from console on project cli and project core, with nothing more than the configuration file above.
Are you using SBT 0.10?