Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement impacted tests detection #8188

Merged
merged 16 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
e8eab99
Pass test source data to retry policy factory
nikita-tkachenko-datadog Jan 13, 2025
71a62e0
Collect pull request info for GitHub and Gitlab
nikita-tkachenko-datadog Jan 15, 2025
ebc6400
Implement git diff fetching and parsing
nikita-tkachenko-datadog Jan 16, 2025
8e492ef
Add PR diff to execution settings
nikita-tkachenko-datadog Jan 16, 2025
2584775
Implement EFD for modified tests
nikita-tkachenko-datadog Jan 16, 2025
6f5c09a
Implement backend config setting and killswitch for impacted tests de…
nikita-tkachenko-datadog Jan 16, 2025
94a681d
Implement is_modified telemetry tag
nikita-tkachenko-datadog Jan 16, 2025
39acdab
Extract Diff interface and implement polymorphic serialization
nikita-tkachenko-datadog Jan 16, 2025
9868991
Implement file-granularity diff
nikita-tkachenko-datadog Jan 16, 2025
8bebe62
Implement fallback to file-level granularity for impacted tests detec…
nikita-tkachenko-datadog Jan 16, 2025
d6c4567
Ensure git repo is not shallow before calculating git diff
nikita-tkachenko-datadog Jan 17, 2025
5e0e4ed
Use commit SHA from backend diff response if no PR info is available …
nikita-tkachenko-datadog Jan 17, 2025
0588338
Fix spotBugs warning
nikita-tkachenko-datadog Jan 17, 2025
e767b74
Fix Weaver compilation after merge
nikita-tkachenko-datadog Jan 20, 2025
2fda4f6
Add instrumentation tests for impacted tests detection
nikita-tkachenko-datadog Jan 22, 2025
9b370b2
Add smoke test for impacted tests detection
nikita-tkachenko-datadog Jan 22, 2025
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 @@ -8,6 +8,7 @@
import datadog.trace.civisibility.ci.CIInfo;
import datadog.trace.civisibility.ci.CIProviderInfo;
import datadog.trace.civisibility.ci.CITagsProvider;
import datadog.trace.civisibility.ci.PullRequestInfo;
import datadog.trace.civisibility.codeowners.Codeowners;
import datadog.trace.civisibility.codeowners.CodeownersProvider;
import datadog.trace.civisibility.codeowners.NoCodeowners;
Expand All @@ -22,6 +23,7 @@
import datadog.trace.civisibility.git.tree.GitDataApi;
import datadog.trace.civisibility.git.tree.GitDataUploader;
import datadog.trace.civisibility.git.tree.GitDataUploaderImpl;
import datadog.trace.civisibility.git.tree.GitRepoUnshallow;
import datadog.trace.civisibility.ipc.ExecutionSettingsRequest;
import datadog.trace.civisibility.ipc.ExecutionSettingsResponse;
import datadog.trace.civisibility.ipc.SignalClient;
Expand All @@ -31,6 +33,8 @@
import datadog.trace.civisibility.source.SourcePathResolver;
import datadog.trace.civisibility.source.index.RepoIndexProvider;
import datadog.trace.civisibility.source.index.RepoIndexSourcePathResolver;
import datadog.trace.util.Strings;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
Expand Down Expand Up @@ -59,16 +63,26 @@ public class CiVisibilityRepoServices {
ciProvider = ciProviderInfo.getProvider();

CIInfo ciInfo = ciProviderInfo.buildCIInfo();
repoRoot = ciInfo.getNormalizedCiWorkspace();
moduleName = getModuleName(services.config, path, ciInfo);
ciTags = new CITagsProvider().getCiTags(ciInfo);
PullRequestInfo pullRequestInfo = ciProviderInfo.buildPullRequestInfo();

if (pullRequestInfo.isNotEmpty()) {
LOGGER.info("PR detected: {}", pullRequestInfo);
}

repoRoot = appendSlashIfNeeded(getRepoRoot(ciInfo, services.gitClientFactory));
moduleName = getModuleName(services.config, repoRoot, path);
ciTags = new CITagsProvider().getCiTags(ciInfo, pullRequestInfo);

GitClient gitClient = services.gitClientFactory.create(repoRoot);
GitRepoUnshallow gitRepoUnshallow = new GitRepoUnshallow(services.config, gitClient);

gitDataUploader =
buildGitDataUploader(
services.config,
services.metricCollector,
services.gitInfoProvider,
services.gitClientFactory,
gitClient,
gitRepoUnshallow,
services.backendApi,
repoRoot);
repoIndexProvider = services.repoIndexProviderFactory.create(repoRoot);
Expand All @@ -84,18 +98,49 @@ public class CiVisibilityRepoServices {
services.config,
services.metricCollector,
services.backendApi,
gitClient,
gitRepoUnshallow,
gitDataUploader,
pullRequestInfo,
repoRoot);
}
}

