<commit_before>import java.net.InetSocketAddress
import java.nio.channels.ServerSocketChannel

import com.typesafe.sbt.SbtMultiJvm
import com.typesafe.sbt.SbtMultiJvm.MultiJvmKeys
import com.typesafe.sbt.SbtMultiJvm.MultiJvmKeys.MultiJvm
import com.typesafe.sbt.SbtMultiJvm.MultiJvmKeys._
import com.typesafe.tools.mima.core._
import lagom.Protobuf
import lagom.build._

import org.scalafmt.sbt.ScalafmtPlugin

// Turn off "Resolving" log messages that clutter build logs
ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet

def defineSbtVersion(scalaBinVer: String): String = scalaBinVer match {
  case "2.12" => "1.2.8"
  case _      => "0.13.18"
}

def evictionSettings: Seq[Setting[_]] = Seq(
  // This avoids a lot of dependency resolution warnings to be showed.
  // They are not required in Lagom since we have a more strict whitelist
  // of which dependencies are allowed. So it should be safe to not have
  // the build logs polluted with evictions warnings.
  evictionWarningOptions in update := EvictionWarningOptions.default
    .withWarnTransitiveEvictions(false)
    .withWarnDirectEvictions(false)
)

def overridesScalaParserCombinators = Seq(
  dependencyOverrides ++= Dependencies.scalaParserCombinatorOverrides
)

def common: Seq[Setting[_]] = releaseSettings ++ bintraySettings ++ evictionSettings ++ Seq(
  organization := "com.lightbend.lagom",
  // Must be "Apache-2.0", because bintray requires that it is a license that it knows about
  licenses := Seq(("Apache-2.0", url("http://www.apache.org/licenses/LICENSE-2.0.html"))),
  homepage := Some(url("https://www.lagomframework.com/")),
  sonatypeProfileName := "com.lightbend",
  headerLicense := Some(
    HeaderLicense.Custom(
      // When updating, keep in sync with docs/build.sbt configuration
      "Copyright (C) 2016-2019 Lightbend Inc. <https://www.lightbend.com>"
    )
  ),
  pomExtra := {
    <scm>
      <url>https://github.com/lagom/lagom</url>
      <connection>scm:git:git@github.com:lagom/lagom.git</connection>
    </scm>
    <developers>
      <developer>
        <id>lagom</id>
        <name>Lagom Contributors</name>
        <url>https://github.com/lagom</url>
      </developer>
    </developers>
  },
  pomIncludeRepository := { _ =>
    false
  },
  concurrentRestrictions in Global += Tags.limit(Tags.Test, 1),
  scalacOptions in (Compile, doc) ++= (scalaBinaryVersion.value match {
    case "2.12" => Seq("-no-java-comments")
    case _      => Seq.empty
  }),
  // Setting javac options in common allows IntelliJ IDEA to import them automatically
  javacOptions in compile ++= Seq(
    "-encoding",
    "UTF-8",
    "-source",
    "1.8",
    "-target",
    "1.8",
    "-parameters",
    "-Xlint:unchecked",
    "-Xlint:deprecation"
  ),
  LagomPublish.validatePublishSettingsSetting
)

def bintraySettings: Seq[Setting[_]] = Seq(
  bintrayOrganization := Some("lagom"),
  bintrayRepository := "sbt-plugin-releases",
  bintrayPackage := "lagom-sbt-plugin",
  bintrayReleaseOnPublish := false
)

// Customise sbt-dynver's behaviour to make it work with Lagom's tags (which aren't v-prefixed)
dynverVTagPrefix in ThisBuild := false

// Sanity-check: assert that version comes from a tag (e.g. not a too-shallow clone)
// https://github.com/dwijnand/sbt-dynver/#sanity-checking-the-version
Global / onLoad := (Global / onLoad).value.andThen { s =>
  val v = version.value
  if (dynverGitDescribeOutput.value.hasNoTags)
    throw new MessageOnlyException(
      s"Failed to derive version from git tags. Maybe run `git fetch --unshallow`? Version: $v"
    )
  s
}

