Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ class GradleProjectConfigurator {
public static final GradleProjectConfigurator INSTANCE = new GradleProjectConfigurator()

private static final String JACOCO_PLUGIN_ID = 'jacoco'
private static final String JACOCO_AGENT_CONFIGURATION_NAME = 'jacocoAgent'
private static final String JACOCO_ANT_CONFIGURATION_NAME = 'jacocoAnt'
private static final String DEPENDENCY_VERIFICATION_WARNING_EMITTED =
'datadog.civisibility.dependency-verification-warning-emitted'

void configureTracer(Task task, Map<String, String> propagatedSystemProperties) {
def config = Config.get()
Expand Down Expand Up @@ -130,15 +134,8 @@ class GradleProjectConfigurator {
def ddJavacPlugin = project.configurations.detachedConfiguration(project.dependencies.create("com.datadoghq:dd-javac-plugin:$compilerPluginVersion"))
def ddJavacPluginClient = project.configurations.detachedConfiguration(project.dependencies.create("com.datadoghq:dd-javac-plugin-client:$compilerPluginVersion"))

// if instrumented project does dependency verification,
// we need to exclude the two detached configurations that we're adding
// as corresponding entries are not in the project's verification-metadata.xml
if (ddJavacPlugin.resolutionStrategy.respondsTo("disableDependencyVerification")) {
ddJavacPlugin.resolutionStrategy.disableDependencyVerification()
}
if (ddJavacPluginClient.resolutionStrategy.respondsTo("disableDependencyVerification")) {
ddJavacPluginClient.resolutionStrategy.disableDependencyVerification()
}
disableDependencyVerificationIfConfigured(project, ddJavacPlugin)
disableDependencyVerificationIfConfigured(project, ddJavacPluginClient)

task.classpath = (task.classpath ?: project.files([])) + ddJavacPluginClient.asFileTree

Expand Down Expand Up @@ -199,14 +196,11 @@ class GradleProjectConfigurator {
project.apply("plugin": JACOCO_PLUGIN_ID)
project.jacoco.toolVersion = config.ciVisibilityJacocoPluginVersion

// if instrumented project does dependency verification,
// we need to exclude configurations added by Jacoco
// as corresponding entries are not in the project's verification-metadata.xml
def jacocoConfigurations = project.configurations.findAll { it.name.startsWith("jacoco") }
def jacocoConfigurations = project.configurations.findAll {
it.name == JACOCO_AGENT_CONFIGURATION_NAME || it.name == JACOCO_ANT_CONFIGURATION_NAME
}
for (def jacocoConfiguration : jacocoConfigurations) {
if (jacocoConfiguration.resolutionStrategy.respondsTo("disableDependencyVerification")) {
jacocoConfiguration.resolutionStrategy.disableDependencyVerification()
}
disableDependencyVerificationIfConfigured(project, jacocoConfiguration)
}

def coverageIncludedPackages = sessionSettings.getCoverageIncludedPackages()
Expand All @@ -227,6 +221,28 @@ class GradleProjectConfigurator {
}
}

private static void disableDependencyVerificationIfConfigured(Project project, def configuration) {
if (Config.get().ciVisibilityGradleDependencyVerificationEnabled
|| !configuration.resolutionStrategy.respondsTo("disableDependencyVerification")) {
return
}

if (project.rootProject.file('gradle/verification-metadata.xml').isFile()) {
def extraProperties = project.rootProject.extensions.extraProperties
if (!extraProperties.has(DEPENDENCY_VERIFICATION_WARNING_EMITTED)) {
extraProperties.set(DEPENDENCY_VERIFICATION_WARNING_EMITTED, true)
project.logger.warn(
"Datadog Test Optimization disabled Gradle dependency verification for dependencies "
+ "injected into this build. To keep verification enabled, set "
+ "DD_CIVISIBILITY_GRADLE_DEPENDENCY_VERIFICATION_ENABLED=true and add "
+ "the Datadog compiler plugin and injected JaCoCo dependencies to "
+ "gradle/verification-metadata.xml. If the metadata is not updated, Gradle will "
+ "fail the build while resolving these dependencies.")
}
}
configuration.resolutionStrategy.disableDependencyVerification()
}

private static void forEveryTestTask(Project project, Closure closure) {
def c = { task ->
if (GradleUtils.isTestTask(task)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package datadog.trace.instrumentation.gradle;

import datadog.trace.api.Config;
import datadog.trace.api.civisibility.domain.BuildModuleLayout;
import java.io.File;
import java.nio.file.Files;
Expand Down Expand Up @@ -30,6 +31,10 @@ public abstract class CiVisibilityPlugin implements Plugin<Project> {

private static final String PLUGIN_EXTENSION_NAME = "dd-ci-visibility";
private static final String JACOCO_PLUGIN_ID = "jacoco";
private static final String JACOCO_AGENT_CONFIGURATION_NAME = "jacocoAgent";
private static final String JACOCO_ANT_CONFIGURATION_NAME = "jacocoAnt";
private static final String DEPENDENCY_VERIFICATION_WARNING_EMITTED =
"datadog.civisibility.dependency-verification-warning-emitted";

private Project project;

Expand Down Expand Up @@ -105,14 +110,44 @@ public void addCompilerPluginConfigurations(CiVisibilityPluginExtension extensio
"com.datadoghq:dd-javac-plugin-client:%s",
extension.getCompilerPluginVersion())));

// if instrumented project does dependency verification,
// we need to exclude the detached configurations that we're adding
// as corresponding entries are not in the project's verification-metadata.xml
configuration.getResolutionStrategy().disableDependencyVerification();
disableDependencyVerificationIfConfigured(configuration);

extension.setCompilerPluginClasspath(configuration);
}

private void disableDependencyVerificationIfConfigured(Configuration configuration) {
if (Config.get().isCiVisibilityGradleDependencyVerificationEnabled()) {
return;
}

if (Files.isRegularFile(
project
.getRootProject()
.getProjectDir()
.toPath()
.resolve("gradle/verification-metadata.xml"))) {
if (!project
.getRootProject()
.getExtensions()
.getExtraProperties()
.has(DEPENDENCY_VERIFICATION_WARNING_EMITTED)) {
project
.getRootProject()
.getExtensions()
.getExtraProperties()
.set(DEPENDENCY_VERIFICATION_WARNING_EMITTED, true);
LOGGER.warn(
"Datadog Test Optimization disabled Gradle dependency verification for dependencies "
+ "injected into this build. To keep verification enabled, set "
+ "DD_CIVISIBILITY_GRADLE_DEPENDENCY_VERIFICATION_ENABLED=true and add "
+ "the Datadog compiler plugin and injected JaCoCo dependencies to "
+ "gradle/verification-metadata.xml. If the metadata is not updated, Gradle will "
+ "fail the build while resolving these dependencies.");
}
}
configuration.getResolutionStrategy().disableDependencyVerification();
}

private void addModuleName(CiVisibilityPluginExtension extension) {
extension.setModuleName(getModuleName());
}
Expand Down Expand Up @@ -156,13 +191,13 @@ private void applyJacocoPlugin(CiVisibilityPluginExtension extension) {

List<Configuration> jacocoConfigurations =
project.getConfigurations().stream()
.filter(c -> c.getName().startsWith("jacoco"))
.filter(
c ->
JACOCO_AGENT_CONFIGURATION_NAME.equals(c.getName())
|| JACOCO_ANT_CONFIGURATION_NAME.equals(c.getName()))
.collect(Collectors.toList());
for (Configuration jacocoConfiguration : jacocoConfigurations) {
// if instrumented project does dependency verification,
// we need to exclude configurations added by Jacoco
// as corresponding entries are not in the project's verification-metadata.xml
jacocoConfiguration.getResolutionStrategy().disableDependencyVerification();
disableDependencyVerificationIfConfigured(jacocoConfiguration);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package datadog.smoketest;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
Expand Down Expand Up @@ -161,6 +162,45 @@ void testRobolectric(String gradleVersion, String projectName, int expectedTrace
mockBackend.waitForCoverages(0));
}

@TableTest({
"scenario | gradleVersion | verificationEnabled",
"legacy-default | 7.6.4 | false ",
"legacy-enabled | 7.6.4 | true ",
"modern-default | 8.3 | false ",
"modern-enabled | 8.3 | true "
})
@ParameterizedTest
void testInjectedDependencyVerification(String gradleVersion, boolean verificationEnabled)
throws IOException {
givenGradleVersionIsCompatibleWithCurrentJvm(gradleVersion);
givenGradleVersionIsSupportedByCurrentGradleTestKit(gradleVersion);
givenGradleProjectFiles("test-gradle-dependency-verification");

Map<String, String> additionalArgs = new HashMap<>();
if (verificationEnabled) {
additionalArgs.put(
CiVisibilityConfig.CIVISIBILITY_GRADLE_DEPENDENCY_VERIFICATION_ENABLED, "true");
}
givenGradleProjectProperties(additionalArgs);
ensureDependenciesDownloaded(gradleVersion);

BuildResult buildResult =
runGradle(
gradleVersion, Arrays.asList("compileJava", "--stacktrace"), !verificationEnabled);

if (verificationEnabled) {
assertTrue(buildResult.getOutput().contains("Dependency verification failed"));
} else {
assertBuildSuccessful(buildResult);
String warning =
"Datadog Test Optimization disabled Gradle dependency verification for dependencies "
+ "injected into this build.";
int firstWarning = buildResult.getOutput().indexOf(warning);
assertTrue(firstWarning >= 0);
assertEquals(firstWarning, buildResult.getOutput().lastIndexOf(warning));
}
}

// TODO: add back LATEST_GRADLE_VERSION after fixing ordering on Gradle 9.3.0
@TableTest({
"scenario | gradleVersion | projectName | flakyTests | expectedOrder | eventsNumber",
Expand Down Expand Up @@ -280,14 +320,19 @@ private void runGradleTest(
}

private void givenGradleProjectProperties() throws IOException {
givenGradleProjectProperties(Collections.emptyMap());
}

private void givenGradleProjectProperties(Map<String, String> additionalArgs) throws IOException {
assertTrue(new java.io.File(AGENT_JAR).isFile());

Path ddApiKeyPath = testKitFolder.resolve(".dd.api.key");
Files.write(ddApiKeyPath, "dummy".getBytes());

Map<String, String> additionalArgs = new HashMap<>();
additionalArgs.put(GeneralConfig.API_KEY_FILE, ddApiKeyPath.toAbsolutePath().toString());
additionalArgs.put(
Map<String, String> effectiveAdditionalArgs = new HashMap<>(additionalArgs);
effectiveAdditionalArgs.put(
GeneralConfig.API_KEY_FILE, ddApiKeyPath.toAbsolutePath().toString());
effectiveAdditionalArgs.put(
CiVisibilityConfig.CIVISIBILITY_JACOCO_PLUGIN_VERSION, JACOCO_PLUGIN_VERSION);
/*
* Some of the smoke tests (in particular the one with the Gradle plugin), are using Gradle Test Kit for their tests.
Expand All @@ -298,9 +343,9 @@ private void givenGradleProjectProperties() throws IOException {
* This causes the tests to fail because the number of reported traces is different.
* To avoid this discrepancy between local and CI runs, we disable tracing instrumentations.
*/
additionalArgs.put(TraceInstrumentationConfig.TRACE_ENABLED, "false");
effectiveAdditionalArgs.put(TraceInstrumentationConfig.TRACE_ENABLED, "false");
List<String> arguments =
buildJvmArguments(mockBackend.getIntakeUrl(), TEST_SERVICE_NAME, additionalArgs);
buildJvmArguments(mockBackend.getIntakeUrl(), TEST_SERVICE_NAME, effectiveAdditionalArgs);

String gradleProperties = "org.gradle.jvmargs=" + String.join(" ", arguments);
// Write to projectFolder (per-test) instead of testKitFolder (shared), so each
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apply plugin: 'java'

repositories {
mavenLocal()

def proxyUrl = System.getenv("MAVEN_REPOSITORY_PROXY")
if (proxyUrl) {
println "Using proxy repository: $proxyUrl"
maven {
url = proxyUrl
allowInsecureProtocol = true
}
}

mavenCentral()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<verification-metadata xmlns="https://schema.gradle.org/dependency-verification"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://schema.gradle.org/dependency-verification https://schema.gradle.org/dependency-verification/dependency-verification-1.2.xsd">
<configuration>
<verify-metadata>true</verify-metadata>
<verify-signatures>false</verify-signatures>
</configuration>
<components/>
</verification-metadata>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'gradle-dependency-verification-test-project'
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package datadog.smoke;

public class Calculator {

public int add(int left, int right) {
return left + right;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ public final class ConfigDefaults {
static final boolean DEFAULT_CIVISIBILITY_BUILD_INSTRUMENTATION_ENABLED = true;
static final boolean DEFAULT_CIVISIBILITY_AUTO_CONFIGURATION_ENABLED = true;
static final boolean DEFAULT_CIVISIBILITY_COMPILER_PLUGIN_AUTO_CONFIGURATION_ENABLED = true;
static final boolean DEFAULT_CIVISIBILITY_GRADLE_DEPENDENCY_VERIFICATION_ENABLED = false;
static final String DEFAULT_CIVISIBILITY_COMPILER_PLUGIN_VERSION = "0.2.4";
static final String DEFAULT_CIVISIBILITY_JACOCO_PLUGIN_VERSION = "0.8.15";
static final String DEFAULT_CIVISIBILITY_JACOCO_PLUGIN_EXCLUDES =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ public final class CiVisibilityConfig {
public static final String TEST_MANAGEMENT_ATTEMPT_TO_FIX_RETRIES =
"test.management.attempt.to.fix.retries";
public static final String TEST_FAILED_TEST_REPLAY_ENABLED = "test.failed.test.replay.enabled";
public static final String CIVISIBILITY_GRADLE_DEPENDENCY_VERIFICATION_ENABLED =
"civisibility.gradle.dependency.verification.enabled";

/* Git PR info */
public static final String GIT_PULL_REQUEST_BASE_BRANCH = "git.pull.request.base.branch";
Expand Down
13 changes: 13 additions & 0 deletions internal-api/src/main/java/datadog/trace/api/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import static datadog.trace.api.ConfigDefaults.DEFAULT_CIVISIBILITY_GIT_UNSHALLOW_ENABLED;
import static datadog.trace.api.ConfigDefaults.DEFAULT_CIVISIBILITY_GIT_UPLOAD_ENABLED;
import static datadog.trace.api.ConfigDefaults.DEFAULT_CIVISIBILITY_GIT_UPLOAD_TIMEOUT_MILLIS;
import static datadog.trace.api.ConfigDefaults.DEFAULT_CIVISIBILITY_GRADLE_DEPENDENCY_VERIFICATION_ENABLED;
import static datadog.trace.api.ConfigDefaults.DEFAULT_CIVISIBILITY_JACOCO_PLUGIN_EXCLUDES;
import static datadog.trace.api.ConfigDefaults.DEFAULT_CIVISIBILITY_JACOCO_PLUGIN_VERSION;
import static datadog.trace.api.ConfigDefaults.DEFAULT_CIVISIBILITY_RESOURCE_FOLDER_NAMES;
Expand Down Expand Up @@ -277,6 +278,7 @@
import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_GIT_UNSHALLOW_ENABLED;
import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_GIT_UPLOAD_ENABLED;
import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_GIT_UPLOAD_TIMEOUT_MILLIS;
import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_GRADLE_DEPENDENCY_VERIFICATION_ENABLED;
import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_GRADLE_SOURCE_SETS;
import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_IMPACTED_TESTS_DETECTION_ENABLED;
import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_INJECTED_TRACER_VERSION;
Expand Down Expand Up @@ -1149,6 +1151,7 @@ public static String getHostName() {
private final boolean ciVisibilityAutoConfigurationEnabled;
private final String ciVisibilityAdditionalChildProcessJvmArgs;
private final boolean ciVisibilityCompilerPluginAutoConfigurationEnabled;
private final boolean ciVisibilityGradleDependencyVerificationEnabled;
private final boolean ciVisibilityCodeCoverageEnabled;
private final Boolean ciVisibilityCoverageLinesEnabled;
private final String ciVisibilityCodeCoverageReportDumpDir;
Expand Down Expand Up @@ -2703,6 +2706,10 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment())
configProvider.getBoolean(
CIVISIBILITY_COMPILER_PLUGIN_AUTO_CONFIGURATION_ENABLED,
DEFAULT_CIVISIBILITY_COMPILER_PLUGIN_AUTO_CONFIGURATION_ENABLED);
ciVisibilityGradleDependencyVerificationEnabled =
configProvider.getBoolean(
CIVISIBILITY_GRADLE_DEPENDENCY_VERIFICATION_ENABLED,
DEFAULT_CIVISIBILITY_GRADLE_DEPENDENCY_VERIFICATION_ENABLED);
ciVisibilityCodeCoverageEnabled =
configProvider.getBoolean(CIVISIBILITY_CODE_COVERAGE_ENABLED, true);
ciVisibilityCoverageLinesEnabled =
Expand Down Expand Up @@ -4447,6 +4454,10 @@ public List<String> getCiVisibilityJacocoGradleSourceSets() {
return ciVisibilityJacocoGradleSourceSets;
}

public boolean isCiVisibilityGradleDependencyVerificationEnabled() {
return ciVisibilityGradleDependencyVerificationEnabled;
}

public boolean isCiVisibilityCodeCoverageReportUploadEnabled() {
return ciVisibilityCodeCoverageReportUploadEnabled;
}
Expand Down Expand Up @@ -6820,6 +6831,8 @@ public String toString() {
+ otlpTracesCompression
+ ", otlpTracesTimeout="
+ otlpTracesTimeout
+ ", ciVisibilityGradleDependencyVerificationEnabled="
+ ciVisibilityGradleDependencyVerificationEnabled
+ ", serviceDiscoveryEnabled="
+ serviceDiscoveryEnabled
+ ", sfnInjectDatadogAttributeEnabled="
Expand Down
8 changes: 8 additions & 0 deletions metadata/supported-configurations.json
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,14 @@
"aliases": []
}
],
"DD_CIVISIBILITY_GRADLE_DEPENDENCY_VERIFICATION_ENABLED": [
{
"version": "A",
"type": "boolean",
"default": "false",
"aliases": []
}
],
"DD_CIVISIBILITY_GRADLE_SOURCESETS": [
{
"version": "A",
Expand Down