Skip to content
Merged
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
@@ -1,5 +1,7 @@
package datadog.trace.civisibility.git;

import datadog.trace.api.civisibility.telemetry.tag.GitProviderDiscrepant;
import datadog.trace.api.civisibility.telemetry.tag.GitProviderExpected;
import datadog.trace.api.git.GitInfo;
import datadog.trace.api.git.GitInfoBuilder;
import datadog.trace.civisibility.git.tree.GitClient;
Expand Down Expand Up @@ -54,4 +56,14 @@ private Path getGitPath(String repositoryPath) {
public int order() {
return 2;
}

@Override
public GitProviderExpected providerAsExpected() {
return GitProviderExpected.LOCAL_GIT;
}

@Override
public GitProviderDiscrepant providerAsDiscrepant() {
return GitProviderDiscrepant.LOCAL_GIT;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package datadog.trace.civisibility.git;

import datadog.trace.api.Config;
import datadog.trace.api.civisibility.telemetry.tag.GitProviderDiscrepant;
import datadog.trace.api.civisibility.telemetry.tag.GitProviderExpected;
import datadog.trace.api.git.GitInfo;
import datadog.trace.api.git.GitInfoBuilder;
import datadog.trace.civisibility.ci.CIProviderInfo;
Expand Down Expand Up @@ -32,4 +34,14 @@ public GitInfo build(@Nullable String repositoryPath) {
public int order() {
return 1;
}

@Override
public GitProviderExpected providerAsExpected() {
return GitProviderExpected.CI_PROVIDER;
}

@Override
public GitProviderDiscrepant providerAsDiscrepant() {
return GitProviderDiscrepant.CI_PROVIDER;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package datadog.trace.civisibility.git;

import datadog.trace.api.Config;
import datadog.trace.api.civisibility.telemetry.tag.GitProviderDiscrepant;
import datadog.trace.api.civisibility.telemetry.tag.GitProviderExpected;
import datadog.trace.api.git.CommitInfo;
import datadog.trace.api.git.GitInfo;
import datadog.trace.api.git.GitInfoBuilder;
Expand Down Expand Up @@ -64,4 +66,14 @@ public GitInfo build(@Nullable String repositoryPath) {
public int order() {
return 3;
}

@Override
public GitProviderExpected providerAsExpected() {
return GitProviderExpected.GIT_CLIENT;
}

@Override
public GitProviderDiscrepant providerAsDiscrepant() {
return GitProviderDiscrepant.GIT_CLIENT;
}
}
1 change: 1 addition & 0 deletions internal-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ excludedClassesCoverage += [
// POJO
"datadog.trace.api.git.GitInfo",
"datadog.trace.api.git.GitInfoProvider",
"datadog.trace.api.git.GitInfoProvider.ShaDiscrepancy",
// POJO
"datadog.trace.api.git.PersonInfo",
// POJO
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
import datadog.trace.api.civisibility.telemetry.tag.ExitCode;
import datadog.trace.api.civisibility.telemetry.tag.FailFastTestOrderEnabled;
import datadog.trace.api.civisibility.telemetry.tag.FlakyTestRetriesEnabled;
import datadog.trace.api.civisibility.telemetry.tag.GitProviderDiscrepant;
import datadog.trace.api.civisibility.telemetry.tag.GitProviderExpected;
import datadog.trace.api.civisibility.telemetry.tag.GitShaDiscrepancyType;
import datadog.trace.api.civisibility.telemetry.tag.GitShaMatch;
import datadog.trace.api.civisibility.telemetry.tag.HasCodeowner;
import datadog.trace.api.civisibility.telemetry.tag.HasFailedAllRetries;
import datadog.trace.api.civisibility.telemetry.tag.ImpactedTestsDetectionEnabled;
Expand Down Expand Up @@ -101,6 +105,14 @@ public enum CiVisibilityCountMetric {
GIT_COMMAND("git.command", Command.class),
/** The number of git commands that errored */
GIT_COMMAND_ERRORS("git.command_errors", Command.class, ExitCode.class),
/** Number of commit sha comparisons and if they matched when building git info for a repo */
GIT_COMMIT_SHA_MATCH("git.commit_sha_match", GitShaMatch.class),
/** Number of sha mismatches when building git info for a repo */
GIT_COMMIT_SHA_DISCREPANCY(
"git.commit_sha_discrepancy",
GitProviderExpected.class,
GitProviderDiscrepant.class,
GitShaDiscrepancyType.class),
/** The number of requests sent to the search commit endpoint */
GIT_REQUESTS_SEARCH_COMMITS("git_requests.search_commits", RequestCompressed.class),
/** The number of search commit requests sent to the endpoint that errored */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package datadog.trace.api.civisibility.telemetry.tag;

import datadog.trace.api.civisibility.telemetry.TagValue;

public enum GitProviderDiscrepant implements TagValue {
USER_SUPPLIED,
CI_PROVIDER,
LOCAL_GIT,
GIT_CLIENT,
EMBEDDED;

@Override
public String asString() {
return "discrepant_provider:" + name().toLowerCase();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package datadog.trace.api.civisibility.telemetry.tag;

import datadog.trace.api.civisibility.telemetry.TagValue;

public enum GitProviderExpected implements TagValue {
USER_SUPPLIED,
CI_PROVIDER,
LOCAL_GIT,
GIT_CLIENT,
EMBEDDED;

@Override
public String asString() {
return "expected_provider:" + name().toLowerCase();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package datadog.trace.api.civisibility.telemetry.tag;

import datadog.trace.api.civisibility.telemetry.TagValue;

public enum GitShaDiscrepancyType implements TagValue {
REPOSITORY_DISCREPANCY,
COMMIT_DISCREPANCY;

@Override
public String asString() {
return "type:" + name().toLowerCase();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package datadog.trace.api.civisibility.telemetry.tag;

import datadog.trace.api.civisibility.telemetry.TagValue;

public enum GitShaMatch implements TagValue {
TRUE,
FALSE;

@Override
public String asString() {
return "matched:" + name().toLowerCase();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package datadog.trace.api.git;

import datadog.trace.api.civisibility.telemetry.tag.GitProviderDiscrepant;
import datadog.trace.api.civisibility.telemetry.tag.GitProviderExpected;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
Expand Down Expand Up @@ -89,4 +91,14 @@ public GitInfo build(@Nullable String repositoryPath) {
public int order() {
return Integer.MAX_VALUE;
}

@Override
public GitProviderExpected providerAsExpected() {
return GitProviderExpected.EMBEDDED;
}

@Override
public GitProviderDiscrepant providerAsDiscrepant() {
return GitProviderDiscrepant.EMBEDDED;
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
package datadog.trace.api.git;

import datadog.trace.api.civisibility.telemetry.tag.GitProviderDiscrepant;
import datadog.trace.api.civisibility.telemetry.tag.GitProviderExpected;
import javax.annotation.Nullable;

public interface GitInfoBuilder {
GitInfo build(@Nullable String repositoryPath);

int order();

/**
* Used for SHA discrepancies telemetry. Two enums are needed, one for each tag:
* `expected_provider`, `discrepant_provider`. A provider can act as either of them depending on
* the discrepancy found.
*/
GitProviderExpected providerAsExpected();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a brief comment explaining why two different enums are needed


GitProviderDiscrepant providerAsDiscrepant();
}
131 changes: 104 additions & 27 deletions internal-api/src/main/java/datadog/trace/api/git/GitInfoProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,24 @@

import datadog.trace.api.cache.DDCache;
import datadog.trace.api.cache.DDCaches;
import datadog.trace.api.civisibility.InstrumentationBridge;
import datadog.trace.api.civisibility.telemetry.CiVisibilityCountMetric;
import datadog.trace.api.civisibility.telemetry.tag.GitProviderDiscrepant;
import datadog.trace.api.civisibility.telemetry.tag.GitProviderExpected;
import datadog.trace.api.civisibility.telemetry.tag.GitShaDiscrepancyType;
import datadog.trace.api.civisibility.telemetry.tag.GitShaMatch;
import datadog.trace.util.Strings;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import javax.annotation.Nullable;
Expand Down Expand Up @@ -51,37 +60,89 @@ public GitInfo getGitInfo(@Nullable String repositoryPath) {

private GitInfo buildGitInfo(String repositoryPath) {
Evaluator evaluator = new Evaluator(repositoryPath, builders);
return new GitInfo(
evaluator.get(
gi -> GitUtils.filterSensitiveInfo(gi.getRepositoryURL()),
GitInfoProvider::validateGitRemoteUrl),
evaluator.get(GitInfo::getBranch, Strings::isNotBlank),
evaluator.get(GitInfo::getTag, Strings::isNotBlank),
new CommitInfo(
evaluator.get(gi1 -> gi1.getCommit().getSha(), Strings::isNotBlank),
new PersonInfo(
GitInfo gitInfo =
new GitInfo(
evaluator.get(
gi -> GitUtils.filterSensitiveInfo(gi.getRepositoryURL()),
GitInfoProvider::validateGitRemoteUrl),
evaluator.get(GitInfo::getBranch, Strings::isNotBlank),
evaluator.get(GitInfo::getTag, Strings::isNotBlank),
new CommitInfo(
evaluator.get(gi1 -> gi1.getCommit().getSha(), Strings::isNotBlank),
new PersonInfo(
evaluator.getIfCommitShaMatches(
gi -> gi.getCommit().getAuthor().getName(), Strings::isNotBlank),
evaluator.getIfCommitShaMatches(
gi -> gi.getCommit().getAuthor().getEmail(), Strings::isNotBlank),
evaluator.getIfCommitShaMatches(
gi -> gi.getCommit().getAuthor().getIso8601Date(), Strings::isNotBlank)),
new PersonInfo(
evaluator.getIfCommitShaMatches(
gi -> gi.getCommit().getCommitter().getName(), Strings::isNotBlank),
evaluator.getIfCommitShaMatches(
gi -> gi.getCommit().getCommitter().getEmail(), Strings::isNotBlank),
evaluator.getIfCommitShaMatches(
gi -> gi.getCommit().getCommitter().getIso8601Date(), Strings::isNotBlank)),
evaluator.getIfCommitShaMatches(
gi -> gi.getCommit().getAuthor().getName(), Strings::isNotBlank),
evaluator.getIfCommitShaMatches(
gi -> gi.getCommit().getAuthor().getEmail(), Strings::isNotBlank),
evaluator.getIfCommitShaMatches(
gi -> gi.getCommit().getAuthor().getIso8601Date(), Strings::isNotBlank)),
new PersonInfo(
evaluator.getIfCommitShaMatches(
gi -> gi.getCommit().getCommitter().getName(), Strings::isNotBlank),
evaluator.getIfCommitShaMatches(
gi -> gi.getCommit().getCommitter().getEmail(), Strings::isNotBlank),
evaluator.getIfCommitShaMatches(
gi -> gi.getCommit().getCommitter().getIso8601Date(), Strings::isNotBlank)),
evaluator.getIfCommitShaMatches(
gi -> gi.getCommit().getFullMessage(), Strings::isNotBlank)));
gi -> gi.getCommit().getFullMessage(), Strings::isNotBlank)));

InstrumentationBridge.getMetricCollector()
.add(
CiVisibilityCountMetric.GIT_COMMIT_SHA_MATCH,
1,
evaluator.shaDiscrepancies.isEmpty() ? GitShaMatch.TRUE : GitShaMatch.FALSE);
for (ShaDiscrepancy mismatch : evaluator.shaDiscrepancies) {
mismatch.addTelemetry();
}

return gitInfo;
}

private static boolean validateGitRemoteUrl(String s) {
// we cannot work with URL that uses "file://" protocol
return Strings.isNotBlank(s) && !s.startsWith("file:");
}

private static final class ShaDiscrepancy {
private final GitProviderExpected expectedGitProvider;
private final GitProviderDiscrepant discrepantGitProvider;
private final GitShaDiscrepancyType discrepancyType;

private ShaDiscrepancy(
GitProviderExpected expectedGitProvider,
GitProviderDiscrepant discrepantGitProvider,
GitShaDiscrepancyType discrepancyType) {
this.expectedGitProvider = expectedGitProvider;
this.discrepantGitProvider = discrepantGitProvider;
this.discrepancyType = discrepancyType;
}

private void addTelemetry() {
InstrumentationBridge.getMetricCollector()
.add(
CiVisibilityCountMetric.GIT_COMMIT_SHA_DISCREPANCY,
1,
expectedGitProvider,
discrepantGitProvider,
discrepancyType);
}

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
ShaDiscrepancy that = (ShaDiscrepancy) obj;
return expectedGitProvider.equals(that.expectedGitProvider)
&& discrepantGitProvider.equals(that.discrepantGitProvider)
&& discrepancyType.equals(that.discrepancyType);
}

@Override
public int hashCode() {
return Objects.hash(expectedGitProvider, discrepantGitProvider, discrepancyType);
}
}

/**
* Uses provided GitInfoBuilder instances to get GitInfo data.
*
Expand All @@ -95,10 +156,12 @@ private static boolean validateGitRemoteUrl(String s) {
private static final class Evaluator {
private final String repositoryPath;
private final Map<GitInfoBuilder, GitInfo> infos;
private final Set<ShaDiscrepancy> shaDiscrepancies;

private Evaluator(String repositoryPath, Collection<GitInfoBuilder> builders) {
this.repositoryPath = repositoryPath;
this.infos = new LinkedHashMap<>();
this.shaDiscrepancies = new HashSet<>();
for (GitInfoBuilder builder : builders) {
infos.put(builder, null);
}
Expand All @@ -121,7 +184,10 @@ private String get(
Function<GitInfo, String> function,
Predicate<String> validator,
boolean checkShaIntegrity) {
String commitSha = null;
String expectedCommitSha = null;
String expectedRepoUrl = null;
GitProviderExpected expectedGitProvider = null;

for (Map.Entry<GitInfoBuilder, GitInfo> e : infos.entrySet()) {
GitInfo info = e.getValue();
if (info == null) {
Expand All @@ -134,11 +200,22 @@ private String get(
CommitInfo currentCommit = info.getCommit();
String currentCommitSha = currentCommit != null ? currentCommit.getSha() : null;
if (Strings.isNotBlank(currentCommitSha)) {
if (commitSha == null) {
commitSha = currentCommitSha;
} else if (!commitSha.equals(currentCommitSha)) {
if (expectedCommitSha == null) {
expectedCommitSha = currentCommitSha;
expectedRepoUrl = info.getRepositoryURL();
expectedGitProvider = e.getKey().providerAsExpected();
} else if (!expectedCommitSha.equals(currentCommitSha)) {
// We already have a commit SHA from source that has higher priority.
// Commit SHA from current source is different, so we have to skip it
GitShaDiscrepancyType discrepancyType = GitShaDiscrepancyType.COMMIT_DISCREPANCY;
String repoUrl = info.getRepositoryURL();
if (expectedRepoUrl != null && repoUrl != null && !repoUrl.equals(expectedRepoUrl)) {
discrepancyType = GitShaDiscrepancyType.REPOSITORY_DISCREPANCY;
}

shaDiscrepancies.add(
new ShaDiscrepancy(
expectedGitProvider, e.getKey().providerAsDiscrepant(), discrepancyType));
continue;
}
}
Expand Down
Loading