PlayRunHook не обслуживает пользовательский интерфейс в многомодульном проекте SBT - PullRequest
0 голосов
/ 26 апреля 2020

Я пытаюсь создать многомодульный проект scala sbt с React. js front-end, где один модуль объединяет Play с React. js. Чтобы добиться этого, я следовал инструкциям, приведенным здесь , хотя из-за многомодульной структуры это кажется немного сложнее. Проект имеет следующую структуру:

enter image description here

Веб-приложение подпроекта имеет React. js интерфейс в каталоге "ui".

Проблема в том, что FrontendRunHook не запускает «npm run start» после успешного запуска игры. Этот метод запускается, но процесс не запускается, и у меня нет React. js пользовательский интерфейс, если я не запускаю "npm run start" вручную. Другой хук (beforeStarted), который запускает "npm install", работает нормально.

FrontendRunHook. scala

import play.sbt.PlayRunHook
import sbt._

import scala.sys.process.Process
/**
 * Frontend build play run hook.
 * https://www.playframework.com/documentation/2.8.x/SBTCookbook
 */
 object FrontendRunHook {
   def apply(base: File): PlayRunHook = {
  object UIBuildHook extends PlayRunHook {

  var process: Option[Process] = None

  /**
    * Change the commands in `FrontendCommands.scala` if you want to use Yarn.
    */
  var install: String = FrontendCommands.dependencyInstall
  var run: String = FrontendCommands.serve

  // Windows requires npm commands prefixed with cmd /c
  if (System.getProperty("os.name").toLowerCase().contains("win")){
    install = "cmd /c" + install
    run = "cmd /c" + run
  }

  /**
    * Executed before play run start.
    * Run npm install if node modules are not installed.
    */
  override def beforeStarted(): Unit = {
    if (!(base / "webapp" / "ui" / "node_modules").exists()) Process(install, base / "webapp" / "ui").!
  }

  /**
    * Executed after play run start.
    * Run npm start
    */
  override def afterStarted(): Unit = {
    process = Some(
       Process(run, base / "webapp" / "ui").run
    )
  }

  /**
    * Executed after play run stop.
    * Cleanup frontend execution processes.
    */
  override def afterStopped(): Unit = {
    process.foreach(p => p.destroy())
    process = None
  }

}

UIBuildHook
 }
}

ui-build.sbt

import java.io.File

import scala.sys.process.Process

/*
 * UI Build hook Scripts
 */

// Execution status success.
val Success = 0

// Execution status failure.
val Error = 1

// Run serve task when Play runs in dev mode, that is, when using 'sbt run'
// https://www.playframework.com/documentation/2.8.x/SBTCookbook

PlayKeys.playRunHooks += baseDirectory.map(FrontendRunHook.apply).value

// True if build running operating system is windows.
val isWindows = System.getProperty("os.name").toLowerCase().contains("win")

// Execute on commandline, depending on the operating system. Used to execute npm commands.
def runOnCommandline(script: String)(implicit dir: File): Int = {
    if(isWindows){ Process("cmd /c set CI=true&&" + script, dir) } else { Process("env CI=true " + script, dir) } }!

// Check of node_modules directory exist in given directory.
def isNodeModulesInstalled(implicit dir: File): Boolean = {
    (dir / "node_modules").exists()
}

// Execute `npm install` command to install all node module dependencies. Return Success if already installed.
def runNpmInstall(implicit dir: File): Int =
    if (isNodeModulesInstalled) Success else runOnCommandline(FrontendCommands.dependencyInstall)

// Execute task if node modules are installed, else return Error status.
def ifNodeModulesInstalled(task: => Int)(implicit dir: File): Int =
    if (runNpmInstall == Success) task
    else Error

// Execute frontend test task. Update to change the frontend test task.
def executeUiTests(implicit dir: File): Int = ifNodeModulesInstalled(runOnCommandline(FrontendCommands.test))

// Execute frontend prod build task. Update to change the frontend prod build task.
def executeProdBuild(implicit dir: File): Int = ifNodeModulesInstalled(runOnCommandline(FrontendCommands.build))


// Create frontend build tasks for prod, dev and test execution.

lazy val `ui-test` = taskKey[Unit]("Run UI tests when testing application.")

`ui-test` := {
    implicit val userInterfaceRoot = baseDirectory.value / "webapp/ui"
    if (executeUiTests != Success) throw new Exception("UI tests failed!")
}

lazy val `ui-prod-build` = taskKey[Unit]("Run UI build when packaging the application.")

`ui-prod-build` := {
    implicit val userInterfaceRoot = baseDirectory.value / "webapp/ui"
    if (executeProdBuild != Success) throw new Exception("Oops! UI Build crashed.")
}

// Execute frontend prod build task prior to play dist execution.
dist := (dist dependsOn `ui-prod-build`).value

// Execute frontend prod build task prior to play stage execution.
stage := (stage dependsOn `ui-prod-build`).value

// Execute frontend test task prior to play test execution.
test := ((test in Test) dependsOn `ui-test`).value

