Browse Source
Allows for updating the depencencies of the project in an automated fashion. Closes gh-9542pull/9549/head
10 changed files with 675 additions and 6 deletions
@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
|
||||
mutation CreateIssueInput($assigneeId: ID!, $labelIds: [ID!], $milestoneId: ID!, $repositoryId: ID!, $title: String!) { |
||||
createIssue(input: {assigneeIds: [$assigneeId], labelIds: $labelIds, milestoneId: $milestoneId, projectIds: [], repositoryId: $repositoryId, title: $title}) { |
||||
issue { |
||||
number |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
query FindCreateIssueInput($owner: String!, $name: String!, $labelQuery: String, $milestoneName: String) { |
||||
repository(owner: $owner, name: $name) { |
||||
id |
||||
labels(query: $labelQuery, first: 1) { |
||||
nodes { |
||||
id |
||||
name |
||||
} |
||||
} |
||||
milestones(query: $milestoneName, states: [OPEN], first: 1) { |
||||
nodes { |
||||
id |
||||
title |
||||
} |
||||
} |
||||
} |
||||
viewer { |
||||
id |
||||
} |
||||
} |
||||
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
query RateLimit { |
||||
rateLimit { |
||||
limit |
||||
cost |
||||
remaining |
||||
resetAt |
||||
} |
||||
} |
||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,179 @@
@@ -0,0 +1,179 @@
|
||||
package org.springframework.security.convention.versions; |
||||
|
||||
import com.apollographql.apollo.ApolloCall; |
||||
import com.apollographql.apollo.ApolloClient; |
||||
import com.apollographql.apollo.api.Input; |
||||
import com.apollographql.apollo.api.Response; |
||||
import com.apollographql.apollo.exception.ApolloException; |
||||
import okhttp3.Interceptor; |
||||
import okhttp3.OkHttpClient; |
||||
import okhttp3.Request; |
||||
import org.jetbrains.annotations.NotNull; |
||||
import reactor.core.publisher.Mono; |
||||
import reactor.util.retry.Retry; |
||||
import reactor.util.retry.RetrySpec; |
||||
|
||||
import java.io.IOException; |
||||
import java.time.Duration; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
import java.util.Optional; |
||||
import java.util.stream.Collectors; |
||||
|
||||
public class GitHubApi { |
||||
|
||||
private final ApolloClient apolloClient; |
||||
|
||||
public GitHubApi(String githubToken) { |
||||
if (githubToken == null) { |
||||
throw new IllegalArgumentException("githubToken is required. You can set it using -PgitHubAccessToken="); |
||||
} |
||||
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); |
||||
clientBuilder.addInterceptor(new AuthorizationInterceptor(githubToken)); |
||||
this.apolloClient = ApolloClient.builder() |
||||
.serverUrl("https://api.github.com/graphql") |
||||
.okHttpClient(clientBuilder.build()) |
||||
.build(); |
||||
} |
||||
|
||||
public Mono<FindCreateIssueResult> findCreateIssueInput(String owner, String name, String milestone) { |
||||
String label = "\"type: dependency-upgrade\""; |
||||
FindCreateIssueInputQuery findCreateIssueInputQuery = new FindCreateIssueInputQuery(owner, name, Input.optional(label), Input.optional(milestone)); |
||||
return Mono.create( sink -> this.apolloClient.query(findCreateIssueInputQuery) |
||||
.enqueue(new ApolloCall.Callback<FindCreateIssueInputQuery.Data>() { |
||||
@Override |
||||
public void onResponse(@NotNull Response<FindCreateIssueInputQuery.Data> response) { |
||||
if (response.hasErrors()) { |
||||
sink.error(new RuntimeException(response.getErrors().stream().map(e -> e.getMessage()).collect(Collectors.joining(" ")))); |
||||
} else { |
||||
FindCreateIssueInputQuery.Data data = response.getData(); |
||||
FindCreateIssueInputQuery.Repository repository = data.repository(); |
||||
List<String> labels = repository.labels().nodes().stream().map(FindCreateIssueInputQuery.Node::id).collect(Collectors.toList()); |
||||
if (labels.isEmpty()) { |
||||
sink.error(new IllegalArgumentException("Could not find label for " + label)); |
||||
return; |
||||
} |
||||
Optional<String> firstMilestoneId = repository.milestones().nodes().stream().map(FindCreateIssueInputQuery.Node1::id).findFirst(); |
||||
if (!firstMilestoneId.isPresent()) { |
||||
sink.error(new IllegalArgumentException("Could not find OPEN milestone id for " + milestone)); |
||||
return; |
||||
} |
||||
String milestoneId = firstMilestoneId.get(); |
||||
String repositoryId = repository.id(); |
||||
String assigneeId = data.viewer().id(); |
||||
sink.success(new FindCreateIssueResult(repositoryId, labels, milestoneId, assigneeId)); |
||||
} |
||||
} |
||||
@Override |
||||
public void onFailure(@NotNull ApolloException e) { |
||||
sink.error(e); |
||||
} |
||||
})); |
||||
} |
||||
|
||||
public static class FindCreateIssueResult { |
||||
private final String repositoryId; |
||||
private final List<String> labelIds; |
||||
private final String milestoneId; |
||||
private final String assigneeId; |
||||
|
||||
public FindCreateIssueResult(String repositoryId, List<String> labelIds, String milestoneId, String assigneeId) { |
||||
this.repositoryId = repositoryId; |
||||
this.labelIds = labelIds; |
||||
this.milestoneId = milestoneId; |
||||
this.assigneeId = assigneeId; |
||||
} |
||||
|
||||
public String getRepositoryId() { |
||||
return repositoryId; |
||||
} |
||||
|
||||
public List<String> getLabelIds() { |
||||
return labelIds; |
||||
} |
||||
|
||||
public String getMilestoneId() { |
||||
return milestoneId; |
||||
} |
||||
|
||||
public String getAssigneeId() { |
||||
return assigneeId; |
||||
} |
||||
} |
||||
|
||||
public Mono<RateLimitQuery.RateLimit> findRateLimit() { |
||||
return Mono.create( sink -> this.apolloClient.query(new RateLimitQuery()) |
||||
.enqueue(new ApolloCall.Callback<RateLimitQuery.Data>() { |
||||
@Override |
||||
public void onResponse(@NotNull Response<RateLimitQuery.Data> response) { |
||||
if (response.hasErrors()) { |
||||
sink.error(new RuntimeException(response.getErrors().stream().map(e -> e.getMessage()).collect(Collectors.joining(" ")))); |
||||
} else { |
||||
sink.success(response.getData().rateLimit()); |
||||
} |
||||
} |
||||
@Override |
||||
public void onFailure(@NotNull ApolloException e) { |
||||
sink.error(e); |
||||
} |
||||
})); |
||||
} |
||||
|
||||
public Mono<Integer> createIssue(String repositoryId, String title, List<String> labelIds, String milestoneId, String assigneeId) { |
||||
CreateIssueInputMutation createIssue = new CreateIssueInputMutation.Builder() |
||||
.repositoryId(repositoryId) |
||||
.title(title) |
||||
.labelIds(labelIds) |
||||
.milestoneId(milestoneId) |
||||
.assigneeId(assigneeId) |
||||
.build(); |
||||
return Mono.create( sink -> this.apolloClient.mutate(createIssue) |
||||
.enqueue(new ApolloCall.Callback<CreateIssueInputMutation.Data>() { |
||||
@Override |
||||
public void onResponse(@NotNull Response<CreateIssueInputMutation.Data> response) { |
||||
if (response.hasErrors()) { |
||||
String message = response.getErrors().stream().map(e -> e.getMessage() + " " + e.getCustomAttributes() + " " + e.getLocations()).collect(Collectors.joining(" ")); |
||||
if (message.contains("was submitted too quickly")) { |
||||
sink.error(new SubmittedTooQuick(message)); |
||||
} else { |
||||
sink.error(new RuntimeException(message)); |
||||
} |
||||
} else { |
||||
sink.success(response.getData().createIssue().issue().number()); |
||||
} |
||||
} |
||||
@Override |
||||
public void onFailure(@NotNull ApolloException e) { |
||||
sink.error(e); |
||||
} |
||||
})) |
||||
.retryWhen( |
||||
RetrySpec.fixedDelay(3, Duration.ofMinutes(1)) |
||||
.filter(SubmittedTooQuick.class::isInstance) |
||||
.doBeforeRetry(r -> System.out.println("Pausing for 1 minute and then retrying due to receiving \"submitted too quickly\" error from GitHub API")) |
||||
) |
||||
.cast(Integer.class); |
||||
} |
||||
|
||||
public static class SubmittedTooQuick extends RuntimeException { |
||||
public SubmittedTooQuick(String message) { |
||||
super(message); |
||||
} |
||||
} |
||||
|
||||
private static class AuthorizationInterceptor implements Interceptor { |
||||
|
||||
private final String token; |
||||
|
||||
public AuthorizationInterceptor(String token) { |
||||
this.token = token; |
||||
} |
||||
|
||||
@Override |
||||
public okhttp3.Response intercept(Chain chain) throws IOException { |
||||
Request request = chain.request().newBuilder() |
||||
.addHeader("Authorization", "Bearer " + this.token).build(); |
||||
return chain.proceed(request); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,168 @@
@@ -0,0 +1,168 @@
|
||||
package org.springframework.security.convention.versions; |
||||
|
||||
import com.github.benmanes.gradle.versions.updates.resolutionstrategy.ComponentSelectionWithCurrent; |
||||
import org.gradle.api.Action; |
||||
|
||||
import java.io.File; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.function.Supplier; |
||||
import java.util.regex.Pattern; |
||||
|
||||
public class UpdateDependenciesExtension { |
||||
private Supplier<List<File>> files; |
||||
|
||||
private UpdateMode updateMode = UpdateMode.COMMIT; |
||||
|
||||
private DependencyExcludes dependencyExcludes = new DependencyExcludes(); |
||||
|
||||
private GitHub gitHub = new GitHub(); |
||||
|
||||
public UpdateDependenciesExtension(Supplier<List<File>> files) { |
||||
this.files = files; |
||||
} |
||||
|
||||
public void setUpdateMode(UpdateMode updateMode) { |
||||
this.updateMode = updateMode; |
||||
} |
||||
|
||||
public UpdateMode getUpdateMode() { |
||||
return updateMode; |
||||
} |
||||
|
||||
GitHub getGitHub() { |
||||
return this.gitHub; |
||||
} |
||||
|
||||
DependencyExcludes getExcludes() { |
||||
return dependencyExcludes; |
||||
} |
||||
|
||||
Supplier<List<File>> getFiles() { |
||||
return files; |
||||
} |
||||
|
||||
public void setFiles(Supplier<List<File>> files) { |
||||
this.files = files; |
||||
} |
||||
|
||||
public void addFiles(Supplier<List<File>> files) { |
||||
Supplier<List<File>> original = this.files; |
||||
setFiles(() -> { |
||||
List<File> result = new ArrayList<>(original.get()); |
||||
result.addAll(files.get()); |
||||
return result; |
||||
}); |
||||
} |
||||
|
||||
public void dependencyExcludes(Action<DependencyExcludes> excludes) { |
||||
excludes.execute(this.dependencyExcludes); |
||||
} |
||||
|
||||
public void gitHub(Action<GitHub> gitHub) { |
||||
gitHub.execute(this.gitHub); |
||||
} |
||||
|
||||
public enum UpdateMode { |
||||
COMMIT, |
||||
GITHUB_ISSUE |
||||
} |
||||
|
||||
public class GitHub { |
||||
private String organization; |
||||
|
||||
private String repository; |
||||
|
||||
private String accessToken; |
||||
|
||||
private String milestone; |
||||
|
||||
public String getOrganization() { |
||||
return organization; |
||||
} |
||||
|
||||
public void setOrganization(String organization) { |
||||
this.organization = organization; |
||||
} |
||||
|
||||
public String getRepository() { |
||||
return repository; |
||||
} |
||||
|
||||
public void setRepository(String repository) { |
||||
this.repository = repository; |
||||
} |
||||
|
||||
public String getAccessToken() { |
||||
return accessToken; |
||||
} |
||||
|
||||
public void setAccessToken(String accessToken) { |
||||
this.accessToken = accessToken; |
||||
} |
||||
|
||||
public String getMilestone() { |
||||
return milestone; |
||||
} |
||||
|
||||
public void setMilestone(String milestone) { |
||||
this.milestone = milestone; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Consider creating some Predicates instead since they are composible |
||||
*/ |
||||
public class DependencyExcludes { |
||||
private List<Action<ComponentSelectionWithCurrent>> actions = new ArrayList<>(); |
||||
|
||||
List<Action<ComponentSelectionWithCurrent>> getActions() { |
||||
return actions; |
||||
} |
||||
|
||||
public DependencyExcludes alphaBetaVersions() { |
||||
this.actions.add(excludeVersionWithRegex("(?i).*?(alpha|beta).*", "an alpha or beta version")); |
||||
return this; |
||||
} |
||||
|
||||
public DependencyExcludes majorVersionBump() { |
||||
this.actions.add((selection) -> { |
||||
String currentVersion = selection.getCurrentVersion(); |
||||
int separator = currentVersion.indexOf("."); |
||||
String major = separator > 0 ? currentVersion.substring(0, separator) : currentVersion; |
||||
String candidateVersion = selection.getCandidate().getVersion(); |
||||
Pattern calVerPattern = Pattern.compile("\\d\\d\\d\\d.*"); |
||||
boolean isCalVer = calVerPattern.matcher(candidateVersion).matches(); |
||||
if (!isCalVer && !candidateVersion.startsWith(major)) { |
||||
selection.reject("Cannot upgrade to new Major Version"); |
||||
} |
||||
}); |
||||
return this; |
||||
} |
||||
|
||||
public DependencyExcludes releaseCandidatesVersions() { |
||||
this.actions.add(excludeVersionWithRegex("(?i).*?rc\\d+.*", "a release candidate version")); |
||||
return this; |
||||
} |
||||
|
||||
public DependencyExcludes milestoneVersions() { |
||||
this.actions.add(excludeVersionWithRegex("(?i).*?m\\d+.*", "a milestone version")); |
||||
return this; |
||||
} |
||||
|
||||
public DependencyExcludes snapshotVersions() { |
||||
this.actions.add(excludeVersionWithRegex(".*?-SNAPSHOT.*", "a SNAPSHOT version")); |
||||
return this; |
||||
} |
||||
|
||||
private Action<ComponentSelectionWithCurrent> excludeVersionWithRegex(String regex, String reason) { |
||||
Pattern pattern = Pattern.compile(regex); |
||||
return (selection) -> { |
||||
String candidateVersion = selection.getCandidate().getVersion(); |
||||
if (pattern.matcher(candidateVersion).matches()) { |
||||
selection.reject(candidateVersion + " is not allowed because it is " + reason); |
||||
} |
||||
}; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,254 @@
@@ -0,0 +1,254 @@
|
||||
package org.springframework.security.convention.versions; |
||||
|
||||
import com.github.benmanes.gradle.versions.reporter.result.DependencyOutdated; |
||||
import com.github.benmanes.gradle.versions.reporter.result.Result; |
||||
import com.github.benmanes.gradle.versions.reporter.result.VersionAvailable; |
||||
import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask; |
||||
import com.github.benmanes.gradle.versions.updates.gradle.GradleUpdateResult; |
||||
import com.github.benmanes.gradle.versions.updates.resolutionstrategy.ComponentSelectionRulesWithCurrent; |
||||
import com.github.benmanes.gradle.versions.updates.resolutionstrategy.ComponentSelectionWithCurrent; |
||||
import com.github.benmanes.gradle.versions.updates.resolutionstrategy.ResolutionStrategyWithCurrent; |
||||
import groovy.lang.Closure; |
||||
import org.gradle.api.Action; |
||||
import org.gradle.api.Plugin; |
||||
import org.gradle.api.Project; |
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier; |
||||
import reactor.core.publisher.Mono; |
||||
|
||||
import java.io.*; |
||||
import java.nio.file.Files; |
||||
import java.time.Duration; |
||||
import java.util.*; |
||||
import java.util.function.Function; |
||||
import java.util.function.Supplier; |
||||
import java.util.regex.Matcher; |
||||
import java.util.regex.Pattern; |
||||
|
||||
public class UpdateDependenciesPlugin implements Plugin<Project> { |
||||
private GitHubApi gitHubApi; |
||||
|
||||
@Override |
||||
public void apply(Project project) { |
||||
UpdateDependenciesExtension updateDependenciesSettings = project.getExtensions().create("updateDependenciesSettings", UpdateDependenciesExtension.class, defaultFiles(project)); |
||||
if (project.hasProperty("updateMode")) { |
||||
String updateMode = String.valueOf(project.findProperty("updateMode")); |
||||
updateDependenciesSettings.setUpdateMode(UpdateDependenciesExtension.UpdateMode.valueOf(updateMode)); |
||||
} |
||||
if (project.hasProperty("nextVersion")) { |
||||
String nextVersion = String.valueOf(project.findProperty("nextVersion")); |
||||
updateDependenciesSettings.getGitHub().setMilestone(nextVersion); |
||||
} |
||||
if (project.hasProperty("gitHubAccessToken")) { |
||||
String gitHubAccessToken = String.valueOf(project.findProperty("gitHubAccessToken")); |
||||
updateDependenciesSettings.getGitHub().setAccessToken(gitHubAccessToken); |
||||
} |
||||
project.getTasks().register("updateDependencies", DependencyUpdatesTask.class, new Action<DependencyUpdatesTask>() { |
||||
@Override |
||||
public void execute(DependencyUpdatesTask updateDependencies) { |
||||
updateDependencies.setDescription("Update the dependencies"); |
||||
updateDependencies.setCheckConstraints(true); |
||||
updateDependencies.setOutputFormatter(new Closure<Void>(null) { |
||||
@Override |
||||
public Void call(Object argument) { |
||||
Result result = (Result) argument; |
||||
if (gitHubApi == null && updateDependenciesSettings.getUpdateMode() != UpdateDependenciesExtension.UpdateMode.COMMIT) { |
||||
gitHubApi = new GitHubApi(updateDependenciesSettings.getGitHub().getAccessToken()); |
||||
} |
||||
updateDependencies(result, project, updateDependenciesSettings); |
||||
updateGradleVersion(result, project, updateDependenciesSettings); |
||||
return null; |
||||
} |
||||
}); |
||||
updateDependencies.resolutionStrategy(new Action<ResolutionStrategyWithCurrent>() { |
||||
@Override |
||||
public void execute(ResolutionStrategyWithCurrent resolution) { |
||||
resolution.componentSelection(new Action<ComponentSelectionRulesWithCurrent>() { |
||||
@Override |
||||
public void execute(ComponentSelectionRulesWithCurrent components) { |
||||
updateDependenciesSettings.getExcludes().getActions().forEach((action) -> { |
||||
components.all(action); |
||||
}); |
||||
components.all((selection) -> { |
||||
ModuleComponentIdentifier candidate = selection.getCandidate(); |
||||
if (candidate.getGroup().startsWith("org.apache.directory.") && !candidate.getVersion().equals(selection.getCurrentVersion())) { |
||||
selection.reject("org.apache.directory.* has breaking changes in newer versions"); |
||||
} |
||||
}); |
||||
String jaxbBetaRegex = ".*?b\\d+.*"; |
||||
components.withModule("javax.xml.bind:jaxb-api", excludeWithRegex(jaxbBetaRegex, "Reject jaxb-api beta versions")); |
||||
components.withModule("com.sun.xml.bind:jaxb-impl", excludeWithRegex(jaxbBetaRegex, "Reject jaxb-api beta versions")); |
||||
components.withModule("commons-collections:commons-collections", excludeWithRegex("^\\d{3,}.*", "Reject commons-collections date based releases")); |
||||
} |
||||
}); |
||||
} |
||||
}); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
private void updateDependencies(Result result, Project project, UpdateDependenciesExtension updateDependenciesSettings) { |
||||
SortedSet<DependencyOutdated> dependencies = result.getOutdated().getDependencies(); |
||||
if (dependencies.isEmpty()) { |
||||
return; |
||||
} |
||||
Map<String, List<DependencyOutdated>> groups = new LinkedHashMap<>(); |
||||
dependencies.forEach(outdated -> { |
||||
groups.computeIfAbsent(outdated.getGroup(), (key) -> new ArrayList<>()).add(outdated); |
||||
}); |
||||
File gradlePropertiesFile = project.getRootProject().file(Project.GRADLE_PROPERTIES); |
||||
Mono<GitHubApi.FindCreateIssueResult> createIssueResult = createIssueResultMono(updateDependenciesSettings); |
||||
List<File> filesWithDependencies = updateDependenciesSettings.getFiles().get(); |
||||
groups.forEach((group, outdated) -> { |
||||
outdated.forEach((dependency) -> { |
||||
String ga = dependency.getGroup() + ":" + dependency.getName() + ":"; |
||||
String originalDependency = ga + dependency.getVersion(); |
||||
String replacementDependency = ga + updatedVersion(dependency); |
||||
System.out.println("Update " + originalDependency + " to " + replacementDependency); |
||||
filesWithDependencies.forEach((fileWithDependency) -> { |
||||
updateDependencyInlineVersion(fileWithDependency, dependency); |
||||
updateDependencyWithVersionVariable(fileWithDependency, gradlePropertiesFile, dependency); |
||||
}); |
||||
}); |
||||
|
||||
// commit
|
||||
DependencyOutdated firstDependency = outdated.get(0); |
||||
String updatedVersion = updatedVersion(firstDependency); |
||||
String title = outdated.size() == 1 ? "Update " + firstDependency.getName() + " to " + updatedVersion : "Update " + firstDependency.getGroup() + " to " + updatedVersion; |
||||
afterGroup(updateDependenciesSettings, project.getRootDir(), title, createIssueResult); |
||||
}); |
||||
} |
||||
|
||||
private void afterGroup(UpdateDependenciesExtension updateDependenciesExtension, File rootDir, String title, Mono<GitHubApi.FindCreateIssueResult> createIssueResultMono) { |
||||
|
||||
String commitMessage = title; |
||||
if (updateDependenciesExtension.getUpdateMode() == UpdateDependenciesExtension.UpdateMode.GITHUB_ISSUE) { |
||||
GitHubApi.FindCreateIssueResult createIssueResult = createIssueResultMono.block(); |
||||
RateLimitQuery.RateLimit rateLimit = gitHubApi.findRateLimit().block(); |
||||
rateLimit = gitHubApi.findRateLimit().block(); |
||||
System.out.println("remaining " + rateLimit.remaining() + " reset at " + rateLimit.resetAt()); |
||||
Integer issueNumber = gitHubApi.createIssue(createIssueResult.getRepositoryId(), title, createIssueResult.getLabelIds(), createIssueResult.getMilestoneId(), createIssueResult.getAssigneeId()).delayElement(Duration.ofSeconds(1)).block(); |
||||
commitMessage += "\n\nCloses gh-" + issueNumber; |
||||
} |
||||
runCommand(rootDir, "git", "commit", "-am", commitMessage); |
||||
} |
||||
|
||||
private Mono<GitHubApi.FindCreateIssueResult> createIssueResultMono(UpdateDependenciesExtension updateDependenciesExtension) { |
||||
return Mono.defer(() -> { |
||||
UpdateDependenciesExtension.GitHub gitHub = updateDependenciesExtension.getGitHub(); |
||||
return gitHubApi.findCreateIssueInput(gitHub.getOrganization(), gitHub.getRepository(), gitHub.getMilestone()).cache(); |
||||
}); |
||||
} |
||||
|
||||
private void updateGradleVersion(Result result, Project project, UpdateDependenciesExtension updateDependenciesSettings) { |
||||
GradleUpdateResult current = result.getGradle().getCurrent(); |
||||
GradleUpdateResult running = result.getGradle().getRunning(); |
||||
if (current.compareTo(running) > 0) { |
||||
String title = "Update Gradle to " + current.getVersion(); |
||||
System.out.println(title); |
||||
runCommand(project.getRootDir(), "./gradlew", "wrapper", "--gradle-version", current.getVersion(), "--no-daemon"); |
||||
afterGroup(updateDependenciesSettings, project.getRootDir(), title, createIssueResultMono(updateDependenciesSettings)); |
||||
} |
||||
} |
||||
|
||||
private static Supplier<List<File>> defaultFiles(Project project) { |
||||
return () -> { |
||||
List<File> result = new ArrayList<>(); |
||||
result.add(project.getBuildFile()); |
||||
project.getChildProjects().values().forEach((childProject) -> |
||||
result.add(childProject.getBuildFile()) |
||||
); |
||||
result.add(project.getRootProject().file("buildSrc/build.gradle")); |
||||
return result; |
||||
}; |
||||
} |
||||
|
||||
static void runCommand(File dir, String... args) { |
||||
try { |
||||
Process process = new ProcessBuilder() |
||||
.directory(dir) |
||||
.command(args) |
||||
.start(); |
||||
writeLinesTo(process.getInputStream(), System.out); |
||||
writeLinesTo(process.getErrorStream(), System.out); |
||||
if (process.waitFor() != 0) { |
||||
new RuntimeException("Failed to run " + Arrays.toString(args)); |
||||
} |
||||
} catch (IOException | InterruptedException e) { |
||||
throw new RuntimeException("Failed to run " + Arrays.toString(args), e); |
||||
} |
||||
} |
||||
|
||||
static void writeLinesTo(InputStream input, PrintStream out) { |
||||
Scanner scanner = new Scanner(input); |
||||
while(scanner.hasNextLine()) { |
||||
out.println(scanner.nextLine()); |
||||
} |
||||
} |
||||
|
||||
|
||||
static Action<ComponentSelectionWithCurrent> excludeWithRegex(String regex, String reason) { |
||||
Pattern pattern = Pattern.compile(regex); |
||||
return (selection) -> { |
||||
String candidateVersion = selection.getCandidate().getVersion(); |
||||
if (pattern.matcher(candidateVersion).matches()) { |
||||
selection.reject(candidateVersion + " is not allowed because it is " + reason); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
static void updateDependencyInlineVersion(File buildFile, DependencyOutdated dependency){ |
||||
String ga = dependency.getGroup() + ":" + dependency.getName() + ":"; |
||||
String originalDependency = ga + dependency.getVersion(); |
||||
String replacementDependency = ga + updatedVersion(dependency); |
||||
replaceFileText(buildFile, buildFileText -> buildFileText.replace(originalDependency, replacementDependency)); |
||||
} |
||||
|
||||
static void replaceFileText(File file, Function<String, String> replaceText) { |
||||
String buildFileText = readString(file); |
||||
String updatedBuildFileText = replaceText.apply(buildFileText); |
||||
writeString(file, updatedBuildFileText); |
||||
} |
||||
|
||||
private static String readString(File file) { |
||||
try { |
||||
byte[] bytes = Files.readAllBytes(file.toPath()); |
||||
return new String(bytes); |
||||
} catch (IOException e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
} |
||||
|
||||
private static void writeString(File file, String text) { |
||||
try { |
||||
Files.write(file.toPath(), text.getBytes()); |
||||
} catch (IOException e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
} |
||||
|
||||
static void updateDependencyWithVersionVariable(File scanFile, File gradlePropertiesFile, DependencyOutdated dependency) { |
||||
if (!gradlePropertiesFile.exists()) { |
||||
return; |
||||
} |
||||
replaceFileText(gradlePropertiesFile, (gradlePropertiesText) -> { |
||||
String ga = dependency.getGroup() + ":" + dependency.getName() + ":"; |
||||
Pattern pattern = Pattern.compile("\"" + ga + "\\$\\{?([^'\"]+?)\\}?\""); |
||||
String buildFileText = readString(scanFile); |
||||
Matcher matcher = pattern.matcher(buildFileText); |
||||
while (matcher.find()) { |
||||
String versionVariable = matcher.group(1); |
||||
gradlePropertiesText = gradlePropertiesText.replace(versionVariable + "=" + dependency.getVersion(), versionVariable + "=" + updatedVersion(dependency)); |
||||
} |
||||
return gradlePropertiesText; |
||||
}); |
||||
} |
||||
|
||||
private static String updatedVersion(DependencyOutdated dependency) { |
||||
VersionAvailable available = dependency.getAvailable(); |
||||
String release = available.getRelease(); |
||||
if (release != null) { |
||||
return release; |
||||
} |
||||
return available.getMilestone(); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue