diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c8b35ffa2c3..0a93a9e9808 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -880,6 +880,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/dd-java-agent/ddprof-lib/build.gradle b/dd-java-agent/ddprof-lib/build.gradle index 0d265cc7db9..3aad9482d2c 100644 --- a/dd-java-agent/ddprof-lib/build.gradle +++ b/dd-java-agent/ddprof-lib/build.gradle @@ -14,6 +14,13 @@ dependencies { api project(':dd-trace-api') } +// Log information about ddprof version being used +afterEvaluate { + if (rootProject.hasProperty('ddprofUseSnapshot')) { + logger.lifecycle("${project.name}: Using ddprof SNAPSHOT version ${rootProject.ext.ddprofSnapshotVersion}") + } +} + tasks.named("shadowJar", ShadowJar) { dependencies { deps.excludeShared diff --git a/docs/publishing-with-ddprof-snapshot.md b/docs/publishing-with-ddprof-snapshot.md new file mode 100644 index 00000000000..f3973c37b56 --- /dev/null +++ b/docs/publishing-with-ddprof-snapshot.md @@ -0,0 +1,141 @@ +# 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`** - Core logic for version calculation and dependency override +2. **`build.gradle.kts`** - Applies the ddprof-snapshot configuration +3. **`dd-java-agent/ddprof-lib/build.gradle`** - Logging for snapshot version usage +4. **`.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` +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 +6. Gradle's `resolutionStrategy.eachDependency` overrides all ddprof dependencies to use the snapshot version +7. The build and publish proceed with the modified version and overridden dependency + +### Dependency Resolution Override + +The override is applied globally to all configurations in all projects: + +```groovy +configurations.all { + resolutionStrategy.eachDependency { DependencyResolveDetails details -> + if (details.requested.group == 'com.datadoghq' && details.requested.name == 'ddprof') { + details.useVersion(ddprofSnapshotVersion) + details.because("Using ddprof snapshot version for integration testing") + } + } +} +``` + +This ensures that even transitive dependencies on ddprof are overridden. + +## 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..585c0b8c532 --- /dev/null +++ b/gradle/ddprof-override.gradle @@ -0,0 +1,73 @@ +// 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. Override the ddprof dependency resolution to use the snapshot version +// 4. Add a qualifier to the dd-trace-java version to avoid overwriting standard snapshots + +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 subprojects + rootProject.ext.ddprofSnapshotVersion = ddprofSnapshotVersion + + // Add qualifier to the project version to differentiate from standard snapshots + // This ensures we don't overwrite the regular SNAPSHOT artifacts + allprojects { + def originalVersion = version.toString() + if (originalVersion.contains('-SNAPSHOT')) { + // Insert qualifier before -SNAPSHOT: X.Y.Z-SNAPSHOT -> X.Y.Z-ddprof-SNAPSHOT + version = originalVersion.replace('-SNAPSHOT', '-ddprof-SNAPSHOT') + } else if (originalVersion.contains('-')) { + // For versions with trailer: X.Y.Z-12-g8ab3f42d -> X.Y.Z-ddprof-12-g8ab3f42d + def parts = originalVersion.split('-', 2) + version = "${parts[0]}-ddprof-${parts[1]}" + } else { + // For release versions (shouldn't happen, but handle it): X.Y.Z -> X.Y.Z-ddprof + version = "${originalVersion}-ddprof" + } + logger.lifecycle("Modified version for ${project.name}: ${originalVersion} -> ${version}") + } + + // Override the ddprof dependency resolution for all configurations + allprojects { + configurations.all { + resolutionStrategy.eachDependency { DependencyResolveDetails details -> + if (details.requested.group == 'com.datadoghq' && details.requested.name == 'ddprof') { + details.useVersion(ddprofSnapshotVersion) + details.because("Using ddprof snapshot version for integration testing") + } + } + } + } +} + +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 +}