Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to update root gradle.properties #41

Merged
merged 1 commit into from
May 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,24 @@ apply {

```

### Multi-project usage
In case you have a Multi-project build and you have some common dependency configuration in some common file in root project
(like *.gradle file), you should apply plugin to all projects. Easiest way to do this is with `allprojects` block like:
```
plugins {
id 'se.patrikerdes.use-latest-versions' version '0.2.13'
id 'com.github.ben-manes.versions' version '0.21.0'
}

allprojects {
apply plugin: 'se.patrikerdes.use-latest-versions'
apply plugin: 'com.github.ben-manes.versions'
}
```
This is because `se.patrikerdes.use-latest-versions` plugin scans files for every project separately.

In case you handle dependencies per project separately this is not needed and you can apply plugin just to selected projects.

## Example

Given this build.gradle file:
Expand Down Expand Up @@ -143,7 +161,22 @@ dependencies {
### useLatestVersions

```bash
# gradle useLatestVersions
gradle useLatestVersions

# Configuration and default values:
useLatestVersions {
# A whitelist of dependencies to update, in the format of group:name
# Equal to command line: --update-dependency=[values]
updateWhitelist = []
# A blacklist of dependencies to update, in the format of group:name
# Equal to command line: --ignore-dependency=[values]
updateBlacklist = []
# When enabled, root project gradle.properties will also be populated with
# versions from subprojects in multi-project build
# Equal to command line: --update-root-properties
updateRootProperties = false
}

```

Updates module and plugin versions in all *.gradle files in the project root folder or any subfolder to the latest
Expand Down
38 changes: 37 additions & 1 deletion src/main/groovy/se/patrikerdes/Common.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import org.gradle.api.Task

import java.nio.file.Paths
import java.util.regex.Matcher
import java.util.regex.Pattern

@CompileStatic
class Common {
Expand Down Expand Up @@ -48,7 +49,9 @@ class Common {

static void getVariablesFromMatches(Matcher variableMatch, Map<String, String> versionVariables,
DependencyUpdate update, Set problemVariables) {
if (variableMatch.size() == 1) {
// File can have more dependencies with same version variable
// We anyway check that versions of dependencies for that variable are the same
if (variableMatch.size() >= 1) {
String variableName = ((List) variableMatch[0])[1]
if (versionVariables.containsKey(variableName) &&
versionVariables[variableName as String] != update.newVersion) {
Expand Down Expand Up @@ -117,5 +120,38 @@ class Common {
}
outputDir
}

static void updateVersionVariables(Map<String, String> gradleFileContents, List<String> dotGradleFileNames,
Map<String, String> versionVariables) {
for (String dotGradleFileName in dotGradleFileNames) {
for (versionVariable in versionVariables) {
gradleFileContents[dotGradleFileName] =
gradleFileContents[dotGradleFileName].replaceAll(
variableDefinitionMatchStringForFileName(versionVariable.key, dotGradleFileName),
newVariableDefinitionString(versionVariable.value))
}
}
}

static String variableDefinitionMatchStringForFileName(String variable, String fileName) {
String splitter = File.separator.replace('\\', '\\\\')
if (fileName.split(splitter).last() == 'gradle.properties') {
return gradlePropertiesVariableDefinitionMatchString(variable)
}
variableDefinitionMatchString(variable)
}

static String variableDefinitionMatchString(String variable) {
'(' + Pattern.quote(variable) + "[ \t]*=[ \t]*[\"'])(.*)([\"'])"
}

static String gradlePropertiesVariableDefinitionMatchString(String variable) {
'(' + Pattern.quote(variable) + '[ \t]*=[ \t]*)(.*)([ \t]*)'
}

static String newVariableDefinitionString(String newVersion) {
'$1' + newVersion + '$3'
}

}

69 changes: 69 additions & 0 deletions src/main/groovy/se/patrikerdes/InternalAggregateRootTask.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package se.patrikerdes

import static se.patrikerdes.UseLatestVersionsPlugin.USE_LATEST_VERSIONS

import org.gradle.api.Project
import groovy.json.JsonSlurper
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction

class InternalAggregateRootTask extends DefaultTask {

InternalAggregateRootTask() {
description = 'Internal task that aggregates versions of all projects to root. ' +
'Currently it updates just gradle.properties in root. Don\'t run it as separate task'
}

@TaskAction
void internalAggregateRootTask() {
List<String> versionVariablesFiles = project.gradle.taskGraph.allTasks
.findAll { it.name == USE_LATEST_VERSIONS }
.collectMany { getVersionVariablesFiles(it.project) }
List<String> dotGradleFileNames =
new FileNameFinder().getFileNames(project.projectDir.absolutePath, 'gradle.properties')

Map<String, String> gradleFileContents = dotGradleFileNames.collectEntries {
[(it): new File(it).getText('UTF-8')]
}
Map<String, String> versionVariables = readVersionVariables(versionVariablesFiles)
Common.updateVersionVariables(gradleFileContents, dotGradleFileNames, versionVariables)

// Write all files back
for (dotGradleFileName in dotGradleFileNames) {
new File(dotGradleFileName).setText(gradleFileContents[dotGradleFileName], 'UTF-8')
}

// Delete temp files in build folder
for (String versionVariablesFile in versionVariablesFiles) {
new File(versionVariablesFile).delete()
}
}

List<String> getVersionVariablesFiles(Project project) {
String buildDir = project.buildDir.absolutePath
new FileNameFinder().getFileNames(buildDir, 'useLatestVersions/version-variables.json')
}

Map<String, String> readVersionVariables(List<String> versionVariablesFiles) {
Map<String, String> versionVariables = [:]
List<String> problemVariables = []
for (String versionVariablesFile in versionVariablesFiles) {
Map<String, String> variables = new JsonSlurper().parseText(new File(versionVariablesFile).text)
variables.forEach { k, v ->
if (versionVariables.containsKey(k) && versionVariables.get(k) != v) {
println("A problem was detected: the variable '$k' has different updated versions in different " +
"projects.\nNew updated versions are: '${versionVariables.get(k)}' and '$v', " +
"root gradle.properties value won't be be changed.")
problemVariables.add(k)
} else {
versionVariables.put(k, v)
}
}
}
for (problemVariable in problemVariables) {
versionVariables.remove(problemVariable)
}
versionVariables
}

}
35 changes: 33 additions & 2 deletions src/main/groovy/se/patrikerdes/UseLatestVersionsPlugin.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,43 @@ package se.patrikerdes
import groovy.transform.CompileStatic
import org.gradle.api.Project
import org.gradle.api.Plugin
import org.gradle.api.Task

@CompileStatic
class UseLatestVersionsPlugin implements Plugin<Project> {

static final String DEPENDENCY_UPDATES = 'dependencyUpdates'
static final String USE_LATEST_VERSIONS = 'useLatestVersions'
static final String USE_LATEST_VERSIONS_CHECK = 'useLatestVersionsCheck'
static final String INTERNAL_ROOT_AGGREGATE = 'internalRootAggregate'

void apply(Project project) {
System.setProperty('outputFormatter', 'json,xml,plain')
project.task('useLatestVersions', type: UseLatestVersionsTask, dependsOn: 'dependencyUpdates')
project.task('useLatestVersionsCheck', type: UseLatestVersionsCheckTask, dependsOn: 'dependencyUpdates')
Task rootAggregate = setupRootAggregateTask(project)
setupUseLatestVersions(project, rootAggregate)
setupUseLatestVersionsCheck(project)
}

Task setupRootAggregateTask(Project project) {
Set<Task> tasks = project.rootProject.getTasksByName(INTERNAL_ROOT_AGGREGATE, false)
if (tasks.isEmpty()) {
// This handles both cases: when UseLatestVersionsPlugin is applied
// to root project and subprojects or when it is applied only to subprojects
return project.rootProject.task(INTERNAL_ROOT_AGGREGATE, type: InternalAggregateRootTask)
}
tasks[0]
}

void setupUseLatestVersions(Project project, Task rootAggregate) {
Task useLatestVersions = project.task(USE_LATEST_VERSIONS, type: UseLatestVersionsTask)
useLatestVersions.dependsOn(DEPENDENCY_UPDATES)
useLatestVersions.finalizedBy(rootAggregate)
rootAggregate.mustRunAfter(useLatestVersions)
}

void setupUseLatestVersionsCheck(Project project) {
Task useLatestVersionCheck = project.task(USE_LATEST_VERSIONS_CHECK, type: UseLatestVersionsCheckTask)
useLatestVersionCheck.dependsOn(DEPENDENCY_UPDATES)
}

}
70 changes: 34 additions & 36 deletions src/main/groovy/se/patrikerdes/UseLatestVersionsTask.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@ import static se.patrikerdes.Common.getCurrentDependencies
import static se.patrikerdes.Common.getDependencyUpdatesJsonReportFilePath
import static se.patrikerdes.Common.getOutDatedDependencies

import org.gradle.api.Project
import groovy.json.JsonBuilder
import groovy.json.JsonSlurper
import groovy.transform.CompileStatic
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.tasks.options.Option
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.options.Option

import java.nio.file.Files
import java.nio.file.StandardCopyOption
import java.util.regex.Matcher
import java.util.regex.Pattern

@CompileStatic
class UseLatestVersionsTask extends DefaultTask {
Expand All @@ -30,24 +31,17 @@ class UseLatestVersionsTask extends DefaultTask {
description = 'A blacklist of dependencies to update, in the format of group:name')
List<String> updateBlacklist = Collections.emptyList()

@Input
@Option(option='update-root-properties',
description = 'Update root project gradle.properties with subprojects versions in multi-project build')
boolean updateRootProperties

UseLatestVersionsTask() {
description = 'Updates module and plugin versions in all *.gradle and *.gradle.kts files to the latest ' +
'available versions.'
group = 'Help'
}

String variableDefinitionMatchString(String variable) {
'(' + Pattern.quote(variable) + "[ \t]*=[ \t]*[\"'])(.*)([\"'])"
}

String gradlePropertiesVariableDefinitionMatchString(String variable) {
'(' + Pattern.quote(variable) + '[ \t]*=[ \t]*)(.*)([ \t]*)'
}

String newVariableDefinitionString(String newVersion) {
'$1' + newVersion + '$3'
}

@TaskAction
void useLatestVersions() {
validateExclusiveWhiteOrBlacklist()
Expand All @@ -59,6 +53,11 @@ class UseLatestVersionsTask extends DefaultTask {
dotGradleFileNames += new FileNameFinder().getFileNames(project.projectDir.absolutePath, '**/*.gradle.kts')
dotGradleFileNames += new FileNameFinder().getFileNames(project.projectDir.absolutePath, '**/gradle.properties')
dotGradleFileNames += new FileNameFinder().getFileNames(project.projectDir.absolutePath, 'buildSrc/**/*.kt')
String rootGradleProperties = getRootGradlePropertiesPath(project)
if (rootGradleProperties && updateRootProperties && project != project.rootProject) {
// Append so we don't update variables if defined in multiple files
dotGradleFileNames += rootGradleProperties
}

// Exclude any files that belong to sub-projects
List<String> subprojectPaths = project.subprojects.collect { it.projectDir.absolutePath }
Expand Down Expand Up @@ -91,11 +90,22 @@ class UseLatestVersionsTask extends DefaultTask {

updateModuleVersions(gradleFileContents, dotGradleFileNames, dependencyUpdates)
updatePluginVersions(gradleFileContents, dotGradleFileNames, dependencyUpdates)
updateVariables(gradleFileContents, dotGradleFileNames, dependencyUpdates, dependencyStables)
Map<String, String> versionVariables = getVersionVariables(gradleFileContents, dotGradleFileNames,
dependencyUpdates, dependencyStables)
Common.updateVersionVariables(gradleFileContents, dotGradleFileNames, versionVariables)

// Write all files back
for (dotGradleFileName in dotGradleFileNames) {
new File(dotGradleFileName).setText(gradleFileContents[dotGradleFileName], 'UTF-8')
if (dotGradleFileName != rootGradleProperties) {
// Root Gradle properties are handled in
// internalAggregateRoot task that reads version-variables.json
new File(dotGradleFileName).setText(gradleFileContents[dotGradleFileName], 'UTF-8')
}
}

if (project == project.rootProject || updateRootProperties) {
new File(project.buildDir, 'useLatestVersions/version-variables.json')
.write(new JsonBuilder(versionVariables).toPrettyString())
}
}

Expand Down Expand Up @@ -143,12 +153,11 @@ class UseLatestVersionsTask extends DefaultTask {
}
}

void updateVariables(Map<String, String> gradleFileContents, List<String> dotGradleFileNames,
Map<String, String> getVersionVariables(Map<String, String> gradleFileContents, List<String> dotGradleFileNames,
List<DependencyUpdate> dependencyUpdates, List<DependencyUpdate> dependencyStables) {
Set problemVariables = []
Map<String, String> versionVariables = Common.findVariables(dotGradleFileNames,
dependencyUpdates + dependencyStables, gradleFileContents, problemVariables)

for (problemVariable in problemVariables) {
versionVariables.remove(problemVariable)
}
Expand All @@ -160,7 +169,7 @@ class UseLatestVersionsTask extends DefaultTask {
for (String dotGradleFileName in dotGradleFileNames) {
for (variableName in versionVariables.keySet()) {
Matcher variableDefinitionMatch = gradleFileContents[dotGradleFileName] =~
variableDefinitionMatchStringForFileName(variableName, dotGradleFileName)
Common.variableDefinitionMatchStringForFileName(variableName, dotGradleFileName)
if (variableDefinitionMatch.size() == 1) {
if (variableDefinitions.contains(variableName)) {
// The variable is assigned to in more than one file
Expand All @@ -183,23 +192,7 @@ class UseLatestVersionsTask extends DefaultTask {
versionVariables.remove(problemVariable)
}

// Update variables
for (String dotGradleFileName in dotGradleFileNames) {
for (versionVariable in versionVariables) {
gradleFileContents[dotGradleFileName] =
gradleFileContents[dotGradleFileName].replaceAll(
variableDefinitionMatchStringForFileName(versionVariable.key, dotGradleFileName),
newVariableDefinitionString(versionVariable.value))
}
}
}

String variableDefinitionMatchStringForFileName(String variable, String fileName) {
String splitter = File.separator.replace('\\', '\\\\')
if (fileName.split(splitter).last() == 'gradle.properties') {
return gradlePropertiesVariableDefinitionMatchString(variable)
}
variableDefinitionMatchString(variable)
versionVariables
}

void saveDependencyUpdatesReport(File dependencyUpdatesJsonReportFile) {
Expand All @@ -212,6 +205,11 @@ class UseLatestVersionsTask extends DefaultTask {
StandardCopyOption.REPLACE_EXISTING)
}

private String getRootGradlePropertiesPath(Project project) {
File rootGradleProperties = new File(project.rootDir.absolutePath, 'gradle.properties')
rootGradleProperties.exists() ? rootGradleProperties.absolutePath : null
}

private void validateExclusiveWhiteOrBlacklist() {
if (!updateWhitelist.empty && !updateBlacklist.empty) {
throw new GradleException(WHITE_BLACKLIST_ERROR_MESSAGE)
Expand Down
8 changes: 8 additions & 0 deletions src/test/groovy/se/patrikerdes/BaseFunctionalTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ class BaseFunctionalTest extends Specification {
.buildAndFail()
}

BuildResult useLatestVersionsUpdatingRootProperties() {
GradleRunner.create()
.withProjectDir(testProjectDir.root)
.withArguments('useLatestVersions', '--update-root-properties')
.withPluginClasspath()
.build()
}

BuildResult useLatestVersions(String gradleVersion) {
GradleRunner.create()
.withProjectDir(testProjectDir.root)
Expand Down
1 change: 1 addition & 0 deletions src/test/groovy/se/patrikerdes/CurrentVersions.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ package se.patrikerdes
class CurrentVersions {
public static final String VERSIONS = '0.28.0'
public static final String JUNIT = '4.13'
public static final String JUNIT_DEPS = '4.11'
public static final String LOG4J = '1.2.17'
}
Loading