10 changed files with 1 additions and 917 deletions
@ -1,49 +0,0 @@
@@ -1,49 +0,0 @@
|
||||
/* |
||||
* Copyright 2019-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.convention.versions; |
||||
|
||||
import java.io.File; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.io.PrintStream; |
||||
import java.util.Arrays; |
||||
import java.util.Scanner; |
||||
|
||||
class CommandLineUtils { |
||||
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); |
||||
} |
||||
} |
||||
|
||||
private static void writeLinesTo(InputStream input, PrintStream out) { |
||||
Scanner scanner = new Scanner(input); |
||||
while(scanner.hasNextLine()) { |
||||
out.println(scanner.nextLine()); |
||||
} |
||||
} |
||||
} |
||||
@ -1,179 +0,0 @@
@@ -1,179 +0,0 @@
|
||||
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); |
||||
} |
||||
} |
||||
} |
||||
@ -1,70 +0,0 @@
@@ -1,70 +0,0 @@
|
||||
/* |
||||
* Copyright 2019-2020 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.convention.versions; |
||||
|
||||
import okhttp3.OkHttpClient; |
||||
import okhttp3.Request; |
||||
import okhttp3.Response; |
||||
import org.w3c.dom.Document; |
||||
import org.xml.sax.SAXException; |
||||
|
||||
import javax.xml.XMLConstants; |
||||
import javax.xml.parsers.DocumentBuilder; |
||||
import javax.xml.parsers.DocumentBuilderFactory; |
||||
import javax.xml.parsers.ParserConfigurationException; |
||||
import javax.xml.xpath.XPath; |
||||
import javax.xml.xpath.XPathExpressionException; |
||||
import javax.xml.xpath.XPathFactory; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
|
||||
class TransitiveDependencyLookupUtils { |
||||
static String OIDC_SDK_NAME = "oauth2-oidc-sdk"; |
||||
static String NIMBUS_JOSE_JWT_NAME = "nimbus-jose-jwt"; |
||||
|
||||
private static OkHttpClient client = new OkHttpClient(); |
||||
|
||||
static String lookupJwtVersion(String oauthSdcVersion) { |
||||
Request request = new Request.Builder() |
||||
.get() |
||||
.url("https://repo.maven.apache.org/maven2/com/nimbusds/" + OIDC_SDK_NAME + "/" + oauthSdcVersion + "/" + OIDC_SDK_NAME + "-" + oauthSdcVersion + ".pom") |
||||
.build(); |
||||
try (Response response = client.newCall(request).execute()) { |
||||
if (!response.isSuccessful()) { |
||||
throw new IOException("Unexpected code " + response); |
||||
} |
||||
InputStream inputStream = response.body().byteStream(); |
||||
return getVersion(inputStream); |
||||
|
||||
} catch (Exception e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
} |
||||
|
||||
private static String getVersion(InputStream inputStream) throws ParserConfigurationException, IOException, SAXException, XPathExpressionException { |
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); |
||||
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); |
||||
DocumentBuilder db = dbf.newDocumentBuilder(); |
||||
|
||||
Document doc = db.parse(inputStream); |
||||
|
||||
doc.getDocumentElement().normalize(); |
||||
|
||||
XPath xPath = XPathFactory.newInstance().newXPath(); |
||||
return xPath.evaluate("/project/dependencies/dependency/version[../artifactId/text() = \"" + NIMBUS_JOSE_JWT_NAME + "\"]", doc); |
||||
} |
||||
} |
||||
@ -1,198 +0,0 @@
@@ -1,198 +0,0 @@
|
||||
package org.springframework.security.convention.versions; |
||||
|
||||
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 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<>(); |
||||
private List<Action<ComponentSelectionRulesWithCurrent>> components = new ArrayList<>(); |
||||
|
||||
List<Action<ComponentSelectionWithCurrent>> getActions() { |
||||
return actions; |
||||
} |
||||
|
||||
public List<Action<ComponentSelectionRulesWithCurrent>> getComponents() { |
||||
return components; |
||||
} |
||||
|
||||
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 minorVersionBump() { |
||||
this.actions.add(createExcludeMinorVersionBump()); |
||||
return this; |
||||
} |
||||
|
||||
public Action<ComponentSelectionWithCurrent> createExcludeMinorVersionBump() { |
||||
return (selection) -> { |
||||
String currentVersion = selection.getCurrentVersion(); |
||||
int majorSeparator = currentVersion.indexOf("."); |
||||
int separator = currentVersion.indexOf(".", majorSeparator + 1); |
||||
String majorMinor = separator > 0 ? currentVersion.substring(0, separator) : currentVersion; |
||||
String candidateVersion = selection.getCandidate().getVersion(); |
||||
if (!candidateVersion.startsWith(majorMinor)) { |
||||
selection.reject("Cannot upgrade to new Minor Version"); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
public DependencyExcludes releaseCandidatesVersions() { |
||||
this.actions.add(excludeVersionWithRegex("(?i).*?rc.*", "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; |
||||
} |
||||
|
||||
public DependencyExcludes addRule(Action<ComponentSelectionRulesWithCurrent> rule) { |
||||
this.components.add(rule); |
||||
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); |
||||
} |
||||
}; |
||||
} |
||||
} |
||||
} |
||||
@ -1,244 +0,0 @@
@@ -1,244 +0,0 @@
|
||||
/* |
||||
* Copyright 2019-2020 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.convention.versions; |
||||
|
||||
import com.github.benmanes.gradle.versions.reporter.result.Dependency; |
||||
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.File; |
||||
import java.time.Duration; |
||||
import java.util.*; |
||||
import java.util.function.Supplier; |
||||
import java.util.regex.Matcher; |
||||
import java.util.regex.Pattern; |
||||
|
||||
import static org.springframework.security.convention.versions.TransitiveDependencyLookupUtils.NIMBUS_JOSE_JWT_NAME; |
||||
import static org.springframework.security.convention.versions.TransitiveDependencyLookupUtils.OIDC_SDK_NAME; |
||||
|
||||
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); |
||||
}); |
||||
updateDependenciesSettings.getExcludes().getComponents().forEach((action) -> { |
||||
action.execute(components); |
||||
}); |
||||
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); |
||||
}); |
||||
List<DependencyOutdated> nimbusds = groups.getOrDefault("com.nimbusds", new ArrayList<>()); |
||||
DependencyOutdated oidcSdc = nimbusds.stream().filter(d -> d.getName().equals(OIDC_SDK_NAME)).findFirst().orElseGet(() -> null); |
||||
if(oidcSdc != null) { |
||||
String oidcVersion = updatedVersion(oidcSdc); |
||||
String jwtVersion = TransitiveDependencyLookupUtils.lookupJwtVersion(oidcVersion); |
||||
|
||||
Dependency nimbusJoseJwtDependency = result.getCurrent().getDependencies().stream().filter(d -> d.getName().equals(NIMBUS_JOSE_JWT_NAME)).findFirst().get(); |
||||
DependencyOutdated outdatedJwt = new DependencyOutdated(); |
||||
outdatedJwt.setVersion(nimbusJoseJwtDependency.getVersion()); |
||||
outdatedJwt.setGroup(oidcSdc.getGroup()); |
||||
outdatedJwt.setName(NIMBUS_JOSE_JWT_NAME); |
||||
VersionAvailable available = new VersionAvailable(); |
||||
available.setRelease(jwtVersion); |
||||
outdatedJwt.setAvailable(available); |
||||
nimbusds.add(outdatedJwt); |
||||
} |
||||
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(); |
||||
Integer issueNumber = gitHubApi.createIssue(createIssueResult.getRepositoryId(), title, createIssueResult.getLabelIds(), createIssueResult.getMilestoneId(), createIssueResult.getAssigneeId()).delayElement(Duration.ofSeconds(1)).block(); |
||||
commitMessage += "\n\nCloses gh-" + issueNumber; |
||||
} |
||||
CommandLineUtils.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) { |
||||
if (!result.getGradle().isEnabled()) { |
||||
return; |
||||
} |
||||
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); |
||||
CommandLineUtils.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 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); |
||||
FileUtils.replaceFileText(buildFile, buildFileText -> buildFileText.replace(originalDependency, replacementDependency)); |
||||
} |
||||
|
||||
static void updateDependencyWithVersionVariable(File scanFile, File gradlePropertiesFile, DependencyOutdated dependency) { |
||||
if (!gradlePropertiesFile.exists()) { |
||||
return; |
||||
} |
||||
FileUtils.replaceFileText(gradlePropertiesFile, (gradlePropertiesText) -> { |
||||
String ga = dependency.getGroup() + ":" + dependency.getName() + ":"; |
||||
Pattern pattern = Pattern.compile("\"" + ga + "\\$\\{?([^'\"]+?)\\}?\""); |
||||
String buildFileText = FileUtils.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(); |
||||
} |
||||
} |
||||
@ -1,86 +0,0 @@
@@ -1,86 +0,0 @@
|
||||
/* |
||||
* Copyright 2019-2020 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.convention.versions; |
||||
|
||||
import com.github.benmanes.gradle.versions.updates.resolutionstrategy.ComponentSelectionWithCurrent; |
||||
import org.gradle.api.Action; |
||||
import org.gradle.api.artifacts.ComponentSelection; |
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import java.util.Collections; |
||||
|
||||
import static org.mockito.ArgumentMatchers.any; |
||||
import static org.mockito.BDDMockito.given; |
||||
import static org.mockito.Mockito.*; |
||||
|
||||
public class DependencyExcludesTests { |
||||
|
||||
@Test |
||||
public void createExcludeMinorVersionBumpWhenMajorVersionBumpThenReject() { |
||||
ComponentSelection componentSelection = executeCreateExcludeMinorVersionBump("1.0.0", "2.0.0"); |
||||
verify(componentSelection).reject(any()); |
||||
} |
||||
|
||||
@Test |
||||
public void createExcludeMinorVersionBumpWhenMajorCalVersionBumpThenReject() { |
||||
ComponentSelection componentSelection = executeCreateExcludeMinorVersionBump("2000.0.0", "2001.0.0"); |
||||
verify(componentSelection).reject(any()); |
||||
} |
||||
|
||||
@Test |
||||
public void createExcludeMinorVersionBumpWhenMinorVersionBumpThenReject() { |
||||
ComponentSelection componentSelection = executeCreateExcludeMinorVersionBump("1.0.0", "1.1.0"); |
||||
verify(componentSelection).reject(any()); |
||||
} |
||||
|
||||
@Test |
||||
public void createExcludeMinorVersionBumpWhenMinorCalVersionBumpThenReject() { |
||||
ComponentSelection componentSelection = executeCreateExcludeMinorVersionBump("2000.0.0", "2000.1.0"); |
||||
verify(componentSelection).reject(any()); |
||||
} |
||||
|
||||
@Test |
||||
public void createExcludeMinorVersionBumpWhenMinorAndPatchVersionBumpThenReject() { |
||||
ComponentSelection componentSelection = executeCreateExcludeMinorVersionBump("1.0.0", "1.1.1"); |
||||
verify(componentSelection).reject(any()); |
||||
} |
||||
|
||||
@Test |
||||
public void createExcludeMinorVersionBumpWhenPatchVersionBumpThenDoesNotReject() { |
||||
ComponentSelection componentSelection = executeCreateExcludeMinorVersionBump("1.0.0", "1.0.1"); |
||||
verify(componentSelection, times(0)).reject(any()); |
||||
} |
||||
|
||||
private ComponentSelection executeCreateExcludeMinorVersionBump(String currentVersion, String candidateVersion) { |
||||
ComponentSelection componentSelection = mock(ComponentSelection.class); |
||||
UpdateDependenciesExtension.DependencyExcludes excludes = new UpdateDependenciesExtension(() -> Collections.emptyList()).new DependencyExcludes(); |
||||
Action<ComponentSelectionWithCurrent> excludeMinorVersionBump = excludes.createExcludeMinorVersionBump(); |
||||
ComponentSelectionWithCurrent selection = currentVersionAndCandidateVersion(componentSelection, currentVersion, candidateVersion); |
||||
excludeMinorVersionBump.execute(selection); |
||||
return componentSelection; |
||||
} |
||||
|
||||
private ComponentSelectionWithCurrent currentVersionAndCandidateVersion(ComponentSelection componentSelection, String currentVersion, String candidateVersion) { |
||||
ModuleComponentIdentifier candidate = mock(ModuleComponentIdentifier.class); |
||||
given(componentSelection.getCandidate()).willReturn(candidate); |
||||
ComponentSelectionWithCurrent selection = new ComponentSelectionWithCurrent(currentVersion, componentSelection); |
||||
given(candidate.getVersion()).willReturn(candidateVersion); |
||||
given(componentSelection.getCandidate()).willReturn(candidate); |
||||
return selection; |
||||
} |
||||
} |
||||
@ -1,31 +0,0 @@
@@ -1,31 +0,0 @@
|
||||
/* |
||||
* Copyright 2019-2020 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.convention.versions; |
||||
|
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
public class TransitiveDependencyLookupUtilsTest { |
||||
|
||||
@Test |
||||
public void lookupJwtVersionWhen93Then961() { |
||||
String s = TransitiveDependencyLookupUtils.lookupJwtVersion("9.3"); |
||||
assertThat(s).isEqualTo("9.6.1"); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue