Skip to content

Commit

Permalink
Improve snapshot versioning
Browse files Browse the repository at this point in the history
  • Loading branch information
Virtlink committed Jul 16, 2024
1 parent f86a519 commit 8beed85
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 19 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to this project are documented in this file, based on [Keep
## [Unreleased]


## [1.6.2] - 2024-07-16
- When creating a forced snapshot version, the snapshot version should be the same regardless of whether the current `HEAD` commit has a relwase tag or not.
- By default, set `firstParentOnly` to `true`.


## [1.6.1] - 2024-07-16
- No changes.

Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/mb/gitonium/GitoniumExtension.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ open class GitoniumExtension @Inject constructor(
.convention(true)
/** Whether to consider the first parent only when looking for tags across merge commits. */
val firstParentOnly: Property<Boolean> = objects.property(Boolean::class.java)
.convention(false)
.convention(true)
/** Whether to check for SNAPSHOT dependencies when publishing a release. */
val checkSnapshotDependenciesInRelease: Property<Boolean> = objects.property(Boolean::class.java)
.convention(true)
Expand Down
50 changes: 37 additions & 13 deletions src/main/kotlin/mb/gitonium/GitoniumVersion.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package mb.gitonium

import mb.gitonium.GitoniumVersion.Companion.getCurrentVersion
import mb.gitonium.git.CommandException
import mb.gitonium.git.GitRepo
import mb.gitonium.git.NativeGitRepo
Expand Down Expand Up @@ -58,7 +59,7 @@ data class GitoniumVersion(

val repo = getGitRepo(repoDirectory) ?: throw IOException("No Git repository found at $repoDirectory.")

val (tagVersion, tagIsSnapshot) = repo.getCurrentVersion(tagPrefix, firstParentOnly)
val (tagVersion, tagIsSnapshot) = repo.computeCurrentVersion(tagPrefix, firstParentOnly, alwaysSnapshot)
val isSnapshot = tagIsSnapshot || alwaysSnapshot
val actualTagVersion = if (tagVersion != null && isSnapshot) tagVersion.copy(
major = tagVersion.major + snapshotMajorIncrease,
Expand Down Expand Up @@ -128,37 +129,60 @@ data class GitoniumVersion(
return repo
}

/** The release version tag on the current commit, if any; otherwise `null`. */
private fun GitRepo.getReleaseTagVersion(tagPrefix: String, firstParentOnly: Boolean): String? {
/** The release version tag on the specified commit, if any; otherwise `null`. */
private fun GitRepo.getReleaseTagVersion(tagPrefix: String, firstParentOnly: Boolean, commit: String): String? {
// If the HEAD points to a commit with a release version tag,
// the output of `git describe` _with hash_ will not include a hash
// and therefore be equal to the output of `git describe` _without hash).
val tag = getRecentReleaseTagVersion(tagPrefix, firstParentOnly) ?: return null
val commitDescription = getTagDescription("$tagPrefix*", withHash = true)
val tag = getRecentReleaseTagVersion(tagPrefix, firstParentOnly, commit) ?: return null
val commitDescription = getTagDescription("$tagPrefix*", withHash = true, commit = commit)
return tag.takeIf { it == commitDescription }
}

/** The most recent release version tag (not necessarily on the current commit), if any; otherwise, `null`. */
private fun GitRepo.getRecentReleaseTagVersion(tagPrefix: String, firstParentOnly: Boolean): String? {
return getTagDescription("$tagPrefix*", withHash = false, firstParentOnly = firstParentOnly)
/** The most recent release version tag (not necessarily on the specified commit), if any; otherwise, `null`. */
private fun GitRepo.getRecentReleaseTagVersion(tagPrefix: String, firstParentOnly: Boolean, commit: String): String? {
return getTagDescription("$tagPrefix*", withHash = false, firstParentOnly = firstParentOnly, commit = commit)
.takeIf { it.isNotBlank() }
}

/** Computes the current (snapshot) version of a repository. */
private fun GitRepo.computeCurrentVersion(tagPrefix: String, firstParentOnly: Boolean, alwaysSnapshot: Boolean): Pair<SemanticVersion?, Boolean> {
val (currentTagVersion, currentTagIsSnapshot) = getCurrentVersion(tagPrefix, firstParentOnly, "HEAD")
return if (alwaysSnapshot && !currentTagIsSnapshot) {
// If we force a snapshot version (`alwaysSnapshot` is true) and the current commit is a release (has a
// tag), then we instead get the version based on the previous commit. This should ensure the snapshot
// version is the same regardless of whether the current commit has a release tag or not.
// Except perhaps in a special case where `firstParentOnly` is `false`
// and the current commit is a merge commit.
try {
getCurrentVersion(tagPrefix, firstParentOnly, commit = "HEAD~")
} catch (ex: CommandException) {
if (ex.exitCode == 128 && "Not a valid object name" in ex.stderr) {
// The previous commit does not exist, so we still use the current commit.
Pair(currentTagVersion, currentTagIsSnapshot)
} else {
throw ex
}
}
} else {
Pair(currentTagVersion, currentTagIsSnapshot)
}
}

/** Gets the current (snapshot) version of a repository. */
private fun GitRepo.getCurrentVersion(tagPrefix: String, firstParentOnly: Boolean): Pair<SemanticVersion?, Boolean> {
val releaseTagVersionStr = getReleaseTagVersion(tagPrefix, firstParentOnly)
?.substringAfter(tagPrefix)
private fun GitRepo.getCurrentVersion(tagPrefix: String, firstParentOnly: Boolean, commit: String): Pair<SemanticVersion?, Boolean> {
val releaseTagVersionStr = getReleaseTagVersion(tagPrefix, firstParentOnly, commit)?.substringAfter(tagPrefix)
val isSnapshot: Boolean
val tagVersion = if (releaseTagVersionStr == null) {
// The HEAD does not have a release version tag
isSnapshot = true
val recentTagVersionStr = getRecentReleaseTagVersion(tagPrefix, firstParentOnly)
val recentTagVersionStr = getRecentReleaseTagVersion(tagPrefix, firstParentOnly, commit)
?.substringAfter(tagPrefix) ?: return (null to isSnapshot)
val tagVersion = SemanticVersion.of(recentTagVersionStr) ?: return (null to isSnapshot)
// Increment the version, such that Gradle accepts this version as _newer_ than the last release version.
tagVersion
} else {
// The HEAD does have a release version tag
// The HEAD does have a release version tag, and we want to create a release version
isSnapshot = false
SemanticVersion.of(releaseTagVersionStr) ?: return (null to isSnapshot)
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/kotlin/mb/gitonium/git/GitRepo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ interface GitRepo {
* @param patterns The glob patterns to match tags against; or none to match all tags.
* @param withHash Whether to include the short commit hash in the name if the tag does not point to the current commit.
* @param firstParentOnly Whether to only consider the first parent when looking for tags across merge commits.
* @param commit The commit for which to find the tag description.
* @return The human-readable name of the last tag on this branch, optionally with a short commit hash suffix
* if the tag does not point to the current commit. If there is no tag, this just returns the short commit hash.
* @throws CommandException If the command fails or returns a non-zero exit code.
Expand All @@ -73,6 +74,7 @@ interface GitRepo {
vararg patterns: String,
withHash: Boolean = false,
firstParentOnly: Boolean = false,
commit: String = "HEAD",
): String

/**
Expand Down
5 changes: 3 additions & 2 deletions src/main/kotlin/mb/gitonium/git/NativeGitRepo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class NativeGitRepo(
vararg patterns: String,
withHash: Boolean,
firstParentOnly: Boolean,
commit: String,
): String {
if (withHash) {
return runGitCommand(
Expand All @@ -66,7 +67,7 @@ class NativeGitRepo(
*(if (firstParentOnly) listOf("--first-parent") else emptyList()).toTypedArray(),
// Match the pattern
*patterns.map { "--match=$it" }.toTypedArray(),
"HEAD",
commit,
)
} else {
try {
Expand All @@ -80,7 +81,7 @@ class NativeGitRepo(
*(if (firstParentOnly) listOf("--first-parent") else emptyList()).toTypedArray(),
// Match the pattern
*patterns.map { "--match=$it" }.toTypedArray(),
"HEAD",
commit,
)
} catch (ex: CommandException) {
if (ex.exitCode == 128 && "No names found, cannot describe anything" in ex.stderr) {
Expand Down
8 changes: 5 additions & 3 deletions src/test/kotlin/mb/gitonium/GitoniumPluginTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,13 @@ class GitoniumPluginTests: FunSpec({
}
""".trimIndent()
)
repo.commit("Initial commit", allowEmpty = true)
repo.tag("release-1.2.2")
// Ignore untracked directory: .gradle
repo.writeFile(".gradle", ".gitignore")
repo.addAll()
repo.commit("Initial commit")
repo.tag("release-1.2.3")
repo.commit("Gitignore commit")
repo.tag("release-1.2.4")


// Act
Expand All @@ -118,7 +120,7 @@ class GitoniumPluginTests: FunSpec({
// Assert
val versionStr = result.output.normaliseLineSeparators()
.substringAfter("> Task :printVersion\n").substringBefore('\n')
versionStr shouldBe "1.2.4-SNAPSHOT"
versionStr shouldBe "1.2.3-SNAPSHOT"
}

test("should print dirty version when git repo has changed files") {
Expand Down

0 comments on commit 8beed85

Please sign in to comment.