Protected commands fail at runtime with cryptic error
trait VersionFileModule extends Module {
/** The file containing the current version. */
def versionFile: define.Source = T.source(millSourcePath / "version")
/** The current version. */
def currentVersion: T[Version] = T { Version.of(os.read(versionFile().path)) }
/** The release version. */
def releaseVersion = T { currentVersion().asRelease }
/** The next snapshot version. */
def nextVersion(bump: String) = T.command { currentVersion().asSnapshot.bump(bump) }
/** Writes the release version to file. */
def setReleaseVersion = T {
setVersionTask(releaseVersion)()
}
/** Writes the release version to file. */
def setNextVersion(bump: String) = T.command {
setVersionTask(nextVersion(bump))()
}
/** Writes the given version to file. */
def setVersion(version: Version) = T.command {
setVersionTask(T.task { version })()
}
protected def setVersionTask(version: T[Version]) = T.command {
T.ctx.log.info(generateCommitMessage(version()))
writeVersionToFile(versionFile(), version())
}
def writeVersionToFile(versionFile: mill.eval.PathRef, version: Version) =
os.write.over(
versionFile.path,
version.toString
)
def generateCommitMessage(version: Version) =
version match {
case release: Version.Release => s"Setting release version to $version"
case snapshot: Version.Snapshot => s"Setting next version to $version"
}
}
If you try to run mill versionFile.setVersion --version 0.3.0, the above code will crash on runtime with a cryptic error:
Exception in thread "main" java.util.NoSuchElementException: None.get
at scala.None$.get(Option.scala:366)
at scala.None$.get(Option.scala:364)
at mill.eval.Evaluator$$anonfun$3.applyOrElse(Evaluator.scala:468)
at mill.eval.Evaluator$$anonfun$3.applyOrElse(Evaluator.scala:447)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:228)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:224)
at mill.define.Graph$.$anonfun$groupAroundImportantTargets$1(Graph.scala:20)
at mill.api.AggWrapper$Agg$Mutable.$anonfun$flatMap$1(AggWrapper.scala:70)
at mill.api.AggWrapper$Agg$Mutable.$anonfun$flatMap$1$adapted(AggWrapper.scala:70)
at scala.collection.Iterator.foreach(Iterator.scala:941)
at scala.collection.Iterator.foreach$(Iterator.scala:941)
at scala.collection.AbstractIterator.foreach(Iterator.scala:1429)
at mill.api.AggWrapper$Agg$Mutable.flatMap(AggWrapper.scala:70)
at mill.define.Graph$.groupAroundImportantTargets(Graph.scala:20)
at mill.eval.Evaluator$.plan(Evaluator.scala:447)
at mill.eval.Evaluator.evaluate(Evaluator.scala:54)
at mill.main.RunScript$.evaluate(RunScript.scala:208)
at mill.main.RunScript$.$anonfun$evaluateTasks$1(RunScript.scala:194)
at scala.util.Either.map(Either.scala:353)
at mill.main.RunScript$.evaluateTasks(RunScript.scala:193)
at mill.main.RunScript$.$anonfun$runScript$4(RunScript.scala:67)
at ammonite.util.Res$Success.flatMap(Res.scala:62)
at mill.main.RunScript$.runScript(RunScript.scala:66)
at mill.main.MainRunner.$anonfun$runScript$1(MainRunner.scala:94)
at mill.main.MainRunner.watchLoop2(MainRunner.scala:57)
at mill.main.MainRunner.runScript(MainRunner.scala:71)
at mill.MillMain$.main0(MillMain.scala:213)
at mill.MillMain$.main(MillMain.scala:30)
at mill.MillMain.main(MillMain.scala)
This line assumes that all commands are public: https://github.com/lihaoyi/mill/blob/master/main/core/src/eval/Evaluator.scala#L468
There should rather be a descriptive error message that explains that the command it tried to look up is not a public member of the module. Though, a runtime error isn't really ideal, it should rather fail at compile time if you try declare something that by design is never going to work at runtime.
Also, the documentation for tasks does not make it clear that a command has to be runable from the command line. The closest that is explicitly mentioned is that anonymous tasks cannot be run from the command line, but that isn't really the same as explicitly saying the other kinds have to be runable.