From 6e271e3eac31f2b4f8528cd0b15369870cdec023 Mon Sep 17 00:00:00 2001 From: "georg.henzler" Date: Thu, 1 Dec 2016 02:45:47 +0100 Subject: [PATCH] #91 Allow to break the build if there are issues that need attention #92 Allow to specify git branch instead of pull request id Also includes existing PRs for #22 and #19 --- .../stash/StashIssueReportingPostJob.java | 69 +++++++++++++++++-- .../org/sonar/plugins/stash/StashPlugin.java | 33 ++++++++- .../stash/StashPluginConfiguration.java | 13 ++++ .../plugins/stash/StashRequestFacade.java | 11 +++ .../plugins/stash/client/StashClient.java | 28 +++++++- ...GitBranchNotFoundOrNotUniqueException.java | 9 +++ .../exceptions/StashFailBuildException.java | 10 +++ .../stash/StashIssueReportingPostJobTest.java | 1 + 8 files changed, 166 insertions(+), 8 deletions(-) create mode 100644 src/main/java/org/sonar/plugins/stash/exceptions/GitBranchNotFoundOrNotUniqueException.java create mode 100644 src/main/java/org/sonar/plugins/stash/exceptions/StashFailBuildException.java diff --git a/src/main/java/org/sonar/plugins/stash/StashIssueReportingPostJob.java b/src/main/java/org/sonar/plugins/stash/StashIssueReportingPostJob.java index 60af48b2..cee7d73e 100755 --- a/src/main/java/org/sonar/plugins/stash/StashIssueReportingPostJob.java +++ b/src/main/java/org/sonar/plugins/stash/StashIssueReportingPostJob.java @@ -1,14 +1,21 @@ package org.sonar.plugins.stash; +import java.util.List; + +import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.PostJob; import org.sonar.api.batch.SensorContext; import org.sonar.api.issue.ProjectIssues; import org.sonar.api.resources.Project; +import org.sonar.api.rule.Severity; import org.sonar.plugins.stash.client.StashClient; import org.sonar.plugins.stash.client.StashCredentials; +import org.sonar.plugins.stash.exceptions.GitBranchNotFoundOrNotUniqueException; import org.sonar.plugins.stash.exceptions.StashConfigurationException; +import org.sonar.plugins.stash.exceptions.StashFailBuildException; +import org.sonar.plugins.stash.issue.SonarQubeIssue; import org.sonar.plugins.stash.issue.SonarQubeIssuesReport; import org.sonar.plugins.stash.issue.StashDiffReport; import org.sonar.plugins.stash.issue.StashUser; @@ -32,12 +39,14 @@ public StashIssueReportingPostJob(StashPluginConfiguration stashPluginConfigurat @Override public void executeOn(Project project, SensorContext context) { + if (!config.hasToNotifyStash()) { return; } - + + SonarQubeIssuesReport issueReport = stashRequestFacade.extractIssueReport(projectIssues, inputFileCache); + try { - SonarQubeIssuesReport issueReport = stashRequestFacade.extractIssueReport(projectIssues, inputFileCache); int issueThreshold = stashRequestFacade.getIssueThreshold(); String sonarQubeURL = config.getSonarQubeURL(); @@ -90,8 +99,11 @@ public void executeOn(Project project, SensorContext context) { issueReport, diffReport, stashClient); } - stashRequestFacade.postAnalysisOverview(stashProject, repository, stashPullRequestId, sonarQubeURL, - issueThreshold, issueReport, stashClient); + boolean includeAnalysisOverview = config.includeAnalysisOverview(); + if (includeAnalysisOverview) { + stashRequestFacade.postAnalysisOverview(stashProject, repository, stashPullRequestId, sonarQubeURL, + issueThreshold, issueReport, stashClient); + } // if no new issues, plugin approves the pull-request if (canApprovePullrequest && issueReport.countIssues() == 0) { @@ -102,7 +114,12 @@ public void executeOn(Project project, SensorContext context) { stashRequestFacade.resetPullRequestApproval(stashProject, repository, stashPullRequestId, stashCredentials.getLogin(), stashClient); } + } + + + } catch (GitBranchNotFoundOrNotUniqueException e) { + LOGGER.info("No unique PR: "+ e.getMessage()); } catch (StashConfigurationException e) { LOGGER.error("Unable to push SonarQube report to Stash: {}", e.getMessage()); LOGGER.debug("Exception stack trace", e); @@ -110,7 +127,51 @@ public void executeOn(Project project, SensorContext context) { } catch (StashMissingElementException e) { LOGGER.error("Process stopped: {}", e.getMessage()); LOGGER.debug("Exception stack trace", e); + } catch (Exception e) { + LOGGER.error("Something unexpected went wrong: {}", e.getMessage(), e); + } + + failBuildIfNecessary(project, issueReport); + + } + + private void failBuildIfNecessary(Project project, SonarQubeIssuesReport issueReport) { + // also, if there are issues plugin can fail the build + String failForIssuesWithSeverity = config.failForIssuesWithSeverity(); + LOGGER.debug("issueReport={}",issueReport); + + if (issueReport!=null + && issueReport.countIssues() > 0 + && StringUtils.isNotBlank(failForIssuesWithSeverity) + && !StringUtils.equals(failForIssuesWithSeverity, StashPlugin.SEVERITY_NONE)) { + List issues = issueReport.getIssues(); + + int failForIssueSeverityAsInt = Severity.ALL.indexOf(failForIssuesWithSeverity.trim().toUpperCase()); + + if (failForIssueSeverityAsInt > -1) { + int issueCountToFailFor = 0; + + for (SonarQubeIssue issue : issues) { + int issueSeverityAsInt = Severity.ALL.indexOf(String.valueOf(issue.getSeverity()).trim().toUpperCase()); + if (issueSeverityAsInt >= failForIssueSeverityAsInt) { + LOGGER.debug("Breaking build because of issue {} that has a severity equal or higher than '{}'", + issue, failForIssuesWithSeverity); + issueCountToFailFor++; + } + } + + if (issueCountToFailFor > 0) { + String msg = "Project " + project.getName() + " has " + issueCountToFailFor + + " issues that are of severity equal or higher than " + failForIssuesWithSeverity; + LOGGER.error(msg); + throw new StashFailBuildException(msg); + } + } else { + LOGGER.warn("Invalid configuration for {}: '{}' is not a valid Sonar severity, skipping fail-build-check.", + StashPlugin.STASH_FAIL_FOR_ISSUES_WITH_SEVERITY, failForIssuesWithSeverity); } + + } } /* diff --git a/src/main/java/org/sonar/plugins/stash/StashPlugin.java b/src/main/java/org/sonar/plugins/stash/StashPlugin.java index c95f572d..bc5c8b5f 100755 --- a/src/main/java/org/sonar/plugins/stash/StashPlugin.java +++ b/src/main/java/org/sonar/plugins/stash/StashPlugin.java @@ -23,6 +23,8 @@ public class StashPlugin extends SonarPlugin { private static final String DEFAULT_STASH_TIMEOUT_VALUE = "10000"; private static final String DEFAULT_STASH_THRESHOLD_VALUE = "100"; + private static final boolean DEFAULT_STASH_ANALYSIS_OVERVIEW = false; + private static final String CONFIG_PAGE_SUB_CATEGORY_STASH = "Stash"; public static final String SEVERITY_NONE = "NONE"; @@ -38,11 +40,14 @@ public class StashPlugin extends SonarPlugin { public static final String STASH_PROJECT = "sonar.stash.project"; public static final String STASH_REPOSITORY = "sonar.stash.repository"; public static final String STASH_PULL_REQUEST_ID = "sonar.stash.pullrequest.id"; + public static final String STASH_PULL_REQUEST_BRANCH = "sonar.stash.pullrequest.branch"; + public static final String STASH_INCLUDE_ANALYSIS_OVERVIEW = "sonar.stash.include.analysis.overview"; public static final String STASH_RESET_COMMENTS = "sonar.stash.comments.reset"; public static final String STASH_URL = "sonar.stash.url"; public static final String STASH_LOGIN = "sonar.stash.login"; public static final String STASH_PASSWORD = "sonar.stash.password"; public static final String STASH_REVIEWER_APPROVAL = "sonar.stash.reviewer.approval"; + public static final String STASH_FAIL_FOR_ISSUES_WITH_SEVERITY = "sonar.stash.failForIssuesWithSeverity"; public static final String STASH_ISSUE_THRESHOLD = "sonar.stash.issue.threshold"; public static final String STASH_TIMEOUT = "sonar.stash.timeout"; public static final String SONARQUBE_URL = "sonar.host.url"; @@ -63,11 +68,17 @@ public List getExtensions() { .subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH) .onQualifiers(Qualifiers.PROJECT).build(), PropertyDefinition.builder(STASH_LOGIN) - .name("Stash base User") + .name("Stash base user") .description("User to push data on Stash instance") .subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH) .onQualifiers(Qualifiers.PROJECT).build(), - PropertyDefinition.builder(STASH_TIMEOUT) + PropertyDefinition.builder(STASH_PASSWORD) + .name("Password for base user") + .description("PW for stash user") + .type(PropertyType.PASSWORD) + .subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH) + .onQualifiers(Qualifiers.PROJECT).build(), + PropertyDefinition.builder(STASH_TIMEOUT) .name("Stash issue Timeout") .description("Timeout when pushing a new issue to Stash (in ms)") .subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH) @@ -80,6 +91,14 @@ public List getExtensions() { .onQualifiers(Qualifiers.PROJECT) .type(PropertyType.BOOLEAN) .defaultValue("false").build(), + PropertyDefinition.builder(STASH_FAIL_FOR_ISSUES_WITH_SEVERITY) + .name("Stash Fail Build") + .description("Fails build if issues with equal or higher severity exist") + .subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH) + .onQualifiers(Qualifiers.PROJECT) + .type(PropertyType.SINGLE_SELECT_LIST) + .defaultValue(SEVERITY_NONE) + .options(ListUtils.sum(Arrays.asList(SEVERITY_NONE), SEVERITY_LIST)).build(), PropertyDefinition.builder(STASH_ISSUE_THRESHOLD) .name("Stash issue Threshold") .description("Threshold to limit the number of issues pushed to Stash server") @@ -93,7 +112,15 @@ public List getExtensions() { .subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH) .onQualifiers(Qualifiers.PROJECT) .defaultValue(SEVERITY_NONE) - .options(ListUtils.sum(Arrays.asList(SEVERITY_NONE), SEVERITY_LIST)).build()); + .options(ListUtils.sum(Arrays.asList(SEVERITY_NONE), SEVERITY_LIST)).build(), + PropertyDefinition.builder(STASH_INCLUDE_ANALYSIS_OVERVIEW) + .name("Include Analysis Overview Comment") + .description("Set to false to prevent creation of the analysis overview comment.") + .type(PropertyType.BOOLEAN) + .subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH) + .onQualifiers(Qualifiers.PROJECT) + .defaultValue(Boolean.toString(DEFAULT_STASH_ANALYSIS_OVERVIEW)).build() + ); } } diff --git a/src/main/java/org/sonar/plugins/stash/StashPluginConfiguration.java b/src/main/java/org/sonar/plugins/stash/StashPluginConfiguration.java index 3ffeca3e..cbd62e94 100755 --- a/src/main/java/org/sonar/plugins/stash/StashPluginConfiguration.java +++ b/src/main/java/org/sonar/plugins/stash/StashPluginConfiguration.java @@ -30,6 +30,10 @@ public String getPullRequestId() { return settings.getString(StashPlugin.STASH_PULL_REQUEST_ID); } + public String getPullRequestBranch() { + return settings.getString(StashPlugin.STASH_PULL_REQUEST_BRANCH); + } + public String getStashURL() { return settings.getString(StashPlugin.STASH_URL); } @@ -69,4 +73,13 @@ public String getTaskIssueSeverityThreshold() { public String getSonarQubeVersion() { return settings.getString(CoreProperties.SERVER_VERSION); } + + public String failForIssuesWithSeverity() { + return settings.getString(StashPlugin.STASH_FAIL_FOR_ISSUES_WITH_SEVERITY); + } + + public boolean includeAnalysisOverview() { + return settings.getBoolean(StashPlugin.STASH_INCLUDE_ANALYSIS_OVERVIEW); + } + } \ No newline at end of file diff --git a/src/main/java/org/sonar/plugins/stash/StashRequestFacade.java b/src/main/java/org/sonar/plugins/stash/StashRequestFacade.java index aba22707..a398c47d 100755 --- a/src/main/java/org/sonar/plugins/stash/StashRequestFacade.java +++ b/src/main/java/org/sonar/plugins/stash/StashRequestFacade.java @@ -261,6 +261,17 @@ public String getStashRepository() throws StashConfigurationException { */ public String getStashPullRequestId() throws StashConfigurationException { String result = config.getPullRequestId(); + + if(StringUtils.isNotBlank(config.getPullRequestBranch())) { + try (StashClient stashClient = new StashClient(getStashURL(), getCredentials(), config.getStashTimeout(), config.getSonarQubeVersion())) { + StashPullRequest pullRequest = stashClient.getPullRequestByBranchName(config.getStashProject(), config.getStashRepository(), config.getPullRequestBranch()); + LOGGER.debug("Found PR for branch {}: {}", config.getPullRequestBranch(), pullRequest); + return pullRequest.getId(); + } catch (StashClientException e) { + throw new StashConfigurationException(MessageFormat.format(EXCEPTION_STASH_CONF, StashPlugin.STASH_PULL_REQUEST_ID)); + } + } + if (result == null){ throw new StashConfigurationException(MessageFormat.format(EXCEPTION_STASH_CONF, StashPlugin.STASH_PULL_REQUEST_ID)); } diff --git a/src/main/java/org/sonar/plugins/stash/client/StashClient.java b/src/main/java/org/sonar/plugins/stash/client/StashClient.java index 53b4a3db..17acf387 100755 --- a/src/main/java/org/sonar/plugins/stash/client/StashClient.java +++ b/src/main/java/org/sonar/plugins/stash/client/StashClient.java @@ -18,6 +18,7 @@ import org.sonar.plugins.stash.StashPlugin; import org.sonar.plugins.stash.exceptions.StashClientException; import org.sonar.plugins.stash.exceptions.StashReportExtractionException; +import org.sonar.plugins.stash.exceptions.GitBranchNotFoundOrNotUniqueException; import org.sonar.plugins.stash.issue.StashComment; import org.sonar.plugins.stash.issue.StashCommentReport; import org.sonar.plugins.stash.issue.StashDiffReport; @@ -26,6 +27,9 @@ import org.sonar.plugins.stash.issue.StashUser; import org.sonar.plugins.stash.issue.collector.StashCollector; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; import java.net.HttpURLConnection; import java.text.MessageFormat; @@ -37,8 +41,10 @@ import static java.net.HttpURLConnection.HTTP_CREATED; -public class StashClient implements AutoCloseable { +public class StashClient implements AutoCloseable { + private static final Logger LOGGER = LoggerFactory.getLogger(StashClient.class); + private final String baseUrl; private final StashCredentials credentials; private final int stashTimeout; @@ -61,6 +67,7 @@ public class StashClient implements AutoCloseable { private static final String PULL_REQUEST_APPROVAL_POST_ERROR_MESSAGE = "Unable to change status of pull-request {0} #{1}."; private static final String PULL_REQUEST_GET_ERROR_MESSAGE = "Unable to retrieve pull-request {0} #{1}."; + private static final String PULL_REQUEST_GET_FOR_BRANCH_ERROR_MESSAGE = "Unable to retrieve pull-request in repo {0} for branch {1}."; private static final String PULL_REQUEST_PUT_ERROR_MESSAGE = "Unable to update pull-request {0} #{1}."; private static final String USER_GET_ERROR_MESSAGE = "Unable to retrieve user {0}."; private static final String COMMENT_POST_ERROR_MESSAGE = "Unable to post a comment to {0} #{1}."; @@ -178,6 +185,25 @@ public StashPullRequest getPullRequest(String project, String repository, String return StashCollector.extractPullRequest(project, repository, pullRequestId, response); } + public StashPullRequest getPullRequestByBranchName(String project, String repository, String branchName) + throws StashClientException { + String request = MessageFormat.format(REPO_API + "pull-requests?at=refs/heads/{3}&direction=OUTGOING", baseUrl, project, repository, branchName); + + JSONObject response = get(request, MessageFormat.format(PULL_REQUEST_GET_FOR_BRANCH_ERROR_MESSAGE, repository, branchName)); + + Long size = (Long) response.get("size"); + if(size==0) { + throw new GitBranchNotFoundOrNotUniqueException("Found no PR for branch "+branchName + " in repo "+repository); + } else if(size==1) { + // the expected case + JSONObject prJsonObject = (JSONObject) ((JSONArray) response.get("values")).get(0); + Long prId = (Long) prJsonObject.get("id"); + return StashCollector.extractPullRequest(project, repository, String.valueOf(prId), prJsonObject); + } else { + throw new GitBranchNotFoundOrNotUniqueException("Found "+size+" PRs for branch "+branchName + " in repo "+repository); + } + } + public void addPullRequestReviewer(String project, String repository, String pullRequestId, long pullRequestVersion, ArrayList reviewers) throws StashClientException { String request = MessageFormat.format(API_ONE_PR, baseUrl, project, repository, pullRequestId); diff --git a/src/main/java/org/sonar/plugins/stash/exceptions/GitBranchNotFoundOrNotUniqueException.java b/src/main/java/org/sonar/plugins/stash/exceptions/GitBranchNotFoundOrNotUniqueException.java new file mode 100644 index 00000000..111b5a66 --- /dev/null +++ b/src/main/java/org/sonar/plugins/stash/exceptions/GitBranchNotFoundOrNotUniqueException.java @@ -0,0 +1,9 @@ +package org.sonar.plugins.stash.exceptions; + +public class GitBranchNotFoundOrNotUniqueException extends RuntimeException { + + public GitBranchNotFoundOrNotUniqueException(String message) { + super(message); + } + +} diff --git a/src/main/java/org/sonar/plugins/stash/exceptions/StashFailBuildException.java b/src/main/java/org/sonar/plugins/stash/exceptions/StashFailBuildException.java new file mode 100644 index 00000000..92f777d5 --- /dev/null +++ b/src/main/java/org/sonar/plugins/stash/exceptions/StashFailBuildException.java @@ -0,0 +1,10 @@ +package org.sonar.plugins.stash.exceptions; + +public class StashFailBuildException extends RuntimeException { + + public StashFailBuildException(String message) { + super(message); + } + + +} diff --git a/src/test/java/org/sonar/plugins/stash/StashIssueReportingPostJobTest.java b/src/test/java/org/sonar/plugins/stash/StashIssueReportingPostJobTest.java index 83bc1b42..a05bd754 100755 --- a/src/test/java/org/sonar/plugins/stash/StashIssueReportingPostJobTest.java +++ b/src/test/java/org/sonar/plugins/stash/StashIssueReportingPostJobTest.java @@ -77,6 +77,7 @@ public void setUp() throws Exception { config = mock(StashPluginConfiguration.class); when(config.hasToNotifyStash()).thenReturn(true); when(config.canApprovePullRequest()).thenReturn(false); + when(config.includeAnalysisOverview()).thenReturn(true); when(config.getStashURL()).thenReturn(STASH_URL); when(config.getSonarQubeURL()).thenReturn(SONARQUBE_URL); when(config.getStashTimeout()).thenReturn(STASH_TIMEOUT);