add git extension

This commit is contained in:
2025-09-08 16:22:41 +02:00
parent 6544d60b5c
commit a979ec6623
5 changed files with 215 additions and 1 deletions

View File

@@ -2,6 +2,10 @@
## Extension.
### Extension Git.
The GitExtension enhances versioning by dynamically appending the Git hash before "snapshot" in the version string. For example, 0.0.0-SNAPSHOT becomes 0.0.0.0a2b3c4d-SNAPSHOT, ensuring each build reflects its commit origin, prevents overwriting existing versions. This feature aids developers during development by providing clear version identification.
## Publish gradle plugin locally.
```shell

View File

@@ -17,7 +17,11 @@ plugins {
}
dependencies {
implementation(aa.jgit)
testImplementation(aa.junit.jupiter.params)
testImplementation(aa.kotlin.junit5)
testImplementation(aa.mockk)
testRuntimeOnly(aa.junit.platform.launcher)
}

View File

@@ -1,7 +1,9 @@
package ltd.lulz.plugin
import ltd.lulz.plugin.extension.GitExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import ltd.lulz.plugin.extension.GitExtension.Companion.PLUGIN_NAME as GIT_PLUGIN_NAME
@Suppress("unused")
class CorePlugin : Plugin<Project> {
@@ -12,6 +14,11 @@ class CorePlugin : Plugin<Project> {
override fun apply(
project: Project,
) {
TODO("Not yet implemented")
gitExtension(project)
}
private fun gitExtension(
project: Project,
): GitExtension = project.extensions
.create(GIT_PLUGIN_NAME, GitExtension::class.java, project)
}

View File

@@ -0,0 +1,37 @@
package ltd.lulz.plugin.extension
import org.eclipse.jgit.api.Git.open
import org.eclipse.jgit.lib.Constants.HEAD
import org.gradle.api.Project
abstract class GitExtension(private val project: Project) {
companion object {
const val PLUGIN_NAME = "git"
private const val HASH_LENGTH = 8
private const val SNAPSHOT = "-SNAPSHOT"
private const val UNAVAILABLE = "n/a"
private val PRIMARY_BRANCHES = setOf("master", "develop")
}
fun version(): String = when {
isHead() || currentBranch() in PRIMARY_BRANCHES -> project.version.toString()
else -> makeVersion(project.version.toString(), currentShortHash())
}
fun currentShortHash(): String = open(project.projectDir)
.use { it.repository.exactRef(HEAD)?.objectId?.name?.take(HASH_LENGTH) ?: UNAVAILABLE }
fun currentBranch(): String = open(project.projectDir)
.use { it.repository.branch ?: UNAVAILABLE }
fun isHead(): Boolean = open(project.projectDir)
.use { it.repository.exactRef(HEAD)?.target?.name == HEAD }
private fun makeVersion(version: String, shortHash: String): String = when {
shortHash == UNAVAILABLE -> version.also { println("Failed to get data from GIT") }
version.endsWith(SNAPSHOT) -> version.replace(SNAPSHOT, ".$shortHash$SNAPSHOT")
else -> version.also { println("Failed version missing suffix $SNAPSHOT") }
}
}

View File

@@ -0,0 +1,162 @@
package ltd.lulz.plugin.extension
import io.mockk.every
import io.mockk.justRun
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.unmockkStatic
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.ObjectId
import org.eclipse.jgit.lib.Ref
import org.eclipse.jgit.lib.Repository
import org.gradle.api.Project
import org.gradle.testfixtures.ProjectBuilder
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.CsvSource
class GitExtensionTest {
companion object {
const val GIT_HASH_SHA_1 = "0a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t"
const val SHORT_GIT_HASH = "0a1b2c3d"
const val SNAPSHOT_HASH_VERSION = "0.0.0.0a1b2c3d-SNAPSHOT"
const val SNAPSHOT_VERSION = "0.0.0-SNAPSHOT"
const val VERSION = "0.0.0"
const val BRANCH_FEATURE = "feature/ABC-123"
const val BRANCH_MASTER = "master"
const val EXTENSION = "git"
const val PLUGIN_ID = "ltd.lulz.plugin.core-plugin"
const val REF_HEAD = "HEAD"
const val UNAVAILABLE = "n/a"
}
private val gitMock: Git = mockk()
private val refMock: Ref = mockk()
private val repositoryMock: Repository = mockk()
private val objectIdMock: ObjectId = mockk()
lateinit var name: String
lateinit var project: Project
@BeforeEach
fun buildUp() {
project = ProjectBuilder.builder().build()
project.version = SNAPSHOT_VERSION
project.pluginManager.apply(PLUGIN_ID)
mockkStatic(Git::class)
every { Git.open(any()) } returns gitMock
every { gitMock.repository } returns repositoryMock
every { repositoryMock.branch } returns null
every { repositoryMock.exactRef(any()) } returns refMock
every { refMock.target } returns refMock
every { refMock.name } returns null
every { refMock.objectId } returns objectIdMock
every { objectIdMock.name } returns null
justRun { gitMock.close() }
}
@AfterEach
fun tearDown() {
unmockkStatic(Git::class)
}
@ParameterizedTest
@CsvSource(
"$REF_HEAD, true",
"$BRANCH_FEATURE, false",
)
fun `current ref is head`(ref: String, expected: Boolean) {
// given
every { refMock.name } returns ref
// when
val extension = project.extensions.getByName(EXTENSION) as GitExtension
// then
assertEquals(expected, extension.isHead())
}
@ParameterizedTest
@CsvSource(
", $UNAVAILABLE",
"$BRANCH_FEATURE, $BRANCH_FEATURE",
)
fun `get current branch`(branch: String?, expected: String) {
// given
every { repositoryMock.branch } returns branch
// when
val extension = project.extensions.getByName(EXTENSION) as GitExtension
// then
assertEquals(expected, extension.currentBranch())
}
@ParameterizedTest
@CsvSource(
", $UNAVAILABLE",
"$GIT_HASH_SHA_1, $SHORT_GIT_HASH",
)
fun `get current short hash`(hash: String?, expected: String) {
// given
every { objectIdMock.name } returns hash
// when
val extension = project.extensions.getByName(EXTENSION) as GitExtension
// then
assertEquals(expected, extension.currentShortHash())
}
@Test
fun `get version - version without snapshot`() {
// given
project.version = VERSION
every { objectIdMock.name } returns GIT_HASH_SHA_1
// when
val extension = project.extensions.getByName(EXTENSION) as GitExtension
// then
assertEquals(VERSION, extension.version())
}
@Test
fun `get version - short hash is blank`() {
// when
val extension = project.extensions.getByName(EXTENSION) as GitExtension
// then
assertEquals(SNAPSHOT_VERSION, extension.version())
}
@ParameterizedTest
@CsvSource(
", , $SNAPSHOT_HASH_VERSION",
", $REF_HEAD, $SNAPSHOT_VERSION",
"$BRANCH_MASTER, , $SNAPSHOT_VERSION",
"$BRANCH_MASTER, , $SNAPSHOT_VERSION",
"$BRANCH_FEATURE, , $SNAPSHOT_HASH_VERSION",
"$BRANCH_MASTER, $REF_HEAD, $SNAPSHOT_VERSION",
"$BRANCH_MASTER, $REF_HEAD, $SNAPSHOT_VERSION",
"$BRANCH_FEATURE, $REF_HEAD, $SNAPSHOT_VERSION",
)
fun `get version - different branches`(branch: String?, ref: String?, expected: String) {
// given
every { repositoryMock.branch } returns branch
every { objectIdMock.name } returns GIT_HASH_SHA_1
every { refMock.name } returns ref
// when
val extension = project.extensions.getByName(EXTENSION) as GitExtension
// then
assertEquals(expected, extension.version())
}
}