-
-
Notifications
You must be signed in to change notification settings - Fork 347
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implements #3548 Modifying init to fetch examples from releases page instead of using g8 template This introduces new top-level module `initmodule` this module generates resource `exampleList.txt` containing json with array of pairs `(exampleId,exampleUrl)` `exampleId` and `exampleUrl` are formed based on already present `exampleZips` target, and `millVersion()`. initmodule introduces external module `MillInitModule` calls to` mill init ` are redirected to `MillInitModule.init` (basically the same logic as it was with `Giter8Module.init`) `MillInitModule.init` called without parameters prints message containing `exampleids` list based on generated exampleList.txt in the form of: ``` Run init with one of the following examples as an argument to download and extract example: depth/cross/1-simple depth/cross/2-cross-source-path ... ``` When called with a parameter it validates if passed parameter is in the list, and if so, it downloads example from github releases page and unpacks it into the directory from where the command was run. --------- Co-authored-by: Li Haoyi <[email protected]>
- Loading branch information
1 parent
05bef7e
commit 5e51a3b
Showing
13 changed files
with
407 additions
and
94 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package build.main.codesig | ||
import mill._, scalalib._ | ||
|
||
object `package` extends RootModule with build.MillPublishScalaModule { | ||
override def ivyDeps = Agg(build.Deps.asmTree, build.Deps.osLib, build.Deps.pprint) | ||
def moduleDeps = Seq(build.main.util) | ||
|
||
override lazy val test: CodeSigTests = new CodeSigTests {} | ||
trait CodeSigTests extends MillScalaTests { | ||
val caseKeys = build.interp.watchValue( | ||
os.walk(millSourcePath / "cases", maxDepth = 3) | ||
.map(_.subRelativeTo(millSourcePath / "cases").segments) | ||
.collect { case Seq(a, b, c) => s"$a-$b-$c" } | ||
) | ||
|
||
def testLogFolder = T { T.dest } | ||
|
||
def caseEnvs[V](f1: CaseModule => Task[V])(s: String, f2: V => String) = { | ||
T.traverse(caseKeys) { i => f1(cases(i)).map(v => s"MILL_TEST_${s}_$i" -> f2(v)) } | ||
} | ||
def forkEnv = T { | ||
Map("MILL_TEST_LOGS" -> testLogFolder().toString) ++ | ||
caseEnvs(_.compile)("CLASSES", _.classes.path.toString)() ++ | ||
caseEnvs(_.compileClasspath)("CLASSPATH", _.map(_.path).mkString(","))() ++ | ||
caseEnvs(_.sources)("SOURCES", _.head.path.toString)() | ||
} | ||
|
||
object cases extends Cross[CaseModule](caseKeys) | ||
trait CaseModule extends ScalaModule with Cross.Module[String] { | ||
def caseName = crossValue | ||
object external extends ScalaModule { | ||
def scalaVersion = build.Deps.scalaVersion | ||
} | ||
|
||
def moduleDeps = Seq(external) | ||
|
||
val Array(prefix, suffix, rest) = caseName.split("-", 3) | ||
def millSourcePath = super.millSourcePath / prefix / suffix / rest | ||
def scalaVersion = build.Deps.scalaVersion | ||
def ivyDeps = T { | ||
if (!caseName.contains("realistic") && !caseName.contains("sourcecode")) super.ivyDeps() | ||
else Agg( | ||
build.Deps.fastparse, | ||
build.Deps.scalatags, | ||
build.Deps.cask, | ||
build.Deps.castor, | ||
build.Deps.mainargs, | ||
build.Deps.requests, | ||
build.Deps.osLib, | ||
build.Deps.upickle | ||
) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package build.main.init | ||
|
||
import mill._ | ||
import scala.util.matching.Regex | ||
|
||
object `package` extends RootModule with build.MillPublishScalaModule { | ||
def moduleDeps = Seq(build.main) | ||
|
||
override def resources = T.sources { | ||
super.resources() ++ Seq(exampleList()) | ||
} | ||
|
||
def exampleList: T[PathRef] = T { | ||
|
||
val versionPattern: Regex = "\\d+\\.\\d+\\.\\d+".r | ||
val rcPattern: Regex = "-RC\\d+".r | ||
|
||
val millVer: String = build.millVersion() | ||
val lastTag = versionPattern.findFirstMatchIn(millVer) match { | ||
case Some(verMatch) => | ||
val maybeRC = rcPattern.findFirstIn(verMatch.after) | ||
verMatch.matched + maybeRC.getOrElse("") | ||
case None => "0.0.0" | ||
} | ||
|
||
val data: Seq[(os.SubPath, String)] = build.examplePathsWithArtifactName().map { case (path, str) => | ||
val downloadUrl = build.Settings.projectUrl + "/releases/download/" + lastTag + "/" + str + ".zip" | ||
val subPath = path.subRelativeTo(T.workspace / "example") | ||
(subPath, downloadUrl) | ||
} | ||
|
||
val libsSortOrder = List("scalalib", "javalib", "kotlinlib", "extending", "external", "thirdparty", "depth") | ||
val categoriesSortOrder = List("basic", "builds", "web") | ||
|
||
def sortCriterium(strOpt: Option[String], sortOrderList: List[String]): Int = | ||
strOpt.flatMap { str => | ||
val idx = sortOrderList.indexOf(str) | ||
Option.when(idx >= 0)(idx) | ||
}.getOrElse(Int.MaxValue) | ||
|
||
val sortedData = data.sortBy { case (p1, _) => | ||
val segmentsReversed = p1.segments.reverse.lift | ||
val libOpt = segmentsReversed(2) | ||
val categoryOpt = segmentsReversed(1) | ||
val nameOpt = segmentsReversed(0) | ||
|
||
val libSortCriterium = sortCriterium(libOpt, libsSortOrder) | ||
val categorySortCriterium = sortCriterium(categoryOpt, categoriesSortOrder) | ||
val nameSortCriterium = nameOpt.flatMap(_.takeWhile(_.isDigit).toIntOption).getOrElse(Int.MinValue) | ||
(libSortCriterium, libOpt, categorySortCriterium, categoryOpt, nameSortCriterium, nameOpt) | ||
} | ||
|
||
val stream = os.write.outputStream(T.dest / "exampleList.txt") | ||
val writer = new java.io.OutputStreamWriter(stream) | ||
try { | ||
val json = upickle.default.writeTo(sortedData.map { case (p, s) => (p.toString(), s) }, writer) | ||
PathRef(T.dest) | ||
} catch { | ||
case ex: Throwable => T.log.error(ex.toString); throw ex | ||
} finally { | ||
writer.close() | ||
stream.close() | ||
} | ||
|
||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package mill.init | ||
|
||
import mainargs.{Flag, arg} | ||
import mill.api.IO | ||
import mill.define.{Discover, ExternalModule} | ||
import mill.util.Util.download | ||
import mill.{Command, Module, T} | ||
|
||
import java.io.IOException | ||
import java.util.UUID | ||
import scala.util.{Failure, Success, Try, Using} | ||
|
||
object InitModule extends ExternalModule with InitModule { | ||
lazy val millDiscover: Discover = Discover[this.type] | ||
} | ||
|
||
trait InitModule extends Module { | ||
|
||
type ExampleUrl = String | ||
type ExampleId = String | ||
|
||
val msg: String = | ||
"""Run `mill init <example-id>` with one of these examples as an argument to download and extract example. | ||
|Run `mill init --show-all` to see full list of examples. | ||
|Run `mill init <Giter8 template>` to generate project from Giter8 template.""".stripMargin | ||
def moduleNotExistMsg(id: String): String = s"Example [$id] is not present in examples list" | ||
def directoryExistsMsg(extractionTargetPath: String): String = | ||
s"Can't download example, because extraction directory [$extractionTargetPath] already exist" | ||
|
||
/** | ||
* @return Seq of example names or Seq with path to parent dir where downloaded example was unpacked | ||
*/ | ||
def init( | ||
@mainargs.arg(positional = true, short = 'e') exampleId: Option[ExampleId], | ||
@arg(name = "show-all") showAll: Flag = Flag() | ||
): Command[Seq[String]] = | ||
T.command { | ||
usingExamples { examples => | ||
val result: Try[(Seq[String], String)] = exampleId match { | ||
case None => | ||
val exampleIds: Seq[ExampleId] = examples.map { case (exampleId, _) => exampleId } | ||
def fullMessage(exampleIds: Seq[ExampleId]) = | ||
msg + "\n\n" + exampleIds.mkString("\n") + "\n\n" + msg | ||
if (showAll.value) | ||
Success((exampleIds, fullMessage(exampleIds))) | ||
else { | ||
val toShow = List("basic", "builds", "web") | ||
val filteredIds = | ||
exampleIds.filter(_.split('/').lift.apply(1).exists(toShow.contains)) | ||
Success((filteredIds, fullMessage(filteredIds))) | ||
} | ||
case Some(value) => | ||
val result: Try[(Seq[String], String)] = for { | ||
url <- examples.toMap.get(value).toRight(new Exception( | ||
moduleNotExistMsg(value) | ||
)).toTry | ||
extractedDirName = { | ||
val zipName = url.split('/').last | ||
if (zipName.toLowerCase.endsWith(".zip")) zipName.dropRight(4) else zipName | ||
} | ||
downloadDir = T.workspace | ||
downloadPath = downloadDir / extractedDirName | ||
_ <- if (os.exists(downloadPath)) Failure(new IOException( | ||
directoryExistsMsg(downloadPath.toString) | ||
)) | ||
else Success(()) | ||
path <- | ||
Try({ | ||
val tmpName = UUID.randomUUID().toString + ".zip" | ||
val downloaded = download(url, os.rel / tmpName)(downloadDir) | ||
val unpacked = IO.unpackZip(downloaded.path, os.rel)(downloadDir) | ||
Try(os.remove(downloaded.path)) | ||
unpacked | ||
}).recoverWith(ex => | ||
Failure( | ||
new IOException(s"Couldn't download example: [$value];\n ${ex.getMessage}") | ||
) | ||
) | ||
} yield (Seq(path.path.toString()), s"Example downloaded to [$downloadPath]") | ||
|
||
result | ||
} | ||
result | ||
}.flatten match { | ||
case Success((ret, msg)) => | ||
T.log.outputStream.println(msg) | ||
ret | ||
case Failure(exception) => | ||
T.log.error(exception.getMessage) | ||
throw exception | ||
} | ||
} | ||
private def usingExamples[T](fun: Seq[(ExampleId, ExampleUrl)] => T): Try[T] = | ||
Using(getClass.getClassLoader.getResourceAsStream("exampleList.txt")) { exampleList => | ||
val reader = upickle.default.reader[Seq[(ExampleId, ExampleUrl)]] | ||
val exampleNames: Seq[(ExampleId, ExampleUrl)] = upickle.default.read(exampleList)(reader) | ||
fun(exampleNames) | ||
} | ||
} |
Oops, something went wrong.