Skip to content

Commit

Permalink
Bundle fixes and Morphir Mill Build extensions (#660)
Browse files Browse the repository at this point in the history
* Refine how we can lookup and index on many Distributions

* Add additional methods around making Distributions and getting Libs

* Add ErrorReason type

* Upgrade to mill 0.11.10

* Setting up MorphirModule mill task to help use mill to support dependencies

* Fix typo

* Improve mill support for MorphirElmModules

* produce artifacts with dist

* Refining how we define MorphirModules in mill

* Change package name for morphir plugin from starting with millbuild

* Formatted build files

* Adding additional projects for build

* Adding additional projects for morphir-elm style build

* Trigger make in other modules as a result of making a dependent module

* Get dependencies working for morphir example projects

* Working to make runtime tests auto build morphir models

* Ensure MorphirElmModules are built before test is run

* Install morphir-elm

* Use setup-node
  • Loading branch information
DamianReeves authored Aug 9, 2024
1 parent 9af9508 commit 44b8b95
Show file tree
Hide file tree
Showing 24 changed files with 859 additions and 26 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'

- name: Install morphir-elm
run: |
npm install -g morphir-elm
- name: Setup Scala and Java
uses: actions/setup-java@v4
with:
Expand Down Expand Up @@ -98,6 +108,16 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'

- name: Install morphir-elm
run: |
npm install -g morphir-elm
- name: Setup Scala and Java
uses: actions/setup-java@v4
with:
Expand Down Expand Up @@ -144,6 +164,16 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'

- name: Install morphir-elm
run: |
npm install -g morphir-elm
- name: Install libuv
run: sudo apt-get update && sudo apt-get install -y libuv1-dev

Expand Down Expand Up @@ -257,6 +287,15 @@ jobs:
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'

- name: Install morphir-elm
run: |
npm install -g morphir-elm
- uses: actions/setup-java@v4
with:
java-version: "11"
Expand Down
2 changes: 1 addition & 1 deletion .mill-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.11.11
0.11.11
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
20.10.0
lts/*
79 changes: 72 additions & 7 deletions build.sc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import mill.testrunner.TestResult
import mill.scalalib.publish.PublishInfo
import $meta._
import $ivy.`de.tototec::de.tobiasroeser.mill.integrationtest::0.7.1`
Expand All @@ -11,6 +12,9 @@ import de.tobiasroeser.mill.integrationtest._
import io.kipp.mill.ci.release.CiReleaseModule
import millbuild._
import millbuild.crossplatform._
import millbuild.jsruntime._
import org.finos.millmorphir._
import org.finos.millmorphir.elm._
import millbuild.settings._
import mill._, mill.scalalib._, mill.scalajslib._, mill.scalanativelib._, scalafmt._
import mill.scalajslib.api.ModuleKind
Expand Down Expand Up @@ -67,11 +71,12 @@ trait MorphirPublishModule extends CiReleaseModule with JavaModule with Mima {
)
}

object morphir extends Cross[MorphirModule](buildSettings.scala.crossScalaVersions) {

object morphir extends Cross[MorphirCrossModule](buildSettings.scala.crossScalaVersions) {
object build extends Module {
object integration extends Module {
object `mill-morphir-elm` extends Cross[MillMorphirElmPlugin](MillVersions.all)
trait MillMorphirElmPlugin
object `mill-morphir-elm` extends Cross[morphirlibElmPlugin](MillVersions.all)
trait morphirlibElmPlugin
extends Cross.Module[String]
with ScalaModule
with ScalafmtModule
Expand Down Expand Up @@ -139,7 +144,7 @@ object morphir extends Cross[MorphirModule](buildSettings.scala.crossScalaVersio
}

}
trait MorphirModule extends Cross.Module[String] with CrossPlatform { morphir =>
trait MorphirCrossModule extends Cross.Module[String] with CrossPlatform { morphir =>
import DevMode._
val workspaceDir = millbuild.build.millSourcePath

Expand Down Expand Up @@ -177,6 +182,7 @@ trait MorphirModule extends Cross.Module[String] with CrossPlatform { morphir =>

trait Shared extends MorphirCommonCrossModule with MorphirPublishModule {
def ivyDeps = super.ivyDeps() ++ Agg(
Deps.org.`scala-lang`.modules.`scala-collection-contrib`,
Deps.com.beachape.enumeratum,
Deps.com.lihaoyi.fansi,
Deps.com.lihaoyi.geny,
Expand Down Expand Up @@ -351,8 +357,25 @@ trait MorphirModule extends Cross.Module[String] with CrossPlatform { morphir =>
def platformSpecificModuleDeps = Seq(morphir, morphir.interop.zio.json)
}

trait RuntimeTests extends TestModule.ZioTest {
def morphirTestSources = T.sources {
examples.`morphir-elm-projects`.`evaluator-tests`.distOutputDirs()
}

def morphirTestSourceFiles = T {
Lib.findSourceFiles(morphirTestSources(), Seq("json")).collect {
case path if path.last.startsWith("morphir-") => PathRef(path)
}
}

override protected def testTask(args: Task[Seq[String]], globSelectors: Task[Seq[String]]): Task[(String, Seq[TestResult])] = T.task {
val _ = morphirTestSourceFiles()
super.testTask(args, globSelectors)()
}
}

object jvm extends Shared with MorphirJVMModule {
object test extends ScalaTests with TestModule.ZioTest {
object test extends ScalaTests with RuntimeTests {
def ivyDeps = Agg(
Deps.com.lihaoyi.`os-lib`,
Deps.com.lihaoyi.sourcecode,
Expand All @@ -364,15 +387,15 @@ trait MorphirModule extends Cross.Module[String] with CrossPlatform { morphir =>
}

object js extends Shared with MorphirJSModule {
object test extends ScalaJSTests with TestModule.ZioTest {
object test extends ScalaJSTests with RuntimeTests {
def ivyDeps = Agg(Deps.dev.zio.`zio-test`, Deps.dev.zio.`zio-test-sbt`)
def moduleDeps = super.moduleDeps ++ Agg(testing.zio.js)
def moduleKind = ModuleKind.CommonJSModule
}
}

object native extends Shared with MorphirNativeModule {
object test extends ScalaNativeTests with TestModule.ZioTest {
object test extends ScalaNativeTests with RuntimeTests {
def ivyDeps = Agg(Deps.dev.zio.`zio-test`, Deps.dev.zio.`zio-test-sbt`)
def moduleDeps = super.moduleDeps ++ Agg(testing.zio.native)
}
Expand Down Expand Up @@ -490,6 +513,48 @@ trait MorphirModule extends Cross.Module[String] with CrossPlatform { morphir =>
}
}

// The following modules are morphir-elm modules that have been setup to be built using mill
// Morphir Elm Projects/Modules:
object examples extends Module {
object `morphir-elm-projects` extends Module {
object finance extends MorphirElmModule

object `evaluator-tests` extends MorphirElmModule
object `unit-test-framework` extends Module {
object `example-project` extends MorphirElmModule {
def morphirModuleDeps = Seq(`morphir-elm`.sdks.`morphir-unit-test`)
}

object `example-project-tests` extends MorphirElmModule {
def morphirModuleDeps = Seq(
`morphir-elm`.sdks.`morphir-unit-test`,
`example-project`
)
}
object `example-project-tests-incomplete` extends MorphirElmModule {
def morphirModuleDeps = Seq(
`morphir-elm`.sdks.`morphir-unit-test`,
`example-project`
)
}
object `example-project-tests-passing` extends MorphirElmModule {
def morphirModuleDeps = Seq(
`morphir-elm`.sdks.`morphir-unit-test`,
`example-project`
)
}
}
}
}

object `morphir-elm` extends Module {
object sdks extends Module {
object `morphir-unit-test` extends MorphirElmModule
}
}

// The following section contains aliases used to simplify build tasks

object MyAliases extends Aliases {
def fmt = alias("mill.scalalib.scalafmt.ScalafmtModule/reformatAll __.sources")
def checkfmt = alias("mill.scalalib.scalafmt.ScalafmtModule/checkFormatAll __.sources")
Expand Down
33 changes: 33 additions & 0 deletions mill-build/src/millbuild/jsruntime/JsModule.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package millbuild.jsruntime

import mill._
import mill.define.Segment
import mill.scalalib._

trait JsModule extends Module {
def artifactName: Target[String] = T(millSourcePath.last)
def segments = T(millModuleSegments.value.collect { case Segment.Label(s) => s })
def jsRunnerExecutable = T(PathRef(os.Path(JsRuntime.NodeJs.executable)))
def jsPackageManagerRunner: Target[String] = T("npx")
def jsPackageManagerCmd: Target[String] = T("npm")

/**
* The folders containing all source files fed into the compiler
*/
def allSources: T[Seq[PathRef]] = T(sources() ++ generatedSources())

/**
* Folders containing source files that are generated rather than hand-written; these files can be generated in this
* target itself, or can refer to files generated from other targets
*/
def generatedSources: T[Seq[PathRef]] = T(Seq.empty[PathRef])
def sources = T.sources(millSourcePath / "src")

/**
* All individual source files fed into the compiler/tooling.
*/
def allSourceFiles: T[Seq[PathRef]] = T {
Lib.findSourceFiles(allSources(), Seq("js", "ts")).map(PathRef(_))
}

}
90 changes: 90 additions & 0 deletions mill-build/src/millbuild/jsruntime/JsRuntime.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package millbuild.jsruntime
import scala.util.Properties.isWin
import mill.api._
import mill.util.Jvm

