Skip to content

Commit

Permalink
Merge pull request #8 from aquarellian/master
Browse files Browse the repository at this point in the history
JENKINS-31892
  • Loading branch information
aquarellian committed Dec 4, 2015
2 parents 2c36e23 + ebf1544 commit cdf70f3
Show file tree
Hide file tree
Showing 8 changed files with 567 additions and 98 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Expand Up @@ -72,7 +72,7 @@
<dependency>
<groupId>com.sonyericsson.hudson.plugins.gerrit</groupId>
<artifactId>gerrit-trigger</artifactId>
<version>2.17.2</version>
<version>2.16.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
Expand Down
Expand Up @@ -17,7 +17,6 @@
import com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.GerritTrigger;
import com.urswolfer.gerrit.client.rest.GerritAuthData;
import com.urswolfer.gerrit.client.rest.GerritRestApiFactory;

import hudson.EnvVars;
import hudson.Extension;
import hudson.FilePath;
Expand All @@ -29,7 +28,6 @@
import hudson.tasks.BuildStepMonitor;
import hudson.tasks.Publisher;
import hudson.util.FormValidation;

import org.jenkinsci.plugins.sonargerrit.data.SonarReportBuilder;
import org.jenkinsci.plugins.sonargerrit.data.converter.CustomIssueFormatter;
import org.jenkinsci.plugins.sonargerrit.data.converter.CustomReportFormatter;
Expand All @@ -44,8 +42,6 @@

import javax.annotation.Nullable;
import javax.servlet.ServletException;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
Expand All @@ -62,7 +58,8 @@

