Parse latestDepTestLibrary and use as library upper bound (#13478)

This commit is contained in:
Jay DeLuca 2025-03-08 18:31:57 -05:00 committed by GitHub
parent ffeb80eb26
commit c7676bddd4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 76 additions and 36 deletions

View File

@ -58,7 +58,7 @@ apache:
srcPath: instrumentation/apache-httpclient/apache-httpclient-4.3
target_versions:
library:
- org.apache.httpcomponents:httpclient:4.3
- org.apache.httpcomponents:httpclient:[4.3,4.+)
- name: apache-httpclient-4.0
srcPath: instrumentation/apache-httpclient/apache-httpclient-4.0
target_versions:
@ -137,7 +137,7 @@ aws:
- com.amazonaws:aws-java-sdk-sqs:[1.10.33,)
- com.amazonaws:aws-java-sdk-core:[1.10.33,)
library:
- com.amazonaws:aws-java-sdk-sqs:1.11.106
- com.amazonaws:aws-java-sdk-sqs:[1.11.106,1.12.583)
- com.amazonaws:aws-java-sdk-core:1.11.0
- name: aws-sdk-2.2
srcPath: instrumentation/aws-sdk/aws-sdk-2.2
@ -352,7 +352,7 @@ graphql:
javaagent:
- com.graphql-java:graphql-java:[12,20)
library:
- com.graphql-java:graphql-java:12.0
- com.graphql-java:graphql-java:[12.0,19.+)
- name: graphql-java-20.0
srcPath: instrumentation/graphql-java/graphql-java-20.0
target_versions:
@ -651,7 +651,7 @@ jetty:
javaagent:
- org.eclipse.jetty:jetty-client:[9.2,10)
library:
- org.eclipse.jetty:jetty-client:9.2.0.v20140526
- org.eclipse.jetty:jetty-client:[9.2.0.v20140526,9.+)
- name: jetty-11.0
srcPath: instrumentation/jetty/jetty-11.0
target_versions:
@ -765,8 +765,8 @@ ktor:
- io.ktor:ktor-client-core:[2.0.0,3.0.0)
- io.ktor:ktor-server-core:[2.0.0,3.0.0)
library:
- io.ktor:ktor-server-core:2.0.0
- io.ktor:ktor-client-core:2.0.0
- io.ktor:ktor-client-core:[2.0.0,2.+)
- io.ktor:ktor-server-core:[2.0.0,2.+)
- name: ktor-3.0
srcPath: instrumentation/ktor/ktor-3.0
target_versions:
@ -780,7 +780,7 @@ ktor:
srcPath: instrumentation/ktor/ktor-1.0
target_versions:
library:
- io.ktor:ktor-server-core:1.0.0
- io.ktor:ktor-server-core:[1.0.0,1.+)
kubernetes:
instrumentations:
- name: kubernetes-client-7.0
@ -1049,7 +1049,7 @@ oshi:
javaagent:
- com.github.oshi:oshi-core:[5.3.1,)
library:
- com.github.oshi:oshi-core:$oshiVersion
- com.github.oshi:oshi-core:5.3.1
payara:
instrumentations:
- name: payara
@ -1069,8 +1069,11 @@ pekko:
srcPath: instrumentation/pekko/pekko-http-1.0
target_versions:
javaagent:
- com.softwaremill.sttp.tapir:tapir-pekko-http-server_3:[1.7,)
- com.softwaremill.sttp.tapir:tapir-pekko-http-server_2.12:[1.7,)
- org.apache.pekko:pekko-http_2.12:[1.0,)
- org.apache.pekko:pekko-http_3:[1.0,)
- com.softwaremill.sttp.tapir:tapir-pekko-http-server_2.13:[1.7,)
- org.apache.pekko:pekko-http_2.13:[1.0,)
play:
instrumentations:
@ -1233,7 +1236,7 @@ restlet:
javaagent:
- org.restlet:org.restlet:[1.1.0, 1.2-M1)
library:
- org.restlet:org.restlet:1.1.5
- org.restlet:org.restlet:[1.1.5,1.+)
- com.noelios.restlet:com.noelios.restlet:1.1.5
- name: restlet-2.0
srcPath: instrumentation/restlet/restlet-2.0
@ -1301,7 +1304,7 @@ rxjava:
javaagent:
- io.reactivex.rxjava3:rxjava:[3.0.0,3.1.0]
library:
- io.reactivex.rxjava3:rxjava:3.0.12
- io.reactivex.rxjava3:rxjava:[3.0.12,3.1.0)
scala:
instrumentations:
- name: scala-fork-join-2.8
@ -1446,7 +1449,7 @@ spring:
javaagent:
- org.springframework.integration:spring-integration-core:[4.1.0.RELEASE,)
library:
- org.springframework.integration:spring-integration-core:4.1.0.RELEASE
- org.springframework.integration:spring-integration-core:[4.1.0.RELEASE,5.+)
- name: spring-jms-2.0
srcPath: instrumentation/spring/spring-jms/spring-jms-2.0
target_versions:

View File

@ -53,11 +53,7 @@ public class SpringWebInstrumentationModule extends InstrumentationModule
### Versions targeted
Javaagent versions are determined by the `muzzle` plugin, so we can attempt to parse the gradle files
We parse gradle files in order to determine the target versions.
Library versions are determined by the library versions used in the gradle files.
### TODO / Notes
- Is the `library` dependency actually the target version? Is there a better way to present the information?
- How to handle oshi target version with a conditional?
- Javaagent versions are determined by the `muzzle` plugin configurations
- Library versions are determined by the library dependency versions

View File

@ -13,18 +13,23 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
class GradleParser {
private static final Pattern passBlockPattern =
Pattern.compile("pass\\s*\\{(.*?)}", Pattern.DOTALL);
private static final Pattern libraryPattern =
Pattern.compile("library\\(\"([^\"]+:[^\"]+:[^\"]+)\"\\)");
private static final Pattern variablePattern =
Pattern.compile("val\\s+(\\w+)\\s*=\\s*\"([^\"]+)\"");
private static final Pattern muzzlePassBlockPattern =
Pattern.compile("pass\\s*\\{(.*?)}", Pattern.DOTALL);
private static final Pattern libraryPattern =
Pattern.compile("library\\(\"([^\"]+:[^\"]+):([^\"]+)\"\\)");
private static final Pattern compileOnlyPattern =
Pattern.compile(
"compileOnly\\(\"([^\"]+)\"\\)\\s*\\{\\s*version\\s*\\{(?:\\s*//.*\\n)*\\s*strictly\\(\"([^\"]+)\"\\)\\s*}\\s*}");
"compileOnly\\(\"([^\"]+:[^\"]+)(?::[^\"]+)?\"\\)\\s*\\{\\s*version\\s*\\{.*?strictly\\(\"([^\"]+)\"\\).*?}\\s*",
Pattern.DOTALL);
private static final Pattern latestDepTestLibraryPattern =
Pattern.compile("latestDepTestLibrary\\(\"([^\"]+:[^\"]+):([^\"]+)\"\\)");
/**
* Parses gradle files for muzzle and dependency information
@ -55,7 +60,7 @@ class GradleParser {
*/
private static Set<String> parseMuzzle(String gradleFileContents, Map<String, String> variables) {
Set<String> results = new HashSet<>();
Matcher passBlockMatcher = passBlockPattern.matcher(gradleFileContents);
Matcher passBlockMatcher = muzzlePassBlockPattern.matcher(gradleFileContents);
while (passBlockMatcher.find()) {
String passBlock = passBlockMatcher.group(1);
@ -74,7 +79,8 @@ class GradleParser {
/**
* Parses the "dependencies" block from the given Gradle file content and extracts information
* about what library versions are supported.
* about what library versions are supported. Looks for library() and compileOnly() blocks for
* lower bounds, and latestDepTestLibrary() for upper bounds.
*
* @param gradleFileContents Contents of a Gradle build file as a String
* @param variables Map of variable names to their values
@ -82,18 +88,41 @@ class GradleParser {
*/
private static Set<String> parseLibraryDependencies(
String gradleFileContents, Map<String, String> variables) {
Set<String> results = new HashSet<>();
Map<String, String> versions = new HashMap<>();
Matcher libraryMatcher = libraryPattern.matcher(gradleFileContents);
while (libraryMatcher.find()) {
String dependency = libraryMatcher.group(1);
results.add(interpolate(dependency, variables));
String groupAndArtifact = libraryMatcher.group(1);
String version = libraryMatcher.group(2);
versions.put(groupAndArtifact, version);
}
Matcher compileOnlyMatcher = compileOnlyPattern.matcher(gradleFileContents);
while (compileOnlyMatcher.find()) {
String dependency = compileOnlyMatcher.group(1) + ":" + compileOnlyMatcher.group(2);
results.add(interpolate(dependency, variables));
String groupAndArtifact = compileOnlyMatcher.group(1);
String version = compileOnlyMatcher.group(2);
versions.put(groupAndArtifact, version);
}
Matcher latestDepTestLibraryMatcher = latestDepTestLibraryPattern.matcher(gradleFileContents);
while (latestDepTestLibraryMatcher.find()) {
String groupAndArtifact = latestDepTestLibraryMatcher.group(1);
String version = latestDepTestLibraryMatcher.group(2);
if (versions.containsKey(groupAndArtifact)) {
versions.put(groupAndArtifact, versions.get(groupAndArtifact) + "," + version);
}
}
Set<String> results = new HashSet<>();
for (Map.Entry<String, String> entry : versions.entrySet()) {
if (entry.getValue().contains(",")) {
results.add(interpolate(entry.getKey() + ":[" + entry.getValue() + ")", variables));
} else {
results.add(interpolate(entry.getKey() + ":" + entry.getValue(), variables));
}
}
return results;
}

View File

@ -32,11 +32,7 @@ class GradleParserTest {
@Test
void testExtractLibraryVersion() {
String gradleBuildFileContent =
"dependencies {\n"
+ " library(\"org.apache.httpcomponents:httpclient:4.3\")\n"
+ " testImplementation(project(\":instrumentation:apache-httpclient:apache-httpclient-4.3:testing\"))\n"
+ " latestDepTestLibrary(\"org.apache.httpcomponents:httpclient:4.+\") // see apache-httpclient-5.0 module\n"
+ "}";
"dependencies {\n" + " library(\"org.apache.httpcomponents:httpclient:4.3\")\n" + "}";
Set<String> versions =
GradleParser.parseGradleFile(gradleBuildFileContent, InstrumentationType.LIBRARY);
assertThat(versions.size()).isEqualTo(1);
@ -44,6 +40,22 @@ class GradleParserTest {
.isEqualTo("org.apache.httpcomponents:httpclient:4.3");
}
@Test
void testExtractLibraryUpperVersion() {
String gradleBuildFileContent =
"dependencies {\n"
+ " library(\"org.apache.httpcomponents:httpclient:4.3\")\n"
+ " testImplementation(project(\":instrumentation:apache-httpclient:apache-httpclient-4.3:testing\"))\n"
+ " latestDepTestLibrary(\"org.apache.httpcomponents:httpclient:4.+\") // see apache-httpclient-5.0 module\n"
+ "}";
Set<String> versions =
GradleParser.parseGradleFile(gradleBuildFileContent, InstrumentationType.LIBRARY);
assertThat(versions.size()).isEqualTo(1);
assertThat(versions.stream().findFirst().get())
.isEqualTo("org.apache.httpcomponents:httpclient:[4.3,4.+)");
}
@Test
void testExtractMuzzleVersions_MultiplePassBlocks() {
String gradleBuildFileContent =