How do you compose tasks in sbt?

I'm trying to build a custom task for building our project in our continuous integration environment. It is a set of steps along the lines of

  • Send build started message to a chat room
  • Compile templates
  • Run npm tests
  • Run jshint
  • Compile
  • Build application artifact
  • Upload artifact to our deployment server
  • Run tests
  • Send test results to our deployment server
  • Send build results message to chat room
  • Note that step 10 should be done if any steps fail, and the message should be customized depending on which step failed, eg if step 5 fails it should say "Compilation failed", if step 8 fails it should say how many tests were run and how many failed.

    To make things extra interesting this is a multi project build, so when running tests and publishing results it should run all the tests and publish aggregated results.

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

    I've been looking at sbt-release for inspiration, but I'm stymied on how to take the value produced by one task and use it in the next one, how to run tasks in aggregate and get the produced values (I see a method in Extracted to run aggregated tasks, but it doesn't give produced values), how to run tasks in a subproject and get the produced value, and how to do the error handling.

    So far I've 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 is always run, even if npmTest fails. And none of the println s are executed.

    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 one doesn't compile because (executeTasks in Test)... produces an Initialize[Task[Unit]] value and a Task[Unit] is required.

    Is there a way to accomplish this with sbt?


    I found a solution that allows you to use good old flatMap and map to compose 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 happen in sequence, just as you would expect.

    链接地址: http://www.djcxy.com/p/88130.html

    上一篇: 在Yii2中更新图像

    下一篇: 你如何在sbt中撰写任务?