diff --git a/baseline.conf b/baseline.conf
index 9437ee66b..c3777dca9 100755
--- a/baseline.conf
+++ b/baseline.conf
@@ -25,3 +25,15 @@
readonly JAVA_CPD_THRESHOLD=32
# Threshold for CPD when run for Scala
readonly SCALA_CPD_THRESHOLD=0
+
+# ******************* Baseline and Threshold numbers for Checkstyle *********************
+# Threshold for Checkstyle errors post which build would fail
+readonly CHECKSTYLE_ERROR_THRESHOLD=1389
+# Baseline for Checkstyle warnings(build wont fail for warnings)
+readonly CHECKSTYLE_WARNING_BASELINE=730
+
+ # ******************* Baseline and Threshold numbers for Scalastyle *********************
+# Threshold for Scalastyle errors post which build would fail
+readonly SCALASTYLE_ERROR_THRESHOLD=274
+# Baseline for Scalastyle warnings(build wont fail for warnings)
+readonly SCALASTYLE_WARNING_BASELINE=37
diff --git a/checkstyle.sbt b/checkstyle.sbt
new file mode 100644
index 000000000..8058dcae4
--- /dev/null
+++ b/checkstyle.sbt
@@ -0,0 +1,27 @@
+//
+// Copyright 2016 LinkedIn Corp.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+//
+// sbt-checkstyle-plugin specific configurations go in this file
+//
+
+// Path and name of checkstyle configuration file
+checkstyleConfigLocation := CheckstyleConfigLocation.File("project/checkstyle-config.xml")
+
+// Generate HTML report in addition to default XML report by applying XSLT transformations
+checkstyleXsltTransformations := {
+ Some(Set(CheckstyleXSLTSettings(baseDirectory(_ / "project/checkstyle-noframes-severity-sorted-modified.xsl").value, target(_ / "checkstyle-report.html").value)))
+}
diff --git a/common.sh b/common.sh
index b6e7215b7..1a084404d 100755
--- a/common.sh
+++ b/common.sh
@@ -39,6 +39,14 @@ readonly CPD_REPORT_BASE_PATH=$REPORTS_BASE_PATH"cpd/"
# Default path for CPD report
readonly CPD_REPORT_PATH=$CPD_REPORT_BASE_PATH"cpd.xml"
+# ******************* Constants for Checkstyle *********************
+# Path for Checkstyle report
+readonly CHECKSTYLE_REPORT_PATH="target/checkstyle-report.xml"
+
+# ******************* Constants for Scalastyle *********************
+# Path for Scalastyle report
+readonly SCALASTYLE_REPORT_PATH="target/scalastyle-result.xml"
+
# ************************ Other constants **************************
# Color coded prefixes for ERROR, WARNING, INFO and SUCCESS messages
readonly ERROR_COLOR_PREFIX="[\033[0;31mERROR\033[0m]"
@@ -78,7 +86,6 @@ function getCPDReportName() {
##########################################################
function checkIfCPDFailed() {
duplicates=`grep "//g'| awk -F" " '{printf("\t%s: %s\n", $2,$1);}'`
+ resultCount=`echo "${result}" | wc -l | xargs`
+ echo -e "$WARNING_COLOR_PREFIX $4 $1 "$3"s detected. Top $resultCount issue types with their counts are as under:"
+ echo "${result}"
+}
+
+##########################################################
+# This function is called when build has to be failed
+# because developer has fixed CPD or Checkstyle or
+# Scalastyle issues but not updated the corresponding
+# threshold variable in this script.
+#
+# Arguments:
+# arg1: Report description
+# arg2: Issue count
+# arg3: Issue description
+# arg4: Name of variable to be updated
+# Returns:
+# None
+##########################################################
+function handleSettingThresholdVariable() {
+ msg="$1 Report has $2 $3"
+ color=$ERROR_COLOR_PREFIX
+ failTheBuildMsg=", hence failing the build.\n\tPlease modify"
+ thresholdOrBaseline="threshold"
+ if [ $3 = "warnings" ]; then
+ color=$WARNING_COLOR_PREFIX
+ failTheBuildMsg=".\n\tYou can modify"
+ thresholdOrBaseline="baseline"
+ fi
+ echo -e "$color $msg and you have fixed some of them as part of this change which is great! But you forgot to update the $thresholdOrBaseline$failTheBuildMsg"\
+ "$4 variable to $2 in baseline.conf to ensure that the new $thresholdOrBaseline takes effect for subsequent builds."
+}
+
+##################################################################
+# Check if there are warnings in the report for the tool whose
+# report is being processed. If warnings exist, dump top 10 issues
+# grouped by issue type. Return an integer indicating whether
+# number of warnings are 0, above or below baseline.
+# Also print messages if baseline variable has to be updated in
+# baseline.conf. Number of warnings are also returned by setting
+# an argument passed to the function.
+#
+# Arguments:
+# arg1: Indicates the tool whose report will be processed
+# (Checkstyle or Scalastyle)
+# arg2: Report location for the tool whose report is to be
+# processed
+# arg3: Warnings baseline for the tool whose report will be
+# processed
+# arg4: Name of the warning baseline constant for the tool
+# arg5: Argument which will be set equal to number of
+# warnings found in the report.
+# Returns:
+# 0: Success
+# 1: Warnings above baseline
+# 2: Warnings fixed but baseline variable not updated
+################################################################
+function checkStyleToolWarnings() {
+ # Local variable which references to arg5.
+ local __numWarnings=$5
+ # Check if there are any warnings in the Checkstyle or Scalastyle report
+ local styleWarnings=`grep 'severity="warning"' $2 | wc -l | xargs`
+ # Effectively sets number of warnings to arg5
+ eval $__numWarnings="'$styleWarnings'"
+ if [ $styleWarnings -gt 0 ]; then
+ dumpTop10StyleIssueTypes $1 $2 "warning" $styleWarnings
+ # Return number of warnings only if over baseline
+ if [ $styleWarnings -gt $3 ]; then
+ return 1;
+ elif [ $styleWarnings -lt $3 ]; then
+ handleSettingThresholdVariable $1 $styleWarnings "warnings" $4
+ return 2;
+ fi
+ else
+ echo -e "$SUCCESS_COLOR_PREFIX $1 Report has no warnings..."
+ fi
+ return 0;
+}
+
+##################################################################
+# Process checkstyle/scalastyle report after the tool has been run
+# This method will find how many errors exist.
+# If errors exist and they are above threshold, fail the build.
+# Fail the build even if errors have been fixed but threshold
+# variable has not been updated in baseline.conf
+# Print top 10 issues at error severity grouped by issue
+# type if errors are equal to threshold (for informational
+# purposes)
+#
+# Arguments:
+# arg1: Indicates the tool whose report will be processed
+# (Checkstyle or Scalastyle)
+# arg2: Report location for the tool whose report is to be
+# processed
+# arg3: Error threshold, above which build would fail, for
+# the tool whose report will be processed
+# arg4: Name of the error threshold constant for the tool
+#
+# Returns:
+# 0: Success
+# 1: Failure due to errors above threshold
+# 2: Failure due to errors fixed but threshold variable not
+# updated.
+##################################################################
+function checkStyleToolErrors() {
+ # Check if there are any errors in the Checkstyle or Scalastyle report and fail the build, if above threshold
+ styleErrors=`grep 'severity="error"' $2 | wc -l | xargs`
+ if [ $styleErrors -gt $3 ]; then
+ echo -e "$ERROR_COLOR_PREFIX Build failed as the code change has introduced $1 ERRORS. $styleErrors found (threshold: $3)"
+ return 1;
+ fi
+
+ # Print top 10 checkstyle/scalastyle error categories if number of errors within threshold
+ if [ $styleErrors -gt 0 ]; then
+ if [ $styleErrors -gt $3 ]; then
+ echo -e "$ERROR_COLOR_PREFIX Build failed as this code change has introduced $1 ERRORS. $styleErrors found (threshold: $3)"
+ return 1;
+ elif [ $styleErrors -eq $3 ]; then
+ dumpTop10StyleIssueTypes $1 $2 "error" $styleErrors
+ echo -e "$WARNING_COLOR_PREFIX Note: The code change may not have introduced $1 errors as count is within threshold. Not failing"\
+ "the build."
+ return 0;
+ else
+ handleSettingThresholdVariable $1 $styleErrors "errors" $4
+ return 2;
+ fi
+ else
+ if [ $3 -gt 0 ]; then
+ handleSettingThresholdVariable $1 $styleErrors "errors" $4
+ return 2;
+ else
+ echo ""
+ echo -e "$SUCCESS_COLOR_PREFIX $1 Report has no errors..."
+ return 0;
+ fi
+ fi
+}
+
##########################################################
# Parse the findbugs report and if any bugs are found,
# fail the build.
@@ -189,3 +393,63 @@ function changeCPDLanguageSetting() {
sed "s/$1/$2/g" cpd.sbt > cpd.sbt.bak
mv cpd.sbt.bak cpd.sbt
}
+
+############################################################
+# Generate a final scalastyle report by removing duplicate
+# errors and sorting the results within a file by line
+# number.
+#
+# Arguments:
+# arg1: Report location for the tool being run
+# Returns:
+# None
+############################################################
+function preProcessScalastyleReport() {
+ # Flag to indicate whether we are processing file tag i.e. we have encountered file begin tag but not the file end tag
+ filetag=0
+ currentLineNum=0
+ currentErrorTag=""
+ count=0
+ while IFS='' read -r line || [[ -n "$line" ]]; do
+ if [[ $line == *"> $1.bak
+ filetag=1
+ elif [[ $line == *""* ]]; then
+ # On end file tag, sort and find unique lines in tmpResult file(contains errors for a file).
+ # This is done to avoid duplicates
+ sortedResults=`cat tmpResult | sort -n -k 1 | uniq`
+ # Remove the line number prepended in tmpResult used for sorting errors by line number
+ finalResults=`echo "${sortedResults}" | sed 's/^[0-9]* / /g'`
+ # Copy errors for a file in sorted order and after removing duplicates
+ echo "${finalResults}" >> $1.bak
+ rm -rf tmpResult
+ # Copy file end tag as well
+ echo -e $line >> $1.bak
+ filetag=0
+ elif [ $filetag -eq 1 ]; then
+ # We are processing errors inside a file
+ # Fetch line number from the corresponding attribute and prepend the line with line number
+ # This is done to ensure sorting of errors within a file by line number and removing duplicates,
+ # if any. Store this result in a tmpResult file
+ lineAttribute=`echo "$line" | sed -n 's/.* line="\([0-9.]*\).*/\1/p'`
+ if [[ $line == *"" ]]; then
+ echo -e $lineAttribute" "$line >> tmpResult
+ else
+ currentLineNum=$lineAttribute
+ currentErrorTag=$line
+ fi
+ elif [[ $line == *"/>" ]]; then
+ # Processing error tag. Encountered end of tag.
+ lineWithoutSpaces=`echo $line | sed 's/^[ ]*//g'`
+ echo -e "$currentLineNum $currentErrorTag $lineWithoutSpaces" >> tmpResult
+ fi
+ else
+ # Not inside file tag. Copy line as is.
+ echo -e $line >> $1.bak
+ fi
+ done< $1
+ # Move the .bak file to the report file
+ mv $1.bak $1
+}
diff --git a/compile.sh b/compile.sh
index f826d6d61..3c09cbedb 100755
--- a/compile.sh
+++ b/compile.sh
@@ -16,6 +16,24 @@
# the License.
#
+########################################################
+#
+# Global constants
+#
+########################################################
+
+# ******************** Constants for Checkstyle *********************
+# Path for Checkstyle HTML report
+readonly CHECKSTYLE_HTML_REPORT_PATH="target/checkstyle-report.html"
+
+# ******************** Constants for Scalastyle *********************
+# Path for Scalastyle HTML report
+readonly SCALASTYLE_HTML_REPORT_PATH="target/scalastyle-result.html"
+# Path for Scalastyle HTML report generation python script
+readonly SCALASTYLE_XSL_FILE="project/checkstyle-noframes-severity-sorted-modified.xsl"
+# Path for Scalastyle HTML report generation python script
+readonly SCALASTYLE_HTML_REPORT_GEN_SCRIPT="project/scalastyle_xml_to_html.py"
+
function print_usage() {
echo ""
echo "Usage: ./compile.sh [config_file_path] [additional_options]"
@@ -25,6 +43,7 @@ function print_usage() {
echo -e "\tcoverage: Runs Jacoco code coverage and fails the build as per configured threshold"
echo -e "\tfindbugs: Runs Findbugs for Java code"
echo -e "\tcpd: Runs Copy Paste Detector(CPD) for Java and Scala code"
+ echo -e "\tstylechecks: Runs Checkstyle for Java and Scalastyle for Scala code"
}
function play_command() {
@@ -46,10 +65,10 @@ function require_programs() {
done
if [ ! -z "$missing_programs" ]; then
- echo "[ERROR] The following programs are required and are missing: $missing_programs"
+ echo -e "$ERROR_COLOR_PREFIX The following programs are required and are missing: $missing_programs"
exit 1
else
- echo "[SUCCESS] Program requirement is fulfilled!"
+ echo -e "$SUCCESS_COLOR_PREFIX Program requirement is fulfilled!"
fi
}
@@ -77,9 +96,7 @@ function processCPDReportByLanguage() {
result=$?
if [ $result -gt 0 ]; then
if [ $result -eq 2 ]; then
- echo ""
- echo -e "$WARNING_COLOR_PREFIX Note: Make sure your local repo is up to date with the branch you want to merge to, otherwise threshold/baseline "\
- "values to be updated in baseline.conf\n\tmight be different and that can lead to CI failure..."
+ echo -e $(noteForUpdatingRepo)" and that can lead to CI failure..."
fi
echo ""
exit 1;
@@ -98,19 +115,21 @@ function processCPDReportByLanguage() {
# None
##########################################################
function runCPD() {
- echo "Running CPD for Java"
+ echo -e "$INFO_COLOR_PREFIX Running CPD for Java"
play_command $1 cpd
if [ $? -ne 0 ]; then
+ echo -e "$ERROR_COLOR_PREFIX CPD for Java failed"
exit 1;
fi
processCPDReportByLanguage "Java" $JAVA_CPD_THRESHOLD "JAVA_CPD_THRESHOLD"
- echo "Running CPD for Scala"
+ echo -e "$INFO_COLOR_PREFIX Running CPD for Scala"
changeCPDLanguageSetting "Language.Java" "Language.Scala"
play_command $OPTS cpd
if [ $? -ne 0 ]; then
# Reset language back to Java
changeCPDLanguageSetting "Language.Scala" "Language.Java"
+ echo -e "$ERROR_COLOR_PREFIX CPD for Scala failed"
exit 1;
fi
processCPDReportByLanguage "Scala" $SCALA_CPD_THRESHOLD "SCALA_CPD_THRESHOLD"
@@ -118,6 +137,132 @@ function runCPD() {
changeCPDLanguageSetting "Language.Scala" "Language.Java"
}
+##########################################################
+# Note for updating repo before updating baseline.conf
+#
+# Arguments:
+# None
+# Returns:
+# Note for updating repo
+##########################################################
+function noteForUpdatingRepo {
+ echo -e "$WARNING_COLOR_PREFIX Note: Make sure your local repo is up to date with the branch you want to merge to, otherwise threshold/baseline "\
+ "values to be updated in baseline.conf\n\tmight be different"
+}
+
+############################################################
+# Process style report based on tool for which report is
+# being processed. Verifies report existence, checks for
+# warning baseline, checks for error threshold breach and
+# if required fail the build or print appropriate message.
+#
+# Arguments:
+# arg1: Indicates the tool whose report will be processed
+# (Checkstyle or Scalastyle)
+# arg2: Report location for the tool whose report is to be
+# processed
+# arg3: Error threshold, above which build would fail, for
+# the tool whose report will be processed
+# arg4: Warnings baseline for the tool whose report will be
+# processed
+# arg5: Name of the error threshold constant for the tool
+# and language
+# arg6: Name of the warning baseline constant for the tool
+# and language
+# Returns:
+# None
+############################################################
+function processStyleReport() {
+ verifyStyleReportExistence $1 $2
+
+ # Check warnings in Checkstyle/Scalastyle report
+ checkStyleToolWarnings $1 $2 $4 $6 numWarnings
+ result=$?
+ if [ $result -gt 0 ]; then
+ if [ $result -eq 1 ]; then
+ msgToResetStyleReportWarning $1 $4 $6 $numWarnings
+ fi
+ echo -e $(noteForUpdatingRepo)"..."
+ fi
+ echo ""
+
+ # Check errors in Checkstyle/Scalastyle report
+ checkStyleToolErrors $1 $2 $3 $5
+ result=$?
+ if [ $result -gt 0 ]; then
+ if [ $result -eq 2 ]; then
+ echo -e $(noteForUpdatingRepo)" and that can lead to CI failure..."
+ fi
+ echo ""
+ exit 1;
+ fi
+ echo ""
+}
+
+############################################################
+# Process both Checkstyle and Scalastyle XML reports. Also
+# generates Scalastyle HTML report(Checkstyle HTML report is
+# automatically generated by checkstyle4sbt plugin).
+# Fail the build if threshold values are breached.
+#
+# Arguments:
+# None
+# Returns:
+# None
+############################################################
+function processCheckstyleAndScalastyleReports() {
+ echo ""
+ echo -e "$INFO_COLOR_PREFIX Checking Checkstyle report..."
+ echo -e "$INFO_COLOR_PREFIX Checkstyle XML report generated at path: $CHECKSTYLE_REPORT_PATH and HTML report generated at path: $CHECKSTYLE_HTML_REPORT_PATH"
+ processStyleReport "Checkstyle" $CHECKSTYLE_REPORT_PATH $CHECKSTYLE_ERROR_THRESHOLD $CHECKSTYLE_WARNING_BASELINE "CHECKSTYLE_ERROR_THRESHOLD" "CHECKSTYLE_WARNING_BASELINE"
+
+ scalastyleHtmlGenMsg=""
+ preProcessScalastyleReport $SCALASTYLE_REPORT_PATH
+ pythonVersion=`python --version 2>&1`
+ if [ $? -ne 0 ]; then
+ echo -e "$WARNING_COLOR_PREFIX Cannot generate Scalastyle HTML report as Python is unavailable. Install Python and add it in PATH"
+ else
+ # Generate Scalastyle HTML Report
+ rm -rf $SCALASTYLE_HTML_REPORT_PATH
+ echo "Using $pythonVersion"
+ pip install lxml
+ if [ $? -ne 0 ]; then
+ echo -e "$WARNING_COLOR_PREFIX Could not install lxml module for Python. Scalastyle HTML report could not be generated"
+ else
+ python $SCALASTYLE_HTML_REPORT_GEN_SCRIPT $SCALASTYLE_REPORT_PATH $SCALASTYLE_XSL_FILE $SCALASTYLE_HTML_REPORT_PATH
+ if [ $? -ne 0 ]; then
+ echo -e "$WARNING_COLOR_PREFIX Scalastyle HTML report could not be generated"
+ else
+ scalastyleHtmlGenMsg=" and HTML report generated at path: $SCALASTYLE_HTML_REPORT_PATH"
+ fi
+ fi
+ fi
+ echo -e "$INFO_COLOR_PREFIX Checking Scalastyle report..."
+ echo -e "$INFO_COLOR_PREFIX Scalastyle XML report generated at path: $SCALASTYLE_REPORT_PATH"$scalastyleHtmlGenMsg
+ processStyleReport "Scalastyle" $SCALASTYLE_REPORT_PATH $SCALASTYLE_ERROR_THRESHOLD $SCALASTYLE_WARNING_BASELINE "SCALASTYLE_ERROR_THRESHOLD" "SCALASTYLE_WARNING_BASELINE"
+}
+
+#############################################################
+# Run Checkstyle and Scalastyle and then process the report.
+# Fail the build if the command fails or if threshold values
+# are breached.
+#
+# Arguments:
+# arg1: Play command OPTS
+# Returns:
+# None
+#############################################################
+function runStyleChecks() {
+ echo -e "$INFO_COLOR_PREFIX Running Checkstyle and Scalastyle"
+ play_command $1 checkstyle scalastyle
+ if [ $? -ne 0 ]; then
+ echo -e "$ERROR_COLOR_PREFIX Either Checkstyle or Scalastyle has failed"
+ echo ""
+ exit 1;
+ fi
+ processCheckstyleAndScalastyleReports
+}
+
require_programs zip unzip
# Default configurations
@@ -129,6 +274,7 @@ extra_commands=""
# Indicates whether a custom configuration file is passed as first parameter.
custom_config="n"
run_CPD="n"
+run_StyleChecks="n"
# Process command line arguments
while :; do
if [ ! -z $1 ]; then
@@ -142,6 +288,13 @@ while :; do
cpd)
run_CPD="y"
;;
+ stylechecks)
+ run_StyleChecks="y"
+ ;;
+ help)
+ print_usage
+ exit 0;
+ ;;
*)
# User may pass the first argument(optional) which is a path to config file
if [[ -z $extra_commands && $custom_config = "n" ]]; then
@@ -268,6 +421,11 @@ if [ $run_CPD = "y" ]; then
runCPD $OPTS
fi
+# Run Checkstyle and Scalastyle if stylechecks is passed as an argument
+if [ $run_StyleChecks = "y" ]; then
+ runStyleChecks $OPTS
+fi
+
set -v
set -x
# Echo the value of pwd in the script so that it is clear what is being removed.
diff --git a/project/checkstyle-config.xml b/project/checkstyle-config.xml
new file mode 100644
index 000000000..1f84b1a5c
--- /dev/null
+++ b/project/checkstyle-config.xml
@@ -0,0 +1,364 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/project/checkstyle-java.header b/project/checkstyle-java.header
new file mode 100644
index 000000000..3c8eb723e
--- /dev/null
+++ b/project/checkstyle-java.header
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2016 LinkedIn Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
diff --git a/project/checkstyle-noframes-severity-sorted-modified.xsl b/project/checkstyle-noframes-severity-sorted-modified.xsl
new file mode 100644
index 000000000..b16a56cea
--- /dev/null
+++ b/project/checkstyle-noframes-severity-sorted-modified.xsl
@@ -0,0 +1,269 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Scalastyle Report
+ Checkstyle Report
+
+
+
+
+
+
+
+
+
+
+ a
+ b
+
+
+
+
+
+
+ error
+ warning
+ a
+
+
+
diff --git a/project/checkstyle-suppressions.xml b/project/checkstyle-suppressions.xml
new file mode 100644
index 000000000..e8718cc6b
--- /dev/null
+++ b/project/checkstyle-suppressions.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
diff --git a/project/plugins.sbt b/project/plugins.sbt
index 32d4f2f33..a9c495142 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -30,3 +30,9 @@ addSbtPlugin("de.johoop" % "findbugs4sbt" % "1.4.0")
// Copy paste detector plugin
addSbtPlugin("de.johoop" % "cpd4sbt" % "1.2.0")
+
+// Checkstyle plugin
+addSbtPlugin("com.etsy" % "sbt-checkstyle-plugin" % "3.1.1")
+
+// Scalastyle plugin
+addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0")
diff --git a/project/scalastyle-config.xml b/project/scalastyle-config.xml
new file mode 100644
index 000000000..043a30f06
--- /dev/null
+++ b/project/scalastyle-config.xml
@@ -0,0 +1,343 @@
+
+
+
+ Scalastyle standard configuration
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mutable\.SynchronizedBuffer
+
+
+
+
+
+ mutable\.SynchronizedMap
+
+
+
+
+
+ mutable\.SynchronizedSet
+
+
+
+
+
+ mutable\.SynchronizedQueue
+
+
+
+
+
+ mutable\.SynchronizedPriorityQueue
+
+
+
+
+
+ mutable\.SynchronizedStack
+
+
+
+
+ ^println$
+
+
+
+ JavaConversions
+ Instead of importing implicits in scala.collection.JavaConversions._, import
+ scala.collection.JavaConverters._ and use .asScala / .asJava methods
+
+
+
+ throw new \w+Error\(
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+ 2
+ 2
+
+
+
+
+
+
+
+
+
+
+
+
+ ARROW, EQUALS, ELSE, TRY, CATCH, FINALLY, LARROW, RARROW
+
+
+
+
+ ARROW, EQUALS, COMMA, COLON, IF, ELSE, DO, WHILE, FOR, MATCH, TRY, CATCH, FINALLY, LARROW, RARROW
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PatDefOrDcl,TypeDefOrDcl,FunDefOrDcl,TmplDef
+ false
+ javadoc
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/project/scalastyle_xml_to_html.py b/project/scalastyle_xml_to_html.py
new file mode 100755
index 000000000..da1c6965c
--- /dev/null
+++ b/project/scalastyle_xml_to_html.py
@@ -0,0 +1,70 @@
+#
+# Copyright 2016 LinkedIn Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+#
+
+#
+# This script is meant to convert Scalastyle XML report to Scalastyle HTML report using
+# XSLT transformations. The XSL file is typically the same as the one used to convert
+# Checkstyle XML report to HTML report.
+#
+
+import lxml.etree as ET
+import os
+import sys
+import traceback
+import os.path
+from os import path
+
+# Takes 3 arguments: Scalastyle XML report, XSL file name and HTML report name to be generated
+if len(sys.argv) < 4:
+ print 'Too few arguments, please specify arguments as under:\n 1st argument: Scalastyle XML report file name \n 2nd', \
+ 'argument: XSL file name\n 3rd argument: Scalastyle HTML report name to be generated...'
+ sys.exit(1)
+
+print 'Generating Scalastyle HTML report'
+
+# Check if input Scalastyle XML report exists
+xmlReportFileName = sys.argv[1]
+if not path.isfile(xmlReportFileName):
+ print 'Scalastyle XML report {0} not found. Cannot generate HTML report!'.format(xmlReportFileName)
+ sys.exit(1)
+
+# Check if input XSL which will be used to transform XML report exists
+xslFileName = sys.argv[2]
+if not path.isfile(xslFileName):
+ print 'XSL file {0} for Scalastyle XML report conversion not found. Cannot generate HTML report!'.format(xslFileName)
+ sys.exit(1)
+
+# HTML report name which will be outputted
+htmlReportFileName = sys.argv[3]
+
+htmlReportFD = None
+try:
+ xmlreport_root = ET.parse(xmlReportFileName)
+ xslt = ET.parse(xslFileName)
+ transform = ET.XSLT(xslt)
+ # Pass reporttype to XSL to ensure scalstyle specific changes can be made while outputting HTML report.
+ htmlreport_root = transform(xmlreport_root, reporttype="'scalastyle'")
+ htmlstring = ET.tostring(htmlreport_root, pretty_print=True)
+ htmlReportFD = os.open(htmlReportFileName, os.O_RDWR|os.O_CREAT)
+ os.write(htmlReportFD, htmlstring)
+except:
+ print 'Issue encountered during Scalastyle HTML report generation...{0} occured.'.format(sys.exc_info()[0])
+ desired_trace = traceback.format_exc(sys.exc_info())
+ print(desired_trace)
+ sys.exit(1)
+finally:
+ if htmlReportFD is not None:
+ os.close(htmlReportFD)
diff --git a/scalastyle.sbt b/scalastyle.sbt
new file mode 100644
index 000000000..429766342
--- /dev/null
+++ b/scalastyle.sbt
@@ -0,0 +1,27 @@
+//
+// Copyright 2016 LinkedIn Corp.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+//
+// scalastyle-sbt-plugin specific configurations go in this file
+//
+
+// Do not fail on scalastyle errors as we want to baseline error numbers till
+// we fix all errors. We would fail the CI build if any new errors are introduced
+// through a PR.
+scalastyleFailOnError := false
+
+// Scalastyle config file location.
+scalastyleConfig := file("project/scalastyle-config.xml")
diff --git a/travis.sh b/travis.sh
index 47d4d5863..06e826941 100755
--- a/travis.sh
+++ b/travis.sh
@@ -20,6 +20,14 @@
# Script to be used for building on Travis CI
#
+########################################################
+#
+# Global constants
+#
+########################################################
+# Tab for use in sed command
+readonly TAB=$'\t'
+
############################################################
# Get files chnged in this PR using git commands.
#
@@ -98,7 +106,9 @@ function dumpCPDSummaryForChangedFiles() {
##########################################################
# Check if there are duplicates in CPD report above the
-# configured threshold for the language.
+# configured threshold for the language. Dump CPD summary
+# for changed files if checkIfCPDFailed method returns 1
+# i.e. if CPD duplicates are above threshold.
#
# Arguments:
# arg1: Language for which CPD is run (Java or Scala)
@@ -147,7 +157,232 @@ function runFindbugs() {
checkFindbugsReport
}
-##########################################################
+############################################################
+# Helper function to generate severity attribute found in
+# checkstyle/scalastyle XML reports, based on the severity
+# passed. Used to match severity in the XML report.
+#
+# Arguments:
+# arg1: Severity of issues (warning or error)
+# Returns:
+# Severity attribute string in the form:
+# severity="arg1"
+############################################################
+function getSeverityStringForStyleReport() {
+ severityWithQuotes='"'$1'"'
+ echo 'severity='$severityWithQuotes
+}
+
+############################################################
+# For the files changed in the PR and severity passed, get
+# a summary of all the checkstyle or scalastyle issues in a
+# printable format by extracting details from XML report.
+# This function is called when warnings surpass the baseline
+# or errors breach the threshold i.e. the PR introduces
+# checkstyle or scalastyle errors.
+#
+# Arguments:
+# arg1: List of files changed in the PR
+# arg2: Report location for the tool whose report is being
+# parsed.
+# arg3: Severity of the issues for which we are dumping
+# results
+# Returns:
+# None
+############################################################
+function getStyleIssuesForChangedFiles() {
+ # Get severity attribute for match in style report.
+ severityString=$(getSeverityStringForStyleReport $3)
+ styleIssuesForChangedFiles=$(
+ # Iterate over all the files changed in this PR.
+ for changedFile in $1; do
+ # sed requires filename separators to be escaped.
+ changedFileEscaped=$(echo $changedFile | sed 's/\//\\\//g')
+
+ # Extract/filter out issues based on filename and severity.
+ results=`sed -n "///g;s/\" /\", /g"`
+ echo "${finalResults}" | awk '{
+ # Iterate over all the Checkstyle/Scalastyle issues for the changed file, filtered by severity
+ numIssues = split($0, issues, "\n")
+ for (issueIdx = 1; issueIdx <= numIssues; issueIdx++) {
+ # Convert special encoding in XML file such as " and &apos
+ gsub(/"/, "", issues[issueIdx]);
+ gsub("\\'", "'"'"'", issues[issueIdx]);
+ gsub("\\"", "\"", issues[issueIdx]);
+ # Next 4 variables hold the attribute values for line, column, message and source attributes respectively.
+ line=""
+ column=""
+ message=""
+ source=""
+
+ # Indicates whether message attribute is being processed.
+ processingMessage = 0;
+
+ # Extract different attributes for each issue by splitting the line by comma
+ # and for each attribute, extract its value. The attributes we are interested
+ # in are line, column, source and message.
+ # 1. Line indicates the line at which checkstyle/scalastyle issue exists
+ # 2. Column indicates the column at which checkstyle/scalastyle issue exists
+ # 3. Message is explanation about the issue.
+ # 4. Source is Checkstyle/Scalastyle check which led to the issue.
+ numAttributes = split(issues[issueIdx], attributes, ",");
+ for (attributeIdx = 1; attributeIdx <= numAttributes; attributeIdx++) {
+ lineIdx = index(attributes[attributeIdx], "line=");
+ if (lineIdx > 0) {
+ line = line "" substr(attributes[attributeIdx], lineIdx + 5);
+ processingMessage = 0;
+ continue;
+ }
+ columnIdx = index(attributes[attributeIdx], "column=");
+ if (columnIdx > 0) {
+ column = column "" substr(attributes[attributeIdx], columnIdx + 7);
+ processingMessage = 0;
+ continue;
+ }
+ sourceIdx = index(attributes[attributeIdx], "source=");
+ if (sourceIdx > 0) {
+ source = source "" substr(attributes[attributeIdx], sourceIdx + 7);
+ processingMessage = 0;
+ continue;
+ }
+
+ # Extract message from message attribute. As message can contain commas as well, continue to append to message
+ # till another attribute is encountered.
+ messageIdx = index(attributes[attributeIdx], "message=");
+ if (messageIdx > 0) {
+ message = message "" substr(attributes[attributeIdx], messageIdx + 8);
+ processingMessage = 1;
+ } else if (processingMessage == 1) {
+ message = message "," attributes[attributeIdx];
+ }
+ }
+ # Remove dot from the end of the message.
+ split(message, chars, "");
+ len = length(message);
+ if (chars[len] == ".") {
+ message="" substr(message, 1, len - 1);
+ }
+
+ # Extract last section of source string, separated by dot.
+ numSourceParts = split(source, sourceParts, ".");
+ # Print style information in the desired format
+ printf("\t + %s (%s) at line: %s%s\n", sourceParts[numSourceParts], message, line, ((column == "") ? "." : (" and column: " column ".")));
+ }
+ }'
+ fi
+ fi
+ done
+ )
+ echo "${styleIssuesForChangedFiles}"
+}
+
+############################################################
+# Capitalizes first character of passed string
+#
+# Arguments:
+# arg1: String whose first character has to be capitalized
+# Returns:
+# String with first character captialized
+############################################################
+function capitalizeFirstCharOfStr() {
+ echo $1 | awk '{
+ split($0, chars, "")
+ for (i = 1; i <= length($0); i++) {
+ if (i == 1) {
+ printf("%s", toupper(chars[i]));
+ } else {
+ printf("%s", chars[i]);
+ }
+ }
+ }'
+}
+
+###################################################################
+# Process checkstyle/scalastyle report after the tool has been run
+# This method will find out whether report has been generated and
+# how many errors/warnings exist.
+# If errors exist and they are above threshold, fail the build.
+# Print summary of checkstyle/scalastyle warnings for changed files
+# if checkStyleToolWarnings function returns 1 i.e. if warnings are
+# above baseline.
+# Print summary of checkstyle/scalastyle errors for changed files
+# if checkStyleToolErrors function returns 1 i.e. if errors are
+# above threshold.
+# If warnings exist and above baseline, warn the user and print
+# top 10 issues at warning severity grouped by issue type.
+# Also print top 10 issues at error severity grouped by issue
+# type if errors are equal to threshold (for informational
+# purposes)
+#
+# Arguments:
+# arg1: Indicates the tool whose report will be processed
+# (Checkstyle or Scalastyle)
+# arg2: Report location for the tool whose report is to be
+# processed
+# arg3: Error threshold, above which build would fail, for
+# the tool whose report will be processed
+# arg4: Warnings baseline for the tool whose report will be
+# processed
+# arg5: Name of the error threshold constant for the tool
+# and language
+# arg6: Name of the warning baseline constant for the tool
+# and language
+# arg7: List of files changed in the PR
+# Returns:
+# None
+###################################################################
+function processStyleReports() {
+ # Verify if style report exists for the tool whose report is being processed
+ verifyStyleReportExistence $1 $2
+
+ # Check warnings in Checkstyle/Scalastyle report
+ checkStyleToolWarnings $1 $2 $4 $6 numWarnings
+ result=$?
+ if [ $result -gt 0 ]; then
+ if [ $result -eq 1 ]; then
+ # If there are warnings above baseline, find all warnings for changed files
+ styleIssuesForChangedFiles=$(getStyleIssuesForChangedFiles "${7}" $2 "warning")
+ fileCnt=`echo "${styleIssuesForChangedFiles}" | grep "Failed checks for file" | wc -l | xargs`
+ if [ $fileCnt -gt 0 ]; then
+ echo -e "$WARNING_COLOR_PREFIX Note: This PR may have introduced $1 warnings (baseline: $4)"
+ echo -e "$WARNING_COLOR_PREFIX Listing $1 WARNINGS for the files changed in the PR:"
+ echo "${styleIssuesForChangedFiles}"
+ else
+ msgToResetStyleReportWarning $1 $4 $6 $numWarnings
+ fi
+ fi
+ fi
+ echo ""
+
+ # Check errors in Checkstyle/Scalastyle report
+ checkStyleToolErrors $1 $2 $3 $5
+ result=$?
+ if [ $result -gt 0 ]; then
+ if [ $result -eq 1 ]; then
+ echo -e "$ERROR_COLOR_PREFIX Listing $1 ERRORS for the files changed in the PR:"
+ styleIssuesForChangedFiles=$(getStyleIssuesForChangedFiles "${7}" $2 "error")
+ echo "${styleIssuesForChangedFiles}"
+ echo ""
+ echo -e "$ERROR_COLOR_PREFIX $1 step failed..."
+ fi
+ echo ""
+ exit 1;
+ fi
+ echo ""
+}
+
+#########################################################
# Run CPD for the langauge passed, check for failures and
# move the final result to a separate file.
#
@@ -158,7 +393,7 @@ function runFindbugs() {
# arg4: List of files changed in the PR
# Returns:
# None
-##########################################################
+#########################################################
function runCPDForLanguage() {
sbt cpd
if [ $? -ne 0 ]; then
@@ -195,6 +430,36 @@ function runCPD() {
echo ""
}
+############################################################
+# Run sbt checkstyle or scalastyle command, parse the report
+# and if errors are found above threshold, fail the build.
+#
+# Arguments:
+# arg1: Command/tool to be run (checkstyle or scalastyle)
+# arg2: Report location for the tool being run
+# arg3: Error threshold, above which build would fail, for
+# the tool being run
+# arg4: Warnings baseline for the tool being run
+# arg5: Name of the error threshold constant for the tool
+# and language
+# arg6: Name of the warning baseline constant for the tool
+# and language
+# arg7: List of files changed in the PR
+# Returns:
+# None
+############################################################
+function runStylingTool() {
+ sbt $1
+ if [ $? -ne 0 ]; then
+ echo -e "$ERROR_COLOR_PREFIX $1 step failed..."
+ exit 1;
+ fi
+ if [ $1 = "scalastyle" ]; then
+ preProcessScalastyleReport $SCALASTYLE_REPORT_PATH
+ fi
+ processStyleReports $(capitalizeFirstCharOfStr $1) $2 $3 $4 $5 $6 "${7}"
+}
+
########################################################
#
# MAIN SCRIPT
@@ -246,7 +511,23 @@ echo -e "$SUCCESS_COLOR_PREFIX Copy Paste Detector(CPD) step succeeded..."
echo ""
echo "************************************************************"
-echo " 4. Run unit tests and code coverage"
+echo " 4. Checkstyle for JAVA code"
+echo "************************************************************"
+echo -e "$INFO_COLOR_PREFIX Running Checkstyle..."
+runStylingTool "checkstyle" $CHECKSTYLE_REPORT_PATH $CHECKSTYLE_ERROR_THRESHOLD $CHECKSTYLE_WARNING_BASELINE "CHECKSTYLE_ERROR_THRESHOLD" "CHECKSTYLE_WARNING_BASELINE" "${changedFilesList}"
+echo -e "$SUCCESS_COLOR_PREFIX Checkstyle step succeeded..."
+
+echo ""
+echo "************************************************************"
+echo " 5. Scalastyle for Scala code"
+echo "************************************************************"
+echo -e "$INFO_COLOR_PREFIX Running Scalastyle..."
+runStylingTool "scalastyle" $SCALASTYLE_REPORT_PATH $SCALASTYLE_ERROR_THRESHOLD $SCALASTYLE_WARNING_BASELINE "SCALASTYLE_ERROR_THRESHOLD" "SCALASTYLE_WARNING_BASELINE" "${changedFilesList}"
+echo -e "$SUCCESS_COLOR_PREFIX Scalastyle step succeeded..."
+
+echo ""
+echo "************************************************************"
+echo " 6. Run unit tests and code coverage"
echo "************************************************************"
echo -e "$INFO_COLOR_PREFIX Running unit tests and code coverage..."
sbt test jacoco:cover