├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── example ├── settings.gradle.kts ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── build.gradle.kts ├── gradlew.bat └── gradlew ├── src ├── main │ └── kotlin │ │ └── io │ │ └── hndrs │ │ └── gradle │ │ └── plugin │ │ └── git │ │ └── properties │ │ ├── common │ │ └── StringEscapeExtensions.kt │ │ ├── data │ │ ├── GitPropertyProvider.kt │ │ ├── BuildHostPropertiesProvider.kt │ │ ├── GitPropertiesProviderChain.kt │ │ ├── GitConfigPropertiesProvider.kt │ │ ├── GitBranchPropertiesProvider.kt │ │ └── GitLogPropertiesProvider.kt │ │ ├── PropertiesFileWriter.kt │ │ ├── GitPropertiesPlugin.kt │ │ └── GenerateGitPropertiesTask.kt └── test │ └── kotlin │ └── io │ └── hndrs │ └── gradle │ └── plugin │ └── git │ └── properties │ ├── data │ ├── BuildHostPropertiesProviderTest.kt │ ├── GitPropertiesProviderChainTest.kt │ ├── GitLogPropertiesProviderTest.kt │ ├── GitConfigPropertiesProviderTest.kt │ └── GitBranchPropertiesProviderTest.kt │ ├── GenerateGitPropertiesTaskTest.kt │ ├── TestGitRepository.kt │ ├── functional │ ├── GeneratesPropertiesTest.kt │ ├── GeneratesPropertiesOnClassesTask.kt │ ├── InvalidatesCachOnNewGitCommit.kt │ ├── InvalidatesCachOnNewGitConfigChange.kt │ ├── BuildFailsOnInvalidGitRepository.kt │ └── GeneratesPropertiesWithBranchNameTest.kt │ └── PropertiesFileWriterTest.kt ├── settings.gradle.kts ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── .github └── workflows │ ├── hndrs-gradle-publish-plugin.yml │ └── hndrs-gradle-check.yml ├── gradlew.bat ├── README.md └── gradlew /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hndrs/gradle-git-properties-plugin/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | includeBuild("../") 3 | } 4 | 5 | enableFeaturePreview("STABLE_CONFIGURATION_CACHE") -------------------------------------------------------------------------------- /example/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hndrs/gradle-git-properties-plugin/HEAD/example/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/kotlin/io/hndrs/gradle/plugin/git/properties/common/StringEscapeExtensions.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties.common 2 | 3 | fun String.escapeLF(): String { 4 | return this.replace("\n", "\\n") 5 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/hndrs/gradle/plugin/git/properties/data/GitPropertyProvider.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties.data 2 | 3 | fun interface GitPropertyProvider { 4 | 5 | fun get(): Map 6 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /example/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import io.hndrs.gradle.plugin.git.properties.GenerateGitPropertiesTask 2 | 3 | plugins { 4 | id("io.hndrs.git-properties") 5 | java 6 | } 7 | 8 | tasks.withType(GenerateGitPropertiesTask::class.java) { 9 | dotGitDirectory.set(File("../.git")) 10 | } -------------------------------------------------------------------------------- /example/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "gradle-git-properties-plugin" 2 | 3 | pluginManagement { 4 | val kotlinVersion = "1.8.21" 5 | plugins { 6 | kotlin("jvm").version(kotlinVersion) 7 | kotlin("kapt").version(kotlinVersion) 8 | id("maven-publish") 9 | id("idea") 10 | } 11 | repositories { 12 | 13 | } 14 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .gradle 3 | **/build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | gradle.properties 6 | 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | **/out/ 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /build/ 27 | /nbbuild/ 28 | /dist/ 29 | /nbdist/ 30 | /.nb-gradle/% 31 | -------------------------------------------------------------------------------- /src/main/kotlin/io/hndrs/gradle/plugin/git/properties/data/BuildHostPropertiesProvider.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties.data 2 | 3 | import java.net.InetAddress 4 | 5 | class BuildHostPropertiesProvider : GitPropertyProvider { 6 | 7 | override fun get(): Map { 8 | return mapOf(GIT_BUILD_HOST to InetAddress.getLocalHost().hostName) 9 | } 10 | 11 | companion object { 12 | private const val GIT_BUILD_HOST = "git.build.host" 13 | } 14 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/hndrs/gradle/plugin/git/properties/data/BuildHostPropertiesProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties.data 2 | 3 | import io.kotest.core.spec.style.StringSpec 4 | import io.kotest.matchers.shouldBe 5 | import java.net.InetAddress 6 | 7 | class BuildHostPropertiesProviderTest : StringSpec({ 8 | 9 | val underTest = BuildHostPropertiesProvider() 10 | 11 | "resolves host provider" { 12 | underTest.get() shouldBe mapOf( 13 | "git.build.host" to InetAddress.getLocalHost().hostName 14 | ) 15 | } 16 | 17 | }) -------------------------------------------------------------------------------- /src/main/kotlin/io/hndrs/gradle/plugin/git/properties/data/GitPropertiesProviderChain.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties.data 2 | 3 | class GitPropertiesProviderChain private constructor( 4 | private val providers: Set 5 | ) : GitPropertyProvider { 6 | override fun get(): Map { 7 | return providers.flatMap { 8 | it.get().entries 9 | }.map { 10 | it.toPair() 11 | }.toMap() 12 | } 13 | 14 | companion object { 15 | fun of(vararg providers: GitPropertyProvider): GitPropertiesProviderChain { 16 | return GitPropertiesProviderChain(providers.toSet()) 17 | } 18 | } 19 | 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/test/kotlin/io/hndrs/gradle/plugin/git/properties/GenerateGitPropertiesTaskTest.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties 2 | 3 | import io.kotest.assertions.assertSoftly 4 | import io.kotest.matchers.shouldNotBe 5 | import org.gradle.kotlin.dsl.withType 6 | import org.gradle.testfixtures.ProjectBuilder 7 | import org.junit.jupiter.api.Test 8 | 9 | class GenerateGitPropertiesPluginTest { 10 | 11 | val project = ProjectBuilder.builder().build().also { 12 | it.plugins.apply(GitPropertiesPlugin::class.java) 13 | } 14 | 15 | @Test 16 | fun `generate properties task is configured`() { 17 | assertSoftly(project.tasks.withType().first()) { 18 | this.dotGitDirectory.asFile.orNull shouldNotBe null 19 | this.output.asFile.orNull shouldNotBe null 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/hndrs/gradle/plugin/git/properties/PropertiesFileWriter.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties 2 | 3 | import io.hndrs.gradle.plugin.git.properties.common.escapeLF 4 | import java.io.File 5 | 6 | class PropertiesFileWriter(private val properties: Map) { 7 | 8 | fun writeTo(file: File) { 9 | val fileContent = with(StringBuilder()) { 10 | properties 11 | .filter { it.value != null } 12 | .map { it.key to it.value.toString().escapeLF() } 13 | .sortedBy { it.first } 14 | .forEach { 15 | if (it.second.isNotBlank()) { 16 | appendLine("${it.first}=${it.second}") 17 | } 18 | } 19 | this.toString() 20 | } 21 | file.writeText(fileContent) 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/hndrs/gradle/plugin/git/properties/GitPropertiesPlugin.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties 2 | 3 | import org.gradle.api.Plugin 4 | import org.gradle.api.Project 5 | import org.gradle.api.plugins.BasePlugin 6 | import org.gradle.api.plugins.JavaPlugin 7 | 8 | class GitPropertiesPlugin : Plugin { 9 | override fun apply(target: Project) { 10 | 11 | val task = target.tasks.register("generateGitProperties", GenerateGitPropertiesTask::class.java) { 12 | group = BasePlugin.BUILD_GROUP 13 | } 14 | /** 15 | * If the java plugin is available we hook the task into the `classes` lifecycle task 16 | */ 17 | target.plugins.withType(JavaPlugin::class.java) { 18 | target.tasks.named(JavaPlugin.CLASSES_TASK_NAME) { 19 | dependsOn(task) 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/hndrs/gradle/plugin/git/properties/TestGitRepository.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties 2 | 3 | import org.eclipse.jgit.api.Git 4 | import org.eclipse.jgit.lib.PersonIdent 5 | import java.io.File 6 | 7 | class TestGitRepository(rootDir: File) { 8 | 9 | private val git = Git.init().setDirectory(rootDir).call() 10 | 11 | fun addCommit() = 12 | git.commit() 13 | .setAllowEmpty(true) 14 | .setAuthor( 15 | PersonIdent( 16 | "name", "email" 17 | ) 18 | ) 19 | .setSign(false) 20 | .setMessage("My Message").call() 21 | 22 | fun changeGitConfigUser(value: String) = git.repository.config 23 | .also { 24 | it.load() 25 | it.setString("user", null, "name", value) 26 | it.save() 27 | } 28 | 29 | fun branch() = git.repository.branch 30 | } 31 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ## [1.0.0] 11 | 12 | ### Added 13 | 14 | - resolution of the following git properties 15 | - `git.commit.id` 16 | - `git.commit.id.abbrev` 17 | - `git.commit.user.name` 18 | - `git.commit.user.email` 19 | - `git.commit.time` 20 | - `git.commit.message.short` 21 | - `git.commit.message.full` 22 | - `git.branch` 23 | - `git.remote.origin.url` 24 | - `git.build.host` 25 | - `git.build.user.name` 26 | - `git.build.user.email` 27 | - gradle `--configuration-cache` support 28 | - allows continuing build with `generateGitProperties --continue-on-error` option 29 | 30 | [unreleased]: https://github.com/hndrs/gradle-git-properties-plugin/compare/v1.0.0...HEAD 31 | 32 | [1.0.0]: https://github.com/hndrs/gradle-git-properties-plugin/compare/7a9a8b9114cea8ce50e2985abe293260b91a5eab...v1.0.0 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 hndrs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/hndrs-gradle-publish-plugin.yml: -------------------------------------------------------------------------------- 1 | name: gradle 2 | 3 | # Controls when the action will run. 4 | on: 5 | push: 6 | tags: 7 | - v* 8 | # Allows you to run this workflow manually from the Actions tab 9 | workflow_dispatch: 10 | 11 | jobs: 12 | publish: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: git checkout 17 | uses: actions/checkout@v3 18 | with: 19 | fetch-depth: 0 20 | 21 | - name: setup java 22 | uses: actions/setup-java@v3 23 | with: 24 | distribution: temurin 25 | java-version: 17 26 | 27 | - name: publish to plugin portal 28 | env: 29 | GRADLE_PLUGIN_PUBLISH_KEY: ${{ secrets.GRADLE_PLUGIN_PUBLISH_KEY }} 30 | GRADLE_PLUGIN_PUBLISH_SECRET: ${{ secrets.GRADLE_PLUGIN_PUBLISH_SECRET }} 31 | ORG_GRADLE_PROJECT_signingKey: ${{ secrets.SIGNING_KEY }} 32 | ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.SIGNING_PASSWORD }} 33 | run: | 34 | ./gradlew publishPlugins -Dgradle.publish.key=$GRADLE_PLUGIN_PUBLISH_KEY -Dgradle.publish.secret=$GRADLE_PLUGIN_PUBLISH_SECRET -------------------------------------------------------------------------------- /src/main/kotlin/io/hndrs/gradle/plugin/git/properties/data/GitConfigPropertiesProvider.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties.data 2 | 3 | import org.eclipse.jgit.api.Git 4 | import java.net.URI 5 | 6 | class GitConfigPropertiesProvider( 7 | private val git: Git 8 | ) : GitPropertyProvider { 9 | 10 | override fun get(): Map { 11 | return git.repository.config.also { 12 | it.load() 13 | }.let { 14 | mapOf( 15 | GIT_BUILD_USER_EMAIL to it.getString("user", null, "email"), 16 | GIT_BUILD_USER_NAME to it.getString("user", null, "name"), 17 | GIT_REMOTE_ORIGIN_URL to it.getString("remote", "origin", "url")?.sanitise() 18 | ) 19 | } 20 | } 21 | 22 | private fun String.sanitise(): String? { 23 | return runCatching { 24 | URI(this).userInfo?.let { 25 | this.replace(it, "").replace("@", "") 26 | } ?: this 27 | }.getOrElse { null } 28 | 29 | } 30 | 31 | companion object { 32 | private const val GIT_REMOTE_ORIGIN_URL = "git.remote.origin.url" 33 | private const val GIT_BUILD_USER_EMAIL = "git.build.user.email" 34 | private const val GIT_BUILD_USER_NAME = "git.build.user.name" 35 | } 36 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/hndrs/gradle/plugin/git/properties/data/GitPropertiesProviderChainTest.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties.data 2 | 3 | import io.kotest.assertions.assertSoftly 4 | import io.kotest.core.spec.style.StringSpec 5 | import io.kotest.matchers.maps.shouldContainAll 6 | 7 | class GitPropertiesProviderChainTest : StringSpec({ 8 | 9 | 10 | "gets all values from all providers" { 11 | GitPropertiesProviderChain.of( 12 | GitPropertyProvider { mapOf("key" to "value") }, 13 | GitPropertyProvider { mapOf("key1" to "value1") }, 14 | GitPropertyProvider { mapOf("key2" to "value2") }, 15 | GitPropertyProvider { mapOf("key3" to 3) }, 16 | ).get() shouldContainAll mapOf( 17 | "key" to "value", 18 | "key1" to "value1", 19 | "key2" to "value2", 20 | "key3" to 3, 21 | ) 22 | } 23 | 24 | "sorts all values from all providers" { 25 | assertSoftly( 26 | GitPropertiesProviderChain.of( 27 | GitPropertyProvider { mapOf("key3" to 3) }, 28 | GitPropertyProvider { mapOf("key2" to "value2") }, 29 | GitPropertyProvider { mapOf("key" to "value") }, 30 | GitPropertyProvider { mapOf("key1" to "value1") }, 31 | ).get().entries 32 | ) { 33 | } 34 | } 35 | }) -------------------------------------------------------------------------------- /src/main/kotlin/io/hndrs/gradle/plugin/git/properties/data/GitBranchPropertiesProvider.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties.data 2 | 3 | import org.eclipse.jgit.api.Git 4 | 5 | class GitBranchPropertiesProvider( 6 | private val git: Git, 7 | private val branchName: String? 8 | ) : GitPropertyProvider { 9 | 10 | override fun get(): Map { 11 | return branchName?.let { 12 | mapOf(GIT_BRANCH to branchName) 13 | } ?: resolveBranchName()?.let { 14 | mapOf(GIT_BRANCH to it) 15 | } ?: mapOf(GIT_BRANCH to git.repository.branch) 16 | } 17 | 18 | private fun resolveBranchName(): String? { 19 | return CI_ENV_BRANCH_MAPPING.entries.firstNotNullOfOrNull { 20 | if (EnvProvider.getEnv(it.key) != null) { 21 | it.value.firstNotNullOfOrNull { EnvProvider.getEnv(it) } 22 | } else null 23 | } 24 | } 25 | 26 | /** 27 | * Wrapper object for mocking 28 | */ 29 | object EnvProvider { 30 | fun getEnv(name: String): String? = System.getenv(name) 31 | } 32 | 33 | companion object { 34 | private const val GIT_BRANCH = "git.branch" 35 | private val CI_ENV_BRANCH_MAPPING = mapOf( 36 | "GITLAB_CI" to arrayOf("CI_COMMIT_REF_NAME"), 37 | "CI" to arrayOf("GITHUB_HEAD_REF", "GITHUB_REF_NAME"), 38 | "TRAVIS" to arrayOf("TRAVIS_BRANCH") 39 | ) 40 | } 41 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/hndrs/gradle/plugin/git/properties/functional/GeneratesPropertiesTest.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties.functional 2 | 3 | import io.hndrs.gradle.plugin.git.properties.TestGitRepository 4 | import io.kotest.core.spec.style.StringSpec 5 | import io.kotest.engine.spec.tempdir 6 | import io.kotest.matchers.shouldBe 7 | import org.gradle.testkit.runner.GradleRunner 8 | import org.gradle.testkit.runner.TaskOutcome 9 | import java.io.File 10 | 11 | class GeneratesPropertiesTest : StringSpec({ 12 | 13 | val testKitDir: File = tempdir() 14 | 15 | val testProjectDir: File = tempdir() 16 | 17 | val buildFile = File(testProjectDir, "build.gradle.kts") 18 | 19 | val gitRepository = TestGitRepository(testProjectDir) 20 | 21 | "apply plugin and generate properties" { 22 | buildFile.writeText( 23 | """ 24 | plugins { 25 | id("io.hndrs.git-properties") 26 | } 27 | """.trimIndent() 28 | ) 29 | 30 | // add first commit 31 | gitRepository.addCommit() 32 | val runner = GradleRunner.create() 33 | .withTestKitDir(testKitDir) 34 | .withProjectDir(testProjectDir) 35 | .withDebug(true) // enabled for test coverage 36 | .withPluginClasspath() 37 | 38 | runner.withArguments("generateGitProperties") 39 | .build().task(":generateGitProperties")?.outcome shouldBe TaskOutcome.SUCCESS 40 | 41 | File(testProjectDir, "build/resources/main/git.properties").exists() shouldBe true 42 | } 43 | 44 | }) -------------------------------------------------------------------------------- /src/main/kotlin/io/hndrs/gradle/plugin/git/properties/data/GitLogPropertiesProvider.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties.data 2 | 3 | import org.eclipse.jgit.api.Git 4 | import java.time.Instant 5 | 6 | class GitLogPropertiesProvider( 7 | private val git: Git 8 | ) : GitPropertyProvider { 9 | 10 | override fun get(): Map { 11 | return git.log().setMaxCount(1).call().firstOrNull() 12 | ?.let { 13 | mapOf( 14 | GIT_COMMIT_ID to it.name, 15 | GIT_COMMIT_SIGNED to (it.rawGpgSignature != null), 16 | GIT_COMMIT_ID_ABBREV to it.name.substring(0, 7), 17 | GIT_COMMIT_MESSAGE_FULL to it.fullMessage.trim(), 18 | GIT_COMMIT_MESSAGE_SHORT to it.shortMessage, 19 | GIT_COMMIT_TIME to Instant.ofEpochSecond(it.commitTime.toLong()), 20 | GIT_COMMIT_USER_EMAIL to it.authorIdent.emailAddress, 21 | GIT_COMMIT_USER_NAME to it.authorIdent.name, 22 | ) 23 | } ?: emptyMap() 24 | } 25 | 26 | companion object { 27 | private const val GIT_COMMIT_ID = "git.commit.id" 28 | private const val GIT_COMMIT_ID_ABBREV = "git.commit.id.abbrev" 29 | private const val GIT_COMMIT_MESSAGE_FULL = "git.commit.message.full" 30 | private const val GIT_COMMIT_MESSAGE_SHORT = "git.commit.message.short" 31 | private const val GIT_COMMIT_TIME = "git.commit.time" 32 | private const val GIT_COMMIT_USER_EMAIL = "git.commit.user.email" 33 | private const val GIT_COMMIT_USER_NAME = "git.commit.user.name" 34 | private const val GIT_COMMIT_SIGNED = "git.commit.signed" 35 | } 36 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/hndrs/gradle/plugin/git/properties/functional/GeneratesPropertiesOnClassesTask.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties.functional 2 | 3 | import io.hndrs.gradle.plugin.git.properties.TestGitRepository 4 | import io.kotest.assertions.assertSoftly 5 | import io.kotest.core.spec.style.StringSpec 6 | import io.kotest.engine.spec.tempdir 7 | import io.kotest.matchers.shouldBe 8 | import org.gradle.testkit.runner.GradleRunner 9 | import org.gradle.testkit.runner.TaskOutcome 10 | import java.io.File 11 | 12 | class GeneratesPropertiesOnClassesTask : StringSpec({ 13 | 14 | val testKitDir: File = tempdir() 15 | 16 | val testProjectDir: File = tempdir() 17 | 18 | val buildFile = File(testProjectDir, "build.gradle.kts") 19 | 20 | val gitRepository = TestGitRepository(testProjectDir) 21 | 22 | "generateGitProperties are generated on `classes` lifecycle task when java plugin is available" { 23 | buildFile.writeText( 24 | """ 25 | plugins { 26 | java 27 | id("io.hndrs.git-properties") 28 | } 29 | """.trimIndent() 30 | ) 31 | 32 | // add first commit 33 | gitRepository.addCommit() 34 | val runner = GradleRunner.create() 35 | .withTestKitDir(testKitDir) 36 | .withProjectDir(testProjectDir) 37 | .withDebug(true) // enabled for test coverage 38 | .withPluginClasspath() 39 | 40 | runner.withArguments("classes") 41 | .build().task(":generateGitProperties")?.outcome shouldBe TaskOutcome.SUCCESS 42 | 43 | assertSoftly(File(testProjectDir, "build/resources/main/git.properties")) { 44 | exists() shouldBe true 45 | } 46 | 47 | } 48 | 49 | }) -------------------------------------------------------------------------------- /src/test/kotlin/io/hndrs/gradle/plugin/git/properties/functional/InvalidatesCachOnNewGitCommit.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties.functional 2 | 3 | import io.hndrs.gradle.plugin.git.properties.TestGitRepository 4 | import io.kotest.core.spec.style.StringSpec 5 | import io.kotest.engine.spec.tempdir 6 | import io.kotest.matchers.shouldBe 7 | import org.gradle.testkit.runner.GradleRunner 8 | import org.gradle.testkit.runner.TaskOutcome 9 | import java.io.File 10 | 11 | class InvalidatesCachOnNewGitCommit : StringSpec({ 12 | 13 | val testKitDir: File = tempdir() 14 | 15 | val testProjectDir: File = tempdir() 16 | 17 | val buildFile = File(testProjectDir, "build.gradle.kts") 18 | 19 | val gitRepository = TestGitRepository(testProjectDir) 20 | 21 | 22 | "cache is invalidated on new commit" { 23 | buildFile.writeText( 24 | """ 25 | plugins { 26 | id("io.hndrs.git-properties") 27 | } 28 | """.trimIndent() 29 | ) 30 | 31 | // add first commit 32 | gitRepository.addCommit() 33 | val runner = GradleRunner.create() 34 | .withTestKitDir(testKitDir) 35 | .withProjectDir(testProjectDir) 36 | .withDebug(true) // enabled for test coverage 37 | .withPluginClasspath() 38 | 39 | runner.withArguments("--build-cache", "generateGitProperties") 40 | .build().task(":generateGitProperties")?.outcome shouldBe TaskOutcome.SUCCESS 41 | 42 | File(testProjectDir, "build").deleteRecursively() 43 | 44 | runner.withArguments("--build-cache", "generateGitProperties") 45 | .build().task(":generateGitProperties")?.outcome shouldBe TaskOutcome.FROM_CACHE 46 | 47 | //add a new commit to invalidate cache 48 | gitRepository.addCommit() 49 | 50 | runner.withArguments("--build-cache", "generateGitProperties") 51 | .build().task(":generateGitProperties")?.outcome shouldBe TaskOutcome.SUCCESS 52 | 53 | File(testProjectDir, "build/resources/main/git.properties").exists() shouldBe true 54 | } 55 | 56 | }) -------------------------------------------------------------------------------- /src/test/kotlin/io/hndrs/gradle/plugin/git/properties/data/GitLogPropertiesProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties.data 2 | 3 | import io.kotest.core.spec.style.StringSpec 4 | import io.kotest.matchers.shouldBe 5 | import io.mockk.every 6 | import io.mockk.mockk 7 | import org.eclipse.jgit.api.Git 8 | import org.eclipse.jgit.lib.PersonIdent 9 | import org.eclipse.jgit.revwalk.RevCommit 10 | import java.time.Instant 11 | import java.time.temporal.ChronoUnit 12 | 13 | class GitLogPropertiesProviderTest : StringSpec({ 14 | 15 | val testSHA = "beb5061f94f29901609ab3f5ab427ea18a8b85871fca519a9c0713ad99a53abd" 16 | val testCommitTime = Instant.now().truncatedTo(ChronoUnit.SECONDS) 17 | val testAuthor = PersonIdent("John Smith", "john.smith@gradlemail.com") 18 | val testFullMessage = """ 19 | Full Message 20 | 21 | Another Nice Message 22 | """.trimIndent() 23 | 24 | val git = mockk() { 25 | every { log().setMaxCount(1).call() } returns listOf( 26 | mockk { 27 | every { name } returns testSHA 28 | every { rawGpgSignature } returns ByteArray(0) 29 | every { fullMessage } returns testFullMessage 30 | every { shortMessage } returns testFullMessage.lines().first() 31 | every { commitTime } returns testCommitTime.epochSecond.toInt() 32 | every { authorIdent } returns testAuthor 33 | } 34 | ) 35 | } 36 | 37 | 38 | val underTest = GitLogPropertiesProvider(git) 39 | 40 | "resolves git branch via git" { 41 | underTest.get() shouldBe mapOf( 42 | "git.commit.id" to testSHA, 43 | "git.commit.id.abbrev" to testSHA.substring(0, 7), 44 | "git.commit.message.full" to testFullMessage, 45 | "git.commit.message.short" to testFullMessage.lines().first(), 46 | "git.commit.time" to testCommitTime, 47 | "git.commit.user.email" to testAuthor.emailAddress, 48 | "git.commit.user.name" to testAuthor.name, 49 | "git.commit.signed" to true, 50 | ) 51 | } 52 | 53 | }) -------------------------------------------------------------------------------- /.github/workflows/hndrs-gradle-check.yml: -------------------------------------------------------------------------------- 1 | name: Gradle 2 | 3 | # Controls when the action will run. 4 | on: 5 | push: 6 | branches: 7 | - main 8 | pull_request: 9 | branches: [ main ] 10 | # Allows you to run this workflow manually from the Actions tab 11 | workflow_dispatch: 12 | 13 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 14 | jobs: 15 | check: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Git Checkout 20 | uses: actions/checkout@v3 21 | with: 22 | fetch-depth: 0 23 | 24 | - name: Setup Java 25 | uses: actions/setup-java@v3 26 | with: 27 | distribution: temurin 28 | java-version: 17 29 | 30 | - name: Setup Build Cache 31 | uses: actions/cache@v3 32 | with: 33 | path: | 34 | ~/.gradle/caches 35 | ~/.gradle/wrapper 36 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} 37 | restore-keys: | 38 | ${{ runner.os }}-gradle- 39 | - name: Check 40 | env: 41 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any 42 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 43 | run: | 44 | ./gradlew check jacocoTestReport sonar 45 | integration: 46 | runs-on: ubuntu-latest 47 | 48 | steps: 49 | - name: Git Checkout 50 | uses: actions/checkout@v3 51 | with: 52 | fetch-depth: 0 53 | 54 | - name: Setup Java 55 | uses: actions/setup-java@v3 56 | with: 57 | distribution: temurin 58 | java-version: 17 59 | 60 | - name: Setup Build Cache 61 | uses: actions/cache@v3 62 | with: 63 | path: | 64 | ~/.gradle/caches 65 | ~/.gradle/wrapper 66 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} 67 | restore-keys: | 68 | ${{ runner.os }}-gradle- 69 | - name: Check 70 | env: 71 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any 72 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 73 | run: | 74 | cd example 75 | ./gradlew --build-cache --configuration-cache generateGitProperties 76 | -------------------------------------------------------------------------------- /src/test/kotlin/io/hndrs/gradle/plugin/git/properties/functional/InvalidatesCachOnNewGitConfigChange.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties.functional 2 | 3 | import io.hndrs.gradle.plugin.git.properties.TestGitRepository 4 | import io.kotest.assertions.assertSoftly 5 | import io.kotest.core.spec.style.StringSpec 6 | import io.kotest.engine.spec.tempdir 7 | import io.kotest.matchers.shouldBe 8 | import io.kotest.matchers.string.shouldContain 9 | import org.gradle.testkit.runner.GradleRunner 10 | import org.gradle.testkit.runner.TaskOutcome 11 | import java.io.File 12 | 13 | class InvalidatesCachOnNewGitConfigChange : StringSpec({ 14 | 15 | val testKitDir: File = tempdir() 16 | 17 | val testProjectDir: File = tempdir() 18 | 19 | val buildFile = File(testProjectDir, "build.gradle.kts") 20 | 21 | val gitRepository = TestGitRepository(testProjectDir) 22 | 23 | "cache is invalidated on config change" { 24 | buildFile.writeText( 25 | """ 26 | plugins { 27 | id("io.hndrs.git-properties") 28 | } 29 | """.trimIndent() 30 | ) 31 | 32 | // add first commit 33 | gitRepository.addCommit() 34 | val runner = GradleRunner.create() 35 | .withTestKitDir(testKitDir) 36 | .withProjectDir(testProjectDir) 37 | .withDebug(true) // enabled for test coverage 38 | .withPluginClasspath() 39 | 40 | runner.withArguments("--build-cache", "generateGitProperties") 41 | .build().task(":generateGitProperties")?.outcome shouldBe TaskOutcome.SUCCESS 42 | 43 | File(testProjectDir, "build").deleteRecursively() 44 | 45 | runner.withArguments("--build-cache", "generateGitProperties") 46 | .build().task(":generateGitProperties")?.outcome shouldBe TaskOutcome.FROM_CACHE 47 | 48 | //add a new commit to invalidate cache 49 | gitRepository.changeGitConfigUser("different-user-name") 50 | 51 | runner.withArguments("--build-cache", "generateGitProperties") 52 | .build().task(":generateGitProperties")?.outcome shouldBe TaskOutcome.SUCCESS 53 | 54 | assertSoftly(File(testProjectDir, "build/resources/main/git.properties")) { 55 | exists() shouldBe true 56 | readText() shouldContain "git.build.user.name=different-user-name" 57 | } 58 | 59 | } 60 | 61 | }) -------------------------------------------------------------------------------- /src/test/kotlin/io/hndrs/gradle/plugin/git/properties/functional/BuildFailsOnInvalidGitRepository.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties.functional 2 | 3 | import io.hndrs.gradle.plugin.git.properties.TestGitRepository 4 | import io.kotest.core.spec.style.StringSpec 5 | import io.kotest.engine.spec.tempdir 6 | import io.kotest.matchers.string.shouldContain 7 | import org.gradle.testkit.runner.GradleRunner 8 | import java.io.File 9 | 10 | 11 | class BuildFailsOnInvalidGitRepository : StringSpec({ 12 | 13 | var testKitDir: File = tempdir() 14 | 15 | var testProjectDir: File = tempdir() 16 | 17 | var buildFile = File(testProjectDir, "build.gradle.kts") 18 | 19 | 20 | 21 | beforeAny { 22 | testKitDir = tempdir() 23 | testProjectDir = tempdir() 24 | buildFile = File(testProjectDir, "build.gradle.kts") 25 | } 26 | 27 | "build fails when git repository is not in valid state" { 28 | TestGitRepository(testProjectDir) // creates .git folder 29 | buildFile.writeText( 30 | """ 31 | plugins { 32 | id("io.hndrs.git-properties") 33 | } 34 | """.trimIndent() 35 | ) 36 | 37 | val runner = GradleRunner.create() 38 | .withTestKitDir(testKitDir) 39 | .withProjectDir(testProjectDir) 40 | .withDebug(true) // enabled for test coverage 41 | .withPluginClasspath() 42 | 43 | val buildResult = runner 44 | .withArguments("generateGitProperties") 45 | .buildAndFail() 46 | 47 | buildResult.output shouldContain "Execution failed for task ':generateGitProperties'." 48 | } 49 | 50 | "build does not fail when git repository is not in valid state with --continue-on-failure" { 51 | TestGitRepository(testProjectDir) // creates .git folder 52 | buildFile.writeText( 53 | """ 54 | plugins { 55 | id("io.hndrs.git-properties") 56 | } 57 | """.trimIndent() 58 | ) 59 | 60 | val runner = GradleRunner.create() 61 | .withTestKitDir(testKitDir) 62 | .withProjectDir(testProjectDir) 63 | .withDebug(true) // enabled for test coverage 64 | .withPluginClasspath() 65 | 66 | val buildResult = runner 67 | .withArguments("generateGitProperties", "--continue-on-error") 68 | .build() 69 | 70 | buildResult.output shouldContain "Execution failed for task ':generateGitProperties' but continuing build with --continue-on-error" 71 | } 72 | }) -------------------------------------------------------------------------------- /src/test/kotlin/io/hndrs/gradle/plugin/git/properties/PropertiesFileWriterTest.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties 2 | 3 | import io.kotest.core.spec.style.StringSpec 4 | import io.kotest.matchers.shouldBe 5 | import java.io.File 6 | 7 | class PropertiesFileWriterTest : StringSpec({ 8 | 9 | "writes properties to file" { 10 | val tempFile = File.createTempFile("git", "properties") 11 | 12 | PropertiesFileWriter( 13 | mapOf( 14 | "key1" to "value1", 15 | "key2" to "value2", 16 | "key3" to "value3", 17 | "key4" to "value4" 18 | ) 19 | ).writeTo(tempFile) 20 | 21 | tempFile.readText() shouldBe """ 22 | key1=value1 23 | key2=value2 24 | key3=value3 25 | key4=value4 26 | 27 | """.trimIndent() 28 | 29 | tempFile.delete() 30 | } 31 | 32 | "escapes multi line values" { 33 | val tempFile = File.createTempFile("git", "properties") 34 | 35 | PropertiesFileWriter( 36 | mapOf( 37 | "key1" to """ 38 | MultiLine 39 | Value 40 | """.trimIndent(), 41 | "key2" to "value2", 42 | ) 43 | ).writeTo(tempFile) 44 | 45 | tempFile.readText() shouldBe """ 46 | key1=MultiLine\nValue 47 | key2=value2 48 | 49 | """.trimIndent() 50 | 51 | tempFile.delete() 52 | } 53 | 54 | "sorts properties by key (asc)" { 55 | val tempFile = File.createTempFile("git", "properties") 56 | 57 | PropertiesFileWriter( 58 | mapOf( 59 | "key.a" to "valueA", 60 | "key.c" to "valueC", 61 | "key.f" to "valueF", 62 | "key.b" to "valueB", 63 | ) 64 | ).writeTo(tempFile) 65 | 66 | tempFile.readText() shouldBe """ 67 | key.a=valueA 68 | key.b=valueB 69 | key.c=valueC 70 | key.f=valueF 71 | 72 | """.trimIndent() 73 | 74 | tempFile.delete() 75 | } 76 | }) -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /example/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /src/test/kotlin/io/hndrs/gradle/plugin/git/properties/data/GitConfigPropertiesProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties.data 2 | 3 | import io.kotest.core.spec.style.StringSpec 4 | import io.kotest.matchers.shouldBe 5 | import io.mockk.every 6 | import io.mockk.mockk 7 | import org.eclipse.jgit.api.Git 8 | import org.eclipse.jgit.lib.StoredConfig 9 | 10 | class GitConfigPropertiesProviderTest : StringSpec({ 11 | 12 | val storedConfig = mockk() { 13 | every { load() } returns Unit 14 | } 15 | val git = mockk() { 16 | every { repository.config } returns storedConfig 17 | } 18 | 19 | val underTest = GitConfigPropertiesProvider(git) 20 | 21 | "resolves git config properties (remote origin with credentials)" { 22 | 23 | every { storedConfig.getString("user", null, "email") } returns "john.smith@gradlemail.com" 24 | every { storedConfig.getString("user", null, "name") } returns "John Smith" 25 | every { storedConfig.getString("remote", "origin", "url") } returns "https://name:password@github.com/hndrs/gradle-git-properties-plugin.git" 26 | 27 | underTest.get() shouldBe mapOf( 28 | "git.build.user.email" to "john.smith@gradlemail.com", 29 | "git.build.user.name" to "John Smith", 30 | "git.remote.origin.url" to "https://github.com/hndrs/gradle-git-properties-plugin.git" 31 | ) 32 | } 33 | 34 | "resolves git config properties (remote with git/ssh )" { 35 | 36 | every { storedConfig.getString("user", null, "email") } returns "john.smith@gradlemail.com" 37 | every { storedConfig.getString("user", null, "name") } returns "John Smith" 38 | every { storedConfig.getString("remote", "origin", "url") } returns "git@github.com/hndrs/gradle-git-properties-plugin.git" 39 | 40 | underTest.get() shouldBe mapOf( 41 | "git.build.user.email" to "john.smith@gradlemail.com", 42 | "git.build.user.name" to "John Smith", 43 | "git.remote.origin.url" to "git@github.com/hndrs/gradle-git-properties-plugin.git" 44 | ) 45 | } 46 | 47 | "resolves git config properties (with unrecognized data)" { 48 | 49 | every { storedConfig.getString("user", null, "email") } returns "john.smith@gradlemail.com" 50 | every { storedConfig.getString("user", null, "name") } returns "John Smith" 51 | every { storedConfig.getString("remote", "origin", "url") } returns "someUnrelatedString" 52 | 53 | underTest.get() shouldBe mapOf( 54 | "git.build.user.email" to "john.smith@gradlemail.com", 55 | "git.build.user.name" to "John Smith", 56 | "git.remote.origin.url" to "someUnrelatedString" 57 | ) 58 | } 59 | 60 | "resolves git config properties (with exception on parsing data)" { 61 | 62 | every { storedConfig.getString("user", null, "email") } returns "john.smith@gradlemail.com" 63 | every { storedConfig.getString("user", null, "name") } returns "John Smith" 64 | every { storedConfig.getString("remote", "origin", "url") } returns "https>" 65 | 66 | underTest.get() shouldBe mapOf( 67 | "git.build.user.email" to "john.smith@gradlemail.com", 68 | "git.build.user.name" to "John Smith", 69 | "git.remote.origin.url" to null 70 | ) 71 | } 72 | }) -------------------------------------------------------------------------------- /src/test/kotlin/io/hndrs/gradle/plugin/git/properties/data/GitBranchPropertiesProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties.data 2 | 3 | import io.hndrs.gradle.plugin.git.properties.data.GitBranchPropertiesProvider.EnvProvider 4 | import io.kotest.core.spec.style.StringSpec 5 | import io.kotest.matchers.shouldBe 6 | import io.mockk.every 7 | import io.mockk.mockk 8 | import io.mockk.mockkObject 9 | import org.eclipse.jgit.api.Git 10 | 11 | class GitBranchPropertiesProviderTest : StringSpec({ 12 | 13 | val git = mockk() { 14 | every { repository.branch } returns "git-main" 15 | } 16 | 17 | "resolves git branch via branch-name-option" { 18 | val underTest = GitBranchPropertiesProvider(git, "main") 19 | underTest.get() shouldBe mapOf( 20 | "git.branch" to "main" 21 | ) 22 | } 23 | 24 | "resolves git branch via git" { 25 | val underTest = GitBranchPropertiesProvider(git, null) 26 | mockkObject(EnvProvider) 27 | // to run tests on CI we need to make sure all return null 28 | every { EnvProvider.getEnv(any()) } returns null 29 | underTest.get() shouldBe mapOf( 30 | "git.branch" to "git-main" 31 | ) 32 | } 33 | 34 | "resolves git branch for github actions via env GITHUB_HEAD_REF" { 35 | val underTest = GitBranchPropertiesProvider(git, null) 36 | mockkObject(EnvProvider) 37 | 38 | every { EnvProvider.getEnv("TRAVIS") } returns null 39 | every { EnvProvider.getEnv("GITLAB_CI") } returns null 40 | every { EnvProvider.getEnv("CI") } returns "true" 41 | every { EnvProvider.getEnv("GITHUB_HEAD_REF") } returns "head-ref-main" 42 | 43 | underTest.get() shouldBe mapOf( 44 | "git.branch" to "head-ref-main" 45 | ) 46 | } 47 | 48 | "resolves git branch for github actions via env GITHUB_REF_NAME" { 49 | val underTest = GitBranchPropertiesProvider(git, null) 50 | mockkObject(EnvProvider) 51 | 52 | every { EnvProvider.getEnv("TRAVIS") } returns null 53 | every { EnvProvider.getEnv("GITLAB_CI") } returns null 54 | every { EnvProvider.getEnv("CI") } returns "true" 55 | every { EnvProvider.getEnv("GITHUB_HEAD_REF") } returns null 56 | every { EnvProvider.getEnv("GITHUB_REF_NAME") } returns "ref-main" 57 | 58 | underTest.get() shouldBe mapOf( 59 | "git.branch" to "ref-main" 60 | ) 61 | } 62 | 63 | "resolves git branch for gitlab-ci via env CI_COMMIT_REF_NAME" { 64 | val underTest = GitBranchPropertiesProvider(git, null) 65 | mockkObject(EnvProvider) 66 | every { EnvProvider.getEnv("TRAVIS") } returns null 67 | every { EnvProvider.getEnv("CI") } returns null 68 | every { EnvProvider.getEnv("GITLAB_CI") } returns "true" 69 | every { EnvProvider.getEnv("CI_COMMIT_REF_NAME") } returns "gitlab-main" 70 | 71 | underTest.get() shouldBe mapOf( 72 | "git.branch" to "gitlab-main" 73 | ) 74 | } 75 | 76 | "resolves git branch for travis-ci via env TRAVIS_BRANCH" { 77 | val underTest = GitBranchPropertiesProvider(git, null) 78 | mockkObject(EnvProvider) 79 | every { EnvProvider.getEnv("GITLAB_CI") } returns null 80 | every { EnvProvider.getEnv("CI") } returns null 81 | every { EnvProvider.getEnv("TRAVIS") } returns "true" 82 | every { EnvProvider.getEnv("TRAVIS_BRANCH") } returns "travis-main" 83 | 84 | underTest.get() shouldBe mapOf( 85 | "git.branch" to "travis-main" 86 | ) 87 | } 88 | 89 | }) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Maven metadata URL](https://img.shields.io/maven-metadata/v?color=green&label=GRADLE%20PLUGIN&metadataUrl=https%3A%2F%2Fplugins.gradle.org%2Fm2%2Fio%2Fhndrs%2Fgit-properties%2Fio.hndrs.git-properties.gradle.plugin%2Fmaven-metadata.xml&style=for-the-badge)](https://plugins.gradle.org/plugin/io.hndrs.git-properties) 2 | [![Coverage](https://img.shields.io/sonar/coverage/hndrs_gradle-git-properties-plugin?server=https%3A%2F%2Fsonarcloud.io&style=for-the-badge)](https://sonarcloud.io/dashboard?id=hndrs_gradle-git-properties-plugin) 3 | [![Supported Java Version](https://img.shields.io/badge/Supported%20Java%20Version-17%2B-informational?style=for-the-badge)]() 4 | [![Sponsor](https://img.shields.io/static/v1?logo=GitHub&label=Sponsor&message=%E2%9D%A4&color=ff69b4&style=for-the-badge)](https://github.com/sponsors/marvinschramm) 5 | 6 | # Gradle Git Properties 7 | 8 | This is a simple gradle plugin that can generate git properties via a `generateGitProperties` task. 9 | If you ask yourself "Why? there is already plugins out there that do this.". You are right! 10 | 11 | This plugin was build specifically with the purpose of supporting 12 | gradles [configuration-cache](https://docs.gradle.org/8.1.1/userguide/configuration_cache.html) feature. 13 | If you don`t need this functionality, you might choose a more mature plugin. 14 | 15 | ### Using the Plugin 16 | 17 | Add the following dependency to your plugin block 18 | 19 | ```kotlin 20 | plugins { 21 | id("io.hndrs.git-properties") version "1.0.0" 22 | } 23 | ``` 24 | 25 | ### GenerateProperties Task 26 | 27 | The Plugin registers a ```generateGitProperties``` task to the project. 28 | If the project includes the Gradle-Java-Plugin the `generateGitProperties` task will be attached to the 29 | `classes` lifecycle task if the java plugin is present 30 | 31 | ### Default Configuration 32 | 33 | When the task is not configured 34 | 35 | - it is assumed that a `.git` folder is present in the root of the project. 36 | - the build will fail when an error occurs during the properties generation unless the `--continue-on-error` option is 37 | provided 38 | - the output file is `resources/main/git.properties` 39 | 40 | The task can be configured the following way if the defaults are not applicable: 41 | 42 | ```kotlin 43 | import io.hndrs.gradle.plugin.git.properties.GenerateGitPropertiesTask 44 | 45 | tasks.withType(GenerateGitPropertiesTask::class.java) { 46 | dotGitDirectory.set(File(".git")) 47 | continueOnError.set(false) 48 | output.set(File("resources/main/git.properties")) 49 | } 50 | ``` 51 | 52 | > File (build.gradle.kts) 53 | 54 | ### Behaviour on CI environments 55 | 56 | As you may know, CI environments handle the git repository differently and probably only checkout detached HEADs 57 | This plugin handles the branch resolution for 58 | 59 | - Github Actions 60 | - Gitlab CI 61 | - Travis CI 62 | 63 | If you are using a different CI service you can provide the branch name via the `branch-name` option 64 | 65 | ```bash 66 | ./gradlew generateGitProperties --branch-name=$CI_SPECIFIC_ENV 67 | ``` 68 | 69 | #### Available Properties 70 | 71 | ```properties 72 | git.branch=main 73 | git.build.host=localHost 74 | git.build.user.email=john.smith@gradlemail.com 75 | git.build.user.name=John Smith 76 | git.commit.id=e2f8a7bb72036e7a7a03ba243ca0414914cdfa82 77 | git.commit.id.abbrev=e2f8a7b 78 | git.commit.message.full=Merge pull request #1 from hndrs/prepare-initial-release\n\nPreparation of first release. Covers important cache test cases and cleans up code 79 | git.commit.message.short=Merge pull request #1 from hndrs/prepare-initial-release 80 | git.commit.signed=true 81 | git.commit.time=2023-05-24T21:42:38Z 82 | git.commit.user.email=john.smith@gradlemail.com 83 | git.commit.user.name=John Smith 84 | git.remote.origin.url=git@github.com:hndrs/gradle-git-properties-plugin.git 85 | ``` -------------------------------------------------------------------------------- /src/main/kotlin/io/hndrs/gradle/plugin/git/properties/GenerateGitPropertiesTask.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties 2 | 3 | import io.hndrs.gradle.plugin.git.properties.data.BuildHostPropertiesProvider 4 | import io.hndrs.gradle.plugin.git.properties.data.GitBranchPropertiesProvider 5 | import io.hndrs.gradle.plugin.git.properties.data.GitConfigPropertiesProvider 6 | import io.hndrs.gradle.plugin.git.properties.data.GitLogPropertiesProvider 7 | import io.hndrs.gradle.plugin.git.properties.data.GitPropertiesProviderChain 8 | import org.eclipse.jgit.api.Git 9 | import org.gradle.api.DefaultTask 10 | import org.gradle.api.file.DirectoryProperty 11 | import org.gradle.api.file.RegularFileProperty 12 | import org.gradle.api.model.ObjectFactory 13 | import org.gradle.api.provider.Property 14 | import org.gradle.api.tasks.CacheableTask 15 | import org.gradle.api.tasks.Input 16 | import org.gradle.api.tasks.InputDirectory 17 | import org.gradle.api.tasks.Optional 18 | import org.gradle.api.tasks.OutputFile 19 | import org.gradle.api.tasks.PathSensitive 20 | import org.gradle.api.tasks.PathSensitivity 21 | import org.gradle.api.tasks.TaskAction 22 | import org.gradle.api.tasks.TaskExecutionException 23 | import org.gradle.api.tasks.options.Option 24 | import org.gradle.kotlin.dsl.property 25 | import java.io.File 26 | import javax.inject.Inject 27 | 28 | 29 | @CacheableTask 30 | abstract class GenerateGitPropertiesTask @Inject constructor( 31 | private val objectFactory: ObjectFactory, 32 | ) : DefaultTask() { 33 | 34 | /** 35 | * Input directory (defaults to .git) this can be changed if the .git folder is 36 | * somewhere else in the tree, but its mainly here to make this task cacheable 37 | */ 38 | @get:InputDirectory 39 | @PathSensitive(PathSensitivity.RELATIVE) 40 | val dotGitDirectory: DirectoryProperty = objectFactory.directoryProperty().apply { 41 | set(File(DOT_GIT_DIRECTORY_PATH)) 42 | } 43 | 44 | @Option(option = "continue-on-error", description = "Continues build on failure ") 45 | @get:Input 46 | val continueOnError: Property = objectFactory.property(Boolean::class.java) 47 | .convention(false) 48 | 49 | @get:Input 50 | @get:Optional 51 | @Option(option = "branch-name", description = "Provide branch name via cli") 52 | val branchName: Property = objectFactory.property(String::class) 53 | 54 | /** 55 | * Output file of this given task defaults to `/build/main/resources/git.properties` 56 | */ 57 | @get:OutputFile 58 | val output: RegularFileProperty = gitPropertiesFileProperty() 59 | 60 | @TaskAction 61 | fun generateGitProperties() { 62 | runCatching { 63 | val git = Git.open(dotGitDirectory.asFile.get()) 64 | val properties = GitPropertiesProviderChain.of( 65 | GitBranchPropertiesProvider(git, branchName.orNull), 66 | GitConfigPropertiesProvider(git), 67 | GitLogPropertiesProvider(git), 68 | BuildHostPropertiesProvider(), 69 | ).get().also { 70 | git.close() 71 | } 72 | 73 | PropertiesFileWriter(properties).writeTo(output.asFile.get()) 74 | 75 | }.onFailure { 76 | if (continueOnError.get()) { 77 | logger.error( 78 | """ 79 | Execution failed for task ':generateGitProperties' but continuing build with --continue-on-error 80 | > ${it.message} 81 | """.trimIndent() 82 | ) 83 | } else { 84 | throw TaskExecutionException(this, it) 85 | } 86 | 87 | } 88 | } 89 | 90 | private fun gitPropertiesFileProperty(): RegularFileProperty { 91 | return objectFactory.fileProperty().apply { 92 | set(File("build$BUILD_RESOURCES_PATH")) 93 | } 94 | } 95 | 96 | companion object { 97 | private const val BUILD_RESOURCES_PATH = "/resources/main/git.properties" 98 | private const val DOT_GIT_DIRECTORY_PATH = ".git" 99 | } 100 | } 101 | 102 | -------------------------------------------------------------------------------- /src/test/kotlin/io/hndrs/gradle/plugin/git/properties/functional/GeneratesPropertiesWithBranchNameTest.kt: -------------------------------------------------------------------------------- 1 | package io.hndrs.gradle.plugin.git.properties.functional 2 | 3 | import io.hndrs.gradle.plugin.git.properties.TestGitRepository 4 | import io.kotest.assertions.assertSoftly 5 | import io.kotest.core.spec.style.StringSpec 6 | import io.kotest.engine.spec.tempdir 7 | import io.kotest.matchers.shouldBe 8 | import io.kotest.matchers.string.shouldContain 9 | import org.gradle.testkit.runner.GradleRunner 10 | import org.gradle.testkit.runner.TaskOutcome 11 | import java.io.File 12 | 13 | class GeneratesPropertiesWithBranchNameTest : StringSpec({ 14 | 15 | var testKitDir: File = tempdir() 16 | 17 | var testProjectDir: File = tempdir() 18 | 19 | var buildFile = File(testProjectDir, "build.gradle.kts") 20 | 21 | var gitRepository = TestGitRepository(testProjectDir) 22 | 23 | beforeAny { 24 | testKitDir = tempdir() 25 | testProjectDir = tempdir() 26 | buildFile = File(testProjectDir, "build.gradle.kts") 27 | gitRepository = TestGitRepository(testProjectDir) 28 | } 29 | 30 | "generate properties with branch-name option" { 31 | buildFile.writeText( 32 | """ 33 | plugins { 34 | id("io.hndrs.git-properties") 35 | } 36 | """.trimIndent() 37 | ) 38 | 39 | // add first commit 40 | gitRepository.addCommit() 41 | val runner = GradleRunner.create() 42 | 43 | .withTestKitDir(testKitDir) 44 | .withProjectDir(testProjectDir) 45 | .withDebug(true) // enabled for test coverage 46 | .withPluginClasspath() 47 | 48 | runner.withArguments("generateGitProperties", "--branch-name=test-branch") 49 | .build().task(":generateGitProperties")?.outcome shouldBe TaskOutcome.SUCCESS 50 | 51 | assertSoftly(File(testProjectDir, "build/resources/main/git.properties")) { 52 | exists() shouldBe true 53 | readText() shouldContain "git.branch=test-branch" 54 | } 55 | } 56 | 57 | // disabled because it cant run on ci 58 | "generate properties with git".config(enabled = false) { 59 | buildFile.writeText( 60 | """ 61 | plugins { 62 | id("io.hndrs.git-properties") 63 | } 64 | """.trimIndent() 65 | ) 66 | 67 | // add first commit 68 | gitRepository.addCommit() 69 | 70 | val runner = GradleRunner.create() 71 | .withTestKitDir(testKitDir) 72 | .withProjectDir(testProjectDir) 73 | .withEnvironment( 74 | mapOf( 75 | "TRAVIS" to null, 76 | "CI" to null, 77 | "GITLAB_CI" to null 78 | ) 79 | ) 80 | .withPluginClasspath() 81 | 82 | runner.withArguments("generateGitProperties") 83 | .build().task(":generateGitProperties")?.outcome shouldBe TaskOutcome.SUCCESS 84 | 85 | assertSoftly(File(testProjectDir, "build/resources/main/git.properties")) { 86 | exists() shouldBe true 87 | readText() shouldContain "git.branch=${gitRepository.branch()}" 88 | } 89 | } 90 | 91 | "generate properties with gitlab-ci envs" { 92 | buildFile.writeText( 93 | """ 94 | plugins { 95 | id("io.hndrs.git-properties") 96 | } 97 | """.trimIndent() 98 | ) 99 | 100 | // add first commit 101 | gitRepository.addCommit() 102 | val runner = GradleRunner.create() 103 | .withTestKitDir(testKitDir) 104 | .withProjectDir(testProjectDir) 105 | .withEnvironment( 106 | mapOf( 107 | "GITLAB_CI" to "true", 108 | "CI_COMMIT_REF_NAME" to "gitlab-ci-branch" 109 | ) 110 | ) 111 | .withPluginClasspath() 112 | 113 | runner.withArguments("generateGitProperties") 114 | .build().task(":generateGitProperties")?.outcome shouldBe TaskOutcome.SUCCESS 115 | 116 | assertSoftly(File(testProjectDir, "build/resources/main/git.properties")) { 117 | exists() shouldBe true 118 | readText() shouldContain "git.branch=gitlab-ci-branch" 119 | } 120 | } 121 | 122 | "generate properties with travis-ci envs" { 123 | buildFile.writeText( 124 | """ 125 | plugins { 126 | id("io.hndrs.git-properties") 127 | } 128 | """.trimIndent() 129 | ) 130 | 131 | // add first commit 132 | gitRepository.addCommit() 133 | val runner = GradleRunner.create() 134 | .withTestKitDir(testKitDir) 135 | .withProjectDir(testProjectDir) 136 | .withEnvironment( 137 | mapOf( 138 | "TRAVIS" to "true", 139 | "TRAVIS_BRANCH" to "travis-branch" 140 | ) 141 | ) 142 | .withPluginClasspath() 143 | 144 | runner.withArguments("generateGitProperties") 145 | .build().task(":generateGitProperties")?.outcome shouldBe TaskOutcome.SUCCESS 146 | 147 | assertSoftly(File(testProjectDir, "build/resources/main/git.properties")) { 148 | exists() shouldBe true 149 | readText() shouldContain "git.branch=travis-branch" 150 | } 151 | } 152 | 153 | "generate properties with github-actions (GITHUB_HEAD_REF) env" { 154 | buildFile.writeText( 155 | """ 156 | plugins { 157 | id("io.hndrs.git-properties") 158 | } 159 | """.trimIndent() 160 | ) 161 | 162 | // add first commit 163 | gitRepository.addCommit() 164 | val runner = GradleRunner.create() 165 | .withTestKitDir(testKitDir) 166 | .withProjectDir(testProjectDir) 167 | .withEnvironment( 168 | mapOf( 169 | "CI" to "true", 170 | "GITHUB_HEAD_REF" to "github-head-ref-branch" 171 | ) 172 | ) 173 | .withPluginClasspath() 174 | 175 | runner.withArguments("generateGitProperties") 176 | .build().task(":generateGitProperties")?.outcome shouldBe TaskOutcome.SUCCESS 177 | 178 | assertSoftly(File(testProjectDir, "build/resources/main/git.properties")) { 179 | exists() shouldBe true 180 | readText() shouldContain "git.branch=github-head-ref-branch" 181 | } 182 | } 183 | 184 | "generate properties with github-actions (GITHUB_REF_NAME) env" { 185 | buildFile.writeText( 186 | """ 187 | plugins { 188 | id("io.hndrs.git-properties") 189 | } 190 | """.trimIndent() 191 | ) 192 | 193 | // add first commit 194 | gitRepository.addCommit() 195 | val runner = GradleRunner.create() 196 | .withTestKitDir(testKitDir) 197 | .withProjectDir(testProjectDir) 198 | .withEnvironment( 199 | mapOf( 200 | "CI" to "true", 201 | "GITHUB_REF_NAME" to "github-ref-name-branch" 202 | ) 203 | ) 204 | .withPluginClasspath() 205 | 206 | runner.withArguments("generateGitProperties") 207 | .build().task(":generateGitProperties")?.outcome shouldBe TaskOutcome.SUCCESS 208 | 209 | assertSoftly(File(testProjectDir, "build/resources/main/git.properties")) { 210 | exists() shouldBe true 211 | readText() shouldContain "git.branch=github-ref-name-branch" 212 | } 213 | } 214 | }) -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 147 | # shellcheck disable=SC3045 148 | MAX_FD=$( ulimit -H -n ) || 149 | warn "Could not query maximum file descriptor limit" 150 | esac 151 | case $MAX_FD in #( 152 | '' | soft) :;; #( 153 | *) 154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 155 | # shellcheck disable=SC3045 156 | ulimit -n "$MAX_FD" || 157 | warn "Could not set maximum file descriptor limit to $MAX_FD" 158 | esac 159 | fi 160 | 161 | # Collect all arguments for the java command, stacking in reverse order: 162 | # * args from the command line 163 | # * the main class name 164 | # * -classpath 165 | # * -D...appname settings 166 | # * --module-path (only if needed) 167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 168 | 169 | # For Cygwin or MSYS, switch paths to Windows format before running java 170 | if "$cygwin" || "$msys" ; then 171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 173 | 174 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 175 | 176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 177 | for arg do 178 | if 179 | case $arg in #( 180 | -*) false ;; # don't mess with options #( 181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 182 | [ -e "$t" ] ;; #( 183 | *) false ;; 184 | esac 185 | then 186 | arg=$( cygpath --path --ignore --mixed "$arg" ) 187 | fi 188 | # Roll the args list around exactly as many times as the number of 189 | # args, so each arg winds up back in the position where it started, but 190 | # possibly modified. 191 | # 192 | # NB: a `for` loop captures its iteration list before it begins, so 193 | # changing the positional parameters here affects neither the number of 194 | # iterations, nor the values presented in `arg`. 195 | shift # remove old arg 196 | set -- "$@" "$arg" # push replacement arg 197 | done 198 | fi 199 | 200 | # Collect all arguments for the java command; 201 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 202 | # shell script including quotes and variable substitutions, so put them in 203 | # double quotes to make sure that they get re-expanded; and 204 | # * put everything else in single quotes, so that it's not re-expanded. 205 | 206 | set -- \ 207 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 208 | -classpath "$CLASSPATH" \ 209 | org.gradle.wrapper.GradleWrapperMain \ 210 | "$@" 211 | 212 | # Stop when "xargs" is not available. 213 | if ! command -v xargs >/dev/null 2>&1 214 | then 215 | die "xargs is not available" 216 | fi 217 | 218 | # Use "xargs" to parse quoted args. 219 | # 220 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 221 | # 222 | # In Bash we could simply go: 223 | # 224 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 225 | # set -- "${ARGS[@]}" "$@" 226 | # 227 | # but POSIX shell has neither arrays nor command substitution, so instead we 228 | # post-process each arg (as a line of input to sed) to backslash-escape any 229 | # character that might be a shell metacharacter, then use eval to reverse 230 | # that process (while maintaining the separation between arguments), and wrap 231 | # the whole thing up as a single "set" statement. 232 | # 233 | # This will of course break if any of these variables contains a newline or 234 | # an unmatched quote. 235 | # 236 | 237 | eval "set -- $( 238 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 239 | xargs -n1 | 240 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 241 | tr '\n' ' ' 242 | )" '"$@"' 243 | 244 | exec "$JAVACMD" "$@" 245 | -------------------------------------------------------------------------------- /example/gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 147 | # shellcheck disable=SC3045 148 | MAX_FD=$( ulimit -H -n ) || 149 | warn "Could not query maximum file descriptor limit" 150 | esac 151 | case $MAX_FD in #( 152 | '' | soft) :;; #( 153 | *) 154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 155 | # shellcheck disable=SC3045 156 | ulimit -n "$MAX_FD" || 157 | warn "Could not set maximum file descriptor limit to $MAX_FD" 158 | esac 159 | fi 160 | 161 | # Collect all arguments for the java command, stacking in reverse order: 162 | # * args from the command line 163 | # * the main class name 164 | # * -classpath 165 | # * -D...appname settings 166 | # * --module-path (only if needed) 167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 168 | 169 | # For Cygwin or MSYS, switch paths to Windows format before running java 170 | if "$cygwin" || "$msys" ; then 171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 173 | 174 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 175 | 176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 177 | for arg do 178 | if 179 | case $arg in #( 180 | -*) false ;; # don't mess with options #( 181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 182 | [ -e "$t" ] ;; #( 183 | *) false ;; 184 | esac 185 | then 186 | arg=$( cygpath --path --ignore --mixed "$arg" ) 187 | fi 188 | # Roll the args list around exactly as many times as the number of 189 | # args, so each arg winds up back in the position where it started, but 190 | # possibly modified. 191 | # 192 | # NB: a `for` loop captures its iteration list before it begins, so 193 | # changing the positional parameters here affects neither the number of 194 | # iterations, nor the values presented in `arg`. 195 | shift # remove old arg 196 | set -- "$@" "$arg" # push replacement arg 197 | done 198 | fi 199 | 200 | # Collect all arguments for the java command; 201 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 202 | # shell script including quotes and variable substitutions, so put them in 203 | # double quotes to make sure that they get re-expanded; and 204 | # * put everything else in single quotes, so that it's not re-expanded. 205 | 206 | set -- \ 207 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 208 | -classpath "$CLASSPATH" \ 209 | org.gradle.wrapper.GradleWrapperMain \ 210 | "$@" 211 | 212 | # Stop when "xargs" is not available. 213 | if ! command -v xargs >/dev/null 2>&1 214 | then 215 | die "xargs is not available" 216 | fi 217 | 218 | # Use "xargs" to parse quoted args. 219 | # 220 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 221 | # 222 | # In Bash we could simply go: 223 | # 224 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 225 | # set -- "${ARGS[@]}" "$@" 226 | # 227 | # but POSIX shell has neither arrays nor command substitution, so instead we 228 | # post-process each arg (as a line of input to sed) to backslash-escape any 229 | # character that might be a shell metacharacter, then use eval to reverse 230 | # that process (while maintaining the separation between arguments), and wrap 231 | # the whole thing up as a single "set" statement. 232 | # 233 | # This will of course break if any of these variables contains a newline or 234 | # an unmatched quote. 235 | # 236 | 237 | eval "set -- $( 238 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 239 | xargs -n1 | 240 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 241 | tr '\n' ' ' 242 | )" '"$@"' 243 | 244 | exec "$JAVACMD" "$@" 245 | --------------------------------------------------------------------------------