build.sbt

import Common._

name := "game_cards"

version := "0.1"

lazy val `root` = (project in file("."))
    .settings(commonSettings: _*)
  .enablePlugins(PlayScala)
  .dependsOn(common)
  .aggregate(common)
    .dependsOn(game)
    .aggregate(game)
    .dependsOn(statistics)
    .aggregate(statistics)
    .dependsOn(webapp)
    .aggregate(webapp)

lazy val `common` = (project in file("common"))
    .settings(commonSettings: _*)
    .settings(
        inThisBuild(List(
            organization    := "com.blackjack.statistics"
        )),
        libraryDependencies ++= Seq(
            "com.typesafe.play" %% "play-json" % "2.8.0",
            "org.apache.kafka" %% "kafka" % kafkaVersion,
        ),
        fork := true
    )
    .settings(PB.targets in Compile := Seq(scalapb.gen() -> (sourceManaged in Compile).value))
    .enablePlugins(PlayScala)


lazy val `game` = (project in file("game"))
    .settings(commonSettings: _*)
    .settings(
        inThisBuild(List(
            organization    := "com.blackjack.game"
        )),
        resolvers += Resolver.bintrayRepo("cakesolutions", "maven"),
        libraryDependencies ++= Seq(
            "com.typesafe.akka" %% "akka-http"                % akkaHttpVersion,
            "com.typesafe.akka" %% "akka-http-spray-json"     % akkaHttpVersion,
            "com.typesafe.akka" %% "akka-actor-typed"         % akkaVersion,
            "com.typesafe.akka" %% "akka-stream"              % akkaVersion,
            "ch.qos.logback"    % "logback-classic"           % "1.2.3",
            "org.apache.kafka" %% "kafka"                     % kafkaVersion,
            "com.typesafe.akka" %% "akka-http-testkit"        % akkaHttpVersion % Test,
            "com.typesafe.akka" %% "akka-actor-testkit-typed" % akkaVersion     % Test,
            "org.scalatest"     %% "scalatest"                % "3.0.8"         % Test,
            "com.thesamet.scalapb" %% "compilerplugin" % "0.9.0",
            "net.cakesolutions" %% "scala-kafka-client-akka" % "2.0.0"
        ),
        fork := true
    )

    .settings(
        resolvers += "Scalaz Bintray Repo" at "http://dl.bintray.com/scalaz/releases"
    )
    .settings(PB.targets in Compile := Seq(scalapb.gen() -> (sourceManaged in Compile).value))
    .enablePlugins(PlayScala)
  .dependsOn(common)

lazy val `statistics` = (project in file("statistics"))
    .settings(commonSettings: _*)
    .settings(
        inThisBuild(List(
            organization    := "com.blackjack.statistics"
        )),
        resolvers += Resolver.bintrayRepo("cakesolutions", "maven"),
        libraryDependencies ++= Seq(
            "com.typesafe.akka" %% "akka-http"                % akkaHttpVersion,
            "com.typesafe.akka" %% "akka-http-spray-json"     % akkaHttpVersion,
            "com.typesafe.akka" %% "akka-actor-typed"         % akkaVersion,
            "com.typesafe.akka" %% "akka-stream"              % akkaVersion,
            "ch.qos.logback"    % "logback-classic"           % "1.2.3",
            "org.apache.kafka" %% "kafka"                     % kafkaVersion,
            "com.typesafe.akka" %% "akka-http-testkit"        % akkaHttpVersion % Test,
            "com.typesafe.akka" %% "akka-actor-testkit-typed" % akkaVersion     % Test,
            "org.scalatest"     %% "scalatest"                % "3.0.8"         % Test,
            "com.thesamet.scalapb" %% "compilerplugin" % "0.9.0",
            "net.cakesolutions" %% "scala-kafka-client-akka" % "2.0.0"
        ),
        fork := true
    )
    .settings(PB.targets in Compile := Seq(scalapb.gen() -> (sourceManaged in Compile).value))
    .settings(
        resolvers += "Scalaz Bintray Repo" at "http://dl.bintray.com/scalaz/releases"
    )
    .enablePlugins(PlayScala)
    .dependsOn(common)


lazy val `webapp` = (project in file("webapp")).enablePlugins(PlayScala)
    .settings(commonSettings: _*)
  .settings(watchSources ++= (baseDirectory.value / "webapp" / "ui" ** "*").get)
    .settings(
        libraryDependencies ++= Seq(
            jdbc , ehcache , ws , specs2 % Test , guice,
            "org.webjars" %% "webjars-play" % "2.6.3",
            "org.webjars" % "angularjs" % "1.7.9",
            "org.webjars" % "bootstrap" % "4.4.1"
        ),
        fork := true
    )
    .settings(
        resolvers += "Scalaz Bintray Repo" at "http://dl.bintray.com/scalaz/releases"
    )
    .enablePlugins(PlayScala)
    .disablePlugins(PlayFilters)
    .settings(
        watchSources ++= (baseDirectory.value / "webapp" / "ui" ** "*").get
    )
...