Skip to content

Commit

Permalink
Add support for configuration cache in Gradle 8.1.+ (#95)
Browse files Browse the repository at this point in the history
  • Loading branch information
tcrawford-figure authored Apr 27, 2023
1 parent cefc5f6 commit f305c30
Show file tree
Hide file tree
Showing 35 changed files with 1,194 additions and 273 deletions.
9 changes: 7 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,12 @@ dependencies {
}

// Leak semver library users of this plugin so that they can implement their own versionModifier strategy
api(libs.swiftzer.semver)
// TODO: For v2, remove need for semver jar for consumers. They shouldn't need to know about this detail
listOf(
libs.swiftzer.semver
).forEach {
api(it)
}

listOf(
gradleTestKit(),
Expand Down Expand Up @@ -95,7 +100,7 @@ tasks.withType<Test>().configureEach {
showStandardStreams = true
showCauses = true
showStackTraces = true
events(*TestLogEvent.values())
events = TestLogEvent.values().toSet()
exceptionFormat = TestExceptionFormat.FULL
}
}
Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-all.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
7 changes: 4 additions & 3 deletions gradlew
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,6 @@ done
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum

Expand Down Expand Up @@ -197,6 +194,10 @@ if "$cygwin" || "$msys" ; then
done
fi


# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'

# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
Expand Down
2 changes: 2 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,7 @@ gradleEnterprise {
buildScan {
termsOfServiceUrl = "https://gradle.com/terms-of-service"
termsOfServiceAgree = "yes"
publishAlwaysIf(System.getenv("GITHUB_ACTIONS") == "true")
publishOnFailure()
}
}
95 changes: 24 additions & 71 deletions src/main/kotlin/com/figure/gradle/semver/SemverExtension.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,42 +10,44 @@ package com.figure.gradle.semver
import com.figure.gradle.semver.external.BranchMatchingConfiguration
import com.figure.gradle.semver.external.VersionCalculatorStrategy
import com.figure.gradle.semver.external.VersionModifier
import com.figure.gradle.semver.external.flatVersionCalculatorStrategy
import com.figure.gradle.semver.external.flowVersionCalculatorStrategy
import com.figure.gradle.semver.internal.git.GitRef
import com.figure.gradle.semver.internal.git.git
import com.figure.gradle.semver.internal.git.hasBranch
import com.figure.gradle.semver.internal.semver.GitContextProviderOperations
import com.figure.gradle.semver.internal.semver.GradleSemverContext
import com.figure.gradle.semver.internal.semver.TargetBranchVersionCalculator
import com.figure.gradle.semver.internal.semver.VersionCalculatorConfig
import com.figure.gradle.semver.internal.semver.versionModifierFromString
import com.figure.gradle.semver.internal.semverError
import com.figure.gradle.semver.internal.semverInfo
import com.figure.gradle.semver.internal.semverLifecycle
import com.figure.gradle.semver.internal.valuesources.gitCalculateSemverProvider
import net.swiftzer.semver.SemVer
import org.eclipse.jgit.api.Git
import org.gradle.api.Project
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.ProviderFactory
import org.gradle.kotlin.dsl.listProperty
import org.gradle.kotlin.dsl.property
import javax.inject.Inject

private val log = Logging.getLogger(Logger.ROOT_LOGGER_NAME)

abstract class SemverExtension @Inject constructor(objects: ObjectFactory, private val project: Project) {
abstract class SemverExtension @Inject constructor(
objects: ObjectFactory,
project: Project,
providerFactory: ProviderFactory,
) {
/**
* This version invocation takes place at project build time of the project that is utilizing this plugin
* The version is not calculated until a build happens that requires `semver.version`
*/
val version: String by lazy { calculateVersion().toString() }
val version: String by lazy {
providerFactory.gitCalculateSemverProvider(
gitDir = gitDir,
tagPrefix = tagPrefix,
initialVersion = initialVersion.orNull.toString(),
overrideVersion = overrideVersion.orNull.toString(),
versionStrategy = versionStrategy,
versionModifier = versionModifier,
).get()
}

val versionTagName: String by lazy { calculateVersionTagName() }

// TODO: For v2, see if rootRepoDirectory can be specified instead (without causing Gradle issues) where
// a semver task relies on the build task to complete. Maybe needs specified as a string instead of File to
// work? Use Git.open(rootRepoDirectory) to create the Git object then.
internal val gitDir: Property<String> =
objects.property<String>()
.convention("${project.rootProject.rootDir.path}/.git")
Expand Down Expand Up @@ -77,8 +79,10 @@ abstract class SemverExtension @Inject constructor(objects: ObjectFactory, priva
fun tagPrefix(prefix: String) {
if (overrideVersion.orNull != null) {
throw IllegalArgumentException(
"Cannot set semver tagPrefix after override version has been set. " +
"The override version depends on the tagPrefix. Set the tagPrefix first."
"""
|Cannot set semver tagPrefix after override version has been set.
| The override version depends on the tagPrefix. Set the tagPrefix first.
""".trimMargin().replace("\n", "")
)
}
this.tagPrefix.set(prefix)
Expand Down Expand Up @@ -106,61 +110,10 @@ abstract class SemverExtension @Inject constructor(objects: ObjectFactory, priva
this.versionStrategy.set(strategy)
}

private fun calculateVersion(): SemVer {
log.semverInfo("Using git directory: ${gitDir.get()}")

val git = project.git(gitDir.get())
val config = buildCalculatorConfig(git)
val ops = GitContextProviderOperations(git, config)
val context = GradleSemverContext(project, ops)

return config.overrideVersion ?: run {
ops.currentBranch()?.let { currentBranch ->
log.semverInfo("Current branch: $currentBranch")
log.semverInfo("Semver configuration while calculating version: $config")

val calculator = TargetBranchVersionCalculator(ops, config, context, currentBranch)
calculator.calculateVersion().getOrElse {
log.semverError("Failed to calculate version", it)
throw Exception(it)
}
} ?: run {
log.semverError("Failed to find current branch, cannot calculate semver")
throw Exception("Failed to find current branch")
}
}.also {
log.semverLifecycle("$it")
}
}

private fun calculateVersionTagName(): String {
return tagPrefix.map { prefix -> "$prefix$version" }.get()
}

private fun buildCalculatorConfig(git: Git): VersionCalculatorConfig {
val initialConfig = VersionCalculatorConfig(
tagPrefix = tagPrefix.get(),
initialVersion = initialVersion.get(),
overrideVersion = overrideVersion.orNull
)
return when {
versionStrategy.isPresent -> {
log.semverInfo("Enabling extension configured strategy")
initialConfig.withBranchMatchingConfig(versionStrategy.get())
}

git.hasBranch(GitRef.Branch.DEVELOP.name).isNotEmpty() -> {
log.semverInfo("Enabling Git Flow mode")
initialConfig.withBranchMatchingConfig(flowVersionCalculatorStrategy(versionModifier.get()))
}

else -> {
log.semverInfo("Enabling Flat mode")
initialConfig.withBranchMatchingConfig(flatVersionCalculatorStrategy(versionModifier.get()))
}
}
}

private fun possiblyPrefixVersion(version: String, prefix: String): SemVer {
return SemVer.parse(version.trimMargin(prefix)) // fail fast, don't let an invalid version propagate to runtime
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ typealias VersionCalculatorStrategy = List<BranchMatchingConfiguration>
typealias VersionModifier = SemVer.() -> SemVer // TODO: Does this need to be a method?
typealias VersionQualifier = SemverContext.(current: GitRef.Branch) -> Pair<PreReleaseLabel, BuildMetadataLabel>

// TODO: Clean this up for v2.
// - The end user shouldn't have to know about the SemVer object.
// - VersionQualifier is probably more complicated than necessary.
// - Support RC versions properly. Should this be a VersionModifier?
data class BranchMatchingConfiguration(
val regex: Regex,
val targetBranch: GitRef.Branch,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,10 @@ private val log = Logging.getLogger(Logger.ROOT_LOGGER_NAME)
interface SemverContext {
val ops: ContextProviderOperations

fun property(name: String): Any?
fun env(name: String): String?

fun preReleaseWithCommitCount(
currentBranch: GitRef.Branch,
targetBranch: GitRef.Branch,
label: String
label: String,
) = PreReleaseLabel(
value = ops.commitsSinceBranchPoint(currentBranch, targetBranch).fold(
onSuccess = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,11 @@ internal class UnexpectedException(message: String) :

internal class MissingBranchMatchingConfigurationException(currentBranch: GitRef.Branch) :
Exception("Missing branch matching configuration for current branch: ${currentBranch.name}")

internal class TagAlreadyExistsException(tag: String) :
Exception(
"""
|Tag $tag already exists on remote! Either skip publishing the artifact on the next run or delete
| the existing tag before running again.
""".trimMargin().replace("\n", "")
)
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,7 @@ package com.figure.gradle.semver.internal.semver

import com.figure.gradle.semver.external.ContextProviderOperations
import com.figure.gradle.semver.external.SemverContext
import org.gradle.api.Project

internal class GradleSemverContext(
private val project: Project,
override val ops: ContextProviderOperations,
) : SemverContext {
override fun property(name: String): Any? =
project.findProperty(name)

override fun env(name: String): String? =
System.getenv(name)
}
) : SemverContext
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

package com.figure.gradle.semver.internal.tasks

import com.figure.gradle.semver.internal.exceptions.TagAlreadyExistsException
import com.figure.gradle.semver.internal.semverLifecycle
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.storage.file.FileRepositoryBuilder
Expand Down Expand Up @@ -39,6 +40,11 @@ abstract class CreateAndPushVersionTagTask : DefaultTask() {
.build()
)

val tags = git.tagList().call()
val tagAlreadyExists = versionTagName.get() in tags.map { it.name.replace("refs/tags/", "") }

if (tagAlreadyExists) throw TagAlreadyExistsException(versionTagName.get())

git.tag().setName(versionTagName.get()).call()
git.push().setPushTags().call()
logger.semverLifecycle("Created and pushed version tag: ${versionTagName.get()}")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Copyright (c) 2023 Figure Technologies and its affiliates.
*
* This source code is licensed under the Apache 2.0 license found in the
* LICENSE.md file in the root directory of this source tree.
*/

package com.figure.gradle.semver.internal.util

import org.eclipse.jgit.api.Git
import org.eclipse.jgit.storage.file.FileRepositoryBuilder
import java.io.File

object MemoizedGit {
private val memoizedGit = mutableMapOf<String, Git>()

fun open(gitDir: String): Git =
memoizedGit.computeIfAbsent(gitDir) {
Git(
FileRepositoryBuilder()
.setGitDir(File(gitDir))
.readEnvironment()
.findGitDir()
.build()
)
}
}
Loading

0 comments on commit f305c30

Please sign in to comment.