From da27672a71613ea3fe28488ddedc0134c738c8b2 Mon Sep 17 00:00:00 2001 From: Vaibhav Khamgaonkar <163758748+opensourcevk@users.noreply.github.com> Date: Wed, 11 Mar 2026 15:17:46 +0530 Subject: [PATCH 1/4] Add repository AGENTS guide --- AGENTS.md | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000000..f99daabee62 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,74 @@ +# AGENTS.md + +This repository is `opentelemetry-java`, the core Java API and SDK implementation for OpenTelemetry. + +## Scope and Layout + +- This is a large multi-module Gradle build using Kotlin DSL. +- Key top-level modules include: + - `api`, `context`, `common` + - `sdk`, `sdk-extensions` + - `exporters`, `extensions` + - `integration-tests` + - `opencensus-shim`, `opentracing-shim` + - `testing-internal`, plus several `*:testing-internal` helper modules +- Module inclusion is declared in `settings.gradle.kts`. Check there before creating new modules or assuming project paths. + +## Environment + +- Use Java 21+ for building this repository. +- Published artifacts generally target Java 8+ compatibility unless a module explicitly documents otherwise. +- On Windows, use `gradlew.bat` instead of `./gradlew`. +- Some tests require a local Docker daemon. Those tests are disabled when Docker is unavailable. + +## Build and Verification + +- Preferred baseline validation: + - `gradlew.bat check` +- Full build: + - `gradlew.bat build` +- Auto-format Java and other formatted sources: + - `gradlew.bat spotlessApply` +- Verify formatting only: + - `gradlew.bat spotlessCheck` +- If you touch a specific module, prefer targeted Gradle tasks before running the full build. +- Be aware that `check` may regenerate files under `docs/apidiffs/current_vs_latest`; include those changes if they are produced by your change. + +## Coding Conventions + +- Follow Google Java Style. Formatting is enforced by Spotless. +- Preserve Java 8 source and ABI compatibility for published artifacts unless the module is explicitly incubating/internal and the change is intentional. +- Avoid breaking public APIs. This project follows semver and maintainers expect compatibility across minor and patch releases. +- Default to non-null. Any nullable argument/member should be annotated with `@Nullable`. +- Prefer `final` for public classes when extension is not intended. +- Keep public/protected Javadoc complete and valid. +- Public API additions should normally include `@since` with the next appropriate minor version. +- Avoid synchronizing on `this` or class intrinsic locks; prefer a dedicated lock object. + +## Testing and Test Utilities + +- Add or update tests with code changes unless the change is truly docs-only or build-only. +- Do not introduce Gradle `java-test-fixtures` for internal test sharing. +- For reusable internal test helpers, follow the repository pattern of dedicated `*:testing-internal` modules. +- Keep tests targeted to the affected module where possible. + +## Documentation and Specs + +- If behavior changes or new features are added, verify alignment with the OpenTelemetry specification. +- Changes to SDK autoconfigure or configuration options should also be reflected in the relevant docs on `opentelemetry.io`. +- When editing documentation, keep changes minimal and consistent with surrounding style. + +## Build Logic and Versioning + +- Shared build logic lives in `buildSrc` and convention plugins are used heavily; inspect existing module build files before adding new configuration. +- Repository versions are derived from git tags, not hardcoded in a single version file. +- If version-derived behavior looks stale locally, fetching tags may be required: `git fetch --all --tags`. +- There is a root `generateBuildSubstitutions` task for composite-build substitution snippets. + +## Practical Guidance for Agents + +- Read the target module's `build.gradle.kts` and nearby package structure before editing. +- Match existing package naming, visibility, and module boundaries; avoid moving classes across published modules without strong justification. +- Prefer small, localized changes over broad refactors. +- When changing public API, check for ripple effects across `all`, BOM, incubator, exporters, shims, and integration tests. +- If a task may touch generated docs or API diff outputs, mention that explicitly in your summary. From 59283a04e7d6878f20319948331bcac70f755c60 Mon Sep 17 00:00:00 2001 From: Vaibhav Khamgaonkar <163758748+opensourcevk@users.noreply.github.com> Date: Wed, 11 Mar 2026 16:06:46 +0530 Subject: [PATCH 2/4] feat: code optimized --- .../javadocs/JavaDocsCrawler.java | 61 ++++++++++++++++++- .../javadocs/JavaDocsCrawlerTest.java | 21 +++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/javadoc-crawler/src/main/java/io/opentelemetry/javadocs/JavaDocsCrawler.java b/javadoc-crawler/src/main/java/io/opentelemetry/javadocs/JavaDocsCrawler.java index 2907885297b..7d8c98efe4a 100644 --- a/javadoc-crawler/src/main/java/io/opentelemetry/javadocs/JavaDocsCrawler.java +++ b/javadoc-crawler/src/main/java/io/opentelemetry/javadocs/JavaDocsCrawler.java @@ -12,6 +12,7 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.Locale; import java.util.Map; @@ -161,7 +162,7 @@ static List crawlJavaDocs( List updatedArtifacts = new ArrayList<>(); for (Artifact artifact : artifacts) { - if (artifact.getVersion().compareTo(minVersion) < 0) { + if (compareVersions(artifact.getVersion(), minVersion) < 0) { logger.info( String.format( "Skipping crawling %s due to version %s being less than minVersion %s", @@ -209,5 +210,63 @@ static List crawlJavaDocs( return updatedArtifacts; } + static int compareVersions(String version, String otherVersion) { + SemanticVersion semanticVersion = SemanticVersion.parse(version); + SemanticVersion otherSemanticVersion = SemanticVersion.parse(otherVersion); + return semanticVersion.compareTo(otherSemanticVersion); + } + + private static final class SemanticVersion implements Comparable { + private static final Comparator> CORE_VERSION_COMPARATOR = + (left, right) -> { + for (int i = 0; i < Math.max(left.size(), right.size()); i++) { + int leftPart = i < left.size() ? left.get(i) : 0; + int rightPart = i < right.size() ? right.get(i) : 0; + int result = Integer.compare(leftPart, rightPart); + if (result != 0) { + return result; + } + } + return 0; + }; + + private final List coreVersionParts; + private final String qualifier; + + private SemanticVersion(List coreVersionParts, String qualifier) { + this.coreVersionParts = coreVersionParts; + this.qualifier = qualifier; + } + + private static SemanticVersion parse(String version) { + String[] versionParts = version.split("-", 2); + List coreVersionParts = new ArrayList<>(); + for (String part : versionParts[0].split("\\.")) { + coreVersionParts.add(Integer.parseInt(part)); + } + String qualifier = versionParts.length == 2 ? versionParts[1] : ""; + return new SemanticVersion(coreVersionParts, qualifier); + } + + @Override + public int compareTo(SemanticVersion other) { + int coreVersionResult = + CORE_VERSION_COMPARATOR.compare(coreVersionParts, other.coreVersionParts); + if (coreVersionResult != 0) { + return coreVersionResult; + } + if (qualifier.isEmpty() && other.qualifier.isEmpty()) { + return 0; + } + if (qualifier.isEmpty()) { + return 1; + } + if (other.qualifier.isEmpty()) { + return -1; + } + return qualifier.compareTo(other.qualifier); + } + } + private JavaDocsCrawler() {} } diff --git a/javadoc-crawler/src/test/java/io/opentelemetry/javadocs/JavaDocsCrawlerTest.java b/javadoc-crawler/src/test/java/io/opentelemetry/javadocs/JavaDocsCrawlerTest.java index 09a3b17e320..93636f6ec9c 100644 --- a/javadoc-crawler/src/test/java/io/opentelemetry/javadocs/JavaDocsCrawlerTest.java +++ b/javadoc-crawler/src/test/java/io/opentelemetry/javadocs/JavaDocsCrawlerTest.java @@ -8,6 +8,8 @@ import static io.opentelemetry.javadocs.JavaDocsCrawler.JAVA_DOC_DOWNLOADED_TEXT; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -91,4 +93,23 @@ void testCrawler() throws IOException, InterruptedException { "https://javadoc.io/doc/io.opentelemetry/opentelemetry-context/1.49.0/opentelemetry/context/package-summary.html"); assertThat(updated).containsExactly(artifact); } + + @Test + void compareVersionsUsesSemanticOrdering() { + assertThat(JavaDocsCrawler.compareVersions("1.9.0", "1.49.0")).isLessThan(0); + assertThat(JavaDocsCrawler.compareVersions("1.60.0", "1.49.0")).isGreaterThan(0); + assertThat(JavaDocsCrawler.compareVersions("1.60.0-alpha", "1.60.0")).isLessThan(0); + } + + @Test + void crawlSkipsArtifactsBelowMinVersionUsingSemanticComparison() + throws IOException, InterruptedException { + Artifact oldArtifact = new Artifact("io.opentelemetry", "opentelemetry-context", "1.9.0"); + + List updated = + JavaDocsCrawler.crawlJavaDocs(mockClient, "1.49.0", List.of(oldArtifact)); + + verify(mockClient, never()).send(argThat(request -> request != null), any()); + assertThat(updated).isEmpty(); + } } From c7a8988529add9c5f2a83ec6d225d2abb9c15ec9 Mon Sep 17 00:00:00 2001 From: Vaibhav Khamgaonkar <163758748+opensourcevk@users.noreply.github.com> Date: Wed, 11 Mar 2026 16:09:02 +0530 Subject: [PATCH 3/4] feat: code optimized --- AGENTS.md | 74 ------------------------------------------------------- 1 file changed, 74 deletions(-) delete mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index f99daabee62..00000000000 --- a/AGENTS.md +++ /dev/null @@ -1,74 +0,0 @@ -# AGENTS.md - -This repository is `opentelemetry-java`, the core Java API and SDK implementation for OpenTelemetry. - -## Scope and Layout - -- This is a large multi-module Gradle build using Kotlin DSL. -- Key top-level modules include: - - `api`, `context`, `common` - - `sdk`, `sdk-extensions` - - `exporters`, `extensions` - - `integration-tests` - - `opencensus-shim`, `opentracing-shim` - - `testing-internal`, plus several `*:testing-internal` helper modules -- Module inclusion is declared in `settings.gradle.kts`. Check there before creating new modules or assuming project paths. - -## Environment - -- Use Java 21+ for building this repository. -- Published artifacts generally target Java 8+ compatibility unless a module explicitly documents otherwise. -- On Windows, use `gradlew.bat` instead of `./gradlew`. -- Some tests require a local Docker daemon. Those tests are disabled when Docker is unavailable. - -## Build and Verification - -- Preferred baseline validation: - - `gradlew.bat check` -- Full build: - - `gradlew.bat build` -- Auto-format Java and other formatted sources: - - `gradlew.bat spotlessApply` -- Verify formatting only: - - `gradlew.bat spotlessCheck` -- If you touch a specific module, prefer targeted Gradle tasks before running the full build. -- Be aware that `check` may regenerate files under `docs/apidiffs/current_vs_latest`; include those changes if they are produced by your change. - -## Coding Conventions - -- Follow Google Java Style. Formatting is enforced by Spotless. -- Preserve Java 8 source and ABI compatibility for published artifacts unless the module is explicitly incubating/internal and the change is intentional. -- Avoid breaking public APIs. This project follows semver and maintainers expect compatibility across minor and patch releases. -- Default to non-null. Any nullable argument/member should be annotated with `@Nullable`. -- Prefer `final` for public classes when extension is not intended. -- Keep public/protected Javadoc complete and valid. -- Public API additions should normally include `@since` with the next appropriate minor version. -- Avoid synchronizing on `this` or class intrinsic locks; prefer a dedicated lock object. - -## Testing and Test Utilities - -- Add or update tests with code changes unless the change is truly docs-only or build-only. -- Do not introduce Gradle `java-test-fixtures` for internal test sharing. -- For reusable internal test helpers, follow the repository pattern of dedicated `*:testing-internal` modules. -- Keep tests targeted to the affected module where possible. - -## Documentation and Specs - -- If behavior changes or new features are added, verify alignment with the OpenTelemetry specification. -- Changes to SDK autoconfigure or configuration options should also be reflected in the relevant docs on `opentelemetry.io`. -- When editing documentation, keep changes minimal and consistent with surrounding style. - -## Build Logic and Versioning - -- Shared build logic lives in `buildSrc` and convention plugins are used heavily; inspect existing module build files before adding new configuration. -- Repository versions are derived from git tags, not hardcoded in a single version file. -- If version-derived behavior looks stale locally, fetching tags may be required: `git fetch --all --tags`. -- There is a root `generateBuildSubstitutions` task for composite-build substitution snippets. - -## Practical Guidance for Agents - -- Read the target module's `build.gradle.kts` and nearby package structure before editing. -- Match existing package naming, visibility, and module boundaries; avoid moving classes across published modules without strong justification. -- Prefer small, localized changes over broad refactors. -- When changing public API, check for ripple effects across `all`, BOM, incubator, exporters, shims, and integration tests. -- If a task may touch generated docs or API diff outputs, mention that explicitly in your summary. From 98e3de3e35dd3983d5cd885d665b6078258f914c Mon Sep 17 00:00:00 2001 From: Vaibhav Khamgaonkar <163758748+opensourcevk@users.noreply.github.com> Date: Thu, 12 Mar 2026 04:48:19 +0530 Subject: [PATCH 4/4] Fix javadoc crawler review feedback --- .../javadocs/JavaDocsCrawler.java | 71 +++++++++++++++---- .../javadocs/JavaDocsCrawlerTest.java | 17 +++-- 2 files changed, 68 insertions(+), 20 deletions(-) diff --git a/javadoc-crawler/src/main/java/io/opentelemetry/javadocs/JavaDocsCrawler.java b/javadoc-crawler/src/main/java/io/opentelemetry/javadocs/JavaDocsCrawler.java index 7d8c98efe4a..c3f919474ec 100644 --- a/javadoc-crawler/src/main/java/io/opentelemetry/javadocs/JavaDocsCrawler.java +++ b/javadoc-crawler/src/main/java/io/opentelemetry/javadocs/JavaDocsCrawler.java @@ -33,11 +33,11 @@ public final class JavaDocsCrawler { // latest periodically to avoid crawling artifacts that stopped being published. private static final Map GROUPS_AND_MIN_VERSION = Map.of( - "io.opentelemetry", "1.49.0", - "io.opentelemetry.instrumentation", "2.15.0", - "io.opentelemetry.contrib", "1.46.0", - "io.opentelemetry.semconv", "1.32.0", - "io.opentelemetry.proto", "1.3.2"); + "io.opentelemetry", "1.60.1", + "io.opentelemetry.instrumentation", "2.25.0", + "io.opentelemetry.contrib", "1.54.0", + "io.opentelemetry.semconv", "1.40.0", + "io.opentelemetry.proto", "1.10.0"); private static final String MAVEN_CENTRAL_BASE_URL = "https://search.maven.org/solrsearch/select?q=g:"; @@ -160,9 +160,10 @@ static List crawlJavaDocs( HttpClient client, String minVersion, List artifacts) throws IOException, InterruptedException { List updatedArtifacts = new ArrayList<>(); + SemanticVersion minSemanticVersion = SemanticVersion.parse(minVersion); for (Artifact artifact : artifacts) { - if (compareVersions(artifact.getVersion(), minVersion) < 0) { + if (SemanticVersion.parse(artifact.getVersion()).compareTo(minSemanticVersion) < 0) { logger.info( String.format( "Skipping crawling %s due to version %s being less than minVersion %s", @@ -210,13 +211,7 @@ static List crawlJavaDocs( return updatedArtifacts; } - static int compareVersions(String version, String otherVersion) { - SemanticVersion semanticVersion = SemanticVersion.parse(version); - SemanticVersion otherSemanticVersion = SemanticVersion.parse(otherVersion); - return semanticVersion.compareTo(otherSemanticVersion); - } - - private static final class SemanticVersion implements Comparable { + static final class SemanticVersion implements Comparable { private static final Comparator> CORE_VERSION_COMPARATOR = (left, right) -> { for (int i = 0; i < Math.max(left.size(), right.size()); i++) { @@ -238,7 +233,7 @@ private SemanticVersion(List coreVersionParts, String qualifier) { this.qualifier = qualifier; } - private static SemanticVersion parse(String version) { + static SemanticVersion parse(String version) { String[] versionParts = version.split("-", 2); List coreVersionParts = new ArrayList<>(); for (String part : versionParts[0].split("\\.")) { @@ -264,7 +259,53 @@ public int compareTo(SemanticVersion other) { if (other.qualifier.isEmpty()) { return -1; } - return qualifier.compareTo(other.qualifier); + return compareQualifier(qualifier, other.qualifier); + } + + private static int compareQualifier(String leftQualifier, String rightQualifier) { + String[] leftParts = leftQualifier.split("\\."); + String[] rightParts = rightQualifier.split("\\."); + + for (int i = 0; i < Math.max(leftParts.length, rightParts.length); i++) { + if (i >= leftParts.length) { + return -1; + } + if (i >= rightParts.length) { + return 1; + } + + String leftIdentifier = leftParts[i]; + String rightIdentifier = rightParts[i]; + if (leftIdentifier.equals(rightIdentifier)) { + continue; + } + + boolean leftNumeric = isNumericIdentifier(leftIdentifier); + boolean rightNumeric = isNumericIdentifier(rightIdentifier); + if (leftNumeric && rightNumeric) { + if (leftIdentifier.length() != rightIdentifier.length()) { + return Integer.compare(leftIdentifier.length(), rightIdentifier.length()); + } + return leftIdentifier.compareTo(rightIdentifier); + } + if (leftNumeric != rightNumeric) { + return leftNumeric ? -1 : 1; + } + return leftIdentifier.compareTo(rightIdentifier); + } + return 0; + } + + private static boolean isNumericIdentifier(String identifier) { + if (identifier.isEmpty()) { + return false; + } + for (int i = 0; i < identifier.length(); i++) { + if (!Character.isDigit(identifier.charAt(i))) { + return false; + } + } + return true; } } diff --git a/javadoc-crawler/src/test/java/io/opentelemetry/javadocs/JavaDocsCrawlerTest.java b/javadoc-crawler/src/test/java/io/opentelemetry/javadocs/JavaDocsCrawlerTest.java index 93636f6ec9c..92150e0949f 100644 --- a/javadoc-crawler/src/test/java/io/opentelemetry/javadocs/JavaDocsCrawlerTest.java +++ b/javadoc-crawler/src/test/java/io/opentelemetry/javadocs/JavaDocsCrawlerTest.java @@ -8,7 +8,6 @@ import static io.opentelemetry.javadocs.JavaDocsCrawler.JAVA_DOC_DOWNLOADED_TEXT; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -96,9 +95,12 @@ void testCrawler() throws IOException, InterruptedException { @Test void compareVersionsUsesSemanticOrdering() { - assertThat(JavaDocsCrawler.compareVersions("1.9.0", "1.49.0")).isLessThan(0); - assertThat(JavaDocsCrawler.compareVersions("1.60.0", "1.49.0")).isGreaterThan(0); - assertThat(JavaDocsCrawler.compareVersions("1.60.0-alpha", "1.60.0")).isLessThan(0); + assertThat(compareVersions("1.9.0", "1.49.0")).isLessThan(0); + assertThat(compareVersions("1.49.0", "1.49.0")).isZero(); + assertThat(compareVersions("1.60.0", "1.49.0")).isGreaterThan(0); + assertThat(compareVersions("1.60.0-alpha", "1.60.0")).isLessThan(0); + assertThat(compareVersions("1.0.0-rc.2", "1.0.0-rc.10")).isLessThan(0); + assertThat(compareVersions("1.0.0-rc.10", "1.0.0")).isLessThan(0); } @Test @@ -109,7 +111,12 @@ void crawlSkipsArtifactsBelowMinVersionUsingSemanticComparison() List updated = JavaDocsCrawler.crawlJavaDocs(mockClient, "1.49.0", List.of(oldArtifact)); - verify(mockClient, never()).send(argThat(request -> request != null), any()); + verify(mockClient, never()).send(any(HttpRequest.class), any()); assertThat(updated).isEmpty(); } + + private static int compareVersions(String left, String right) { + return JavaDocsCrawler.SemanticVersion.parse(left) + .compareTo(JavaDocsCrawler.SemanticVersion.parse(right)); + } }