flyway-sbt icon indicating copy to clipboard operation
flyway-sbt copied to clipboard

Prevent compile task being triggered prior to a flyway task being run

Open davidmweber opened this issue 7 years ago • 10 comments

SBT insists on compiling sources when running a flyway task. This is problematic for people who use code generators such as Slick and can mean a simple operation like flywayClean being very slow due to the compilation that gets triggered. I am really not sure why SBT is triggering the compile or how to prevent it. Ideas welcome.

davidmweber avatar Jan 22 '18 09:01 davidmweber

I think fullClasspath triggers compile

https://github.com/flyway/flyway-sbt/blob/71ed19ab6af72f2205a9b43b638dd66575446b93/src/main/scala/org/flywaydb/sbt/FlywayPlugin.scala#L178-L187

xuwei-k avatar Feb 21 '18 08:02 xuwei-k

This is a tricky one. If I change it to something like externalDependencyClasspath, no compilation is triggered but this classpath does not include the stuff in src/main/resources/ that contains the actual migration data. Any ideas?

davidmweber avatar Feb 21 '18 09:02 davidmweber

The fullClassPath key points to the compiled JAR file which contains the resources containing the migration data which is the reason it needs to compile the project first. Either we just live with this or we find the right resource file for each build and explicitly add it in.

davidmweber avatar Feb 21 '18 10:02 davidmweber

I think the only solution is to enable flyway on a different project. If you think about it, the real problem is that there's nothing stopping you from having a "Java" migration (e.g. written in scala) that depends on code generated by slick-codegen. Classpath location means allowing for code migrations. (If there was a way to just get a resources "classpath," that might help if there was a way to switch off code migrations.) And code migrations can do anything code can do. So it's kind of an inherent problem. Again, the only solution I can think of is to put migrations in a different subproject (or configuration, but that's harder to do).

nafg avatar Mar 09 '18 09:03 nafg

I had a similar issue and creating a separate sub-project seems to be a good option - e.g.

lazy val flywayrunner = project
  .settings(
    commonSettings,
    libraryDependencies ++= Seq(
      "org.flywaydb" % "flyway-core" % "5.1.4",
      "org.postgresql" % "postgresql" % "9.4.1208"
    ),
    flywayUser := "my_db_user",
    flywayUrl := "jdbc:postgresql://localhost:5432/my_db",
  ).enablePlugins(FlywayPlugin)

Then you can set up a task to run whichever flyway tasks you need in the top level project - this is to run the flyway clean and migrate tasks on compile for example:

lazy val fullMigrate = taskKey[Unit]("Run all migrations")

fullMigrate := Def.sequential(
  flywayClean in flywayrunner,
  flywayMigrate in flywayrunner
).value

compile in Compile := (compile in Compile).dependsOn(fullMigrate).value

stevewillcock avatar Oct 12 '18 22:10 stevewillcock

@nafg has a plan that I have been dreaming about. Check where the actual migration is and select the classpath "mode" accordingly. But after I get some updates done.

davidmweber avatar Oct 15 '18 06:10 davidmweber

@stevewillcock has a great interim solution that will work for many use cases. Perhaps I can stick that in the docs?

davidmweber avatar Oct 15 '18 06:10 davidmweber

Wait what? All I said is that separate subprojects are demonstrably necessary to prevent a logical dependency cycle.

That's what @stevewillcock said too, adding that you can also write a custom task for convenience. Personally I don't bother, it's easy enough to have the root project aggregate it and access the task that way, or else just use the subprojects prefix, for example you can run db/flywayMigrate.

nafg avatar Oct 15 '18 19:10 nafg

The cycle is that code migrations can depend on application code, but application code can depend on generated code, which depends on the database, which depends on migrations, which include code migrations.

The only way out of it is that any code migrations have to be upstream of the generated code.

Even SQL migrations are an issue because at runtime they are not read from their source location -- the runtime classpath is elsewhere, and typically resources are copied there as part of the compile step.

The only other option is that during development, it could read SQL migrations from a file location based on the sbt resource directory setting.

nafg avatar Oct 15 '18 19:10 nafg

How about the solution in #33? I also added a test for it.

nerdydrew avatar Nov 30 '18 03:11 nerdydrew