public class SonarToGerritPublisher extends Publisher {

private static final String DEFAULT_PATH = "target/sonar/sonar-report.json";
private static final String DEFAULT_SONAR_REPORT_PATH = "target/sonar/sonar-report.json";
private static final String DEFAULT_PROJECT_PATH = "";
private static final String DEFAULT_SONAR_URL = "http://localhost:9000";
private static final String DEFAULT_CATEGORY = "Code-Review";
private static final int DEFAULT_SCORE = 0;
Expand All @@ -77,9 +74,12 @@ public class SonarToGerritPublisher extends Publisher {
public static final String GERRIT_NAME_ENV_VAR_NAME = "GERRIT_NAME";
public static final String GERRIT_PATCHSET_NUMBER_ENV_VAR_NAME = "GERRIT_PATCHSET_NUMBER";

// left here for compatibility with previous version. will be removed in further releases
private final String path;
private final String projectPath;

private final String sonarURL;
private final String path;
private List<SubJobConfig> subJobConfigs;
private final String severity;
private final boolean changedLinesOnly;
private final boolean newIssuesOnly;
Expand All @@ -96,14 +96,13 @@ public class SonarToGerritPublisher extends Publisher {


@DataBoundConstructor
public SonarToGerritPublisher(String projectPath, String sonarURL, String path,
String severity, boolean changedLinesOnly, boolean newIssuesOnly,
String noIssuesToPostText, String someIssuesToPostText, String issueComment,
boolean postScore, String category, String noIssuesScore, String issuesScore,
String noIssuesNotification, String issuesNotification) {
this.projectPath = MoreObjects.firstNonNull(projectPath, EMPTY_STR);
public SonarToGerritPublisher(String sonarURL, List<SubJobConfig> subJobConfigs,
String severity, boolean changedLinesOnly, boolean newIssuesOnly,
String noIssuesToPostText, String someIssuesToPostText, String issueComment,
boolean postScore, String category, String noIssuesScore, String issuesScore,
String noIssuesNotification, String issuesNotification) {
this.sonarURL = MoreObjects.firstNonNull(sonarURL, DEFAULT_SONAR_URL);
this.path = MoreObjects.firstNonNull(path, DEFAULT_PATH);
this.subJobConfigs = subJobConfigs;
this.severity = MoreObjects.firstNonNull(severity, Severity.MAJOR.name());
this.changedLinesOnly = changedLinesOnly;
this.newIssuesOnly = newIssuesOnly;
Expand All @@ -116,12 +115,13 @@ public SonarToGerritPublisher(String projectPath, String sonarURL, String path,
this.issuesScore = issuesScore;
this.noIssuesNotification = noIssuesNotification;
this.issuesNotification = issuesNotification;
}

public String getPath() {
return path;
// old values - not used anymore. will be deleted in further releases
this.path = null;
this.projectPath = null;
}


public String getSeverity() {
return severity;
}
Expand Down Expand Up @@ -150,10 +150,6 @@ public String getIssueComment() {
return issueComment;
}

public String getProjectPath() {
return projectPath;
}

@SuppressWarnings(value = "unused")
public boolean isPostScore() {
return postScore;
Expand Down Expand Up @@ -186,25 +182,14 @@ public String getIssuesScore() {
@Override
public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws IOException,
InterruptedException {
FilePath reportPath = build.getWorkspace().child(getPath());
if (!reportPath.exists()) {
logMessage(listener, "jenkins.plugin.error.sonar.report.not.exists", Level.SEVERE, reportPath);

List<ReportInfo> issueInfos = readSonarReports(listener, build.getWorkspace());
if (issueInfos == null) {
logMessage(listener, "jenkins.plugin.validation.path.no.project.config.available", Level.SEVERE);
return false;
}
logMessage(listener, "jenkins.plugin.getting.report", Level.INFO, reportPath);

SonarReportBuilder builder = new SonarReportBuilder();
String reportJson = reportPath.readToString();
Report report = builder.fromJson(reportJson);
logMessage(listener, "jenkins.plugin.report.loaded", Level.INFO, report.getIssues().size());

// Step 1 - Filter issues by issues only predicates
Iterable<Issue> filtered = filterIssuesByPredicates(report);
// LOGGER.log(Level.INFO, "{0} issues left after filtration by predicates (severity, ... etc)", Lists.newArrayList(filtered).size());

// Step 2 - Calculate real file name for issues and store to multimap
Multimap<String, Issue> file2issues = generateFilenameToIssuesMap(report, filtered);
// logResultMap(file2issues, "Map file2issues contains {0} elements");
Multimap<String, Issue> file2issues = generateFilenameToIssuesMapFilteredByPredicates(issueInfos);

// Step 3 - Prepare Gerrit REST API client
// Check Gerrit configuration is available
Expand Down Expand Up @@ -271,14 +256,62 @@ public boolean apply(@Nullable String input) {
return true;
}

@VisibleForTesting
Multimap<String, Issue> generateFilenameToIssuesMapFilteredByPredicates(List<ReportInfo> issueInfos) {
Multimap<String, Issue> file2issues = LinkedListMultimap.create();
for (ReportInfo info : issueInfos) {

Report report = info.report;

// Step 1 - Filter issues by issues only predicates
Iterable<Issue> filtered = filterIssuesByPredicates(report.getIssues());

// Step 2 - Calculate real file name for issues and store to multimap
file2issues.putAll(generateFilenameToIssuesMapFilteredByPredicates(info.directoryPath, report, filtered));
}
return file2issues;
}

private Report readSonarReport(BuildListener listener, FilePath workspace, SubJobConfig config) throws IOException,
InterruptedException {
FilePath reportPath = workspace.child(config.getSonarReportPath());
if (!reportPath.exists()) {
logMessage(listener, "jenkins.plugin.error.sonar.report.not.exists", Level.SEVERE, reportPath);
return null;
}
logMessage(listener, "jenkins.plugin.getting.report", Level.INFO, reportPath);

SonarReportBuilder builder = new SonarReportBuilder();
String reportJson = reportPath.readToString();
Report report = builder.fromJson(reportJson);
logMessage(listener, "jenkins.plugin.report.loaded", Level.INFO, report.getIssues().size());
return report;
}

@VisibleForTesting
List<ReportInfo> readSonarReports(BuildListener listener, FilePath workspace) throws IOException,
InterruptedException {
List<ReportInfo> reports = new ArrayList<ReportInfo>();
for (SubJobConfig subJobConfig : getSubJobConfigs(false)) { // to be replaced by this.subJobConfigs in further releases - this code is to support older versions
Report report = readSonarReport(listener, workspace, subJobConfig);
if (report == null) {
return null;
}
reports.add(new ReportInfo(subJobConfig.getProjectPath(), report));
}
return reports;
}

private String getEnvVar(AbstractBuild build, BuildListener listener, String name) throws IOException, InterruptedException {
EnvVars envVars = build.getEnvironment(listener);
return envVars.get(name);
}

private void logMessage(BuildListener listener, String message, Level l, Object... params) {
message = getLocalized(message, params);
listener.getLogger().println(message);
if (listener != null) { // it can be it tests
listener.getLogger().println(message);
}
LOGGER.log(l, message);
}

Expand All @@ -291,6 +324,23 @@ private int getReviewMark(int finalIssuesCount) {
return parseNumber(mark, DEFAULT_SCORE);
}

public List<SubJobConfig> getSubJobConfigs() {
return getSubJobConfigs(true);
}

public List<SubJobConfig> getSubJobConfigs(boolean addDefault) {
if (subJobConfigs == null) {
subJobConfigs = new ArrayList<SubJobConfig>();
// add configuration from previous plugin version
if (path != null || projectPath != null) {
subJobConfigs.add(new SubJobConfig(projectPath, path));
} else if (addDefault) {
subJobConfigs.add(new SubJobConfig(DEFAULT_PROJECT_PATH, DEFAULT_SONAR_REPORT_PATH));
}
}
return subJobConfigs;
}

private ReviewInput.NotifyHandling getNotificationSettings(int finalIssuesCount) {
if (finalIssuesCount > 0) {
ReviewInput.NotifyHandling value = (issuesNotification == null ? null : ReviewInput.NotifyHandling.valueOf(issuesNotification));
Expand Down Expand Up @@ -380,7 +430,7 @@ void filterIssuesByChangedLines(Multimap<String, Issue> finalIssues, RevisionApi
}

@VisibleForTesting
Multimap<String, Issue> generateFilenameToIssuesMap(Report report, Iterable<Issue> filtered) {
Multimap<String, Issue> generateFilenameToIssuesMapFilteredByPredicates(String projectPath, Report report, Iterable<Issue> filtered) {
Multimap<String, Issue> file2issues = LinkedListMultimap.create();

/* The next code prepares data to process situations like this one:
Expand Down Expand Up @@ -426,8 +476,7 @@ private String appendDelimiter(String subPath) {
}

@VisibleForTesting
Iterable<Issue> filterIssuesByPredicates(Report report) {
List<Issue> issues = report.getIssues();
Iterable<Issue> filterIssuesByPredicates(List<Issue> issues) {
Severity sev = Severity.valueOf(severity);
return Iterables.filter(issues,
Predicates.and(
Expand All @@ -444,6 +493,22 @@ public DescriptorImpl getDescriptor() {
return (DescriptorImpl) super.getDescriptor();
}

@Override
public BuildStepMonitor getRequiredMonitorService() {
return BuildStepMonitor.NONE;
}

@VisibleForTesting
class ReportInfo {
private String directoryPath;
private Report report;

public ReportInfo(String directoryPath, Report report) {
this.directoryPath = directoryPath;
this.report = report;
}
}

/**
* Descriptor for {@link SonarToGerritPublisher}. Used as a singleton.
* The class is marked as public so that it can be accessed from views.
Expand All @@ -463,27 +528,6 @@ public DescriptorImpl() {
load();
}

/**
* Performs on-the-fly validation of the form field 'path'.
*
* @param value This parameter receives the value that the user has typed.
* @return Indicates the outcome of the validation. This is sent to the browser.
* <p/>
* Note that returning {@link FormValidation#error(String)} does not
* prevent the form from being saved. It just means that a message
* will be displayed to the user.
*/
@SuppressWarnings(value = "unused")
public FormValidation doCheckPath(@QueryParameter String value)
throws IOException, ServletException {
if (value.length() == 0)
return FormValidation.warning(getLocalized("jenkins.plugin.validation.path.empty"));
File f = new File(value);
if (!f.exists())
return FormValidation.error(String.format(getLocalized("jenkins.plugin.validation.path.no.such.file"), value));
return FormValidation.ok();
}

/**
* Performs on-the-fly validation of the form field 'sonarURL'.
*
Expand Down Expand Up @@ -669,11 +713,7 @@ public boolean isApplicable(Class<? extends AbstractProject> aClass) {
public String getDisplayName() {
return getLocalized("jenkins.plugin.build.step.name");
}
}

@Override
public BuildStepMonitor getRequiredMonitorService() {
return BuildStepMonitor.NONE;
}
}

43 changes: 43 additions & 0 deletions src/main/java/org/jenkinsci/plugins/sonargerrit/SubJobConfig.java
@@ -0,0 +1,43 @@
package org.jenkinsci.plugins.sonargerrit;

import hudson.Extension;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;
import org.kohsuke.stapler.DataBoundConstructor;

/**
* Project: Sonar-Gerrit Plugin
* Author: Tatiana Didik
* Created: 02.12.2015 12:11
* <p/>
* $Id$
*/

public class SubJobConfig extends AbstractDescribableImpl<SubJobConfig> {
private String projectPath;
private String sonarReportPath;

@DataBoundConstructor
public SubJobConfig(String projectPath, String sonarReportPath) {
this.projectPath = projectPath;
this.sonarReportPath = sonarReportPath;
}

public String getProjectPath() {
return projectPath;
}

public String getSonarReportPath() {
return sonarReportPath;
}

@Extension
public static class DescriptorImpl extends Descriptor<SubJobConfig> {
public String getDisplayName() {
return "SubJobConfig";
}
}

}


2 changes: 2 additions & 0 deletions src/main/resources/messages.properties
Expand Up @@ -15,6 +15,8 @@ jenkins.plugin.error.gerrit.server.empty=Cannot obtain Gerrit server name. Pleas
jenkins.plugin.error.gerrit.config.empty=Cannot obtain Gerrit configuration. Please check your Gerrit Trigger settings
jenkins.plugin.error.gerrit.user.empty=Gerrit authentication is not configured. Please check Gerrit Trigger settings
jenkins.plugin.error.sonar.report.not.exists=SonarQube report '%s' does not exist. Please check plugin settings
jenkins.plugin.validation.path.no.project.config.available=No SonarQube report available. Please check your Project Settings
jenkins.plugin.validation.path.no.project.config=At least one Project Settings configuration required
jenkins.plugin.validation.review.severity.unknown=Unknown severity
jenkins.plugin.validation.review.category.unknown=Unknown category
jenkins.plugin.validation.review.score.not.numeric=Score should be numeric
Expand Down

0 comments on commit cdf70f3

Please sign in to comment.