aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xscripts/generate_batch_reports.sh53
-rwxr-xr-xscripts/replay_batch.sh15
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java56
3 files changed, 116 insertions, 8 deletions
diff --git a/scripts/generate_batch_reports.sh b/scripts/generate_batch_reports.sh
new file mode 100755
index 00000000000..227d734b923
--- /dev/null
+++ b/scripts/generate_batch_reports.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+##############################
+#
+# Generates batch reports dumps of a specific project into a given dump repository
+# from the Git history of that project.
+#
+# This script runs the analysis of the project in the current directory of the N
+# last commits of the current project. N can be configured.
+#
+# Batch report dumps can be processed with the replay_batch.sh script
+#
+#
+##############################
+
+
+set -euo pipefail
+
+DUMP_DIR="/tmp/batch_dumps"
+HISTORY_LENGTH=30
+SONAR_HOST="http://localhost:9000"
+SONAR_USER="admin"
+SONAR_PASSWORD="admin"
+SONAR_JDBC_URL="jdbc:postgresql://localhost:5432/sonar"
+SONAR_JDBC_USERNAME="sonar"
+SONAR_JDBC_PASSWORD="sonar"
+
+function clean() {
+ git co $branch_name
+ git branch -D working_branch
+}
+
+trap "clean" EXIT
+
+# retrieve ${HISTORY_LENGTH} commits for the current directory
+git log -${HISTORY_LENGTH} --pretty=%h -- . | tac > /tmp/sha1s.txt
+
+branch_name=$(git symbolic-ref -q HEAD)
+branch_name=${branch_name##refs/heads/}
+branch_name=${branch_name:-HEAD}
+
+git co -b working_branch
+while read sha1; do
+ echo $sha1
+ git reset --hard $sha1
+ date=`git show -s --format=%ci | sed 's/ /T/' | sed 's/ //'`
+
+ echo ""
+ echo "======================== analyzing at $date ($sha1) ======================================="
+ $M2_HOME/bin/mvn sonar:sonar -Dsonar.host.url=$SONAR_HOST -Dsonar.login=$SONAR_USER -Dsonar.password=$SONAR_PASSWORD -Dsonar.analysis.mode=analysis -Dsonar.issuesReport.html.enable= -Dsonar.issuesReport.console.enable= -Dsonar.jdbc.url=$SONAR_JDBC_URL -Dsonar.jdbc.username=$SONAR_JDBC_USERNAME -Dsonar.jdbc.password=$SONAR_JDBC_PASSWORD -Dsonar.batch.dumpReportDir=$DUMP_DIR -Dsonar.projectDate=$date
+
+done < /tmp/sha1s.txt
+
diff --git a/scripts/replay_batch.sh b/scripts/replay_batch.sh
new file mode 100755
index 00000000000..c1bd9825016
--- /dev/null
+++ b/scripts/replay_batch.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+set -euo pipefail
+
+DUMP_DIR="/tmp/batch_dumps"
+SQ_ROOT_URL="http://localhost:9000"
+
+cd $DUMP_DIR
+for file in *.zip; do
+ base=${file%.zip}
+ url=$(cat ${base}.txt)
+ echo "base=$base, url=$url"
+
+ curl -u admin:admin -F report=@$DUMP_DIR/${base}.zip ${SQ_ROOT_URL}${url}
+done
diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java
index 5660e8fc23f..0f60fe4f876 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java
@@ -22,9 +22,12 @@ package org.sonar.batch.report;
import com.github.kevinsawicki.http.HttpRequest;
import com.google.common.annotations.VisibleForTesting;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
+import java.net.URISyntaxException;
import java.net.URL;
+import java.util.Date;
import org.apache.commons.io.FileUtils;
import org.picocontainer.Startable;
import org.slf4j.Logger;
@@ -41,11 +44,14 @@ import org.sonar.batch.bootstrap.ServerClient;
import org.sonar.batch.protocol.output.BatchReportWriter;
import org.sonar.batch.scan.ImmutableProjectReactor;
+import static java.lang.String.format;
+
@BatchSide
public class ReportPublisher implements Startable {
private static final Logger LOG = LoggerFactory.getLogger(ReportPublisher.class);
public static final String KEEP_REPORT_PROP_KEY = "sonar.batch.keepReport";
+ public static final String DUMP_REPORT_PROP_KEY = "sonar.batch.dumpReportDir";
private final ServerClient serverClient;
private final Server server;
@@ -98,7 +104,7 @@ public class ReportPublisher implements Startable {
if (!analysisMode.isPreview()) {
File report = prepareReport();
if (!analysisMode.isMediumTest()) {
- uploadMultiPartReport(report);
+ sendOrDumpReport(report);
}
}
logSuccess(LoggerFactory.getLogger(getClass()));
@@ -125,38 +131,72 @@ public class ReportPublisher implements Startable {
}
@VisibleForTesting
- void uploadMultiPartReport(File report) {
+ void sendOrDumpReport(File report) {
+ ProjectDefinition projectDefinition = projectReactor.getRoot();
+ String effectiveKey = projectDefinition.getKeyWithBranch();
+ String relativeUrl = "/api/computation/submit_report?projectKey=" + effectiveKey + "&projectName=" + ServerClient.encodeForUrl(projectDefinition.getName());
+
+ String dumpDirLocation = settings.getString(DUMP_REPORT_PROP_KEY);
+ if (dumpDirLocation == null) {
+ uploadMultiPartReport(report, relativeUrl);
+ } else {
+ dumpReport(dumpDirLocation, effectiveKey, relativeUrl, report);
+ }
+ }
+
+ private void uploadMultiPartReport(File report, String relativeUrl) {
LOG.debug("Publish results");
long startTime = System.currentTimeMillis();
URL url;
try {
- ProjectDefinition projectDefinition = projectReactor.getRoot();
- String effectiveKey = projectDefinition.getKeyWithBranch();
- url = new URL(serverClient.getURL() + "/api/computation/submit_report?projectKey=" + effectiveKey + "&projectName=" + ServerClient.encodeForUrl(projectDefinition.getName()));
+ url = new URL(serverClient.getURL() + relativeUrl);
} catch (MalformedURLException e) {
throw new IllegalArgumentException("Invalid URL", e);
}
HttpRequest request = HttpRequest.post(url);
request.trustAllCerts();
request.trustAllHosts();
- request.header("User-Agent", String.format("SonarQube %s", server.getVersion()));
+ request.header("User-Agent", format("SonarQube %s", server.getVersion()));
request.basic(serverClient.getLogin(), serverClient.getPassword());
request.part("report", null, "application/octet-stream", report);
if (!request.ok()) {
int responseCode = request.code();
if (responseCode == 401) {
- throw new IllegalStateException(String.format(serverClient.getMessageWhenNotAuthorized(), CoreProperties.LOGIN, CoreProperties.PASSWORD));
+ throw new IllegalStateException(format(serverClient.getMessageWhenNotAuthorized(), CoreProperties.LOGIN, CoreProperties.PASSWORD));
}
if (responseCode == 403) {
// SONAR-4397 Details are in response content
throw new IllegalStateException(request.body());
}
- throw new IllegalStateException(String.format("Fail to execute request [code=%s, url=%s]: %s", responseCode, url, request.body()));
+ throw new IllegalStateException(format("Fail to execute request [code=%s, url=%s]: %s", responseCode, url, request.body()));
}
long stopTime = System.currentTimeMillis();
LOG.info("Analysis reports sent to server in " + (stopTime - startTime) + "ms");
}
+ private void dumpReport(String dumpDirLocation, String projectKey, String relativeUrl, File report) {
+ LOG.debug("Dump report to file");
+ try {
+ dumpReportImpl(dumpDirLocation, projectKey, relativeUrl, report);
+ } catch (IOException | URISyntaxException e) {
+ LOG.error("Failed to dump report to directory " + dumpDirLocation, e);
+ }
+ }
+
+ private void dumpReportImpl(String dumpDirLocation, String projectKey, String relativeUrl, File report) throws IOException, URISyntaxException {
+ File dumpDir = new File(dumpDirLocation);
+ if (!dumpDir.exists() || !dumpDir.isDirectory()) {
+ LOG.warn("Report dump directory '{}' does not exist or is not a directory", dumpDirLocation);
+ return;
+ }
+ long dateTime = new Date().getTime();
+ File dumpedZip = new File(dumpDir, format("batch-report_%s_%s.zip", projectKey, dateTime));
+ FileUtils.copyFile(report, new FileOutputStream(dumpedZip));
+ File dumpedMetadata = new File(dumpDir, format("batch-report_%s_%s.txt", projectKey, dateTime));
+ FileUtils.write(dumpedMetadata, relativeUrl);
+ LOG.info("Batch report dumped to {}", dumpedZip.getAbsolutePath());
+ }
+
@VisibleForTesting
void logSuccess(Logger logger) {
if (analysisMode.isPreview() || analysisMode.isMediumTest()) {