Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files
[JENKINS-13056][JENKINS-31812] Added option to specify a reference job.
The reference results will now be obtained either
a) from the current job
b) from a specified reference job (given by the name)

The reference job will be selected using the properties:
- ignoreAnalysisResult: ignores the result of the previous analysis run
- overallResultMustBeSuccess: picks only builds with overall SUCCESS
  • Loading branch information
uhafner committed Mar 10, 2018
1 parent 95043f2 commit 3429aead0352f32c2b2c2114bd868e2a8a110dcf
Showing 18 changed files with 497 additions and 250 deletions.
@@ -2,13 +2,20 @@

import java.io.Serializable;
import java.util.List;
import java.util.Optional;

import org.acegisecurity.AccessDeniedException;
import org.apache.commons.lang3.StringUtils;

import jenkins.model.Jenkins;

import hudson.DescriptorExtensionList;
import hudson.ExtensionPoint;
import hudson.model.BallColor;
import hudson.model.Describable;
import hudson.model.Descriptor;
import hudson.model.Item;
import hudson.model.Job;
import hudson.security.Permission;

/**
@@ -42,12 +49,13 @@ public <T> List<T> getExtensionsFor(final Class<T> extensionType) {
* type of the describable
* @param <D>
* type of the descriptor
*
* @param descriptorType
* the base type that represents the descriptor of the describable
*
* @return the discovered instances, might be an empty list
*/
public <T extends Describable<T>, D extends Descriptor<T>> DescriptorExtensionList<T, D> getDescriptorsFor(Class<T> descriptorType) {
public <T extends Describable<T>, D extends Descriptor<T>> DescriptorExtensionList<T, D> getDescriptorsFor(
Class<T> descriptorType) {
return getJenkins().getDescriptorList(descriptorType);
}

@@ -66,4 +74,61 @@ public boolean hasPermission(final Permission permission) {
private Jenkins getJenkins() {
return Jenkins.getInstance();
}

/**
* Gets a {@link Job} by its full name. Full names are like path names, where each name of {@link Item} is combined
* by '/'.
*
* @param name
* the full name of the job
*
* @return the selected job, if it exists under the given full name and if it is accessible
*/
@SuppressWarnings("unchecked")
public Optional<Job<?, ?>> getJob(final String name) {
try {
return Optional.ofNullable(getJenkins().getItemByFullName(name, Job.class));
}
catch (AccessDeniedException ignore) {
return Optional.empty();
}
}

/**
* Returns the absolute URL for the specified ball icon.
*
* @param color
* the color
*
* @return the absolute URL
*/
public String getImagePath(final BallColor color) {
return color.getImageOf("16x16");
}

/**
* Returns an absolute URL for the specified url elements: e.g., creates the sequence ${rootUrl}/element1/element2.
*
* @param urlElements
* the url elements
*
* @return the absolute URL
*/
public String getAbsoluteUrl(final String... urlElements) {
return getAbsoluteUrl(StringUtils.join(urlElements, "/"));

}

private String getAbsoluteUrl(final String url) {
try {
String rootUrl = getJenkins().getRootUrl();
if (rootUrl != null) {
return rootUrl + "/" + url;
}
}
catch (IllegalStateException ignored) {
// ignored
}
return url;
}
}
@@ -1,12 +1,12 @@
package io.jenkins.plugins.analysis.core.graphs;

import javax.annotation.CheckForNull;
import java.awt.*;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import javax.annotation.CheckForNull;

import org.apache.commons.lang3.StringUtils;
import org.jfree.chart.JFreeChart;
@@ -15,14 +15,15 @@
import org.jfree.chart.renderer.category.StackedBarRenderer;
import org.jfree.data.category.CategoryDataset;

import edu.hm.hafner.analysis.Issue;
import edu.hm.hafner.analysis.Issues;
import io.jenkins.plugins.analysis.core.history.ResultHistory;
import io.jenkins.plugins.analysis.core.model.AnalysisResult;

import hudson.plugins.analysis.Messages;
import hudson.util.DataSetBuilder;

import edu.hm.hafner.analysis.Issue;
import edu.hm.hafner.analysis.Issues;

/**
* A build result graph that can create a graph based on authors of annotations.
*
@@ -36,7 +37,7 @@ public JFreeChart create(final GraphConfiguration configuration,
final ResultHistory history, @CheckForNull final String pluginName) {
Map<String, Integer[]> annotationCountByUser = new HashMap<>();

mergeResults(history.getBaseline(), annotationCountByUser);
mergeResults(history.getBaselineResult(), annotationCountByUser);

return createGraphFromUserMapping(configuration, pluginName, annotationCountByUser);
}
@@ -55,7 +56,7 @@ public JFreeChart createAggregation(final GraphConfiguration configuration, fina
Map<String, Integer[]> annotationCountByUser = new HashMap<>();

for (ResultHistory history : resultActions) {
mergeResults(history.getBaseline(), annotationCountByUser);
mergeResults(history.getBaselineResult(), annotationCountByUser);
}

return createGraphFromUserMapping(configuration, pluginName, annotationCountByUser);
@@ -1,9 +1,9 @@
package io.jenkins.plugins.analysis.core.history;

import javax.annotation.Nonnull;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Optional;
import javax.annotation.Nonnull;

import io.jenkins.plugins.analysis.core.model.AnalysisResult;
import io.jenkins.plugins.analysis.core.views.ResultAction;
@@ -37,8 +37,12 @@ public BuildHistory(final Run<?, ?> baseline, final ResultSelector selector) {
}

@Override
public Optional<AnalysisResult> getBaseline() {
return selector.get(baseline).map(ResultAction::getResult);
public Optional<AnalysisResult> getBaselineResult() {
return getBaselineAction().map(ResultAction::getResult);
}

protected Optional<ResultAction> getBaselineAction() {
return selector.get(baseline);
}

/**
@@ -0,0 +1,65 @@
package io.jenkins.plugins.analysis.core.history;

import java.util.Optional;

import io.jenkins.plugins.analysis.core.views.ResultAction;

import hudson.model.Result;
import hudson.model.Run;

import edu.hm.hafner.analysis.Issues;

/**
* Find the last available analysis run for the specified job. You can specify if the overall result of the run should
* be {@link Result#SUCCESS} and if the sub-result of the static analysis run should be considered as well.
*
* @author Ullrich Hafner
*/
public class OtherJobReferenceFinder extends ReferenceFinder {
private final boolean ignoreAnalysisResult;
private final boolean overallResultMustBeSuccess;

/**
* Creates a {@link ReferenceProvider} that obtains the reference results from another job.
*
* @param baseline
* the run to use as baseline when searching for a reference
* @param selector
* selects the type of the result (to get a result for the same type of static analysis)
* @param ignoreAnalysisResult
* if {@code true} then the result of the previous analysis run is ignored when searching for the reference,
* otherwise the result of the static analysis reference must be {@link Result#SUCCESS}.
* @param overallResultMustBeSuccess
* if {@code true} then only runs with an overall result of {@link Result#SUCCESS} are considered as a
* reference, otherwise every run that contains results of the same static analysis configuration is
* considered
*/
public OtherJobReferenceFinder(final Run<?, ?> baseline, final ResultSelector selector,
final boolean ignoreAnalysisResult, final boolean overallResultMustBeSuccess) {
super(baseline, selector);

this.ignoreAnalysisResult = ignoreAnalysisResult;
this.overallResultMustBeSuccess = overallResultMustBeSuccess;
}

@Override
protected Optional<ResultAction> getReferenceAction() {
Optional<ResultAction> baseline = getBaselineAction();
if (baseline.isPresent()) {
return baseline;
}
return getPreviousAction(ignoreAnalysisResult, overallResultMustBeSuccess);
}

@Override
public Issues<?> getIssues() {
return getReferenceAction()
.map(resultAction -> resultAction.getResult().getIssues())
.orElse(new Issues<>());
}

@Override
public Optional<Run<?, ?>> getAnalysisRun() {
return getReferenceAction().map(ResultAction::getOwner);
}
}
@@ -60,24 +60,18 @@ protected ReferenceFinder(final Run<?, ?> baseline, final ResultSelector selecto
/**
* Returns the action of the reference build.
*
* @return the action of the reference build, or {@code null} if no such build exists
* @return the action of the reference build
*/
protected abstract Optional<ResultAction> getReferenceAction();

@Override
public int getNumber() {
return getReferenceAction().map(pipelineResultAction -> pipelineResultAction.getOwner().getNumber())
.orElse(NO_REFERENCE_FOUND);
public Optional<Run<?, ?>> getAnalysisRun() {
return getReferenceAction()
.map(pipelineResultAction -> pipelineResultAction.getOwner());
}

/**
* Returns the issues of the reference build.
*
* @return the issues of the reference build
*/
// FIXME: generic
@Override
public Issues getIssues() {
public Issues<?> getIssues() {
return getReferenceAction()
.map(resultAction -> resultAction.getResult().getIssues())
.orElseGet(Issues::new);
@@ -1,33 +1,31 @@
package io.jenkins.plugins.analysis.core.history;

import edu.hm.hafner.analysis.Issue;
import edu.hm.hafner.analysis.Issues;
import java.util.Optional;

import hudson.model.Run;
import hudson.plugins.analysis.core.IssueDifference;

import edu.hm.hafner.analysis.Issues;

/**
* Provides the reference result for a new static analysis run. When old, new, and fixed issues are computed (see {@link
* IssueDifference}) for a new static analysis run then an instance of this reference is used as a baseline.
* Provides the reference result for a new static analysis run. When outstanding, new, and fixed issues are computed
* (see {@link IssueDifference}) for a new static analysis run then an instance of this reference is used as a
* baseline.
*
* @author Ullrich Hafner
*/
public interface ReferenceProvider {
/** Indicates that no reference has been found. */
int NO_REFERENCE_FOUND = -1;

/**
* Returns the issues of the reference build.
* Returns the issues of the reference run. If there is no reference, then an empty set of issues is returned.
*
* @return the issues of the reference build
* @return the issues of the reference run
*/
Issues<Issue> getIssues();
Issues<?> getIssues();

/**
* Returns the number of the reference run.
* Returns the actual reference run.
*
* @return the number of the {@link Run} that is used as reference, or {@link #NO_REFERENCE_FOUND}
* if no such run exists.
* @return the {@link Run} that is used as reference
*/
int getNumber();
Optional<Run<?, ?>> getAnalysisRun();
}
@@ -15,12 +15,13 @@ public interface ResultHistory extends Iterable<AnalysisResult> {
*
* @return the baseline result
*/
Optional<AnalysisResult> getBaseline();
Optional<AnalysisResult> getBaselineResult();

/**
* Returns the previous result (if there is one with plugin result SUCCESS).
*
* @return the previous result
*/
// FIXME: currently the status must be SUCCESS!
Optional<AnalysisResult> getPreviousResult();
}

0 comments on commit 3429aea

Please sign in to comment.