diff --git a/CHANGELOG.md b/CHANGELOG.md index 0186e47..3315edf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project are documented in this file, based on [Keep ## [Unreleased] +## [1.7.5] - 2024-07-24 +- Warn and continue if the version could not be determined, or if the directory is not a Git repository. + + ## [1.7.4] - 2024-07-24 - Add `printProjects` task that prints the GAV coordinates of all projects. @@ -119,7 +123,9 @@ All notable changes to this project are documented in this file, based on [Keep - `Project.DEFAULT_VERSION` (`"unspecified"`) is assigned as version if no Git repository is found, instead of failing. -[Unreleased]: https://github.com/metaborg/gitonium/compare/release-1.7.3...HEAD +[Unreleased]: https://github.com/metaborg/gitonium/compare/release-1.7.5...HEAD +[1.7.5]: https://github.com/metaborg/gitonium/compare/release-1.7.4...release-1.7.5 +[1.7.4]: https://github.com/metaborg/gitonium/compare/release-1.7.3...release-1.7.4 [1.7.3]: https://github.com/metaborg/gitonium/compare/release-1.7.2...release-1.7.3 [1.7.2]: https://github.com/metaborg/gitonium/compare/release-1.7.1...release-1.7.2 [1.7.1]: https://github.com/metaborg/gitonium/compare/release-1.7.0...release-1.7.1 diff --git a/plugin/src/main/kotlin/mb/gitonium/GitoniumExtension.kt b/plugin/src/main/kotlin/mb/gitonium/GitoniumExtension.kt index 8af6f9c..ef103f0 100644 --- a/plugin/src/main/kotlin/mb/gitonium/GitoniumExtension.kt +++ b/plugin/src/main/kotlin/mb/gitonium/GitoniumExtension.kt @@ -95,12 +95,7 @@ open class GitoniumExtension @Inject constructor( * and `-SNAPSHOT` as the suffix (e.g., `"1.0.1-develop-SNAPSHOT"`). * If the repository is dirty, the version is suffixed with `+dirty` (e.g., `"1.0.1-SNAPSHOT+dirty"`). */ - val version: String by lazy { - versionInfo.versionString ?: run { - LOG.warn("Gitonium could not determine version from Git repository, using default version.") - Project.DEFAULT_VERSION - } - } + val version: String get() = versionInfo.versionString /** Whether the current commit has a release version tag. */ val isRelease: Boolean get() = versionInfo.isRelease diff --git a/plugin/src/main/kotlin/mb/gitonium/GitoniumPlugin.kt b/plugin/src/main/kotlin/mb/gitonium/GitoniumPlugin.kt index 67be953..405bc00 100644 --- a/plugin/src/main/kotlin/mb/gitonium/GitoniumPlugin.kt +++ b/plugin/src/main/kotlin/mb/gitonium/GitoniumPlugin.kt @@ -130,7 +130,7 @@ class GitoniumPlugin : Plugin { propertiesFile.writer().use { w -> val p = Properties() with(extension.versionInfo) { - versionString?.let { p["version"] = it } + p["version"] = versionString releaseVersionString?.let { p["release-version"] = it } commit?.let { p["commit"] = it } p["build-time"] = buildDateTimeStr diff --git a/plugin/src/main/kotlin/mb/gitonium/GitoniumVersion.kt b/plugin/src/main/kotlin/mb/gitonium/GitoniumVersion.kt index 1c5652b..fd72d36 100644 --- a/plugin/src/main/kotlin/mb/gitonium/GitoniumVersion.kt +++ b/plugin/src/main/kotlin/mb/gitonium/GitoniumVersion.kt @@ -1,12 +1,16 @@ package mb.gitonium -import mb.gitonium.GitoniumVersion.Companion.getCurrentVersion import mb.gitonium.git.CommandException import mb.gitonium.git.GitRepo import mb.gitonium.git.NativeGitRepo +import org.gradle.api.Project +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.io.File import java.io.IOException +private val LOG: Logger = LoggerFactory.getLogger(GitoniumVersion::class.java) + /** * A Gitonium version, determined from a Git repository. */ @@ -15,8 +19,8 @@ data class GitoniumVersion( val branch: String?, /** The current commit ID; of `null` if it could not be determined. */ val commit: String?, - /** The current version string; or `null` if it could not be determined. */ - val versionString: String?, + /** The current version string; or `"unspecified"` if it could not be determined. */ + val versionString: String, /** The current version; or `null` if it could not be determined. */ val version: SemanticVersion?, /** The most recent release version string; or `null` if it could not be determined. */ @@ -56,48 +60,66 @@ data class GitoniumVersion( /** The name of the main branch. */ mainBranch: String? = "main", ): GitoniumVersion { + try { + val repo = getGitRepo(repoDirectory) ?: throw IOException("No Git repository found at $repoDirectory.") + + val (tagVersion, tagIsSnapshot) = repo.computeCurrentVersion(tagPrefix, firstParentOnly, alwaysSnapshot) + val isSnapshot = tagIsSnapshot || alwaysSnapshot + val actualTagVersion = if (tagVersion != null && isSnapshot) tagVersion.copy( + major = tagVersion.major + snapshotMajorIncrease, + minor = tagVersion.minor + snapshotMinorIncrease, + patch = tagVersion.patch + snapshotPatchIncrease, + ) else tagVersion + + // Determine the current branch name + val branch = if (isSnapshot && snapshotIncludeBranch) repo.getCurrentBranchOrNull() else null + val commit = repo.getCurrentCommitHashOrNull() + val snapshotVersionSuffix = if (isSnapshot) buildString { + if (branch != mainBranch) append(branch ?: "") + if (snapshotSuffix.isNotBlank()) { + if (isNotEmpty()) append("-") + append(snapshotSuffix) + } + }.ifBlank { null } else null + val dirtyVersionSuffix = if (repo.isDirty()) dirtySuffix.ifBlank { null } else null + + val version = actualTagVersion?.let { + SemanticVersion( + major = it.major, + minor = it.minor, + patch = it.patch, + preRelease = it.preRelease + listOfNotNull(snapshotVersionSuffix), + build = it.build + listOfNotNull(dirtyVersionSuffix), + ) + } - val repo = getGitRepo(repoDirectory) ?: throw IOException("No Git repository found at $repoDirectory.") - - val (tagVersion, tagIsSnapshot) = repo.computeCurrentVersion(tagPrefix, firstParentOnly, alwaysSnapshot) - val isSnapshot = tagIsSnapshot || alwaysSnapshot - val actualTagVersion = if (tagVersion != null && isSnapshot) tagVersion.copy( - major = tagVersion.major + snapshotMajorIncrease, - minor = tagVersion.minor + snapshotMinorIncrease, - patch = tagVersion.patch + snapshotPatchIncrease, - ) else tagVersion - - // Determine the current branch name - val branch = if (isSnapshot && snapshotIncludeBranch) repo.getCurrentBranchOrNull() else null - val commit = repo.getCurrentCommitHashOrNull() - val snapshotVersionSuffix = if (isSnapshot) buildString { - if (branch != mainBranch) append(branch ?: "") - if (snapshotSuffix.isNotBlank()) { - if (isNotEmpty()) append("-") - append(snapshotSuffix) + if (version == null) { + LOG.warn("Gitonium could not determine version from Git repository, using version `${Project.DEFAULT_VERSION}`.") } - }.ifBlank { null } else null - val dirtyVersionSuffix = if (repo.isDirty()) dirtySuffix.ifBlank { null } else null - - val version = actualTagVersion?.let { - SemanticVersion( - major = it.major, - minor = it.minor, - patch = it.patch, - preRelease = it.preRelease + listOfNotNull(snapshotVersionSuffix), - build = it.build + listOfNotNull(dirtyVersionSuffix), + + return GitoniumVersion( + branch = branch, + commit = commit, + versionString = version?.toString() ?: Project.DEFAULT_VERSION, + version = version, + releaseVersionString = tagVersion?.toString(), + releaseVersion = tagVersion, + isDirty = repo.isDirty(), + isRelease = !isSnapshot + ) + } catch (ex: IOException) { + LOG.warn("Gitonium could not determine version from Git repository, using version `${Project.DEFAULT_VERSION}`: ${ex.message}") + return GitoniumVersion( + branch = null, + commit = null, + versionString = Project.DEFAULT_VERSION, + version = null, + releaseVersionString = null, + releaseVersion = null, + isDirty = false, + isRelease = false ) } - return GitoniumVersion( - branch = branch, - commit = commit, - versionString = version?.toString(), - version = version, - releaseVersionString = tagVersion?.toString(), - releaseVersion = tagVersion, - isDirty = repo.isDirty(), - isRelease = !isSnapshot - ) } /** diff --git a/plugin/src/main/kotlin/mb/gitonium/LazyGitoniumVersion.kt b/plugin/src/main/kotlin/mb/gitonium/LazyGitoniumVersion.kt deleted file mode 100644 index 1ada140..0000000 --- a/plugin/src/main/kotlin/mb/gitonium/LazyGitoniumVersion.kt +++ /dev/null @@ -1,19 +0,0 @@ -package mb.gitonium - -import org.gradle.api.Project - -/** Uses the [GitoniumExtension] to lazily compute the version string. */ -class LazyGitoniumVersion( - /** The Gitonium extension to use. */ - private val extension: GitoniumExtension, - /** Whether this is a subproject. */ - private val isSubProject: Boolean, -) { - override fun toString(): String { - return when { - extension.setVersion.get() && !isSubProject -> extension.version - extension.setSubprojectVersions.get() && isSubProject -> extension.version - else -> Project.DEFAULT_VERSION - } - } -} diff --git a/plugin/src/test/kotlin/mb/gitonium/GitoniumPluginTests.kt b/plugin/src/test/kotlin/mb/gitonium/GitoniumPluginTests.kt index 05fe426..826e0f6 100644 --- a/plugin/src/test/kotlin/mb/gitonium/GitoniumPluginTests.kt +++ b/plugin/src/test/kotlin/mb/gitonium/GitoniumPluginTests.kt @@ -1,12 +1,14 @@ package mb.gitonium import io.kotest.core.spec.style.FunSpec +import io.kotest.engine.spec.tempdir import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain import mb.gitonium.git.GitTestUtils.copyTestGitConfig import mb.gitonium.git.GitTestUtils.createEmptyRepository import mb.gitonium.git.GitTestUtils.writeFile import mb.gitonium.git.NativeGitRepo +import org.gradle.api.Project import org.gradle.kotlin.dsl.support.normaliseLineSeparators import org.gradle.testkit.runner.GradleRunner import org.gradle.testkit.runner.TaskOutcome @@ -59,6 +61,32 @@ class GitoniumPluginTests: FunSpec({ } context("task :printVersion") { + test("should print `unspecified` when directory is not a Git repo") { + // Arrange + val repoDir = tempdir() + val buildFile = repoDir.resolve("build.gradle.kts") + buildFile.writeText( + """ + plugins { + `java-library` + id("org.metaborg.gitonium") + } + + version = gitonium.version + """.trimIndent() + ) + + // Act + val result = GradleRunner.create() + .withProjectDir(repoDir) + .withArguments(":printVersion", "--quiet") + .withPluginClasspath() + .build() + + // Assert + result.output.trim() shouldBe Project.DEFAULT_VERSION + } + test("should print clean version when git repo is clean") { // Arrange val repo = createEmptyRepository(::gitRepoBuilder) @@ -83,14 +111,12 @@ class GitoniumPluginTests: FunSpec({ // Act val result = GradleRunner.create() .withProjectDir(repo.directory) - .withArguments(":printVersion") + .withArguments(":printVersion", "--quiet") .withPluginClasspath() .build() // Assert - val versionStr = result.output.normaliseLineSeparators() - .substringAfter("> Task :printVersion\n").substringBefore('\n') - versionStr shouldBe "1.2.3" + result.output.trim() shouldBe "1.2.3" } test("should print snapshot version when git repo is clean but gitonium.isSnapshot is set") { @@ -119,14 +145,12 @@ class GitoniumPluginTests: FunSpec({ // Act val result = GradleRunner.create() .withProjectDir(repo.directory) - .withArguments(":printVersion", "-Pgitonium.isSnapshot=true") + .withArguments(":printVersion", "--quiet", "-Pgitonium.isSnapshot=true") .withPluginClasspath() .build() // Assert - val versionStr = result.output.normaliseLineSeparators() - .substringAfter("> Task :printVersion\n").substringBefore('\n') - versionStr shouldBe "1.2.3-SNAPSHOT" + result.output.trim() shouldBe "1.2.3-SNAPSHOT" } test("should print dirty version when git repo has changed files") { @@ -154,14 +178,12 @@ class GitoniumPluginTests: FunSpec({ // Act val result = GradleRunner.create() .withProjectDir(repo.directory) - .withArguments(":printVersion") + .withArguments(":printVersion", "--quiet") .withPluginClasspath() .build() // Assert - val versionStr = result.output.normaliseLineSeparators() - .substringAfter("> Task :printVersion\n").substringBefore('\n') - versionStr shouldBe "1.2.3+dirty" + result.output.trim() shouldBe "1.2.3+dirty" } test("should print snapshot version when git repo has had a commit since the tag") { @@ -191,14 +213,12 @@ class GitoniumPluginTests: FunSpec({ // Act val result = GradleRunner.create() .withProjectDir(repo.directory) - .withArguments(":printVersion") + .withArguments(":printVersion", "--quiet") .withPluginClasspath() .build() // Assert - val versionStr = result.output.normaliseLineSeparators() - .substringAfter("> Task :printVersion\n").substringBefore('\n') - versionStr shouldBe "1.2.4-SNAPSHOT" + result.output.trim() shouldBe "1.2.4-SNAPSHOT" } test("should print snapshot and dirty version when git repo has had changes and a commit since the tag") { @@ -230,14 +250,12 @@ class GitoniumPluginTests: FunSpec({ // Act val result = GradleRunner.create() .withProjectDir(repo.directory) - .withArguments(":printVersion") + .withArguments(":printVersion", "--quiet") .withPluginClasspath() .build() // Assert - val versionStr = result.output.normaliseLineSeparators() - .substringAfter("> Task :printVersion\n").substringBefore('\n') - versionStr shouldBe "1.2.4-SNAPSHOT+dirty" + result.output.trim() shouldBe "1.2.4-SNAPSHOT+dirty" } } diff --git a/plugin/src/test/kotlin/mb/gitonium/GitoniumVersionTests.kt b/plugin/src/test/kotlin/mb/gitonium/GitoniumVersionTests.kt index e04e742..b463103 100644 --- a/plugin/src/test/kotlin/mb/gitonium/GitoniumVersionTests.kt +++ b/plugin/src/test/kotlin/mb/gitonium/GitoniumVersionTests.kt @@ -10,6 +10,7 @@ import mb.gitonium.git.GitTestUtils.commitFile import mb.gitonium.git.GitTestUtils.copyTestGitConfig import mb.gitonium.git.GitTestUtils.createEmptyRepository import mb.gitonium.git.NativeGitRepo +import org.gradle.api.Project import java.io.File import java.io.IOException @@ -26,19 +27,27 @@ class GitoniumVersionTests: FunSpec({ } context("determineVersion()") { - test("should throw, when the directory is not a Git repository") { + test("should return `unspecified`, when the directory is not a Git repository") { // Arrange val repoDir = tempdir() - // Act/Assert - shouldThrow { - GitoniumVersion.determineVersion( - repoDirectory = repoDir, - ) + // Act + val versionInfo = GitoniumVersion.determineVersion( + repoDirectory = repoDir, + ) + + // Assert + assertSoftly { + versionInfo.version shouldBe null + versionInfo.versionString shouldBe Project.DEFAULT_VERSION + versionInfo.branch shouldBe null + versionInfo.isDirty shouldBe false + versionInfo.isRelease shouldBe false + versionInfo.isSnapshot shouldBe true } } - test("should return no version, when the directory is an empty Git repository") { + test("should return `unspecified`, when the directory is an empty Git repository") { // Arrange val repo = createEmptyRepository(::buildGitRepo) @@ -50,7 +59,7 @@ class GitoniumVersionTests: FunSpec({ // Assert assertSoftly { versionInfo.version shouldBe null - versionInfo.versionString shouldBe null + versionInfo.versionString shouldBe Project.DEFAULT_VERSION versionInfo.branch shouldBe "main" versionInfo.isDirty shouldBe false versionInfo.isRelease shouldBe false