import os.SubProcess

trait JsRuntime {
import JsRuntime._
def toolName: String

def findTool(name: String): String =
whereIs(name)

def executable: String =
findTool(toolName)

def runSubprocess(entryPoint: String, args: Seq[String], envArgs: Map[String, String], workingDir: os.Path)(implicit
ctx: Ctx
): Unit = {
val commandArgs = Vector(executable) ++ args
println(s"CommandArgs: $commandArgs")
val process: SubProcess = Jvm.spawnSubprocessWithBackgroundOutputs(
commandArgs,
envArgs,
workingDir,
backgroundOutputs = None
)
println(s"Createds process: ${process}")

val shutdownHook = new Thread("subprocess-shutdown") {
override def run(): Unit = {
System.err.println(s"Host executable for $toolName shutdown. Forcefully destroying subprocess ...")
process.destroy()
}
}
Runtime.getRuntime().addShutdownHook(shutdownHook)
try
process.waitFor()
catch {
case e: InterruptedException =>
System.err.println("Interrupted. Forcefully destroying subprocess ...")
process.destroy()
// rethrow
throw e
} finally
Runtime.getRuntime().removeShutdownHook(shutdownHook)
if (process.exitCode() == 0) ()
else throw new Exception("Interactive Subprocess Failed (exit code " + process.exitCode() + ")")

}
}

