Skip to content

Commit d5d53cd

Browse files
Add commit discrepancies telemetry when building repository git information (#8763)
1 parent c2faab3 commit d5d53cd

File tree

14 files changed

+334
-27
lines changed

14 files changed

+334
-27
lines changed

dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/CILocalGitInfoBuilder.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package datadog.trace.civisibility.git;
22

3+
import datadog.trace.api.civisibility.telemetry.tag.GitProviderDiscrepant;
4+
import datadog.trace.api.civisibility.telemetry.tag.GitProviderExpected;
35
import datadog.trace.api.git.GitInfo;
46
import datadog.trace.api.git.GitInfoBuilder;
57
import datadog.trace.civisibility.git.tree.GitClient;
@@ -54,4 +56,14 @@ private Path getGitPath(String repositoryPath) {
5456
public int order() {
5557
return 2;
5658
}
59+
60+
@Override
61+
public GitProviderExpected providerAsExpected() {
62+
return GitProviderExpected.LOCAL_GIT;
63+
}
64+
65+
@Override
66+
public GitProviderDiscrepant providerAsDiscrepant() {
67+
return GitProviderDiscrepant.LOCAL_GIT;
68+
}
5769
}

dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/CIProviderGitInfoBuilder.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package datadog.trace.civisibility.git;
22

33
import datadog.trace.api.Config;
4+
import datadog.trace.api.civisibility.telemetry.tag.GitProviderDiscrepant;
5+
import datadog.trace.api.civisibility.telemetry.tag.GitProviderExpected;
46
import datadog.trace.api.git.GitInfo;
57
import datadog.trace.api.git.GitInfoBuilder;
68
import datadog.trace.civisibility.ci.CIProviderInfo;
@@ -32,4 +34,14 @@ public GitInfo build(@Nullable String repositoryPath) {
3234
public int order() {
3335
return 1;
3436
}
37+
38+
@Override
39+
public GitProviderExpected providerAsExpected() {
40+
return GitProviderExpected.CI_PROVIDER;
41+
}
42+
43+
@Override
44+
public GitProviderDiscrepant providerAsDiscrepant() {
45+
return GitProviderDiscrepant.CI_PROVIDER;
46+
}
3547
}

dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/GitClientGitInfoBuilder.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package datadog.trace.civisibility.git;
22

33
import datadog.trace.api.Config;
4+
import datadog.trace.api.civisibility.telemetry.tag.GitProviderDiscrepant;
5+
import datadog.trace.api.civisibility.telemetry.tag.GitProviderExpected;
46
import datadog.trace.api.git.CommitInfo;
57
import datadog.trace.api.git.GitInfo;
68
import datadog.trace.api.git.GitInfoBuilder;
@@ -64,4 +66,14 @@ public GitInfo build(@Nullable String repositoryPath) {
6466
public int order() {
6567
return 3;
6668
}
69+
70+
@Override
71+
public GitProviderExpected providerAsExpected() {
72+
return GitProviderExpected.GIT_CLIENT;
73+
}
74+
75+
@Override
76+
public GitProviderDiscrepant providerAsDiscrepant() {
77+
return GitProviderDiscrepant.GIT_CLIENT;
78+
}
6779
}

internal-api/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ excludedClassesCoverage += [
139139
// POJO
140140
"datadog.trace.api.git.GitInfo",
141141
"datadog.trace.api.git.GitInfoProvider",
142+
"datadog.trace.api.git.GitInfoProvider.ShaDiscrepancy",
142143
// POJO
143144
"datadog.trace.api.git.PersonInfo",
144145
// POJO

internal-api/src/main/java/datadog/trace/api/civisibility/telemetry/CiVisibilityCountMetric.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
import datadog.trace.api.civisibility.telemetry.tag.ExitCode;
1515
import datadog.trace.api.civisibility.telemetry.tag.FailFastTestOrderEnabled;
1616
import datadog.trace.api.civisibility.telemetry.tag.FlakyTestRetriesEnabled;
17+
import datadog.trace.api.civisibility.telemetry.tag.GitProviderDiscrepant;
18+
import datadog.trace.api.civisibility.telemetry.tag.GitProviderExpected;
19+
import datadog.trace.api.civisibility.telemetry.tag.GitShaDiscrepancyType;
20+
import datadog.trace.api.civisibility.telemetry.tag.GitShaMatch;
1721
import datadog.trace.api.civisibility.telemetry.tag.HasCodeowner;
1822
import datadog.trace.api.civisibility.telemetry.tag.HasFailedAllRetries;
1923
import datadog.trace.api.civisibility.telemetry.tag.ImpactedTestsDetectionEnabled;
@@ -101,6 +105,14 @@ public enum CiVisibilityCountMetric {
101105
GIT_COMMAND("git.command", Command.class),
102106
/** The number of git commands that errored */
103107
GIT_COMMAND_ERRORS("git.command_errors", Command.class, ExitCode.class),
108+
/** Number of commit sha comparisons and if they matched when building git info for a repo */
109+
GIT_COMMIT_SHA_MATCH("git.commit_sha_match", GitShaMatch.class),
110+
/** Number of sha mismatches when building git info for a repo */
111+
GIT_COMMIT_SHA_DISCREPANCY(
112+
"git.commit_sha_discrepancy",
113+
GitProviderExpected.class,
114+
GitProviderDiscrepant.class,
115+
GitShaDiscrepancyType.class),
104116
/** The number of requests sent to the search commit endpoint */
105117
GIT_REQUESTS_SEARCH_COMMITS("git_requests.search_commits", RequestCompressed.class),
106118
/** The number of search commit requests sent to the endpoint that errored */
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package datadog.trace.api.civisibility.telemetry.tag;
2+
3+
import datadog.trace.api.civisibility.telemetry.TagValue;
4+
5+
public enum GitProviderDiscrepant implements TagValue {
6+
USER_SUPPLIED,
7+
CI_PROVIDER,
8+
LOCAL_GIT,
9+
GIT_CLIENT,
10+
EMBEDDED;
11+
12+
@Override
13+
public String asString() {
14+
return "discrepant_provider:" + name().toLowerCase();
15+
}
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package datadog.trace.api.civisibility.telemetry.tag;
2+
3+
import datadog.trace.api.civisibility.telemetry.TagValue;
4+
5+
public enum GitProviderExpected implements TagValue {
6+
USER_SUPPLIED,
7+
CI_PROVIDER,
8+
LOCAL_GIT,
9+
GIT_CLIENT,
10+
EMBEDDED;
11+
12+
@Override
13+
public String asString() {
14+
return "expected_provider:" + name().toLowerCase();
15+
}
16+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package datadog.trace.api.civisibility.telemetry.tag;
2+
3+
import datadog.trace.api.civisibility.telemetry.TagValue;
4+
5+
public enum GitShaDiscrepancyType implements TagValue {
6+
REPOSITORY_DISCREPANCY,
7+
COMMIT_DISCREPANCY;
8+
9+
@Override
10+
public String asString() {
11+
return "type:" + name().toLowerCase();
12+
}
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package datadog.trace.api.civisibility.telemetry.tag;
2+
3+
import datadog.trace.api.civisibility.telemetry.TagValue;
4+
5+
public enum GitShaMatch implements TagValue {
6+
TRUE,
7+
FALSE;
8+
9+
@Override
10+
public String asString() {
11+
return "matched:" + name().toLowerCase();
12+
}
13+
}

internal-api/src/main/java/datadog/trace/api/git/EmbeddedGitInfoBuilder.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package datadog.trace.api.git;
22

3+
import datadog.trace.api.civisibility.telemetry.tag.GitProviderDiscrepant;
4+
import datadog.trace.api.civisibility.telemetry.tag.GitProviderExpected;
35
import java.io.IOException;
46
import java.io.InputStream;
57
import java.util.Arrays;
@@ -89,4 +91,14 @@ public GitInfo build(@Nullable String repositoryPath) {
8991
public int order() {
9092
return Integer.MAX_VALUE;
9193
}
94+
95+
@Override
96+
public GitProviderExpected providerAsExpected() {
97+
return GitProviderExpected.EMBEDDED;
98+
}
99+
100+
@Override
101+
public GitProviderDiscrepant providerAsDiscrepant() {
102+
return GitProviderDiscrepant.EMBEDDED;
103+
}
92104
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
package datadog.trace.api.git;
22

3+
import datadog.trace.api.civisibility.telemetry.tag.GitProviderDiscrepant;
4+
import datadog.trace.api.civisibility.telemetry.tag.GitProviderExpected;
35
import javax.annotation.Nullable;
46

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

810
int order();
11+
12+
/**
13+
* Used for SHA discrepancies telemetry. Two enums are needed, one for each tag:
14+
* `expected_provider`, `discrepant_provider`. A provider can act as either of them depending on
15+
* the discrepancy found.
16+
*/
17+
GitProviderExpected providerAsExpected();
18+
19+
GitProviderDiscrepant providerAsDiscrepant();
920
}

internal-api/src/main/java/datadog/trace/api/git/GitInfoProvider.java

Lines changed: 104 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,24 @@
22

33
import datadog.trace.api.cache.DDCache;
44
import datadog.trace.api.cache.DDCaches;
5+
import datadog.trace.api.civisibility.InstrumentationBridge;
6+
import datadog.trace.api.civisibility.telemetry.CiVisibilityCountMetric;
7+
import datadog.trace.api.civisibility.telemetry.tag.GitProviderDiscrepant;
8+
import datadog.trace.api.civisibility.telemetry.tag.GitProviderExpected;
9+
import datadog.trace.api.civisibility.telemetry.tag.GitShaDiscrepancyType;
10+
import datadog.trace.api.civisibility.telemetry.tag.GitShaMatch;
511
import datadog.trace.util.Strings;
612
import java.nio.file.Paths;
713
import java.util.ArrayList;
814
import java.util.Collection;
915
import java.util.Collections;
1016
import java.util.Comparator;
17+
import java.util.HashSet;
1118
import java.util.LinkedHashMap;
1219
import java.util.List;
1320
import java.util.Map;
21+
import java.util.Objects;
22+
import java.util.Set;
1423
import java.util.function.Function;
1524
import java.util.function.Predicate;
1625
import javax.annotation.Nullable;
@@ -51,37 +60,89 @@ public GitInfo getGitInfo(@Nullable String repositoryPath) {
5160

5261
private GitInfo buildGitInfo(String repositoryPath) {
5362
Evaluator evaluator = new Evaluator(repositoryPath, builders);
54-
return new GitInfo(
55-
evaluator.get(
56-
gi -> GitUtils.filterSensitiveInfo(gi.getRepositoryURL()),
57-
GitInfoProvider::validateGitRemoteUrl),
58-
evaluator.get(GitInfo::getBranch, Strings::isNotBlank),
59-
evaluator.get(GitInfo::getTag, Strings::isNotBlank),
60-
new CommitInfo(
61-
evaluator.get(gi1 -> gi1.getCommit().getSha(), Strings::isNotBlank),
62-
new PersonInfo(
63+
GitInfo gitInfo =
64+
new GitInfo(
65+
evaluator.get(
66+
gi -> GitUtils.filterSensitiveInfo(gi.getRepositoryURL()),
67+
GitInfoProvider::validateGitRemoteUrl),
68+
evaluator.get(GitInfo::getBranch, Strings::isNotBlank),
69+
evaluator.get(GitInfo::getTag, Strings::isNotBlank),
70+
new CommitInfo(
71+
evaluator.get(gi1 -> gi1.getCommit().getSha(), Strings::isNotBlank),
72+
new PersonInfo(
73+
evaluator.getIfCommitShaMatches(
74+
gi -> gi.getCommit().getAuthor().getName(), Strings::isNotBlank),
75+
evaluator.getIfCommitShaMatches(
76+
gi -> gi.getCommit().getAuthor().getEmail(), Strings::isNotBlank),
77+
evaluator.getIfCommitShaMatches(
78+
gi -> gi.getCommit().getAuthor().getIso8601Date(), Strings::isNotBlank)),
79+
new PersonInfo(
80+
evaluator.getIfCommitShaMatches(
81+
gi -> gi.getCommit().getCommitter().getName(), Strings::isNotBlank),
82+
evaluator.getIfCommitShaMatches(
83+
gi -> gi.getCommit().getCommitter().getEmail(), Strings::isNotBlank),
84+
evaluator.getIfCommitShaMatches(
85+
gi -> gi.getCommit().getCommitter().getIso8601Date(), Strings::isNotBlank)),
6386
evaluator.getIfCommitShaMatches(
64-
gi -> gi.getCommit().getAuthor().getName(), Strings::isNotBlank),
65-
evaluator.getIfCommitShaMatches(
66-
gi -> gi.getCommit().getAuthor().getEmail(), Strings::isNotBlank),
67-
evaluator.getIfCommitShaMatches(
68-
gi -> gi.getCommit().getAuthor().getIso8601Date(), Strings::isNotBlank)),
69-
new PersonInfo(
70-
evaluator.getIfCommitShaMatches(
71-
gi -> gi.getCommit().getCommitter().getName(), Strings::isNotBlank),
72-
evaluator.getIfCommitShaMatches(
73-
gi -> gi.getCommit().getCommitter().getEmail(), Strings::isNotBlank),
74-
evaluator.getIfCommitShaMatches(
75-
gi -> gi.getCommit().getCommitter().getIso8601Date(), Strings::isNotBlank)),
76-
evaluator.getIfCommitShaMatches(
77-
gi -> gi.getCommit().getFullMessage(), Strings::isNotBlank)));
87+
gi -> gi.getCommit().getFullMessage(), Strings::isNotBlank)));
88+
89+
InstrumentationBridge.getMetricCollector()
90+
.add(
91+
CiVisibilityCountMetric.GIT_COMMIT_SHA_MATCH,
92+
1,
93+
evaluator.shaDiscrepancies.isEmpty() ? GitShaMatch.TRUE : GitShaMatch.FALSE);
94+
for (ShaDiscrepancy mismatch : evaluator.shaDiscrepancies) {
95+
mismatch.addTelemetry();
96+
}
97+
98+
return gitInfo;
7899
}
79100

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

106+
private static final class ShaDiscrepancy {
107+
private final GitProviderExpected expectedGitProvider;
108+
private final GitProviderDiscrepant discrepantGitProvider;
109+
private final GitShaDiscrepancyType discrepancyType;
110+
111+
private ShaDiscrepancy(
112+
GitProviderExpected expectedGitProvider,
113+
GitProviderDiscrepant discrepantGitProvider,
114+
GitShaDiscrepancyType discrepancyType) {
115+
this.expectedGitProvider = expectedGitProvider;
116+
this.discrepantGitProvider = discrepantGitProvider;
117+
this.discrepancyType = discrepancyType;
118+
}
119+
120+
private void addTelemetry() {
121+
InstrumentationBridge.getMetricCollector()
122+
.add(
123+
CiVisibilityCountMetric.GIT_COMMIT_SHA_DISCREPANCY,
124+
1,
125+
expectedGitProvider,
126+
discrepantGitProvider,
127+
discrepancyType);
128+
}
129+
130+
@Override
131+
public boolean equals(Object obj) {
132+
if (this == obj) return true;
133+
if (obj == null || getClass() != obj.getClass()) return false;
134+
ShaDiscrepancy that = (ShaDiscrepancy) obj;
135+
return expectedGitProvider.equals(that.expectedGitProvider)
136+
&& discrepantGitProvider.equals(that.discrepantGitProvider)
137+
&& discrepancyType.equals(that.discrepancyType);
138+
}
139+
140+
@Override
141+
public int hashCode() {
142+
return Objects.hash(expectedGitProvider, discrepantGitProvider, discrepancyType);
143+
}
144+
}
145+
85146
/**
86147
* Uses provided GitInfoBuilder instances to get GitInfo data.
87148
*
@@ -95,10 +156,12 @@ private static boolean validateGitRemoteUrl(String s) {
95156
private static final class Evaluator {
96157
private final String repositoryPath;
97158
private final Map<GitInfoBuilder, GitInfo> infos;
159+
private final Set<ShaDiscrepancy> shaDiscrepancies;
98160

99161
private Evaluator(String repositoryPath, Collection<GitInfoBuilder> builders) {
100162
this.repositoryPath = repositoryPath;
101163
this.infos = new LinkedHashMap<>();
164+
this.shaDiscrepancies = new HashSet<>();
102165
for (GitInfoBuilder builder : builders) {
103166
infos.put(builder, null);
104167
}
@@ -121,7 +184,10 @@ private String get(
121184
Function<GitInfo, String> function,
122185
Predicate<String> validator,
123186
boolean checkShaIntegrity) {
124-
String commitSha = null;
187+
String expectedCommitSha = null;
188+
String expectedRepoUrl = null;
189+
GitProviderExpected expectedGitProvider = null;
190+
125191
for (Map.Entry<GitInfoBuilder, GitInfo> e : infos.entrySet()) {
126192
GitInfo info = e.getValue();
127193
if (info == null) {
@@ -134,11 +200,22 @@ private String get(
134200
CommitInfo currentCommit = info.getCommit();
135201
String currentCommitSha = currentCommit != null ? currentCommit.getSha() : null;
136202
if (Strings.isNotBlank(currentCommitSha)) {
137-
if (commitSha == null) {
138-
commitSha = currentCommitSha;
139-
} else if (!commitSha.equals(currentCommitSha)) {
203+
if (expectedCommitSha == null) {
204+
expectedCommitSha = currentCommitSha;
205+
expectedRepoUrl = info.getRepositoryURL();
206+
expectedGitProvider = e.getKey().providerAsExpected();
207+
} else if (!expectedCommitSha.equals(currentCommitSha)) {
140208
// We already have a commit SHA from source that has higher priority.
141209
// Commit SHA from current source is different, so we have to skip it
210+
GitShaDiscrepancyType discrepancyType = GitShaDiscrepancyType.COMMIT_DISCREPANCY;
211+
String repoUrl = info.getRepositoryURL();
212+
if (expectedRepoUrl != null && repoUrl != null && !repoUrl.equals(expectedRepoUrl)) {
213+
discrepancyType = GitShaDiscrepancyType.REPOSITORY_DISCREPANCY;
214+
}
215+
216+
shaDiscrepancies.add(
217+
new ShaDiscrepancy(
218+
expectedGitProvider, e.getKey().providerAsDiscrepant(), discrepancyType));
142219
continue;
143220
}
144221
}

0 commit comments

Comments
 (0)