Skip to content

Remove ComputeVersion.Command, make ComputeVersion classes positioned #2350

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,6 @@ import scala.build.options.ComputeVersion
import scala.build.tests.{TestInputs, TestUtil}

class ComputeVersionTests extends munit.FunSuite {

test("command") {
val ver = "1.2.3"
val inputs = TestInputs(
os.rel / "version" -> ver
)
inputs.fromRoot { root =>
val cv = ComputeVersion.Command(Seq("cat", "version"))
val readVersion = cv.get(root)
.fold(ex => throw new Exception(ex), identity)
expect(readVersion == ver)
}
}

test("git tag") {
TestInputs().fromRoot { root =>
val ghRepo = "scala-cli/compute-version-test"
Expand All @@ -31,7 +17,7 @@ class ComputeVersionTests extends munit.FunSuite {
os.proc("git", "clone", repo)
.call(cwd = root, stdin = os.Inherit, stdout = os.Inherit)
val dir = root / "compute-version-test"
val cv = ComputeVersion.GitTag(os.rel, true, "0.0.1-SNAPSHOT")
val cv = ComputeVersion.GitTag(os.rel, true, Nil, "0.0.1-SNAPSHOT")

val commitExpectedVersions = Seq(
"8ea4e87f202fbcc369bec9615e7ddf2c14b39e9d" -> "0.2.0-1-g8ea4e87-SNAPSHOT",
Expand All @@ -56,7 +42,7 @@ class ComputeVersionTests extends munit.FunSuite {
expect(!hasHead)

val defaultVersion = "0.0.2-SNAPSHOT"
val cv = ComputeVersion.GitTag(os.rel, true, defaultVersion)
val cv = ComputeVersion.GitTag(os.rel, true, Nil, defaultVersion)

val version = cv.get(root)
.fold(ex => throw new Exception(ex), identity)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class SourceGeneratorTests extends munit.FunSuite {
println(s"Git initialized at $cwd")
}

test(s"BuildInfo source generated") {
test("BuildInfo source generated") {
val inputs = TestInputs(
os.rel / "main.scala" ->
"""//> using dep com.lihaoyi::os-lib:0.9.1
Expand Down Expand Up @@ -154,7 +154,7 @@ class SourceGeneratorTests extends munit.FunSuite {

}

test(s"BuildInfo for native") {
test("BuildInfo for native") {
val inputs = TestInputs(
os.rel / "main.scala" ->
s"""//> using dep "com.lihaoyi::os-lib:0.9.1"
Expand Down Expand Up @@ -230,7 +230,7 @@ class SourceGeneratorTests extends munit.FunSuite {
}
}

test(s"BuildInfo for js") {
test("BuildInfo for js") {
val inputs = TestInputs(
os.rel / "main.scala" ->
s"""//> using dep "com.lihaoyi::os-lib:0.9.1"
Expand All @@ -244,7 +244,6 @@ class SourceGeneratorTests extends munit.FunSuite {
|//> using platform scala-js
|//> using jsVersion 1.13.1
|//> using jsEsVersionStr es2015
|//> using computeVersion "command:echo TestVersion"
|
|//> using buildInfo
|
Expand Down Expand Up @@ -281,7 +280,7 @@ class SourceGeneratorTests extends munit.FunSuite {
| val jsEsVersion = Some("es2015")
| val scalaNativeVersion = None
| val mainClass = Some("Main")
| val projectVersion = Some("TestVersion")
| val projectVersion = None
|
| object Main {
| val sources = Seq("${root / "main.scala"}")
Expand All @@ -308,7 +307,7 @@ class SourceGeneratorTests extends munit.FunSuite {
}
}

test(s"BuildInfo for Scala 2") {
test("BuildInfo for Scala 2") {
val inputs = TestInputs(
os.rel / "main.scala" ->
s"""//> using dep "com.lihaoyi::os-lib:0.9.1"
Expand All @@ -319,7 +318,6 @@ class SourceGeneratorTests extends munit.FunSuite {
|//> using mainClass "Main"
|//> using resourceDir ./resources
|//> using jar TEST1.jar TEST2.jar
|//> using computeVersion "command:echo TestVersion"
|
|//> using buildInfo
|
Expand Down Expand Up @@ -356,7 +354,7 @@ class SourceGeneratorTests extends munit.FunSuite {
| val jsEsVersion = None
| val scalaNativeVersion = None
| val mainClass = Some("Main")
| val projectVersion = Some("TestVersion")
| val projectVersion = None
|
| object Main {
| val sources = Seq("${root / "main.scala"}")
Expand All @@ -382,4 +380,47 @@ class SourceGeneratorTests extends munit.FunSuite {
)
}
}

test("BuildInfo no git repository error") {
val usingPrefix = "//> using computeVersion \""
val usingValue = "git:tag"
val usingSuffix = "\""

val inputs = TestInputs(
os.rel / "main.scala" ->
s"""
|$usingPrefix$usingValue$usingSuffix
|//> using buildInfo
|
|import scala.cli.build.BuildInfo
|
|object Main extends App {
| println(s"Scala version: $${BuildInfo.projectVersion}")
|}
|""".stripMargin
)

inputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) {
(root, _, maybeBuild) =>
maybeBuild match {
case Left(buildException) =>
expect(buildException.positions.size == 1)
val position = buildException.positions.head

assertEquals(
position,
scala.build.Position.File(
Right(root / "main.scala"),
(1, usingPrefix.length),
(1, (usingPrefix + usingValue).length)
)
)
assertNoDiff(
buildException.message,
s"BuildInfo generation error: $root doesn't look like a Git repository"
)
case _ => fail("Build should fail")
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ object Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {
name
}
def defaultComputeVersion(mayDefaultToGitTag: Boolean): Option[ComputeVersion] =
if (mayDefaultToGitTag) Some(ComputeVersion.GitTag(os.rel, dynVer = false))
if (mayDefaultToGitTag) Some(ComputeVersion.GitTag(os.rel, dynVer = false, positions = Nil))
else None
def defaultVersionError =
new MissingPublishOptionError("version", "--version", "publish.version")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
package scala.build.errors

final class BuildInfoGenerationError(cause: Exception)
extends BuildException(s"BuildInfo generation error: ${cause.getMessage}", cause = cause)
import scala.build.Position

final class BuildInfoGenerationError(msg: String, positions: Seq[Position], cause: Exception)
extends BuildException(
s"BuildInfo generation error: $msg",
positions = positions,
cause = cause
)
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,28 @@ object GenerateReferenceDoc extends CaseApp[InternalDocOptions] {
|```""".stripMargin
)

b.section(
"""## Project version
|
|A part of the BuildInfo object is the project version. By default, an attempt is made to deduce it using git tags
|of the workspace repository. If this fails (e.g. no git repository is present), the version is set to `0.1.0-SNAPSHOT`.
|You can override this behaviour by passing the `--project-version` option to Scala CLI or by using a
|`//> using projectVersion` directive.
|
|Please note that only tags that follow the semantic versioning are taken into consideration.
|
|Values available for project version configuration are:
|- `git:tag` or `git`: use the latest stable git tag, if it is older than HEAD then try to increment it
| and add a suffix `-SNAPSHOT`, if no tag is available then use `0.1.0-SNAPSHOT`
|- `git:dynver`: use the latest (stable or unstable) git tag, if it is older than HEAD then use the output of
| `-{distance from last tag}-g{shortened version of HEAD commit hash}-SNAPSHOT`, if no tag is available then use `0.1.0-SNAPSHOT`
|
|The difference between stable and unstable tags are, that the latter can contain letters, e.g. `v0.1.0-RC1`.
|It is also possible to specify the path to the repository, e.g. `git:tag:../my-repo`, `git:dynver:../my-repo`.
|
|""".stripMargin
)

b.mkString
}

Expand Down
12 changes: 9 additions & 3 deletions modules/options/src/main/scala/scala/build/info/BuildInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -100,19 +100,25 @@ object BuildInfo {
def apply(
options: BuildOptions,
workspace: os.Path
): Either[BuildException, BuildInfo] = either {
): Either[BuildException, BuildInfo] = either[Exception] {
Seq(
BuildInfo(
mainClass = options.mainClass,
projectVersion = options.sourceGeneratorOptions.computeVersion
.map(cv => value(cv.get(workspace)))
.orElse(ComputeVersion.GitTag(os.rel, dynVer = false).get(workspace).toOption)
.orElse(
ComputeVersion.GitTag(os.rel, dynVer = false, positions = Nil).get(workspace).toOption
)
),
scalaVersionSettings(options),
platformSettings(options)
)
.reduceLeft(_ + _)
}.left.map(BuildInfoGenerationError(_))
}.left.map {
case e: BuildException =>
BuildInfoGenerationError(e.message, positions = e.positions, cause = e)
case e => BuildInfoGenerationError(e.getMessage, Nil, e)
}

def escapeBackslashes(s: String): String =
s.replace("\\", "\\\\")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,23 @@ import com.github.plokhotnyuk.jsoniter_scala.macros.*
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.{Constants, Ref}

import scala.build.Positioned
import scala.build.errors.{BuildException, MalformedInputError}
import scala.build.{Position, Positioned}
import scala.io.Codec
import scala.jdk.CollectionConverters.*
import scala.util.{Success, Try, Using}

sealed abstract class ComputeVersion extends Product with Serializable {
def get(workspace: os.Path): Either[BuildException, String]

val positions: Seq[Position]
}

object ComputeVersion {

final case class Command(command: Seq[String]) extends ComputeVersion {
def get(workspace: os.Path): Either[BuildException, String] = {
val maybeRes = Try(os.proc(command).call(stdin = os.Inherit, cwd = workspace, check = false))
maybeRes match {
case Success(res) if res.exitCode == 0 =>
Right(res.out.trim(Codec.default))
case _ =>
Left(new Command.ComputeVersionCommandError(
command,
maybeRes.map(_.exitCode).getOrElse(1)
))
}
}
}

object Command {
final class ComputeVersionCommandError(command: Seq[String], exitCode: Int)
extends BuildException(
s"Error running command ${command.mkString(" ")} (exit code: $exitCode)"
)
}

final case class GitTag(
repo: os.FilePath,
dynVer: Boolean,
positions: Seq[Position],
defaultFirstVersion: String = "0.1.0-SNAPSHOT"
) extends ComputeVersion {
import GitTag.GitTagError
Expand Down Expand Up @@ -115,17 +95,19 @@ object ComputeVersion {
Option(tagOrNull) match {
case None =>
Left(new GitTagError(
positions,
s"Unexpected error when running git describe from Git repository $repo0 (git describe doesn't find back tag $tag)"
))
case Some(tag) =>
versionOf(tag).map(_ + "-SNAPSHOT").toRight(
new GitTagError(
positions,
s"Unexpected error when running git describe from Git repository $repo0 (git describe-provided tag $tag doesn't have the expected shape)"
)
)
}
case (Some(_), None) =>
Left(new GitTagError(s"No stable tag found in Git repository $repo0"))
Left(new GitTagError(positions, s"No stable tag found in Git repository $repo0"))
case (_, Some((tag, name))) =>
val idx = name.lastIndexOf('.')
if (
Expand All @@ -134,6 +116,7 @@ object ComputeVersion {
Right(name.take(idx + 1) + (name.drop(idx + 1).toInt + 1).toString + "-SNAPSHOT")
else
Left(new GitTagError(
positions,
s"Don't know how to bump version in tag $tag in Git repository $repo0"
))
}
Expand All @@ -142,55 +125,40 @@ object ComputeVersion {
Right(defaultFirstVersion)
}
else
Left(new GitTagError(s"$repo0 doesn't look like a Git repository"))
Left(new GitTagError(positions, s"$repo0 doesn't look like a Git repository"))
}
}
object GitTag {
final class GitTagError(message: String) extends BuildException(message)
final class GitTagError(positions: Seq[Position], message: String)
extends BuildException(message, positions)
}

private lazy val commandCodec: JsonValueCodec[List[String]] =
JsonCodecMaker.make

def parse(input: Positioned[String]): Either[BuildException, ComputeVersion] =
if (input.value == "git" || input.value == "git:tag")
Right(ComputeVersion.GitTag(os.rel, dynVer = false))
Right(ComputeVersion.GitTag(os.rel, dynVer = false, positions = input.positions))
else if (input.value.startsWith("git:tag:"))
Right(ComputeVersion.GitTag(os.FilePath(input.value.stripPrefix("git:tag:")), dynVer = false))
Right(ComputeVersion.GitTag(
os.FilePath(input.value.stripPrefix("git:tag:")),
dynVer = false,
positions = input.positions
))
else if (input.value == "git:dynver")
Right(ComputeVersion.GitTag(os.rel, dynVer = true))
Right(ComputeVersion.GitTag(os.rel, dynVer = true, positions = input.positions))
else if (input.value.startsWith("git:dynver:"))
Right(ComputeVersion.GitTag(
os.FilePath(input.value.stripPrefix("git:dynver:")),
dynVer = true
dynVer = true,
positions = input.positions
))
else if (input.value.startsWith("command:["))
try {
val command = readFromString(input.value.stripPrefix("command:"))(commandCodec)
Right(ComputeVersion.Command(command))
}
catch {
case e: JsonReaderException =>
Left(
new MalformedInputError(
"compute-version",
input.value,
"git|git:tag|command:…",
input.positions,
cause = Some(e)
)
)
}
else if (input.value.startsWith("command:")) {
val command = input.value.stripPrefix("command:").split("\\s+").toSeq
Right(ComputeVersion.Command(command))
}
else
Left(
new MalformedInputError(
"compute-version",
input.value,
"git|git:tag|command:…",
"git|git:tag|git:dynver",
input.positions
)
)
Expand Down
Loading