object JsRuntime {
case object NodeJs extends JsRuntime {
def toolName: String = "node"
}

def whereIs(name: String) = {
val commandArgs =
if (isWin) {
Vector("where", name)
} else {
Vector("whereis", name)
}

val result = os.proc(commandArgs).call()

val location =
if (isWin) {
result.out.lines().headOption.flatMap(_.trim().split(" ").headOption)
} else {

result.out.lines().headOption.flatMap { line =>
val parts = line.trim().split(" ")
parts match {
case Array(_, loc, _*) => Some(loc)
case _ => None
}

}

}

location match {
case None => throw new Exception(s"Failed to locate tool: $name")
case Some(loc) => loc
}
}
}
43 changes: 43 additions & 0 deletions mill-build/src/millbuild/util/helpers.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package millbuild.util
import scala.util.Properties.isWin

object ProcessHelper {
def whereIs(name: String) = {
val commandArgs =
if (isWin) {
Vector("where", name)
} else {
Vector("whereis", name)
}

val result = os.proc(commandArgs).call()

val location =
if (isWin) {
result.out.lines().headOption.flatMap(_.trim().split(" ").headOption)
} else {

result.out.lines().headOption.flatMap { line =>
val parts = line.trim().split(" ")
parts match {
case Array(_, loc, _*) => Some(loc)
case _ => None
}
}
}

location match {
case None => throw new Exception(s"Failed to locate tool: $name")
case Some(loc) => loc
}
}
}

object Collections {
implicit class SeqOps[+A](private val self: Seq[A]) extends AnyVal {
def appendIf[A1 >: A](cond: Boolean)(items: A1*) = if (cond) self ++ items else self
def appendWhen[A1 >: A](cond: => Boolean)(items: A1*) = if (cond) self ++ items else self

def when[A1 >: A](cond: Boolean)(items: A1*) = if (cond) items else Seq.empty[A1]
}
}
Loading

0 comments on commit 44b8b95

Please sign in to comment.