def releaseSettings: Seq[Setting[_]] = Seq(
  releasePublishArtifactsAction := PgpKeys.publishSigned.value,
  releaseTagName := (version in ThisBuild).value,
  releaseProcess := {
    import ReleaseTransformations._

    Seq[ReleaseStep](
      checkSnapshotDependencies,
      releaseStepCommandAndRemaining("+publishSigned"),
      releaseStepTask(bintrayRelease in thisProjectRef.value),
      releaseStepCommand("sonatypeRelease"),
      pushChanges
    )
  }
)

/**
 * sbt release's releaseStepCommand does not execute remaining commands, which sbt-doge relies on
 */
def releaseStepCommandAndRemaining(command: String): State => State = { originalState =>
  import sbt.complete.Parser

  // Capture current remaining commands
  val originalRemaining = originalState.remainingCommands

  def runCommand(command: String, state: State): State = {
    val newState = Parser.parse(command, state.combinedParser) match {
      case Right(cmd) => cmd()
      case Left(msg)  => throw sys.error(s"Invalid programmatic input:\n$msg")
    }
    if (newState.remainingCommands.isEmpty) {
      newState
    } else {
      runCommand(
        newState.remainingCommands.head.commandLine,
        newState.copy(remainingCommands = newState.remainingCommands.tail)
      )
    }
  }

  runCommand(command, originalState.copy(remainingCommands = Nil)).copy(remainingCommands = originalRemaining)
}

def sonatypeSettings: Seq[Setting[_]] = Seq(
  publishTo := sonatypePublishTo.value
)

def runtimeScalaSettings: Seq[Setting[_]] = Seq(
  crossScalaVersions := Dependencies.Versions.Scala,
  scalaVersion := Dependencies.Versions.Scala.head,
  // compile options
  scalacOptions in Compile ++= Seq(
    "-encoding",
    "UTF-8",
    "-target:jvm-1.8",
    "-feature",
    "-unchecked",
    "-Xlog-reflective-calls",
    "-deprecation"
  )
)

def sbtScalaSettings: Seq[Setting[_]] = Seq(
  crossScalaVersions := Dependencies.Versions.Scala,
  scalaVersion := Dependencies.Versions.Scala.head,
)

def runtimeLibCommon: Seq[Setting[_]] = common ++ sonatypeSettings ++ runtimeScalaSettings ++ Seq(
  Dependencies.validateDependenciesSetting,
  Dependencies.pruneWhitelistSetting,
  Dependencies.dependencyWhitelistSetting,
  // show full stack traces and test case durations
  testOptions in Test += Tests.Argument("-oDF"),
  // -v Log "test run started" / "test started" / "test run finished" events on log level "info" instead of "debug".
  // -a Show stack traces and exception class name for AssertionErrors.
  testOptions += Tests.Argument(TestFrameworks.JUnit, "-v", "-a")
)

val defaultMultiJvmOptions: List[String] = {
  import scala.collection.JavaConverters._
  // multinode.D= and multinode.X= makes it possible to pass arbitrary
  // -D or -X arguments to the forked jvm, e.g.
  // -Djava.net.preferIPv4Stack=true or -Dmultinode.Xmx512m
  val MultinodeJvmArgs = "multinode\\.(D|X)(.*)".r
  val knownPrefix      = Set("akka.", "lagom.")
  val properties = System.getProperties.stringPropertyNames.asScala.toList.collect {
    case MultinodeJvmArgs(a, b) =>
      val value = System.getProperty("multinode." + a + b)
      "-" + a + b + (if (value == "") "" else "=" + value)
    case key if knownPrefix.exists(pre => key.startsWith(pre)) => "-D" + key + "=" + System.getProperty(key)
  }

  "-Xmx256m" :: properties
}

def databasePortSetting: List[String] = {
  def gimmePort = {
    val serverSocket = ServerSocketChannel.open().socket()
    try {
      serverSocket.bind(new InetSocketAddress("127.0.0.1", 0))
      serverSocket.getLocalPort
    } finally serverSocket.close()
  }
  List(
    s"-Djavadsl.database.port=$gimmePort",
    s"-Dscaladsl.database.port=$gimmePort",
  )
}