static String getModuleName(Config config, Path path, CIInfo ciInfo) {
private static String getRepoRoot(CIInfo ciInfo, GitClient.Factory gitClientFactory) {
String ciWorkspace = ciInfo.getNormalizedCiWorkspace();
if (Strings.isNotBlank(ciWorkspace)) {
return ciWorkspace;

} else {
try {
return gitClientFactory.create(".").getRepoRoot();

} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOGGER.error("Interrupted while getting repo root", e);
return null;

} catch (Exception e) {
LOGGER.error("Error while getting repo root", e);
return null;
}
}
}

private static String appendSlashIfNeeded(String repoRoot) {
if (repoRoot != null && !repoRoot.endsWith(File.separator)) {
return repoRoot + File.separator;
} else {
return repoRoot;
}
}

static String getModuleName(Config config, String repoRoot, Path path) {
// if parent process is instrumented, it will provide build system's module name
String parentModuleName = config.getCiVisibilityModuleName();
if (parentModuleName != null) {
return parentModuleName;
}
String repoRoot = ciInfo.getNormalizedCiWorkspace();
if (repoRoot != null && path.startsWith(repoRoot)) {
String relativePath = Paths.get(repoRoot).relativize(path).toString();
if (!relativePath.isEmpty()) {
Expand Down Expand Up @@ -126,7 +171,10 @@ private static ExecutionSettingsFactory buildExecutionSettingsFactory(
Config config,
CiVisibilityMetricCollector metricCollector,
BackendApi backendApi,
GitClient gitClient,
GitRepoUnshallow gitRepoUnshallow,
GitDataUploader gitDataUploader,
PullRequestInfo pullRequestInfo,
String repoRoot) {
ConfigurationApi configurationApi;
if (backendApi == null) {
Expand All @@ -138,7 +186,14 @@ private static ExecutionSettingsFactory buildExecutionSettingsFactory(
}

ExecutionSettingsFactoryImpl factory =
new ExecutionSettingsFactoryImpl(config, configurationApi, gitDataUploader, repoRoot);
new ExecutionSettingsFactoryImpl(
config,
configurationApi,
gitClient,
gitRepoUnshallow,
gitDataUploader,
pullRequestInfo,
repoRoot);
if (processHierarchy.isHeadless()) {
return factory;
} else {
Expand All @@ -150,7 +205,8 @@ private static GitDataUploader buildGitDataUploader(
Config config,
CiVisibilityMetricCollector metricCollector,
GitInfoProvider gitInfoProvider,
GitClient.Factory gitClientFactory,
GitClient gitClient,
GitRepoUnshallow gitRepoUnshallow,
BackendApi backendApi,
String repoRoot) {
if (!config.isCiVisibilityGitUploadEnabled()) {
Expand All @@ -171,9 +227,15 @@ private static GitDataUploader buildGitDataUploader(

String remoteName = config.getCiVisibilityGitRemoteName();
GitDataApi gitDataApi = new GitDataApi(backendApi, metricCollector);
GitClient gitClient = gitClientFactory.create(repoRoot);
return new GitDataUploaderImpl(
config, metricCollector, gitDataApi, gitClient, gitInfoProvider, repoRoot, remoteName);
config,
metricCollector,
gitDataApi,
gitClient,
gitRepoUnshallow,
gitInfoProvider,
repoRoot,
remoteName);
}

private static SourcePathResolver buildSourcePathResolver(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
import datadog.communication.ddagent.SharedCommunicationObjects;
import datadog.communication.http.HttpRetryPolicy;
import datadog.communication.http.OkHttpUtils;
import datadog.communication.util.IOUtils;
import datadog.trace.api.Config;
import datadog.trace.api.civisibility.telemetry.CiVisibilityCountMetric;
import datadog.trace.api.civisibility.telemetry.CiVisibilityMetricCollector;
import datadog.trace.api.civisibility.telemetry.tag.Command;
import datadog.trace.api.git.GitInfoProvider;
import datadog.trace.civisibility.ci.CIProviderInfoFactory;
import datadog.trace.civisibility.ci.env.CiEnvironment;
Expand All @@ -22,24 +25,28 @@
import datadog.trace.civisibility.git.CIProviderGitInfoBuilder;
import datadog.trace.civisibility.git.GitClientGitInfoBuilder;
import datadog.trace.civisibility.git.tree.GitClient;
import datadog.trace.civisibility.git.tree.NoOpGitClient;
import datadog.trace.civisibility.git.tree.ShellGitClient;
import datadog.trace.civisibility.ipc.SignalClient;
import datadog.trace.civisibility.source.BestEffortLinesResolver;
import datadog.trace.civisibility.source.ByteCodeLinesResolver;
import datadog.trace.civisibility.source.CompilerAidedLinesResolver;
import datadog.trace.civisibility.source.LinesResolver;
import datadog.trace.civisibility.source.index.*;
import datadog.trace.civisibility.utils.ShellCommandExecutor;
import java.io.File;
import java.lang.reflect.Type;
import java.net.InetSocketAddress;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -78,7 +85,7 @@ public class CiVisibilityServices {
this.backendApi =
new BackendApiFactory(config, sco).createBackendApi(BackendApiFactory.Intake.API);
this.jvmInfoFactory = new CachingJvmInfoFactory(config, new JvmInfoFactoryImpl());
this.gitClientFactory = new GitClient.Factory(config, metricCollector);
this.gitClientFactory = buildGitClientFactory(config, metricCollector);

CiEnvironment environment = buildCiEnvironment(config, sco);
this.ciProviderInfoFactory = new CIProviderInfoFactory(config, environment);
Expand Down Expand Up @@ -111,7 +118,30 @@ public class CiVisibilityServices {
}
}

@NotNull
private static GitClient.Factory buildGitClientFactory(
Config config, CiVisibilityMetricCollector metricCollector) {
if (!config.isCiVisibilityGitClientEnabled()) {
return r -> NoOpGitClient.INSTANCE;
}
try {
ShellCommandExecutor shellCommandExecutor =
new ShellCommandExecutor(new File("."), config.getCiVisibilityGitCommandTimeoutMillis());
String gitVersion = shellCommandExecutor.executeCommand(IOUtils::readFully, "git", "version");
logger.debug("Detected git executable version {}", gitVersion);
return new ShellGitClient.Factory(config, metricCollector);

} catch (Exception e) {
metricCollector.add(
CiVisibilityCountMetric.GIT_COMMAND_ERRORS,
1,
Command.OTHER,
ShellCommandExecutor.getExitCode(e));
logger.info("No git executable detected, some features will not be available");
return r -> NoOpGitClient.INSTANCE;
}
}

@Nonnull
private static CiEnvironment buildCiEnvironment(Config config, SharedCommunicationObjects sco) {
String remoteEnvVarsProviderUrl = config.getCiVisibilityRemoteEnvVarsProviderUrl();
if (remoteEnvVarsProviderUrl != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,11 @@ private static TestFrameworkSession.Factory childTestFrameworkSessionFactory(
new TestDecoratorImpl(component, sessionName, testCommand, repoServices.ciTags);

ExecutionStrategy executionStrategy =
new ExecutionStrategy(services.config, executionSettings);
new ExecutionStrategy(
services.config,
executionSettings,
repoServices.sourcePathResolver,
services.linesResolver);

return new ProxyTestSession(
services.processHierarchy.parentProcessModuleContext,
Expand Down Expand Up @@ -268,7 +272,11 @@ private static TestFrameworkSession.Factory headlessTestFrameworkEssionFactory(
new TestDecoratorImpl(component, sessionName, projectName, repoServices.ciTags);

ExecutionStrategy executionStrategy =
new ExecutionStrategy(services.config, executionSettings);
new ExecutionStrategy(
services.config,
executionSettings,
repoServices.sourcePathResolver,
services.linesResolver);
return new HeadlessTestSession(
projectName,
startTime,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import datadog.trace.api.git.GitInfo;
import datadog.trace.api.git.PersonInfo;
import datadog.trace.civisibility.ci.env.CiEnvironment;
import javax.annotation.Nonnull;

class AppVeyorInfo implements CIProviderInfo {

Expand Down Expand Up @@ -79,6 +80,12 @@ public CIInfo buildCIInfo() {
.build();
}

@Nonnull
@Override
public PullRequestInfo buildPullRequestInfo() {
return PullRequestInfo.EMPTY;
}

private String buildGitBranch(final String repoProvider) {
if ("github".equals(repoProvider)) {
String branch = environment.get(APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import datadog.trace.api.civisibility.telemetry.tag.Provider;
import datadog.trace.api.git.GitInfo;
import datadog.trace.civisibility.ci.env.CiEnvironment;
import javax.annotation.Nonnull;

class AwsCodePipelineInfo implements CIProviderInfo {

Expand Down Expand Up @@ -35,6 +36,12 @@ public CIInfo buildCIInfo() {
.build();
}

@Nonnull
@Override
public PullRequestInfo buildPullRequestInfo() {
return PullRequestInfo.EMPTY;
}

@Override
public Provider getProvider() {
return Provider.AWS;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import datadog.trace.api.git.GitInfo;
import datadog.trace.api.git.PersonInfo;
import datadog.trace.civisibility.ci.env.CiEnvironment;
import javax.annotation.Nonnull;

class AzurePipelinesInfo implements CIProviderInfo {

Expand Down Expand Up @@ -81,6 +82,12 @@ public CIInfo buildCIInfo() {
.build();
}

@Nonnull
@Override
public PullRequestInfo buildPullRequestInfo() {
return PullRequestInfo.EMPTY;
}

private String buildGitBranch() {
String gitBranchOrTag = getGitBranchOrTag();
if (!isTagReference(gitBranchOrTag)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import datadog.trace.api.git.GitInfo;
import datadog.trace.civisibility.ci.env.CiEnvironment;
import datadog.trace.util.Strings;
import javax.annotation.Nonnull;

class BitBucketInfo implements CIProviderInfo {

Expand Down Expand Up @@ -70,6 +71,12 @@ public CIInfo buildCIInfo() {
.build();
}

@Nonnull
@Override
public PullRequestInfo buildPullRequestInfo() {
return PullRequestInfo.EMPTY;
}

private String buildPipelineUrl(final String repo, final String number) {
return String.format(
"https://bitbucket.org/%s/addon/pipelines/home#!/results/%s", repo, number);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import datadog.trace.api.git.GitInfo;
import datadog.trace.api.git.PersonInfo;
import datadog.trace.civisibility.ci.env.CiEnvironment;
import javax.annotation.Nonnull;

class BitriseInfo implements CIProviderInfo {

Expand Down Expand Up @@ -66,6 +67,12 @@ public CIInfo buildCIInfo() {
.build();
}

@Nonnull
@Override
public PullRequestInfo buildPullRequestInfo() {
return PullRequestInfo.EMPTY;
}

private String buildGitCommit() {
final String fromCommit = environment.get(BITRISE_GIT_PR_COMMIT);
if (fromCommit != null && !fromCommit.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import datadog.trace.api.git.GitInfo;
import datadog.trace.api.git.PersonInfo;
import datadog.trace.civisibility.ci.env.CiEnvironment;
import javax.annotation.Nonnull;

class BuddyInfo implements CIProviderInfo {

Expand Down Expand Up @@ -58,6 +59,12 @@ public CIInfo buildCIInfo() {
.build();
}

@Nonnull
@Override
public PullRequestInfo buildPullRequestInfo() {
return PullRequestInfo.EMPTY;
}

private String getPipelineId(String pipelineNumber) {
String pipelineId = environment.get(BUDDY_PIPELINE_ID);
if (pipelineId == null) {
Expand Down
Loading
Loading