How do you create tasks in sbt?

I am trying to create a custom task to create our project in our continuous integration environment. This is a set of line steps.

  • Send a chat start message
  • Compile templates
  • Running npm tests
  • Run jshint
  • Compile
  • Create an application artifact
  • Upload artifact to our deployment server
  • Running tests
  • Send test results to our deployment server
  • Send assembly results message to chat

Please note that step 10 should be performed if any steps are not performed, and the message should be configured depending on which step failed, for example. if step 5 failed, he should say: “Compilation error”, if step 8 failed, he should say how many tests were completed and how many failed.

To make something interesting, this is an assembly of several projects, so when running tests and publishing the results, it should run all the tests and publish the aggregated results.

To make things even more interesting, the npm, jshint and artifact tests really make sense in the webapp subproject, where Javascript lives, and the web server.

I looked at sbt-release for inspiration, but I was involved in how to take the value created by one task and use it in the next, how to run tasks together and get produced values ​​(I see a method in Extracted to run aggregated tasks, but it does not give produced values) how to run tasks in a subproject and get the produced value, and how to perform error handling.

So far I have tried two approaches

 npmTest.result.value match { case Inc(inc) => println(inc) case Value(res) => Def.taskDyn { (executeTests in Test).result.value match { case Inc(inc) => println(inc) case Value(res) => println(res) } } 

The problem with the above is that executeTests always runs, even if npmTest fails. And not one of println is running.

 npmTest.result. flatMap {- case Inc(inc) => task { println(inc) } case Value(res) =>- (executeTests in Test).result. flatMap { case Inc(inc) => task { println(inc) } case Value(res) => task { println(res) } } } 

This does not compile because (executeTasks in Test)... creates the value Initialize[Task[Unit]] , and << 29> is required.

Is there any way to accomplish this with sbt?

+7
scala sbt
source share
1 answer

I found a solution that allows you to use the good old flatMap and map to create tasks.

 sealed abstract class Step[A] { def run: Def.Initialize[Task[Result[A]]] def map[B](f: A => B): Step[B] def flatMap[B](f: A => Step[B]): Step[B] } object Step { val thisProjectRef = settingKey(Keys.thisProjectRef) val clean = taskKey(Keys.clean) val compile = taskKey(Keys.compile.in(Compile)) val assembly = taskKey(sbtassembly.AssemblyPlugin.autoImport.assembly) private[this] def apply[A](task: Def.Initialize[Task[Result[A]]]): Step[A] = new Step[A] { val run = task def map[B](f: A => B): Step[B] = apply[B](Def.taskDyn { run.value match { case Inc(inc) => Def.task(Inc(inc): Result[B]) case Value(a) => Def.task(Value(f(a))) } }) def flatMap[B](f: A => Step[B]): Step[B] = apply[B](Def.taskDyn { run.value match { case Inc(inc) => Def.task(Inc(inc): Result[B]) case Value(a) => Def.task(f(a).run.value) } }) } def task[A](t: Def.Initialize[Task[A]]): Step[A] = apply(t.result) def taskKey[A](t: TaskKey[A]): Step[A] = apply(Def.task(t.result.value)) def settingKey[A](s: SettingKey[A]): Step[A] = apply(Def.task(s.value).result) } 

Then you can define your tasks as

  rainicornPublish <<= { val result = for { ass <- Step.assembly juri <- uploadAssemblyTask(ass) to <- runAllTests _ <- finish(ass, juri, to) } yield (ass, to) Def.task(result.run.value match { case Inc(inc) => throw new RainicornException(None) case Value(v) => v }) } 

And each task will be performed sequentially, exactly as you expected.

+4
source share

All Articles