spring security UpdateDependenciesPlugin 源码

  • 2022-08-13
  • 浏览 (275)

spring security UpdateDependenciesPlugin 代码

文件路径:/buildSrc/src/main/java/org/springframework/security/convention/versions/UpdateDependenciesPlugin.java

/*
 * 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();
	}
}

相关信息

spring security 源码目录

相关文章

spring security CommandLineUtils 源码

spring security FileUtils 源码

spring security GitHubApi 源码

spring security TransitiveDependencyLookupUtils 源码

spring security UpdateDependenciesExtension 源码

spring security UpdateProjectVersionPlugin 源码

spring security UpdateProjectVersionTask 源码

spring security UpdateToSnapshotVersionTask 源码

0  赞