Is it possible to disable publish without disabling publishLocal in sbt? - sbt

I have an sbt project where docker:publishLocal will create a docker image on my machine for testing, and docker:publish will publish the image to a repository and also publish jar files from the build to a repository.
If my project is a snapshot, I would like to disable publishing to the repositories, while still being able to build the local image.
ThisBuild / publishArtifact := ! isSnapshot.value
does the right thing for the publish command, but it also disables publishLocal.
I want to write something like
if (isSnapshot.value) {
publish := { }
}
but that gives me an error that I do not understand at all:
[info] Loading project definition from /Users/dev/project
/Users/dev/build.sbt:1: error: type mismatch;
found : Unit
required: sbt.internal.DslEntry
if (isSnapshot.value) {
^
Past experience dictates that redefining publish to conditionally call the original version won't work, as
publish := {
if (!isSnapshot.value) publish.value
}
gives warnings that the task is always evaluated.
Is there a way to do this?

The problem with this code is that it evaluates publish.value regardless of the if structure. I recommend reading the documentation on task dependencies. If you want to "delay" the evaluation of a task in one of the if branches, you need to use dynamic task definition:
publish := Def.taskDyn {
if (isSnapshot.value)
Def.task {} // doing nothing
else
Def.task { publish.value } // could be written as just publish
}.value
But apart from fixing your code, you should be aware that there is a special setting for the functionality you want, it's called skip:
publish/skip := isSnapshot.value
Another thing to notice, is the scoping. If you want to override docker:publish, which is the same as Docker/publish in the new syntax, you should add this Docker/ scope prefix to every mention of publish in the code above.

Related

Execute sbt task without prior compiling (for generating database classes with JOOQ)

I have a PlayFramework 2.7 application which is build by sbt.
For accessing the database, I am using JOOQ. As you know, JOOQ reads my database schema and generates the Java source classes, which then are used in my application.
The application only compiles, if the database classes are present.
I am generating the classes with this custom sbt task:
// https://mvnrepository.com/artifact/org.jooq/jooq-meta
libraryDependencies += "org.jooq" % "jooq-meta" % "3.12.1"
lazy val generateJOOQ = taskKey[Seq[File]]("Generate JooQ classes")
generateJOOQ := {
(runner in Compile).value.run("org.jooq.codegen.GenerationTool",
(fullClasspath in Compile).value.files,
Array("conf/db.conf.xml"),
streams.value.log).failed foreach (sys error _.getMessage)
((sourceManaged.value / "main/generated") ** "*.java").get
}
I googled around and found the script above and modified it a little bit according to my needs, but I do not really understand it, since sbt/scala are new to me.
The problem now is, when I start the generateJOOQ, sbt tries to compile the project first, which fails, because the database classes are missing. So what I have to do is, comment all code out which uses the generated classes, execute the task which compiles my project, generates the database classes and then enable the commented out code again. This is a pain!
I guess the problem is the command (runner in Compile) but I did not find any possibility to execute my custom task WITHOUT compiling first.
Please help! Thank you!
Usually, when you want to generate sources, you should use a source generator, see https://www.scala-sbt.org/1.x/docs/Howto-Generating-Files.html
sourceGenerators in Compile += generateJOOQ
Doing that automatically causes SBT to execute your task first before trying to compile the Scala/Java sources.
Then, you should not really use the runner task, since that is used to run your project, which depends on the compile task, which needs to execute first of course.
You should add the jooq-meta library as a dependeny of the build, not of your sources. That means you should add the libraryDependencies += "org.jooq" % "jooq-meta" % "3.12.1" line e.g. to project/jooq.sbt.
Then, you can simply call the GenerationTool of jooq just as usually in your task:
// build.sbt
generateJOOQ := {
org.jooq.codegen.GenerationTool.main(Array("conf/db.conf.xml"))
((sourceManaged.value / "main/generated") ** "*.java").get
}

Override project's setting inside of SBT task

In my build.sbt a compilation phase depends on running scapegoat inspection
(compile in Compile) := (compile in Compile).dependsOn(scapegoat).value
I'm trying to introduce a new task for running tests (for development purposes to speed things up) that does not depend on scapegoat like this:
lazy val fastTests = taskKey[Unit]("")
fastTests := {
scapegoat in Compile := {}
(test in Test).value
}
but gets ignored
You cannot do it with a task because tasks cannot change settings. You can solve it either with different configurations or with a command (which can change settings). See for example:
How to disable “Slow” tagged Scalatests by default, allow execution with option?
(for the configurations approach)
How to change setting inside SBT command?

How to publish an artifact with pom-packaging in SBT?

I have a multi-project build in SBT where some projects should aggregate dependencies and contain no code. So then clients could depend on these projects as a single dependency instead of directly depending on all of their aggregated dependencies. With Maven, this is a common pattern, e.g. when using Spring Boot.
In SBT, I figured I can suppress the generation of the empty artifacts by adding this setting to these projects:
packagedArtifacts := Classpaths.packaged(Seq(makePom)).value
However, the makePom task writes <packaging>jar</packaging> in the generated POM. But now that there is no JAR anymore, this should read <packaging>pom</packaging> instead.
How can I do this?
This question is a bit old, but I just came across the same issue and found a solution. The original answer does point to the right page where this info can be found, but here is an example. It uses the pomPostProcess setting to transform the generated POM right before it is written to disk. Essentially, we loop over all the XML nodes, looking for the element we care about and then rewrite it.
import scala.xml.{Node => XmlNode, NodeSeq => XmlNodeSeq, _}
import scala.xml.transform._
pomPostProcess := { node: XmlNode =>
val rule = new RewriteRule {
override def transform(n: XmlNode): XmlNodeSeq = n match {
case e: Elem if e != null && e.label == "packaging" =>
<packaging>pom</packaging>
case _ => n
}
}
new RuleTransformer(rule).transform(node).head
},
Maybe you could modify the result pom as described here: Modifying the generated POM
You can disable publishing the default artifacts of JAR, sources, and docs, then opt in explicitly to publishing the POM. sbt produces and publishes a POM only, with <packaging>pom</packaging>.
// This project has no sources, I want <packaging>pom</pom> with dependencies
lazy val bundle = project
.dependsOn(moduleA, moduleB)
.settings(
publishArtifact := false, // Disable jar, sources, docs
publishArtifact in makePom := true,
)
lazy val moduleA = project
lazy val moduleB = project
lazy val moduleC = project
Run sbt bundle/publishM2 to verify the POM in ~/.m2/repository.
I dare say this is almost intuitive, a rare moment of pleasant surprise with sbt 😅
I confirmed this with current sbt 1.3.9, and 1.0.1, the oldest launcher I happen to have installed on my machine.
The Artifacts page in the reference docs may be helpful, perhaps this trick should be added there.

SBT: Add dependencies to project at runtime

There is sbt project declaration
lazy val myProject = (Project("myProject", file("someRoot"))
enablePlugins ...
settings (...)
It has taskKey that extracts some dependencies to file system.
My problem is that for the moment of loading SBT I can't determine all the dependencies, it could be done only after private Command Alias is executed
addCommandAlias("resolveDependencies", "; resolveDependenciesTask; TODO: update myProject dependencies and reload it")
Is there anyway to do that?
Actually, disregard my comment on your question. You can use a command to modify the state of the build, so after you run it, the changes it made stay.
Something along these lines:
// in your build.sbt
commands += Command.command("yourCustomCommand")(state =>
Project.extract(state).append(
Seq(libraryDependencies += // settings you want to modify
"com.lihaoyi" % "ammonite-repl" % "0.5.7" cross CrossVersion.full),
state))
Then call it with sbt yourCustomCommand.
The state instance you return from the command becomes the new state of the build, i.e. if you've added some dependencies, the build will see them.

How to stop xsbt from reloading webapp on resources change

We are using sbt with xsbt-web-plugin to develop our liftweb app. In our project build we have several subprojects and we use dependencies of a Project to share some stuff between all the subprojects.
object ProjectBuild extends Build {
//...
lazy val standalone = Project(
id = "standalone",
base = file("standalone"),
settings = Seq(...),
dependencies = Seq(core) // please notice this
)
lazy val core = Project(
id = "core",
base = file("core"),
settings = Seq(...)
}
// ...
}
To ease the development we use 'project standalone' '~;container:start; container:reload /' command automatically recompile changed files.
We decided to serve some common assets from shared core project as well. This works fine with lift. But what we faced when added our files to core/src/main/resources/toserve folder, is that any change to any javascript or css file causes application to restart jetty. This is annoying since such reload takes lots of resources.
So I started investigating on how to prevent this, even found someone mentioning watchSources sbt task that scans for changed files.
But adding this code as a watchSources modification (event that println prints all the files) does not prevent from reloading webapp each time I change assets located in core resources folder.
lazy val core = Project(
id = "core",
base = file("core"),
settings = Seq(
// ...
// here I added some tuning of watchSources
watchSources ~= { (ws: Seq[File]) => ws filterNot { path =>
println(path.getAbsolutePath)
path.getAbsolutePath.endsWith(".js")
} }
)
I also tried adding excludeFilter to unmanagedSorces, unmanagedResorces but with no luck.
I'm not an sbt expert and such modification of settings looks more like a magic for me (rather then a usual code). Also such tuning seem to be uncovered by documentation =(
Can anyone please help me to prevent sbt from reloading webapp on each asset file change?
Thanks a lot!
You're on the right track by using watchSources, but you'll also need to exclude the resources directory itself:
watchSources ~= { (ws: Seq[File]) =>
ws filterNot { path =>
path.getName.endsWith(".js") || path.getName == ("resources")
}
}
Can you switch from using "resources" folder to using "webapp" folder? That will also free you from restarts. Here's a demo project for Lift (that uses "webapp"):
https://github.com/lift/lift_26_sbt/
For example, the "basic" template:
https://github.com/lift/lift_26_sbt/tree/master/scala_211/lift_basic/src/main/webapp

Resources