diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 049ac973c29..a4354c59d6d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -888,6 +888,31 @@ deploy_to_maven_central: - 'workspace/dd-trace-api/build/libs/*.jar' - 'workspace/dd-trace-ot/build/libs/*.jar' +deploy_snapshot_with_ddprof_snapshot: + extends: .gradle_build + stage: publish + needs: [ build ] + variables: + CACHE_TYPE: "lib" + rules: + - if: '$POPULATE_CACHE' + when: never + # Manual trigger only - for testing with ddprof snapshot versions + - when: manual + allow_failure: true + script: + - export MAVEN_CENTRAL_USERNAME=$(aws ssm get-parameter --region us-east-1 --name ci.dd-trace-java.central_username --with-decryption --query "Parameter.Value" --out text) + - export MAVEN_CENTRAL_PASSWORD=$(aws ssm get-parameter --region us-east-1 --name ci.dd-trace-java.central_password --with-decryption --query "Parameter.Value" --out text) + - export GPG_PRIVATE_KEY=$(aws ssm get-parameter --region us-east-1 --name ci.dd-trace-java.signing.gpg_private_key --with-decryption --query "Parameter.Value" --out text) + - export GPG_PASSWORD=$(aws ssm get-parameter --region us-east-1 --name ci.dd-trace-java.signing.gpg_passphrase --with-decryption --query "Parameter.Value" --out text) + - echo "Publishing dd-trace-java snapshot with ddprof snapshot dependency" + - ./gradlew -PbuildInfo.build.number=$CI_JOB_ID -PddprofUseSnapshot publishToSonatype -PskipTests $GRADLE_ARGS + artifacts: + paths: + - 'workspace/dd-java-agent/build/libs/*.jar' + - 'workspace/dd-trace-api/build/libs/*.jar' + - 'workspace/dd-trace-ot/build/libs/*.jar' + deploy_artifacts_to_github: stage: publish image: registry.ddbuild.io/images/dd-octo-sts-ci-base:2025.06-1 diff --git a/build.gradle.kts b/build.gradle.kts index 0a54e90658b..6b421263717 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -26,6 +26,7 @@ description = "dd-trace-java" val isCI = providers.environmentVariable("CI") apply(from = rootDir.resolve("gradle/repositories.gradle")) +apply(from = rootDir.resolve("gradle/ddprof-override.gradle")) spotless { // only resolve the spotless dependencies once in the build diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/version/TracerVersionPlugin.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/version/TracerVersionPlugin.kt index 85385ea5e2d..1250e96d907 100644 --- a/buildSrc/src/main/kotlin/datadog/gradle/plugin/version/TracerVersionPlugin.kt +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/version/TracerVersionPlugin.kt @@ -28,6 +28,10 @@ class TracerVersionPlugin @Inject constructor( .orElse(false) ) + extension.versionQualifier.set( + providerFactory.gradleProperty("tracerVersion.qualifier") + ) + val versionProvider = versionProvider(targetProject, extension) targetProject.allprojects { version = versionProvider @@ -125,6 +129,14 @@ class TracerVersionPlugin @Inject constructor( return buildString { append(version.toString()) + // Add optional version qualifier (e.g., "-ddprof") + if (extension.versionQualifier.isPresent) { + val qualifier = extension.versionQualifier.get() + if (qualifier.isNotBlank()) { + append("-").append(qualifier) + } + } + if (hasLaterCommits) { append(if (extension.useSnapshot.get()) "-SNAPSHOT" else describeTrailer) } @@ -143,5 +155,6 @@ class TracerVersionPlugin @Inject constructor( val useSnapshot = objectFactory.property(Boolean::class) .convention(true) val detectDirty = objectFactory.property(Boolean::class) + val versionQualifier = objectFactory.property(String::class) } } diff --git a/buildSrc/src/main/kotlin/dd-trace-java.profiling-ddprof-override.gradle.kts b/buildSrc/src/main/kotlin/dd-trace-java.profiling-ddprof-override.gradle.kts new file mode 100644 index 00000000000..8fe48f9dc41 --- /dev/null +++ b/buildSrc/src/main/kotlin/dd-trace-java.profiling-ddprof-override.gradle.kts @@ -0,0 +1,24 @@ +/** + * Convention plugin for overriding ddprof dependency version with snapshot. + * + * When the root project has the property 'ddprofUseSnapshot' set, this plugin: + * 1. Reads the calculated snapshot version from root project + * 2. Overrides all ddprof dependencies to use the snapshot version + * + * Apply this plugin only to projects that depend on ddprof. + */ + +if (rootProject.hasProperty("ddprofUseSnapshot")) { + val ddprofSnapshotVersion = rootProject.property("ddprofSnapshotVersion").toString() + + configurations.all { + resolutionStrategy.eachDependency { + if (requested.group == "com.datadoghq" && requested.name == "ddprof") { + useVersion(ddprofSnapshotVersion) + because("Using ddprof snapshot version for integration testing") + } + } + } + + logger.lifecycle("${project.name}: Configured to use ddprof SNAPSHOT version $ddprofSnapshotVersion") +} diff --git a/dd-java-agent/ddprof-lib/build.gradle b/dd-java-agent/ddprof-lib/build.gradle index 0d265cc7db9..b4f3219ae23 100644 --- a/dd-java-agent/ddprof-lib/build.gradle +++ b/dd-java-agent/ddprof-lib/build.gradle @@ -2,6 +2,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar plugins { id "com.gradleup.shadow" + id "dd-trace-java.profiling-ddprof-override" } apply from: "$rootDir/gradle/java.gradle" diff --git a/docs/publishing-with-ddprof-snapshot.md b/docs/publishing-with-ddprof-snapshot.md new file mode 100644 index 00000000000..977befdd9e6 --- /dev/null +++ b/docs/publishing-with-ddprof-snapshot.md @@ -0,0 +1,158 @@ +# Publishing dd-trace-java Snapshots with ddprof SNAPSHOT Dependency + +## Overview + +This feature allows publishing dd-trace-java snapshot versions that depend on a ddprof SNAPSHOT version with an incremented minor version. + +**ddprof Version Calculation:** Current ddprof version `X.Y.Z` → Dependency becomes `X.(Y+1).0-SNAPSHOT` + +**Example:** ddprof `1.34.4` → Uses dependency `1.35.0-SNAPSHOT` + +### Version Qualification + +To avoid overwriting standard snapshot artifacts, builds with `-PddprofUseSnapshot` will have a `-ddprof` qualifier added to their version: + +- Standard snapshot: `1.58.0-SNAPSHOT` +- With ddprof snapshot: `1.58.0-ddprof-SNAPSHOT` + +This ensures that both versions can coexist in Maven Central Snapshots repository without conflicts. + +## Local Usage + +### Testing Dependency Resolution + +To verify that the ddprof snapshot version is correctly calculated and applied: + +```bash +./gradlew -PddprofUseSnapshot :dd-java-agent:ddprof-lib:dependencies --configuration runtimeClasspath +``` + +Look for the output: +- `Using ddprof snapshot version: X.Y.0-SNAPSHOT` +- `Modified version for dd-trace-java: 1.58.0-SNAPSHOT -> 1.58.0-ddprof-SNAPSHOT` +- `ddprof-lib: Using ddprof SNAPSHOT version X.Y.0-SNAPSHOT` +- Dependency resolution showing: `com.datadoghq:ddprof:X.Y.Z -> X.(Y+1).0-SNAPSHOT` + +### Building with ddprof Snapshot + +To build the project with the ddprof snapshot dependency: + +```bash +./gradlew build -PddprofUseSnapshot +``` + +### Publishing to Maven Central Snapshots + +To publish artifacts with the ddprof snapshot dependency: + +```bash +./gradlew publishToSonatype -PddprofUseSnapshot -PskipTests +``` + +**Note:** You must have the required credentials configured: +- `MAVEN_CENTRAL_USERNAME` +- `MAVEN_CENTRAL_PASSWORD` +- `GPG_PRIVATE_KEY` +- `GPG_PASSWORD` + +## GitLab CI Usage + +### Manual Job Trigger + +A GitLab CI job named `deploy_snapshot_with_ddprof_snapshot` is available for manual execution. + +**To trigger:** +1. Navigate to the pipeline in GitLab CI +2. Find the `deploy_snapshot_with_ddprof_snapshot` job in the `publish` stage +3. Click the manual play button to trigger it + +**What it does:** +- Builds dd-trace-java with `-PddprofUseSnapshot` +- Publishes to Maven Central Snapshots repository +- Produces artifacts with the ddprof snapshot dependency + +**When to use:** +- Testing integration with unreleased ddprof features +- Validating compatibility before ddprof release +- Creating test builds for early adopters + +## Implementation Details + +### Files Modified + +1. **`gradle/ddprof-override.gradle`** - Calculates snapshot version and stores it for convention plugins +2. **`build.gradle.kts`** - Applies the ddprof-snapshot configuration +3. **`buildSrc/src/main/kotlin/dd-trace-java.profiling-ddprof-override.gradle.kts`** - Convention plugin for dependency override +4. **`buildSrc/src/main/kotlin/datadog/gradle/plugin/version/TracerVersionPlugin.kt`** - Enhanced to support version qualifiers +5. **`dd-java-agent/ddprof-lib/build.gradle`** - Applies the convention plugin +6. **`.gitlab-ci.yml`** - New CI job for snapshot publishing + +### How It Works + +1. The Gradle property `-PddprofUseSnapshot` activates the feature +2. The configuration reads `gradle/libs.versions.toml` to get the current ddprof version +3. Version is parsed using regex: `ddprof = "X.Y.Z"` +4. Snapshot version is calculated: `X.(Y+1).0-SNAPSHOT` and stored in `rootProject.ext.ddprofSnapshotVersion` +5. **The dd-trace-java version is modified** to add a `-ddprof` qualifier: + - `1.58.0-SNAPSHOT` → `1.58.0-ddprof-SNAPSHOT` + - This prevents overwriting standard snapshot artifacts + - Users can also explicitly set this via `-PtracerVersion.qualifier=ddprof` +6. The `dd-trace-java.profiling-ddprof-override` convention plugin is applied to projects that depend on ddprof +7. The convention plugin overrides ddprof dependencies to use the snapshot version +8. The build and publish proceed with the modified version and overridden dependency + +### Dependency Resolution Override + +The override is applied via a convention plugin to only the projects that need it: + +```kotlin +// Convention plugin: dd-trace-java.profiling-ddprof-override +if (rootProject.hasProperty("ddprofUseSnapshot")) { + val ddprofSnapshotVersion = rootProject.property("ddprofSnapshotVersion").toString() + + configurations.all { + resolutionStrategy.eachDependency { + if (requested.group == "com.datadoghq" && requested.name == "ddprof") { + useVersion(ddprofSnapshotVersion) + because("Using ddprof snapshot version for integration testing") + } + } + } +} +``` + +Projects apply this plugin explicitly in their build files: + +```groovy +plugins { + id "dd-trace-java.profiling-ddprof-override" +} +``` + +This ensures that only projects that actually depend on ddprof are affected by the override. + +## Limitations + +- Only works with semantic versioning in format `X.Y.Z` +- Requires ddprof SNAPSHOT to be published to Maven Central Snapshots repository +- Cannot override local JAR files specified with `-Pddprof.jar=/path/to/jar` + +## Troubleshooting + +### "Could not find com.datadoghq:ddprof:X.Y.0-SNAPSHOT" + +**Cause:** The calculated ddprof snapshot version doesn't exist in Maven Central Snapshots. + +**Solutions:** +- Verify ddprof has published the snapshot version +- Check Maven Central Snapshots repository: https://central.sonatype.com/repository/maven-snapshots/ +- Wait for ddprof CI to complete if a new snapshot is being published + +### Version not being overridden + +**Cause:** The property might not be correctly set or parsed. + +**Solutions:** +- Ensure you're using `-PddprofUseSnapshot` (not `-DddprofUseSnapshot`) +- Check Gradle output for "Using ddprof snapshot version" message +- Run with `--info` flag to see detailed dependency resolution logs diff --git a/gradle/ddprof-override.gradle b/gradle/ddprof-override.gradle new file mode 100644 index 00000000000..c6bacfd327e --- /dev/null +++ b/gradle/ddprof-override.gradle @@ -0,0 +1,63 @@ +// Configuration for using ddprof snapshot versions +// When -PddprofUseSnapshot=true is set, this will: +// 1. Parse the current ddprof version from libs.versions.toml +// 2. Calculate the next minor snapshot version: X.Y.Z -> X.(Y+1).0-SNAPSHOT +// 3. Store the snapshot version for use by convention plugins +// 4. Set the version qualifier property to add "ddprof" to the version +// This ensures we don't overwrite the regular SNAPSHOT artifacts +// The version will become: X.Y.Z-ddprof-SNAPSHOT instead of X.Y.Z-SNAPSHOT + +def ddprofUseSnapshot = project.hasProperty("ddprofUseSnapshot") + +if (ddprofUseSnapshot) { + def ddprofSnapshotVersion = calculateDdprofSnapshotVersion() + logger.lifecycle("Using ddprof snapshot version: ${ddprofSnapshotVersion}") + + // Store the calculated version as an extra property for use in convention plugins + rootProject.ext.ddprofSnapshotVersion = ddprofSnapshotVersion + + // Set the version qualifier property that TracerVersionPlugin will read + // Note: Users can also explicitly set this via -PtracerVersion.qualifier=ddprof + if (!project.hasProperty("tracerVersion.qualifier")) { + // Add the qualifier to the version after it has been calculated + // This is a workaround since we can't set gradle properties programmatically + allprojects { + def originalVersion = it.version.toString() + if (originalVersion.contains('-SNAPSHOT')) { + it.version = originalVersion.replace('-SNAPSHOT', '-ddprof-SNAPSHOT') + } else if (originalVersion.contains('-')) { + def parts = originalVersion.split('-', 2) + it.version = "${parts[0]}-ddprof-${parts[1]}" + } else { + it.version = "${originalVersion}-ddprof" + } + } + } +} + +def calculateDdprofSnapshotVersion() { + // Read the libs.versions.toml file + def versionsFile = rootProject.file('gradle/libs.versions.toml') + if (!versionsFile.exists()) { + throw new GradleException("Could not find gradle/libs.versions.toml") + } + + def currentVersion = null + versionsFile.eachLine { line -> + // Look for the ddprof version line: ddprof = "X.Y.Z" + def matcher = line =~ /^\s*ddprof\s*=\s*"([0-9]+)\.([0-9]+)\.([0-9]+)"\s*$/ + if (matcher) { + def major = matcher[0][1] + def minor = matcher[0][2] + // Increment the minor version + def nextMinor = (minor as Integer) + 1 + currentVersion = "${major}.${nextMinor}.0-SNAPSHOT" + } + } + + if (currentVersion == null) { + throw new GradleException("Could not parse ddprof version from gradle/libs.versions.toml") + } + + return currentVersion +}