def multiJvm(project: Project): Project = {
  project
    .enablePlugins(MultiJvmPlugin)
    .configs(MultiJvm)
    .settings(inConfig(MultiJvm)(ScalafmtPlugin.scalafmtConfigSettings))
    .settings {
      // change multi-jvm lib folder to reflect the scala version used during crossbuild
      // must be done using a dynamic setting because we must read crossTarget.value
      def crossbuildMultiJvm: Def.Initialize[File] = Def.settingDyn {
        val path = crossTarget.value.getName
        Def.setting {
          target.apply { targetFile =>
            new File(targetFile, path + "/multi-run-copied-libraries")
          }.value
        }
      }

      forkedTests ++
        // enabling HeaderPlugin in MultiJvm requires two sets of settings.
        // see https://github.com/sbt/sbt-header/issues/37
        headerSettings(MultiJvm) ++
        automateHeaderSettings(MultiJvm) ++
        Seq(
          parallelExecution in Test := false,
          parallelExecution in MultiJvm := false,
          MultiJvmKeys.jvmOptions in MultiJvm := databasePortSetting ::: defaultMultiJvmOptions,
          // tag MultiJvm tests so that we can use concurrentRestrictions to disable parallel tests
          executeTests in MultiJvm := (executeTests in MultiJvm).tag(Tags.Test).value,
          // change multi-jvm lib folder to reflect the scala version used during crossbuild
          multiRunCopiedClassLocation in MultiJvm := crossbuildMultiJvm.value
        )<commit_msg>Generate test reports for multi-jvm tests<commit_after>import java.net.InetSocketAddress
import java.nio.channels.ServerSocketChannel

import com.typesafe.sbt.SbtMultiJvm
import com.typesafe.sbt.SbtMultiJvm.MultiJvmKeys
import com.typesafe.sbt.SbtMultiJvm.MultiJvmKeys.MultiJvm
import com.typesafe.sbt.SbtMultiJvm.MultiJvmKeys._
import com.typesafe.tools.mima.core._
import lagom.Protobuf
import lagom.build._

import org.scalafmt.sbt.ScalafmtPlugin

// Turn off "Resolving" log messages that clutter build logs
ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet

def defineSbtVersion(scalaBinVer: String): String = scalaBinVer match {
  case "2.12" => "1.2.8"
  case _      => "0.13.18"
}

def evictionSettings: Seq[Setting[_]] = Seq(
  // This avoids a lot of dependency resolution warnings to be showed.
  // They are not required in Lagom since we have a more strict whitelist
  // of which dependencies are allowed. So it should be safe to not have
  // the build logs polluted with evictions warnings.
  evictionWarningOptions in update := EvictionWarningOptions.default
    .withWarnTransitiveEvictions(false)
    .withWarnDirectEvictions(false)
)

def overridesScalaParserCombinators = Seq(
  dependencyOverrides ++= Dependencies.scalaParserCombinatorOverrides
)

def common: Seq[Setting[_]] = releaseSettings ++ bintraySettings ++ evictionSettings ++ Seq(
  organization := "com.lightbend.lagom",
  // Must be "Apache-2.0", because bintray requires that it is a license that it knows about
  licenses := Seq(("Apache-2.0", url("http://www.apache.org/licenses/LICENSE-2.0.html"))),
  homepage := Some(url("https://www.lagomframework.com/")),
  sonatypeProfileName := "com.lightbend",
  headerLicense := Some(
    HeaderLicense.Custom(
      // When updating, keep in sync with docs/build.sbt configuration
      "Copyright (C) 2016-2019 Lightbend Inc. <https://www.lightbend.com>"
    )
  ),
  pomExtra := {
    <scm>
      <url>https://github.com/lagom/lagom</url>
      <connection>scm:git:git@github.com:lagom/lagom.git</connection>
    </scm>
    <developers>
      <developer>
        <id>lagom</id>
        <name>Lagom Contributors</name>
        <url>https://github.com/lagom</url>
      </developer>
    </developers>
  },
  pomIncludeRepository := { _ =>
    false
  },
  concurrentRestrictions in Global += Tags.limit(Tags.Test, 1),
  scalacOptions in (Compile, doc) ++= (scalaBinaryVersion.value match {
    case "2.12" => Seq("-no-java-comments")
    case _      => Seq.empty
  }),
  // Setting javac options in common allows IntelliJ IDEA to import them automatically
  javacOptions in compile ++= Seq(
    "-encoding",
    "UTF-8",
    "-source",
    "1.8",
    "-target",
    "1.8",
    "-parameters",
    "-Xlint:unchecked",
    "-Xlint:deprecation"
  ),
  LagomPublish.validatePublishSettingsSetting
)

def bintraySettings: Seq[Setting[_]] = Seq(
  bintrayOrganization := Some("lagom"),
  bintrayRepository := "sbt-plugin-releases",
  bintrayPackage := "lagom-sbt-plugin",
  bintrayReleaseOnPublish := false
)

// Customise sbt-dynver's behaviour to make it work with Lagom's tags (which aren't v-prefixed)
dynverVTagPrefix in ThisBuild := false

// Sanity-check: assert that version comes from a tag (e.g. not a too-shallow clone)
// https://github.com/dwijnand/sbt-dynver/#sanity-checking-the-version
Global / onLoad := (Global / onLoad).value.andThen { s =>
  val v = version.value
  if (dynverGitDescribeOutput.value.hasNoTags)
    throw new MessageOnlyException(
      s"Failed to derive version from git tags. Maybe run `git fetch --unshallow`? Version: $v"
    )
  s
}

def releaseSettings: Seq[Setting[_]] = Seq(
  releasePublishArtifactsAction := PgpKeys.publishSigned.value,
  releaseTagName := (version in ThisBuild).value,
  releaseProcess := {
    import ReleaseTransformations._

    Seq[ReleaseStep](
      checkSnapshotDependencies,
      releaseStepCommandAndRemaining("+publishSigned"),
      releaseStepTask(bintrayRelease in thisProjectRef.value),
      releaseStepCommand("sonatypeRelease"),
      pushChanges
    )
  }
)

/**
 * sbt release's releaseStepCommand does not execute remaining commands, which sbt-doge relies on
 */
def releaseStepCommandAndRemaining(command: String): State => State = { originalState =>
  import sbt.complete.Parser

  // Capture current remaining commands
  val originalRemaining = originalState.remainingCommands

  def runCommand(command: String, state: State): State = {
    val newState = Parser.parse(command, state.combinedParser) match {
      case Right(cmd) => cmd()
      case Left(msg)  => throw sys.error(s"Invalid programmatic input:\n$msg")
    }
    if (newState.remainingCommands.isEmpty) {
      newState
    } else {
      runCommand(
        newState.remainingCommands.head.commandLine,
        newState.copy(remainingCommands = newState.remainingCommands.tail)
      )
    }
  }

  runCommand(command, originalState.copy(remainingCommands = Nil)).copy(remainingCommands = originalRemaining)
}

def sonatypeSettings: Seq[Setting[_]] = Seq(
  publishTo := sonatypePublishTo.value
)

def runtimeScalaSettings: Seq[Setting[_]] = Seq(
  crossScalaVersions := Dependencies.Versions.Scala,
  scalaVersion := Dependencies.Versions.Scala.head,
  // compile options
  scalacOptions in Compile ++= Seq(
    "-encoding",
    "UTF-8",
    "-target:jvm-1.8",
    "-feature",
    "-unchecked",
    "-Xlog-reflective-calls",
    "-deprecation"
  )
)

def sbtScalaSettings: Seq[Setting[_]] = Seq(
  crossScalaVersions := Dependencies.Versions.Scala,
  scalaVersion := Dependencies.Versions.Scala.head,
)

def runtimeLibCommon: Seq[Setting[_]] = common ++ sonatypeSettings ++ runtimeScalaSettings ++ Seq(
  Dependencies.validateDependenciesSetting,
  Dependencies.pruneWhitelistSetting,
  Dependencies.dependencyWhitelistSetting,
  // show full stack traces and test case durations
  testOptions in Test += Tests.Argument("-oDF"),
  // -v Log "test run started" / "test started" / "test run finished" events on log level "info" instead of "debug".
  // -a Show stack traces and exception class name for AssertionErrors.
  testOptions += Tests.Argument(TestFrameworks.JUnit, "-v", "-a")
)

val defaultMultiJvmOptions: List[String] = {
  import scala.collection.JavaConverters._
  // multinode.D= and multinode.X= makes it possible to pass arbitrary
  // -D or -X arguments to the forked jvm, e.g.
  // -Djava.net.preferIPv4Stack=true or -Dmultinode.Xmx512m
  val MultinodeJvmArgs = "multinode\\.(D|X)(.*)".r
  val knownPrefix      = Set("akka.", "lagom.")
  val properties = System.getProperties.stringPropertyNames.asScala.toList.collect {
    case MultinodeJvmArgs(a, b) =>
      val value = System.getProperty("multinode." + a + b)
      "-" + a + b + (if (value == "") "" else "=" + value)
    case key if knownPrefix.exists(pre => key.startsWith(pre)) => "-D" + key + "=" + System.getProperty(key)
  }

  "-Xmx256m" :: properties
}

def databasePortSetting: List[String] = {
  def gimmePort = {
    val serverSocket = ServerSocketChannel.open().socket()
    try {
      serverSocket.bind(new InetSocketAddress("127.0.0.1", 0))
      serverSocket.getLocalPort
    } finally serverSocket.close()
  }
  List(
    s"-Djavadsl.database.port=$gimmePort",
    s"-Dscaladsl.database.port=$gimmePort",
  )
}

def multiJvm(project: Project): Project = {
  project
    .enablePlugins(MultiJvmPlugin)
    .configs(MultiJvm)
    .settings(inConfig(MultiJvm)(ScalafmtPlugin.scalafmtConfigSettings))
    .settings {
      // change multi-jvm lib folder to reflect the scala version used during crossbuild
      // must be done using a dynamic setting because we must read crossTarget.value
      def crossbuildMultiJvm: Def.Initialize[File] = Def.settingDyn {
        val path = crossTarget.value.getName
        Def.setting {
          target.apply { targetFile =>
            new File(targetFile, path + "/multi-run-copied-libraries")
          }.value
        }
      }

      forkedTests ++
        // enabling HeaderPlugin in MultiJvm requires two sets of settings.
        // see https://github.com/sbt/sbt-header/issues/37
        headerSettings(MultiJvm) ++
        automateHeaderSettings(MultiJvm) ++
        Seq(
          parallelExecution in Test := false,
          parallelExecution in MultiJvm := false,
          // -o D(report the duration of the tests) F(show full stack traces)
          // -u select the JUnit XML reporter
          scalatestOptions in MultiJvm := Seq("-oDF", "-u", (target.value / "test-reports").getAbsolutePath),
          MultiJvmKeys.jvmOptions in MultiJvm := databasePortSetting ::: defaultMultiJvmOptions,
          // tag MultiJvm tests so that we can use concurrentRestrictions to disable parallel tests
          executeTests in MultiJvm := (executeTests in MultiJvm).tag(Tags.Test).value,
          // change multi-jvm lib folder to reflect the scala version used during crossbuild
          multiRunCopiedClassLocation in MultiJvm := crossbuildMultiJvm.value
        )
{'fraction_non_alphanumeric': 0.11754742547425474, 'fraction_numerical': 0.005984643179765131, 'mean_word_length': 3.519775452921664, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 65, 'https://': 12, 'lorem ipsum': 0, 'www.': 6, 'xml': 0}, 'pii_count': 4, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3970111', 'n_tokens_mistral': 5634, 'n_tokens_neox': 5506, 'n_words': 1303}