scala - 你如何在 sbt 中编写任务?

标签 scala sbt

我正在尝试构建一个自定义任务,用于在我们的持续集成环境中构建我们的项目。这是一组沿着以下路线的步骤

  • 向聊天室发送构建开始消息
  • 编译模板
  • 运行 npm 测试
  • 运行 jshint
  • 编译
  • 构建应用神器
  • 将工件上传到我们的部署服务器
  • 运行测试
  • 将测试结果发送到我们的部署服务器
  • 向聊天室发送构建结果消息

  • 请注意,如果任何步骤失败,则应执行步骤 10,并且应根据失败的步骤自定义消息,例如如果第 5 步失败,它应该说“编译失败”,如果第 8 步失败,它应该说运行了多少测试以及失败了多少。

    为了让事情变得更加有趣,这是一个多项目构建,因此在运行测试和发布结果时,它应该运行所有测试并发布聚合结果。

    更有趣的是,npm 测试、jshint 和 artifact 只在 webapp 中才真正有意义。子项目,Javascript 所在的位置和 Web 服务器所在的位置。

    我一直在寻找 sbt-release 的灵感,但我在如何获取一个任务产生的值并在下一个任务中使用它、如何聚合运行任务并获取产生的值(我看到一个Extracted 中的方法来运行聚合任务,但它不提供生成值),如何在子项目中运行任务并获取生成值,以及如何进行错误处理。

    到目前为止,我已经尝试了两种方法
    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)                                                                            
        }                                                                                           
      }
    

    上面的问题是executeTests始终运行,即使 npmTest失败。 println 都没有s 被执行。
    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) }                                                                  
           }                                                                                          
     }                
    

    这个不能编译,因为 (executeTasks in Test)...产生一个 Initialize[Task[Unit]]值和 Task[Unit]是必须的。

    有没有办法用sbt来完成这个?

    最佳答案

    我找到了一个解决方案,可以让您使用旧的 flatMapmap来编写任务。

    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)
    }
    

    然后你可以将你的任务定义为
        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
          })
        }
    

    每个任务都会按顺序发生,正如您所期望的那样。

    关于scala - 你如何在 sbt 中编写任务?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32928384/

    相关文章:

    scala - 无法专门化具有可专门化特征的 Scala 方法作为返回类型

    scala - 如何在其主构造函数中使用 var 子类化对象

    scala - Scala 中的 Spark 分组 map UDF

    postgresql - Finatra 和 Slick 3.1 在响应 header 中返回成功、失败和值

    java - Play 2.6.x Java 应用程序在开发模式下工作但在生产模式下失败

    build - : org. scala-stm:scala-stm 中冲突的跨版本后缀

    sbt-assembly:重命名具有合并冲突的类(阴影)

    scala - 如何开始一个 Scala akka Actor

    关于 Heroku 问题的 Scala-redis 客户端连接到 RedisCloud

    sbt - 如何显示用于运行任务的类路径?