Repository navigation
Prefer local Git metadata over CI provider values #13279
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
base: master
Are you sure you want to change the base?
Changes from all commits
d40da19
b55af59
ee3e64d
2b4e051
f6b959a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -96,7 +96,15 @@ private void uploadGitData() { | |
|
|
||
| GitInfo gitInfo = gitInfoProvider.getGitInfo(repoRoot); | ||
| String remoteUrl = gitInfo.getRepositoryURL(); | ||
| List<String> latestCommits = gitClient.getLatestCommits(); | ||
| String selectedCommit = gitInfo.getCommit().getSha(); | ||
| List<String> latestCommits = gitClient.getLatestCommits(selectedCommit); | ||
|
juan-fernandez marked this conversation as resolved.
Resolved
|
||
| boolean unshallowed = false; | ||
| if (latestCommits.isEmpty() && config.isCiVisibilityGitUnshallowDefer()) { | ||
| unshallowed = gitRepoUnshallow.unshallow(); | ||
| if (unshallowed) { | ||
| latestCommits = gitClient.getLatestCommits(selectedCommit); | ||
|
Comment on lines
+102
to
+105
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When an explicit Useful? React with 👍 / 👎. |
||
| } | ||
| } | ||
| if (latestCommits.isEmpty()) { | ||
| LOGGER.debug("No commits in the last month, skipping git data upload"); | ||
| callback.complete(null); | ||
|
|
@@ -112,8 +120,10 @@ private void uploadGitData() { | |
| return; | ||
| } | ||
|
|
||
| if (config.isCiVisibilityGitUnshallowDefer() && gitRepoUnshallow.unshallow()) { | ||
| latestCommits = gitClient.getLatestCommits(); | ||
| if (config.isCiVisibilityGitUnshallowDefer() | ||
| && !unshallowed | ||
| && gitRepoUnshallow.unshallow()) { | ||
| latestCommits = gitClient.getLatestCommits(selectedCommit); | ||
| commitsToSkip = gitDataApi.searchCommits(remoteUrl, latestCommits); | ||
| } | ||
|
|
||
|
|
@@ -132,7 +142,7 @@ private void uploadGitData() { | |
| return; | ||
| } | ||
|
|
||
| String currentCommit = latestCommits.get(0); | ||
| String currentCommit = gitInfo.getCommit().getSha(); | ||
|
|
||
| Path packFilesDirectory = gitClient.createPackFiles(objectHashes); | ||
| try { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,6 +24,7 @@ | |
| import java.util.Collection; | ||
| import java.util.Collections; | ||
| import java.util.Comparator; | ||
| import java.util.LinkedHashSet; | ||
| import java.util.List; | ||
| import java.util.Objects; | ||
| import java.util.concurrent.TimeoutException; | ||
|
|
@@ -45,7 +46,7 @@ public class ShellGitClient implements GitClient { | |
| Arrays.asList("release/", "hotfix/"); | ||
| private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\s+"); | ||
| private static final String ORIGIN = "origin"; | ||
| private static final Pattern COMMIT_INFO_SPLIT = Pattern.compile("\",\""); | ||
| private static final Pattern COMMIT_INFO_SPLIT = Pattern.compile("\\x00"); | ||
|
|
||
| private final CiVisibilityMetricCollector metricCollector; | ||
| private final String repoRoot; | ||
|
|
@@ -323,10 +324,9 @@ public List<String> getTags(String commit) | |
| () -> { | ||
| try { | ||
| return commandExecutor.executeCommand( | ||
| IOUtils::readLines, buildGitCommand("describe", "--tags", "--exact-match", commit)); | ||
| IOUtils::readLines, buildGitCommand("tag", "--no-column", "--points-at", commit)); | ||
| } catch (ShellCommandExecutor.ShellCommandFailedException e) { | ||
| // if provided commit is not tagged, | ||
| // command will fail because "--exact-match" is specified | ||
| // An unavailable revision cannot have local tags. | ||
| return Collections.emptyList(); | ||
| } | ||
| }); | ||
|
|
@@ -438,14 +438,15 @@ public CommitInfo getCommitInfo(String commit, boolean fetchIfNotPresent) | |
| "show", | ||
| commit, | ||
| "-s", | ||
| "--format=%H\",\"%an\",\"%ae\",\"%aI\",\"%cn\",\"%ce\",\"%cI\",\"%B")) | ||
| "--no-show-signature", | ||
| "--format=%H%x00%an%x00%ae%x00%aI%x00%cn%x00%ce%x00%cI%x00%B")) | ||
| .trim(); | ||
| } catch (ShellCommandExecutor.ShellCommandFailedException e) { | ||
| LOGGER.error("Failed to fetch commit info", e); | ||
| return CommitInfo.NOOP; | ||
| } | ||
|
|
||
| String[] fields = COMMIT_INFO_SPLIT.split(info); | ||
| String[] fields = COMMIT_INFO_SPLIT.split(info, 8); | ||
| if (fields.length < 8) { | ||
| LOGGER.error("Could not parse commit info: {}", info); | ||
| return CommitInfo.NOOP; | ||
|
|
@@ -472,17 +473,42 @@ public CommitInfo getCommitInfo(String commit, boolean fetchIfNotPresent) | |
| @Override | ||
| public List<String> getLatestCommits() | ||
| throws IOException, TimeoutException, InterruptedException { | ||
| return executeCommand( | ||
| Command.GET_LOCAL_COMMITS, | ||
| () -> | ||
| commandExecutor.executeCommand( | ||
| IOUtils::readLines, | ||
| buildGitCommand( | ||
| "log", | ||
| "--format=%H", | ||
| "-n", | ||
| String.valueOf(latestCommitsLimit), | ||
| String.format("--since='%s'", latestCommitsSince)))); | ||
| return getLatestCommits(HEAD); | ||
| } | ||
|
|
||
| @Nonnull | ||
| @Override | ||
| public List<String> getLatestCommits(String commit) | ||
| throws IOException, TimeoutException, InterruptedException { | ||
| if (GitUtils.isNotValidCommit(commit)) { | ||
| return Collections.emptyList(); | ||
| } | ||
| List<String> commits; | ||
| try { | ||
| commits = | ||
| executeCommand( | ||
| Command.GET_LOCAL_COMMITS, | ||
| () -> | ||
| commandExecutor.executeCommand( | ||
| IOUtils::readLines, | ||
| buildGitCommand( | ||
| "log", | ||
| "--format=%H", | ||
| "-n", | ||
| String.valueOf(latestCommitsLimit), | ||
| String.format("--since='%s'", latestCommitsSince), | ||
| commit, | ||
| "--"))); | ||
| } catch (ShellCommandExecutor.ShellCommandFailedException e) { | ||
| if (!isCommitPresent(commit)) { | ||
| return Collections.emptyList(); | ||
| } | ||
| throw e; | ||
| } | ||
| if (!HEAD.equals(commit) && !commits.contains(commit) && isCommitPresent(commit)) { | ||
| commits.add(0, commit); | ||
| } | ||
| return commits; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -505,22 +531,45 @@ public List<String> getObjects( | |
| return executeCommand( | ||
| Command.GET_OBJECTS, | ||
| () -> { | ||
| String[] gitArgs = new String[5 + commitsToSkip.size() + commitsToInclude.size()]; | ||
| String[] gitArgs = new String[6 + commitsToSkip.size() + commitsToInclude.size()]; | ||
| gitArgs[0] = "rev-list"; | ||
| gitArgs[1] = "--objects"; | ||
| gitArgs[2] = "--no-object-names"; | ||
| gitArgs[3] = "--filter=blob:none"; | ||
| gitArgs[4] = String.format("--since='%s'", latestCommitsSince); | ||
| gitArgs[5] = "--max-count=" + latestCommitsLimit; | ||
|
|
||
| int count = 5; | ||
| int count = 6; | ||
| for (String commitToSkip : commitsToSkip) { | ||
| gitArgs[count++] = "^" + commitToSkip; | ||
| } | ||
| for (String commitToInclude : commitsToInclude) { | ||
| gitArgs[count++] = commitToInclude; | ||
| } | ||
|
|
||
| return commandExecutor.executeCommand(IOUtils::readLines, buildGitCommand(gitArgs)); | ||
| List<String> objects = | ||
| commandExecutor.executeCommand(IOUtils::readLines, buildGitCommand(gitArgs)); | ||
| LinkedHashSet<String> outsideWindow = new LinkedHashSet<>(commitsToInclude); | ||
| for (String object : objects) { | ||
| outsideWindow.remove(object); | ||
| } | ||
|
Comment on lines
+552
to
+555
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the backend knows a newer commit but not every ancestor returned in Useful? React with 👍 / 👎. |
||
| if (!outsideWindow.isEmpty()) { | ||
| List<String> selectedArgs = new ArrayList<>(); | ||
| Collections.addAll( | ||
| selectedArgs, | ||
| "rev-list", | ||
| "--objects", | ||
| "--no-object-names", | ||
| "--filter=blob:none", | ||
| "--no-walk=unsorted"); | ||
| selectedArgs.addAll(outsideWindow); | ||
| LinkedHashSet<String> combined = new LinkedHashSet<>(objects); | ||
| combined.addAll( | ||
| commandExecutor.executeCommand( | ||
| IOUtils::readLines, buildGitCommand(selectedArgs.toArray(new String[0])))); | ||
| return new ArrayList<>(combined); | ||
| } | ||
| return objects; | ||
| }); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| package datadog.trace.civisibility.git; | ||
|
|
||
| import static java.util.Arrays.asList; | ||
| import static java.util.Collections.singletonList; | ||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertFalse; | ||
| import static org.junit.jupiter.api.Assertions.assertNull; | ||
| import static org.junit.jupiter.api.Assertions.assertTrue; | ||
| import static org.mockito.Mockito.mock; | ||
| import static org.mockito.Mockito.never; | ||
| import static org.mockito.Mockito.verify; | ||
| import static org.mockito.Mockito.when; | ||
|
|
||
| import datadog.trace.api.Config; | ||
| import datadog.trace.api.git.CommitInfo; | ||
| import datadog.trace.api.git.GitInfo; | ||
| import datadog.trace.civisibility.git.tree.GitClient; | ||
| import java.util.concurrent.TimeoutException; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| class GitClientGitInfoBuilderUnitTest { | ||
| @Test | ||
| void interruptedRemoteLookupAbortsAndPreservesInterruption() throws Exception { | ||
| GitClient client = mock(GitClient.class); | ||
| when(client.getRemoteUrl("origin")).thenThrow(new InterruptedException()); | ||
| try { | ||
| assertEquals(GitInfo.NOOP, builder(client).build("/repo")); | ||
| assertTrue(Thread.currentThread().isInterrupted()); | ||
| verify(client, never()).getCurrentBranch(); | ||
| } finally { | ||
| Thread.interrupted(); | ||
| } | ||
| assertFalse(Thread.currentThread().isInterrupted()); | ||
| } | ||
|
|
||
| @Test | ||
| void timedOutRemoteLookupAborts() throws Exception { | ||
| GitClient client = mock(GitClient.class); | ||
| when(client.getRemoteUrl("origin")).thenThrow(new TimeoutException()); | ||
| assertEquals(GitInfo.NOOP, builder(client).build("/repo")); | ||
| verify(client, never()).getCurrentBranch(); | ||
| } | ||
|
|
||
| @Test | ||
| void tagsAreOnlyInferredForAnUnambiguousDetachedCheckout() throws Exception { | ||
| GitClient client = mock(GitClient.class); | ||
| when(client.getCommitInfo(GitClient.HEAD, false)).thenReturn(new CommitInfo("local-sha")); | ||
| when(client.getCurrentBranch()).thenReturn("main", "", ""); | ||
| when(client.getTags(GitClient.HEAD)).thenReturn(singletonList("v1"), asList("v1", "stable")); | ||
| GitClientGitInfoBuilder builder = builder(client); | ||
| assertNull(builder.build("/repo").getTag()); | ||
| assertEquals("v1", builder.build("/repo").getTag()); | ||
| assertNull(builder.build("/repo").getTag()); | ||
| } | ||
|
|
||
| private GitClientGitInfoBuilder builder(GitClient client) { | ||
| Config config = mock(Config.class); | ||
| when(config.getCiVisibilityGitRemoteName()).thenReturn("origin"); | ||
| GitClient.Factory factory = mock(GitClient.Factory.class); | ||
| when(factory.create("/repo")).thenReturn(client); | ||
| return new GitClientGitInfoBuilder(config, factory); | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.