Add external links to `spring-boot-dependencies`
Update the BOM `Library` model to support external links that we can use in documentation and the release process. An additional `checkLinks` task has also been added to verify returned HTTP status codes. Closes gh-39779 Co-authored-by: Andy Wilkinson <andy.wilkinson@broadcom.com>
This commit is contained in:
parent
e44ec27fd6
commit
75c7bed6c6
|
@ -47,6 +47,7 @@ dependencies {
|
|||
implementation("commons-codec:commons-codec:${versions.commonsCodec}")
|
||||
implementation("de.undercouch.download:de.undercouch.download.gradle.plugin:5.5.0")
|
||||
implementation("io.spring.javaformat:spring-javaformat-gradle-plugin:${javaFormatVersion}")
|
||||
implementation("org.apache.httpcomponents.client5:httpclient5:5.3.1")
|
||||
implementation("org.apache.maven:maven-embedder:${versions.maven}")
|
||||
implementation("org.asciidoctor:asciidoctor-gradle-jvm:3.3.2")
|
||||
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}")
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.util.HashMap;
|
|||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
@ -66,11 +67,14 @@ import org.springframework.boot.build.bom.Library.VersionAlignment;
|
|||
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
|
||||
import org.springframework.boot.build.mavenplugin.MavenExec;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.PropertyPlaceholderHelper;
|
||||
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
|
||||
|
||||
/**
|
||||
* DSL extensions for {@link BomPlugin}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class BomExtension {
|
||||
|
||||
|
@ -119,7 +123,8 @@ public class BomExtension {
|
|||
this.project, this.libraries, libraryHandler.groups)
|
||||
: null;
|
||||
addLibrary(new Library(name, libraryHandler.calendarName, libraryVersion, libraryHandler.groups,
|
||||
libraryHandler.prohibitedVersions, libraryHandler.considerSnapshots, versionAlignment));
|
||||
libraryHandler.prohibitedVersions, libraryHandler.considerSnapshots, versionAlignment,
|
||||
libraryHandler.links));
|
||||
}
|
||||
|
||||
public void effectiveBomArtifact() {
|
||||
|
@ -227,6 +232,8 @@ public class BomExtension {
|
|||
|
||||
private AlignWithVersionHandler alignWithVersion;
|
||||
|
||||
private final Map<String, Function<LibraryVersion, String>> links = new HashMap<>();
|
||||
|
||||
@Inject
|
||||
public LibraryHandler(String version) {
|
||||
this.version = version;
|
||||
|
@ -263,6 +270,12 @@ public class BomExtension {
|
|||
action.execute(this.alignWithVersion);
|
||||
}
|
||||
|
||||
public void links(Action<LinksHandler> action) {
|
||||
LinksHandler handler = new LinksHandler();
|
||||
action.execute(handler);
|
||||
this.links.putAll(handler.links);
|
||||
}
|
||||
|
||||
public static class ProhibitedHandler {
|
||||
|
||||
private String reason;
|
||||
|
@ -398,6 +411,67 @@ public class BomExtension {
|
|||
|
||||
}
|
||||
|
||||
public static class LinksHandler {
|
||||
|
||||
private final Map<String, Function<LibraryVersion, String>> links = new HashMap<>();
|
||||
|
||||
public void site(String linkTemplate) {
|
||||
site(asFactory(linkTemplate));
|
||||
}
|
||||
|
||||
public void site(Function<LibraryVersion, String> linkFactory) {
|
||||
add("site", linkFactory);
|
||||
}
|
||||
|
||||
public void github(String linkTemplate) {
|
||||
github(asFactory(linkTemplate));
|
||||
}
|
||||
|
||||
public void github(Function<LibraryVersion, String> linkFactory) {
|
||||
add("github", linkFactory);
|
||||
}
|
||||
|
||||
public void javadoc(String linkTemplate) {
|
||||
javadoc(asFactory(linkTemplate));
|
||||
}
|
||||
|
||||
public void javadoc(Function<LibraryVersion, String> linkFactory) {
|
||||
add("javadoc", linkFactory);
|
||||
}
|
||||
|
||||
public void reference(String linkTemplate) {
|
||||
reference(asFactory(linkTemplate));
|
||||
}
|
||||
|
||||
public void reference(Function<LibraryVersion, String> linkFactory) {
|
||||
add("reference", linkFactory);
|
||||
}
|
||||
|
||||
public void releaseNotes(String linkTemplate) {
|
||||
releaseNotes(asFactory(linkTemplate));
|
||||
}
|
||||
|
||||
public void releaseNotes(Function<LibraryVersion, String> linkFactory) {
|
||||
add("releaseNotes", linkFactory);
|
||||
}
|
||||
|
||||
public void add(String name, String linkTemplate) {
|
||||
add(name, asFactory(linkTemplate));
|
||||
}
|
||||
|
||||
public void add(String name, Function<LibraryVersion, String> linkFactory) {
|
||||
this.links.put(name, linkFactory);
|
||||
}
|
||||
|
||||
private Function<LibraryVersion, String> asFactory(String linkTemplate) {
|
||||
return (version) -> {
|
||||
PlaceholderResolver resolver = (name) -> "version".equals(name) ? version.toString() : null;
|
||||
return new PropertyPlaceholderHelper("{", "}").replacePlaceholders(linkTemplate, resolver);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class UpgradeHandler {
|
||||
|
||||
private UpgradePolicy upgradePolicy;
|
||||
|
|
|
@ -66,8 +66,8 @@ public class BomPlugin implements Plugin<Project> {
|
|||
project.getTasks().named("check").configure((check) -> check.dependsOn(checkBom));
|
||||
project.getTasks().create("bomrUpgrade", UpgradeBom.class, bom);
|
||||
project.getTasks().create("moveToSnapshots", MoveToSnapshots.class, bom);
|
||||
project.getTasks().register("checkLinks", CheckLinks.class, bom);
|
||||
new PublishingCustomizer(project, bom).customize();
|
||||
|
||||
}
|
||||
|
||||
private void createApiEnforcedConfiguration(Project project) {
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright 2024-2024 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.boot.build.bom;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.hc.client5.http.config.RequestConfig;
|
||||
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
|
||||
import org.apache.hc.client5.http.impl.classic.HttpClients;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.internal.impldep.org.apache.http.client.config.CookieSpecs;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.web.client.DefaultResponseErrorHandler;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* Task to check that links are working.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class CheckLinks extends DefaultTask {
|
||||
|
||||
private final BomExtension bom;
|
||||
|
||||
@Inject
|
||||
public CheckLinks(BomExtension bom) {
|
||||
this.bom = bom;
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void releaseNotes() {
|
||||
RequestConfig config = RequestConfig.custom().setCookieSpec(CookieSpecs.IGNORE_COOKIES).build();
|
||||
CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(config).build();
|
||||
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
|
||||
RestTemplate restTemplate = new RestTemplate(requestFactory);
|
||||
restTemplate.setErrorHandler(new IgnoringErrorHandler());
|
||||
for (Library library : this.bom.getLibraries()) {
|
||||
library.getLinks().forEach((name, link) -> {
|
||||
URI uri;
|
||||
try {
|
||||
uri = new URI(link);
|
||||
ResponseEntity<String> response = restTemplate.exchange(uri, HttpMethod.HEAD, null, String.class);
|
||||
System.out.println("[%3d] %s - %s (%s)".formatted(response.getStatusCode().value(),
|
||||
library.getName(), name, uri));
|
||||
}
|
||||
catch (URISyntaxException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static class IgnoringErrorHandler extends DefaultResponseErrorHandler {
|
||||
|
||||
@Override
|
||||
public void handleError(ClientHttpResponse response) throws IOException {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.boot.build.bom;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -24,6 +25,8 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
|
||||
import org.apache.maven.artifact.versioning.VersionRange;
|
||||
|
@ -59,6 +62,8 @@ public class Library {
|
|||
|
||||
private final VersionAlignment versionAlignment;
|
||||
|
||||
private final Map<String, Function<LibraryVersion, String>> links;
|
||||
|
||||
/**
|
||||
* Create a new {@code Library} with the given {@code name}, {@code version}, and
|
||||
* {@code groups}.
|
||||
|
@ -70,9 +75,11 @@ public class Library {
|
|||
* @param prohibitedVersions version of the library that are prohibited
|
||||
* @param considerSnapshots whether to consider snapshots
|
||||
* @param versionAlignment version alignment, if any, for the library
|
||||
* @param links a list of HTTP links relevant to the library
|
||||
*/
|
||||
public Library(String name, String calendarName, LibraryVersion version, List<Group> groups,
|
||||
List<ProhibitedVersion> prohibitedVersions, boolean considerSnapshots, VersionAlignment versionAlignment) {
|
||||
List<ProhibitedVersion> prohibitedVersions, boolean considerSnapshots, VersionAlignment versionAlignment,
|
||||
Map<String, Function<LibraryVersion, String>> links) {
|
||||
this.name = name;
|
||||
this.calendarName = (calendarName != null) ? calendarName : name;
|
||||
this.version = version;
|
||||
|
@ -82,6 +89,7 @@ public class Library {
|
|||
this.prohibitedVersions = prohibitedVersions;
|
||||
this.considerSnapshots = considerSnapshots;
|
||||
this.versionAlignment = versionAlignment;
|
||||
this.links = Collections.unmodifiableMap(links);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
@ -116,6 +124,12 @@ public class Library {
|
|||
return this.versionAlignment;
|
||||
}
|
||||
|
||||
public Map<String, String> getLinks() {
|
||||
Map<String, String> links = new TreeMap<>();
|
||||
this.links.forEach((name, linkFactory) -> links.put(name, linkFactory.apply(this.version)));
|
||||
return Collections.unmodifiableMap(links);
|
||||
}
|
||||
|
||||
/**
|
||||
* A version or range of versions that are prohibited from being used in a bom.
|
||||
*/
|
||||
|
@ -184,6 +198,44 @@ public class Library {
|
|||
return this.version;
|
||||
}
|
||||
|
||||
public int[] componentInts() {
|
||||
return Arrays.stream(parts()).mapToInt(Integer::parseInt).toArray();
|
||||
}
|
||||
|
||||
public String major() {
|
||||
return parts()[0];
|
||||
}
|
||||
|
||||
public String minor() {
|
||||
return parts()[1];
|
||||
}
|
||||
|
||||
public String patch() {
|
||||
return parts()[2];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.version.toString();
|
||||
}
|
||||
|
||||
public String toString(String separator) {
|
||||
return this.version.toString().replace(".", separator);
|
||||
}
|
||||
|
||||
public String forAntora() {
|
||||
String[] parts = parts();
|
||||
String result = parts[0] + "." + parts[1];
|
||||
if (toString().endsWith("SNAPSHOT")) {
|
||||
result += "-SNAPSHOT";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String[] parts() {
|
||||
return toString().split("[\\.-]");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.io.FileInputStream;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Collections;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -53,7 +54,7 @@ class UpgradeApplicatorTests {
|
|||
FileCopyUtils.copy(new File("src/test/resources/gradle.properties"), gradleProperties);
|
||||
new UpgradeApplicator(bom.toPath(), gradleProperties.toPath())
|
||||
.apply(new Upgrade(new Library("ActiveMQ", null, new LibraryVersion(DependencyVersion.parse("5.15.11")),
|
||||
null, null, false, null), DependencyVersion.parse("5.16")));
|
||||
null, null, false, null, Collections.emptyMap()), DependencyVersion.parse("5.16")));
|
||||
String bomContents = Files.readString(bom.toPath());
|
||||
assertThat(bomContents).hasSize(originalContents.length() - 3);
|
||||
}
|
||||
|
@ -66,7 +67,7 @@ class UpgradeApplicatorTests {
|
|||
FileCopyUtils.copy(new File("src/test/resources/gradle.properties"), gradleProperties);
|
||||
new UpgradeApplicator(bom.toPath(), gradleProperties.toPath())
|
||||
.apply(new Upgrade(new Library("Kotlin", null, new LibraryVersion(DependencyVersion.parse("1.3.70")), null,
|
||||
null, false, null), DependencyVersion.parse("1.4")));
|
||||
null, false, null, Collections.emptyMap()), DependencyVersion.parse("1.4")));
|
||||
Properties properties = new Properties();
|
||||
try (InputStream in = new FileInputStream(gradleProperties)) {
|
||||
properties.load(in);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue