From cf1a4ec1751d0c1ce0044be75905df95ef698d9b Mon Sep 17 00:00:00 2001 From: Daniel Rodriguez Hernandez Date: Mon, 26 Oct 2020 09:36:10 +0100 Subject: [PATCH] Add git.default_branch to Jenkins Build/Pipeline traces --- .../plugins/datadog/model/BuildData.java | 32 +++-- .../datadog/model/GitRepositoryAction.java | 42 +++++++ .../plugins/datadog/traces/CITags.java | 1 + .../traces/DatadogTraceBuildLogic.java | 2 + .../traces/DatadogTracePipelineLogic.java | 23 ++++ .../plugins/datadog/util/git/GitUtils.java | 112 +++++++++++++++--- .../datadog/util/git/RepositoryInfo.java | 18 +++ .../util/git/RepositoryInfoCallback.java | 47 ++++++++ .../listeners/DatadogBuildListenerIT.java | 1 + .../listeners/DatadogGraphListenerTest.java | 1 + 10 files changed, 256 insertions(+), 23 deletions(-) create mode 100644 src/main/java/org/datadog/jenkins/plugins/datadog/model/GitRepositoryAction.java create mode 100644 src/main/java/org/datadog/jenkins/plugins/datadog/util/git/RepositoryInfo.java create mode 100644 src/main/java/org/datadog/jenkins/plugins/datadog/util/git/RepositoryInfoCallback.java diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/model/BuildData.java b/src/main/java/org/datadog/jenkins/plugins/datadog/model/BuildData.java index 6636c2f91..3c3a072e9 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/model/BuildData.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/model/BuildData.java @@ -77,6 +77,7 @@ public class BuildData implements Serializable { private String gitCommitterName; private String gitCommitterEmail; private String gitCommitterDate; + private String gitDefaultBranch; // Environment variable from the promoted build plugin // - See https://plugins.jenkins.io/promoted-builds @@ -213,15 +214,20 @@ private void populateEnvVariables(EnvVars envVars){ * @param envVars */ private void populateGitVariables(Run run, TaskListener listener, EnvVars envVars) { - final GitCommitAction action = GitUtils.buildGitCommitAction(run, listener, envVars, this.gitCommit, this.nodeName, this.workspace); - if(action != null) { - this.gitMessage = action.getMessage(); - this.gitAuthorName = action.getAuthorName(); - this.gitAuthorEmail = action.getAuthorEmail(); - this.gitAuthorDate = action.getAuthorDate(); - this.gitCommitterName = action.getCommitterName(); - this.gitCommitterEmail = action.getCommitterEmail(); - this.gitCommitterDate = action.getCommitterDate(); + final GitCommitAction gitCommitAction = GitUtils.buildGitCommitAction(run, listener, envVars, this.gitCommit, this.nodeName, this.workspace); + if(gitCommitAction != null) { + this.gitMessage = gitCommitAction.getMessage(); + this.gitAuthorName = gitCommitAction.getAuthorName(); + this.gitAuthorEmail = gitCommitAction.getAuthorEmail(); + this.gitAuthorDate = gitCommitAction.getAuthorDate(); + this.gitCommitterName = gitCommitAction.getCommitterName(); + this.gitCommitterEmail = gitCommitAction.getCommitterEmail(); + this.gitCommitterDate = gitCommitAction.getCommitterDate(); + } + + final GitRepositoryAction gitRepositoryAction = GitUtils.buildGitRepositoryAction(run, listener, envVars, this.nodeName, this.workspace); + if(gitRepositoryAction != null) { + this.gitDefaultBranch = gitRepositoryAction.getDefaultBranch(); } } @@ -496,6 +502,14 @@ public void setGitCommitterDate(String gitCommitterDate) { this.gitCommitterDate = gitCommitterDate; } + public String getGitDefaultBranch(String value) { + return defaultIfNull(gitDefaultBranch, value); + } + + public void setGitDefaultBranch(String gitDefaultBranch) { + this.gitDefaultBranch = gitDefaultBranch; + } + public String getPromotedUrl(String value) { return defaultIfNull(promotedUrl, value); } diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/model/GitRepositoryAction.java b/src/main/java/org/datadog/jenkins/plugins/datadog/model/GitRepositoryAction.java new file mode 100644 index 000000000..b128ab2e7 --- /dev/null +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/model/GitRepositoryAction.java @@ -0,0 +1,42 @@ +package org.datadog.jenkins.plugins.datadog.model; + +import hudson.model.InvisibleAction; + +import java.io.Serializable; + +/** + * Keeps the Git repository related information. + */ +public class GitRepositoryAction extends InvisibleAction implements Serializable { + + private static final long serialVersionUID = 1L; + + private final String defaultBranch; + + private GitRepositoryAction(final Builder builder) { + this.defaultBranch = builder.defaultBranch; + } + + public String getDefaultBranch() { + return defaultBranch; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public static class Builder { + private String defaultBranch; + + private Builder(){} + + public Builder withDefaultBranch(final String defaultBranch) { + this.defaultBranch = defaultBranch; + return this; + } + + public GitRepositoryAction build(){ + return new GitRepositoryAction(this); + } + } +} diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/traces/CITags.java b/src/main/java/org/datadog/jenkins/plugins/datadog/traces/CITags.java index 1e8195bec..58e9c1d20 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/traces/CITags.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/traces/CITags.java @@ -31,6 +31,7 @@ public class CITags { public static final String GIT_COMMIT_COMMITTER_NAME = "git.commit.committer.name"; public static final String GIT_COMMIT_COMMITTER_EMAIL = "git.commit.committer.email"; public static final String GIT_COMMIT_COMMITTER_DATE = "git.commit.committer.date"; + public static final String GIT_DEFAULT_BRANCH = "git.default_branch"; public static final String GIT_BRANCH = "git.branch"; public static final String GIT_TAG = "git.tag"; diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/traces/DatadogTraceBuildLogic.java b/src/main/java/org/datadog/jenkins/plugins/datadog/traces/DatadogTraceBuildLogic.java index 79b911498..6240601da 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/traces/DatadogTraceBuildLogic.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/traces/DatadogTraceBuildLogic.java @@ -144,6 +144,8 @@ public void finishBuildTrace(final BuildData buildData, final Run run) { final String gitCommitterDate = buildData.getGitCommitterDate("").isEmpty() ? pipelineData.getGitCommitterDate("") : buildData.getGitCommitterDate(""); buildSpan.setTag(CITags.GIT_COMMIT_COMMITTER_DATE, gitCommitterDate); + final String gitDefaultBranch = buildData.getGitDefaultBranch("").isEmpty() ? pipelineData.getGitDefaultBranch("") : buildData.getGitDefaultBranch(""); + buildSpan.setTag(CITags.GIT_DEFAULT_BRANCH, gitDefaultBranch); final String rawGitBranch = buildData.getBranch("").isEmpty() ? pipelineData.getBranch("") : buildData.getBranch(""); final String gitBranch = normalizeBranch(rawGitBranch); diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/traces/DatadogTracePipelineLogic.java b/src/main/java/org/datadog/jenkins/plugins/datadog/traces/DatadogTracePipelineLogic.java index 971fd2595..99e5671db 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/traces/DatadogTracePipelineLogic.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/traces/DatadogTracePipelineLogic.java @@ -22,6 +22,7 @@ import org.datadog.jenkins.plugins.datadog.model.BuildPipeline; import org.datadog.jenkins.plugins.datadog.model.BuildPipelineNode; import org.datadog.jenkins.plugins.datadog.model.GitCommitAction; +import org.datadog.jenkins.plugins.datadog.model.GitRepositoryAction; import org.datadog.jenkins.plugins.datadog.util.git.GitUtils; import org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode; import org.jenkinsci.plugins.workflow.graph.BlockEndNode; @@ -181,6 +182,13 @@ private void updateBuildData(BuildData buildData, Run run, FlowNode node) } } + final GitRepositoryAction repositoryAction = buildGitRepositoryAction(run, pipelineNode, node); + if(repositoryAction != null) { + if(buildData.getGitDefaultBranch("").isEmpty()) { + buildData.setGitDefaultBranch(repositoryAction.getDefaultBranch()); + } + } + final String workspace = pipelineNode.getWorkspace(); if(workspace != null && buildData.getWorkspace("").isEmpty()){ buildData.setWorkspace(workspace); @@ -197,6 +205,7 @@ private void updateBuildData(BuildData buildData, Run run, FlowNode node) } } + private void sendTrace(final Tracer tracer, final BuildData buildData, final BuildPipelineNode current, final SpanContext parentSpanContext) { if(!isTraceable(current)){ logger.severe("Node " + current.getName() + " is not traceable."); @@ -431,4 +440,18 @@ private GitCommitAction buildGitCommitAction(Run run, BuildPipelineNode pi return null; } } + + + private GitRepositoryAction buildGitRepositoryAction(Run run, BuildPipelineNode pipelineNode, FlowNode node) { + try { + final TaskListener listener = node.getExecution().getOwner().getListener(); + final EnvVars envVars = new EnvVars(pipelineNode.getEnvVars()); + final String nodeName = pipelineNode.getNodeName(); + final String workspace = pipelineNode.getWorkspace(); + return GitUtils.buildGitRepositoryAction(run, listener, envVars, nodeName, workspace); + } catch (Exception e) { + logger.fine("Unable to build GitRepositoryAction. Error: " + e); + return null; + } + } } diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/util/git/GitUtils.java b/src/main/java/org/datadog/jenkins/plugins/datadog/util/git/GitUtils.java index 41b33fc9f..d521adb67 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/util/git/GitUtils.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/util/git/GitUtils.java @@ -8,9 +8,11 @@ import org.apache.commons.lang.StringUtils; import org.datadog.jenkins.plugins.datadog.DatadogUtilities; import org.datadog.jenkins.plugins.datadog.model.GitCommitAction; +import org.datadog.jenkins.plugins.datadog.model.GitRepositoryAction; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.revwalk.RevCommit; import org.jenkinsci.plugins.gitclient.Git; +import org.jenkinsci.plugins.gitclient.GitClient; import org.jenkinsci.plugins.workflow.FilePathUtils; import java.util.logging.Logger; @@ -69,33 +71,41 @@ public static FilePath buildFilePath(final Run run){ /** * Return the RevCommit for a certain commit based on the information * stored in a certain workspace of a certain node. - * @param run - * @param listener - * @param envVars * @param gitCommit - * @param nodeName - * @param workspace * @return revCommit */ - public static RevCommit searchRevCommit(final Run run, final TaskListener listener, final EnvVars envVars, final String gitCommit, final String nodeName, final String workspace) { + public static RevCommit searchRevCommit(final GitClient gitClient, final String gitCommit) { try { - FilePath ws = GitUtils.buildFilePath(run); - if(ws == null){ - ws = GitUtils.buildFilePath(nodeName, workspace); + if(gitClient == null) { + return null; } - if(ws == null) { + return gitClient.withRepository(new RevCommitRepositoryCallback(gitCommit)); + } catch (Exception e) { + LOGGER.fine("Unable to search RevCommit. Error: " + e); + return null; + } + } + + /** + * Return the {@code RepositoryInfo} for a certain Git repository. + * @param gitClient + * @return repositoryInfo + */ + public static RepositoryInfo searchRepositoryInfo(final GitClient gitClient) { + try { + if(gitClient == null){ return null; } - final Git git = Git.with(listener, envVars).in(ws); - return git.getClient().withRepository(new RevCommitRepositoryCallback(gitCommit)); + return gitClient.withRepository(new RepositoryInfoCallback()); } catch (Exception e) { - LOGGER.fine("Unable to search RevCommit. Error: " + e); + LOGGER.fine("Unable to search Repository Info. Error: " + e); return null; } } + /** * Returns the GitCommitAction of the Run instance. * If the Run instance does not have GitCommitAction or @@ -118,7 +128,12 @@ public static GitCommitAction buildGitCommitAction(Run run, TaskListener l GitCommitAction commitAction = run.getAction(GitCommitAction.class); if(commitAction == null || !gitCommit.equals(commitAction.getCommit())) { try { - final RevCommit revCommit = GitUtils.searchRevCommit(run, listener, envVars, gitCommit, nodeName, workspace); + final GitClient gitClient = GitUtils.newGitClient(run, listener, envVars, nodeName, workspace); + if(gitClient == null){ + return null; + } + + final RevCommit revCommit = GitUtils.searchRevCommit(gitClient, gitCommit); if(revCommit == null) { return null; } @@ -157,4 +172,73 @@ public static GitCommitAction buildGitCommitAction(Run run, TaskListener l return commitAction; } + /** + * Returns the GitRepositoryAction of the Run instance. + * If the Run instance does not have GitRepositoryAction or + * some infor is not populated in the GitRepositoryAction, + * then a new GitCommitAction is built and stored in the Run instance. + * + * The GitRepository information is stored in an action because + * it's fairly expensive to calculate. To avoid calculating + * every time, it's store in the Run instance as an action. + * @param run + * @param listener + * @param envVars + * @param nodeName + * @param workspace + * @return + */ + public static GitRepositoryAction buildGitRepositoryAction(Run run, TaskListener listener, EnvVars envVars, final String nodeName, final String workspace) { + GitRepositoryAction repoAction = run.getAction(GitRepositoryAction.class); + if(repoAction == null || repoAction.getDefaultBranch() == null) { + try { + final GitClient gitClient = GitUtils.newGitClient(run, listener, envVars, nodeName, workspace); + if(gitClient == null){ + return null; + } + + final RepositoryInfo repositoryInfo = GitUtils.searchRepositoryInfo(gitClient); + if(repositoryInfo == null) { + return null; + } + + final GitRepositoryAction.Builder builder = GitRepositoryAction.newBuilder(); + builder.withDefaultBranch(repositoryInfo.getDefaultBranch()); + + repoAction = builder.build(); + run.addOrReplaceAction(repoAction); + } catch (Exception e) { + LOGGER.fine("Unable to build GitRepositoryAction. Error: " + e); + } + } + return repoAction; + } + + /** + * Creates a new instance of a {@code GitClient}. + * @param run + * @param listener + * @param envVars + * @param nodeName + * @param workspace + * @return gitClient + */ + public static GitClient newGitClient(final Run run, final TaskListener listener, final EnvVars envVars, final String nodeName, final String workspace) { + try { + FilePath ws = GitUtils.buildFilePath(run); + if(ws == null){ + ws = GitUtils.buildFilePath(nodeName, workspace); + } + + if(ws == null) { + return null; + } + + final Git git = Git.with(listener, envVars).in(ws); + return git.getClient(); + } catch (Exception e) { + LOGGER.fine("Unable to search RevCommit. Error: " + e); + return null; + } + } } diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/util/git/RepositoryInfo.java b/src/main/java/org/datadog/jenkins/plugins/datadog/util/git/RepositoryInfo.java new file mode 100644 index 000000000..fc320b6ec --- /dev/null +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/util/git/RepositoryInfo.java @@ -0,0 +1,18 @@ +package org.datadog.jenkins.plugins.datadog.util.git; + +import java.io.Serializable; + +public class RepositoryInfo implements Serializable { + + private static final long serialVersionUID = 1L; + + private final String defaultBranch; + + public RepositoryInfo(String defaultBranch) { + this.defaultBranch = defaultBranch; + } + + public String getDefaultBranch() { + return defaultBranch; + } +} diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/util/git/RepositoryInfoCallback.java b/src/main/java/org/datadog/jenkins/plugins/datadog/util/git/RepositoryInfoCallback.java new file mode 100644 index 000000000..005db8ac8 --- /dev/null +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/util/git/RepositoryInfoCallback.java @@ -0,0 +1,47 @@ +package org.datadog.jenkins.plugins.datadog.util.git; + +import hudson.remoting.VirtualChannel; +import org.datadog.jenkins.plugins.datadog.traces.GitInfoUtils; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.Repository; +import org.jenkinsci.plugins.gitclient.RepositoryCallback; + +import java.io.IOException; +import java.util.logging.Logger; + +/** + * Returns the RepositoryInfo instance for a certain repository + * using the JGit. + * + * This must be called using gitClient.withRepository(...) method. + * See GitUtils. + */ +public final class RepositoryInfoCallback implements RepositoryCallback { + + private static transient final Logger LOGGER = Logger.getLogger(RepositoryInfoCallback.class.getName()); + private static final long serialVersionUID = 1L; + + @Override + public RepositoryInfo invoke(Repository repository, VirtualChannel channel) throws IOException, InterruptedException { + try { + Ref head = repository.getRefDatabase().findRef("HEAD"); + if(head == null) { + LOGGER.fine("Unable to build RepositoryInfo. HEAD is null."); + return null; + } + + // Discarded if it's not a symbolic to refs. + if(!head.isSymbolic()) { + LOGGER.fine("Unable to build RepositoryInfo. HEAD is not symbolic."); + return null; + } + + final String defaultBranch = GitInfoUtils.normalizeBranch(head.getTarget().getName()); + return new RepositoryInfo(defaultBranch); + + } catch (Exception e) { + LOGGER.fine("Unable to build RepositoryInfo. Error: " + e); + return null; + } + } +} diff --git a/src/test/java/org/datadog/jenkins/plugins/datadog/listeners/DatadogBuildListenerIT.java b/src/test/java/org/datadog/jenkins/plugins/datadog/listeners/DatadogBuildListenerIT.java index 515c8ef32..96700dc83 100644 --- a/src/test/java/org/datadog/jenkins/plugins/datadog/listeners/DatadogBuildListenerIT.java +++ b/src/test/java/org/datadog/jenkins/plugins/datadog/listeners/DatadogBuildListenerIT.java @@ -121,5 +121,6 @@ private void assertGitVariables(DDSpan span) { assertEquals("401d997a6eede777602669ccaec059755c98161f", span.getTag(CITags.GIT_COMMIT_SHA)); assertEquals("master", span.getTag(CITags.GIT_BRANCH)); assertEquals("https://github.com/johndoe/foobar.git", span.getTag(CITags.GIT_REPOSITORY_URL)); + assertEquals("master", span.getTag(CITags.GIT_DEFAULT_BRANCH)); } } diff --git a/src/test/java/org/datadog/jenkins/plugins/datadog/listeners/DatadogGraphListenerTest.java b/src/test/java/org/datadog/jenkins/plugins/datadog/listeners/DatadogGraphListenerTest.java index 54ef9a92f..3cdfe695e 100644 --- a/src/test/java/org/datadog/jenkins/plugins/datadog/listeners/DatadogGraphListenerTest.java +++ b/src/test/java/org/datadog/jenkins/plugins/datadog/listeners/DatadogGraphListenerTest.java @@ -416,6 +416,7 @@ private void assertGitVariables(DDSpan span) { assertEquals("401d997a6eede777602669ccaec059755c98161f", span.getTag(CITags.GIT_COMMIT_SHA)); assertEquals("master", span.getTag(CITags.GIT_BRANCH)); assertEquals("https://github.com/johndoe/foobar.git", span.getTag(CITags.GIT_REPOSITORY_URL)); + assertEquals("master", span.getTag(CITags.GIT_DEFAULT_BRANCH)); } }