Skip to content

Commit

Permalink
Refactor the plugin, improve the logging for the git provider
Browse files Browse the repository at this point in the history
  • Loading branch information
Azurelol committed Apr 23, 2024
1 parent e6a2014 commit 84f6ea6
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 65 deletions.
124 changes: 69 additions & 55 deletions src/main/groovy/wooga/gradle/version/VersionPlugin.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,19 @@ import org.gradle.api.Project
import org.gradle.api.provider.Provider
import wooga.gradle.version.internal.DefaultVersionCodeExtension
import wooga.gradle.version.internal.DefaultVersionPluginExtension
import wooga.gradle.version.internal.GitBuildService
import wooga.gradle.version.internal.ToStringProvider
import wooga.gradle.version.internal.VersionCode
import wooga.gradle.version.internal.release.base.ReleaseVersion
import wooga.gradle.version.internal.release.semver.ChangeScope

import java.util.stream.Stream

/**
* A plugin that sets the project version based on selected {@code VersionSchemes}
*/
class VersionPlugin implements Plugin<Project> {

static String EXTENSION_NAME = "versionBuilder"
static String VERSION_CODE_EXTENSION_NAME = "versionCode"

Expand All @@ -43,57 +48,27 @@ class VersionPlugin implements Plugin<Project> {
setProjectVersion(project, extension)
}

static void applyOnCurrentAndSubProjects(Project project, Closure operation) {
operation(project)
project.childProjects.values().each { prj ->
operation(prj)
applyOnCurrentAndSubProjects(prj, operation)
}
project.parent
}

private static void setProjectVersion(Project project, VersionPluginExtension extension) {
def versionProvider = new ToStringProvider(extension.version.map({ it.version }.memoize()))
applyOnCurrentAndSubProjects(project) { Project prj ->
prj.setVersion(versionProvider)
def versionCodeExt = project.extensions.findByName(VERSION_CODE_EXTENSION_NAME) as VersionCodeExtension
if(!versionCodeExt) {
versionCodeExt = DefaultVersionCodeExtension.empty(prj, VERSION_CODE_EXTENSION_NAME)
}
versionCodeExt.convention(extension.versionCode)
}
}

static File findNearestGitFolder(File projectDir, int maxDepth) {
if(maxDepth > 0) {
def maybeDotGit = Stream.of(projectDir.listFiles( { File file ->
file.directory && file.name == ".git"
} as FileFilter)).findFirst()

return maybeDotGit.orElseGet {
findNearestGitFolder(projectDir.parentFile, maxDepth-1)
}
}
return null
}

/**
* Configures the extension used by the plugin with the default conventions
*/
protected static VersionPluginExtension createAndConfigureExtension(Project project) {

def extension = project.extensions.create(VersionPluginExtension, EXTENSION_NAME, DefaultVersionPluginExtension) as DefaultVersionPluginExtension
// Register the git build service
Provider<GitBuildService> gitBuildService = project.gradle.sharedServices.registerIfAbsent("git",
GitBuildService.class, spec -> {
})

// Create the extension
def extension = project.extensions.create(VersionPluginExtension, EXTENSION_NAME, DefaultVersionPluginExtension) as DefaultVersionPluginExtension

Provider<String> gitRoot = VersionPluginConventions.gitRoot.getStringValueProvider(project)
.orElse(VersionPluginConventions.maxGitRootSearchDepth.getIntegerValueProvider(project).map {
findNearestGitFolder(project.projectDir, it)?.absolutePath
})
.orElse(VersionPluginConventions.maxGitRootSearchDepth.getIntegerValueProvider(project).map {
findNearestGitFolder(project.projectDir, it)?.absolutePath
})

extension.git.convention(ProviderExtensions.mapOnce(gitRoot) { String it ->
try {
Grgit git = Grgit.open(dir: it)
project.gradle.buildFinished {
project.logger.info "Closing Git repo: ${git.repository.rootDir}"
git.close()
}
return git
return gitBuildService.get().getRepository(it)
} catch (RepositoryNotFoundException ignore) {
project.logger.warn("Git repository not found at $gitRoot ")
}
Expand All @@ -115,20 +90,20 @@ class VersionPlugin implements Plugin<Project> {
extension.mainBranchPattern.convention(VersionPluginConventions.mainBranchPattern.getStringValueProvider(project))

extension.scope.convention(VersionPluginConventions.scope.getStringValueProvider(project)
.map {it?.trim()?.empty? null: it }
.map { ChangeScope.valueOf(it.toUpperCase()) })
.map {it?.trim()?.empty? null: it }
.map { ChangeScope.valueOf(it.toUpperCase()) })

extension.version.convention(VersionPluginConventions.version.getStringValueProvider(project)
.map {new ReleaseVersion(version: it) }
.orElse(inferVersionIfGitIsPresent(project, extension))
.orElse(createInferredByGitVersionProvider(project, extension))
.orElse(new ReleaseVersion(version: VersionPluginConventions.UNINITIALIZED_VERSION))
)

extension.versionCode.convention(extension.versionCodeScheme.map({ VersionCodeSchemes scheme ->
def version = extension.version.map { it.version }.orNull
return VersionCode.Schemes
.fromExternal(scheme)
.versionCodeFor(version, extension.versionRepo.orNull, extension.versionCodeOffset.getOrElse(0))
.fromExternal(scheme)
.versionCodeFor(version, extension.versionRepo.orNull, extension.versionCodeOffset.getOrElse(0))
}.memoize()))

extension.stage.convention(VersionPluginConventions.stage.getStringValueProvider(project))
Expand All @@ -138,16 +113,55 @@ class VersionPlugin implements Plugin<Project> {
return extension
}

private static Provider<ReleaseVersion> inferVersionIfGitIsPresent(Project project, VersionPluginExtension extension) {
def inferredVersion = ProviderExtensions.mapOnce(extension.git) { Grgit git ->
private static Provider<ReleaseVersion> createInferredByGitVersionProvider(Project project, VersionPluginExtension extension) {

def provider = ProviderExtensions.mapOnce(extension.git) { Grgit git ->
def version = extension.inferVersion(extension.versionScheme.get(), extension.stage, extension.scope)
.orElse(project.provider {
throw new GradleException('No version strategies were selected. Run build with --info for more detail.')
}).get()
.orElse(project.provider {
throw new GradleException('Could not resolve the project version as no version strategies could be selected. Run build with --info for more detail.')
}).get()
project.logger.warn('Inferred project: {}, version: {}', project.name, version.version)
return version
} as Provider<ReleaseVersion>

return inferredVersion.orElse(new ReleaseVersion(version: VersionPluginConventions.UNINITIALIZED_VERSION))
// If git was not found
return provider.orElse(new ReleaseVersion(version: VersionPluginConventions.UNINITIALIZED_VERSION))
}

private static File findNearestGitFolder(File projectDir, int maxDepth) {
if(maxDepth > 0) {
def maybeDotGit = Stream.of(projectDir.listFiles( { File file ->
file.directory && file.name == ".git"
} as FileFilter)).findFirst()

return maybeDotGit.orElseGet {
findNearestGitFolder(projectDir.parentFile, maxDepth-1)
}
}
return null
}

/**
* Sets the project version onto the current project and any of its subprojects
*/
private static void setProjectVersion(Project project, VersionPluginExtension extension) {
def versionProvider = new ToStringProvider(extension.version.map({ it.version }.memoize()))
applyOnCurrentAndSubProjects(project) { Project prj ->
prj.setVersion(versionProvider)
def versionCodeExt = project.extensions.findByName(VERSION_CODE_EXTENSION_NAME) as VersionCodeExtension
if(!versionCodeExt) {
versionCodeExt = DefaultVersionCodeExtension.empty(prj, VERSION_CODE_EXTENSION_NAME)
}
versionCodeExt.convention(extension.versionCode)
}
}

private static void applyOnCurrentAndSubProjects(Project project, Closure operation) {
operation(project)
project.childProjects.values().each { prj ->
operation(prj)
applyOnCurrentAndSubProjects(prj, operation)
}
project.parent
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package wooga.gradle.version.internal

import org.ajoberstar.grgit.Grgit
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters

/**
* Implemented from https://docs.gradle.org/current/userguide/build_services.html#build_services
*/
abstract class GitBuildService implements BuildService<BuildServiceParameters.None>, AutoCloseable {

List<Grgit> instances = []

GitBuildService() {
}

/**
* Opens the repository at the given directory path. When the gradle build has finished,
* will automatically close it
* @param directory The file path to the git repository
* @return A managed git repository
*/
Grgit getRepository(String directory) {
Grgit git = Grgit.open(dir: directory)
instances.add(git)
return git
}

@Override
void close() throws Exception {
for(instance in instances) {
System.out.println("Closing Git repository: ${instance.repository.rootDir}")
instance.close()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,28 +137,31 @@ final class SemVerStrategy implements DefaultVersionStrategy {
* Composes a string detailing the current repository staged/unstaged files
*/
static String composeRepositoryStatus(Status status) {
StringBuilder str = new StringBuilder()
StringBuilder builder = new StringBuilder()

Closure printChangeSet = { label, changeSet ->
str.append("\n> ${label}\n")
str.append(changeSet.added.collect({ "[ADDED] ${it}" }).join("\n"))
str.append(changeSet.modified.collect({ "[MODIFIED] ${it}" }).join("\n"))
str.append(changeSet.removed.collect({ "[REMOVED] ${it}" }).join("\n"))
builder.append("\n> ${label}\n")

List<String> lines = []
lines.addAll(changeSet.added.collect({ "[ADDED] ${it}" }))
lines.addAll(changeSet.modified.collect({ "[MODIFIED] ${it}"}))
lines.addAll(changeSet.removed.collect({ "[REMOVED] ${it}"}))

builder.append(lines.join("\n"))
}

str.append("Repository Status:")
printChangeSet("Staged", status.staged)
printChangeSet("Unstaged", status.unstaged)
builder.append("Git Repository Status:")
printChangeSet("Staged Files", status.staged)
printChangeSet("Unstaged Files", status.unstaged)

str.toString()
builder.toString()
}

/**
* Infers the version to use for this build. Uses the normal, pre-release, and build metadata
* strategies in order to infer the version. If the {@code release.stage} is not set, uses the
* first value in the {@code stages} set (i.e. the one with the lowest precedence). After inferring
* the version precedence will be enforced, if required by this strategy.
*
*/
@Override
ReleaseVersion infer(VersionInferenceParameters params) {
Expand Down

0 comments on commit 84f6ea6

Please sign in to comment.