blob: 58a52f6e36503ce396fa65be50e6ef2219a4a122 [file] [log] [blame]
#!/usr/bin/env bash
#
# 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.
if [ "${TESTPATCHDEBUG}" == "true" ] ; then
set -x
fi
BASEDIR=$(pwd)
TASKNAME="SPOTBUGS_DIFF"
OP=""
TEMPDIR=${BASEDIR}/tmp
REPORTDIR=""
SUMMARYFILE=""
SUMMARYFILE_FULL=""
STDOUT="/dev/null"
MVNPASSTHRU=""
FINDBUGS_JAR=findbugs-diff-0.1.0-all.jar
FINDBUGS_JAR_URL=https://repo1.maven.org/maven2/me/andrz/findbugs/findbugs-diff/0.1.0/$FINDBUGS_JAR
# Priorities.HIGH_PRIORITY and Priorities.NORMAL_PRIORITY
SPOTBUGS_PRIORITY_THRESHOLD=2
# Scariest and scary
SPOTBUGS_RANK_THRESHOLD=9
SPOTBUGS_XML_NAME=spotbugsXml.xml
BUG_LIMIT_PER_PROJECT=5
FINDBUGS_DIFF_XML=findbugs-new.xml
cleanup_and_exit() {
remove_file_if_present "${DIFF_DIR}/${FINDBUGS_JAR}"
remove_file_if_present "${DIFF_DIR}/${FINDBUGS_JAR}.md5"
exit "$1"
}
remove_file_if_present() {
FILE_NAME=$1
if [ -f "${FILE_NAME}" ]; then
rm -f "${FILE_NAME}"
echo "[TRACE] File [${FILE_NAME}] removed"
fi
}
print_usage() {
echo "Usage: $0 --taskname | (--op=pre|post|report --tempdir=<TEMP DIR> --reportdir=<REPORT DIR> --summaryfile=<SUMMARY FILE> --summaryfile-full=<FULL SUMMARY FILE>) [--verbose] [-D<VALUE>...] [-P<VALUE>...]"
echo
}
parse_args() {
for i in "$@"; do
case $i in
--taskname)
echo ${TASKNAME}
exit 0
;;
--op=*)
OP=${i#*=}
;;
--tempdir=*)
TEMPDIR=${i#*=}
;;
--reportdir=*)
REPORTDIR=${i#*=}
;;
--summaryfile=*)
SUMMARYFILE=${i#*=}
;;
--summaryfile-full=*)
SUMMARYFILE_FULL=${i#*=}
;;
--verbose)
STDOUT="/dev/stdout"
;;
-D*)
MVNPASSTHRU="${MVNPASSTHRU} $i"
;;
-P*)
MVNPASSTHRU="${MVNPASSTHRU} $i"
;;
esac
done
if [[ "${TASKNAME}" == "" || "${OP}" == "" || "${TEMPDIR}" == "" || "${REPORTDIR}" == "" || "${SUMMARYFILE}" == "" || "${SUMMARYFILE_FULL}" == "" ]] ; then
echo "Missing options"
echo
print_usage
cleanup_and_exit 1
fi
if [[ "${OP}" != "pre" && "${OP}" != "post" && "${OP}" != "report" ]] ; then
echo "Invalid operation"
echo
print_usage
cleanup_and_exit 1
fi
}
verify_and_save_spotbugs_output() {
SPOTBUGS_OUTPUT_DIR=$1
BRANCH_LABEL=$2
REPORT_SUFFIX=$3
echo "[TRACE] Verifying and saving SpotBugs output in ${BRANCH_LABEL}"
echo "[TRACE] mvn clean compile test-compile spotbugs:check -Dspotbugs.failOnError=false -Dcheckstyle.failOnViolation=false -DskipTests ${MVNPASSTHRU} | tee ${REPORTDIR}/${TASKNAME}-${REPORT_SUFFIX}.txt >> ${STDOUT}"
mvn clean compile test-compile spotbugs:check -Dspotbugs.failOnError=false -Dcheckstyle.failOnViolation=false -DskipTests ${MVNPASSTHRU} | tee "${REPORTDIR}/${TASKNAME}-${REPORT_SUFFIX}.txt" >> ${STDOUT}
if [ ! -d "$SPOTBUGS_OUTPUT_DIR" ]; then
mkdir -p "${SPOTBUGS_OUTPUT_DIR}"
fi
find . -name ${SPOTBUGS_XML_NAME} ! -path '*/test-patch/*' -exec rsync -avqR {} "${SPOTBUGS_OUTPUT_DIR}" ";"
if [ -n "$(ls -A "${SPOTBUGS_OUTPUT_DIR}")" ] ; then
echo "{color:green}+1{color} ${BRANCH_LABEL} produces SpotBugs output" >> "${TEMPDIR}/${TASKNAME}-${REPORT_SUFFIX}.txt"
else
echo "{color:red}-1{color} ${BRANCH_LABEL} does not produce SpotBugs output" >> "${TEMPDIR}/${TASKNAME}-${REPORT_SUFFIX}.txt"
fi
echo "[TRACE] SpotBugs output in ${BRANCH_LABEL} verified and saved"
}
create_missing_spotbugs_files() {
PRE_DIR=$1
POST_DIR=$2
create_missing_xml "${PRE_DIR}" "pre" "post"
create_missing_xml "${POST_DIR}" "post" "pre"
}
create_missing_xml() {
sourceFolder=$1
sourcePart=$2
targetPart=$3
sourceXmlFiles=()
while IFS= read -r -d '' fn; do
sourceXmlFiles+=("${fn}")
done < <(find "${sourceFolder}" -name ${SPOTBUGS_XML_NAME} -print0)
for index in "${!sourceXmlFiles[@]}"; do
targetXmlFile=${sourceXmlFiles[index]/${sourcePart}/${targetPart}}
targetFolder=${targetXmlFile%${SPOTBUGS_XML_NAME}}
if [ ! -d "${targetFolder}" ]; then
echo "[TRACE] Folder [${targetFolder}] does not exist, creating"
mkdir -p "${targetFolder}"
fi
if [ ! -f "${targetXmlFile}" ]; then
echo "[TRACE] XML file [${targetXmlFile}] does not exist, creating"
echo "<BugCollection />" > "${targetXmlFile}"
fi
done
}
download_and_check_findbugs_diff_jar() {
DIFF_DIR=$1
BASH_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
mkdir -p "${DIFF_DIR}"
echo "[TRACE] Downloading FindBugs diff JAR from ${FINDBUGS_JAR_URL}"
curl -Ls ${FINDBUGS_JAR_URL} > "${DIFF_DIR}/${FINDBUGS_JAR}"
echo "[TRACE] FindBugs diff JAR downloaded"
if hash md5 2>/dev/null; then
echo "[TRACE] Calculating md5 content using md5"
md5Content="$(md5 -q "${DIFF_DIR}/${FINDBUGS_JAR}")"
elif hash md5sum 2>/dev/null; then
echo "[TRACE] Calculating md5 content using md5sum"
md5Content="$(md5sum "${DIFF_DIR}/${FINDBUGS_JAR}" | awk '{ print $1 }')"
else
echo "[ERROR] Neither md5 nor md5sum are present, cannot check FindBugs diff JAR"
summary_both "{color:red}-1{color} Neither md5 nor md5sum are present, cannot check FindBugs diff JAR."
cleanup_and_exit 1
fi
echo "${md5Content}" > "${DIFF_DIR}/${FINDBUGS_JAR}.md5"
jarMd5DiffCount=$(diff "${BASH_DIR}"/${FINDBUGS_JAR}.md5 "${DIFF_DIR}"/${FINDBUGS_JAR}.md5 | wc -l)
if [ "${jarMd5DiffCount}" -gt "0" ]; then
echo "[TRACE] md5 hash of FindBugs diff jar is $(cat "${DIFF_DIR}"/${FINDBUGS_JAR}.md5)"
echo "[ERROR] FindBugs diff JAR has a weird MD5 sum, rejecting"
summary_both "{color:red}-1{color} FindBugs diff JAR has a weird MD5 sum, rejecting."
cleanup_and_exit 1
fi
echo "[TRACE] FindBugs diff JAR checked, is safe to use"
}
perform_spotbugs_diffs() {
PRE_DIR=$1
POST_DIR=$2
DIFF_DIR=$3
echo "[TRACE] Performing SpotBugs diffs"
sourceXmlFiles=()
while IFS= read -r -d '' fn; do
preSpotbugsDiffs+=("${fn}")
done < <(find "${PRE_DIR}" -name ${SPOTBUGS_XML_NAME} -print0)
postSpotbugsDiffs=()
while IFS= read -r -d '' fn; do
postSpotbugsDiffs+=("${fn}")
done < <(find "${POST_DIR}" -name ${SPOTBUGS_XML_NAME} -print0)
for index in "${!preSpotbugsDiffs[@]}"; do
preSpotbugsDir=${preSpotbugsDiffs[index]}
diffDirPostfix=${preSpotbugsDir/*${TASKNAME}/${TASKNAME}}
diffDirPostfix=${diffDirPostfix:18}
diffDirPostfix=${diffDirPostfix%%/target/spotbugs/$SPOTBUGS_XML_NAME}
check_minimum_file_size "${preSpotbugsDiffs[index]}"
check_minimum_file_size "${postSpotbugsDiffs[index]}"
java -jar -Dorg.slf4j.simpleLogger.defaultLogLevel=ERROR "${DIFF_DIR}"/${FINDBUGS_JAR} --outDir="${DIFF_DIR}/${diffDirPostfix}" "${preSpotbugsDiffs[index]}" "${postSpotbugsDiffs[index]}"
done
echo "[TRACE] SpotBugs diffs performed"
}
check_minimum_file_size() {
FILE_NAME=$1
actualSize=$(wc -c < "${FILE_NAME}")
minimumSize=10
if [ "${actualSize}" -le "${minimumSize}" ]; then
echo "[ERROR] File [${FILE_NAME}] is below minimum size (has only [${actualSize}] bytes), cannot perform SpotBugs diff"
cleanup_and_exit 1
fi
}
check_spotbugs_diffs_and_create_reports() {
DIFF_DIR=$1
REPORT=()
REPORT_FULL=()
echo "[TRACE] Checking SpotBugs diffs and creating reports"
belowThresholdCount=0
totalCount=0
while IFS= read -r -d '' fn; do
componentDir=${fn/*${TASKNAME}/${TASKNAME}}
componentDir=${componentDir:19}
htmlFileName=${componentDir%%.xml}.html
componentDir=${componentDir%%/$FINDBUGS_DIFF_XML}
xmlLintXPathCompatible=$(xmllint | grep -ce '\-\-xpath')
if [ "${xmlLintXPathCompatible}" -eq "0" ]; then
echo "[TRACE] Old XMLLib present, calling 'xmllint --shell' to get bug instance counts"
newBugTotalCount=$(xmllint --shell "${fn}" <<< 'xpath count(/BugCollection/BugInstance)' | grep -oE '[^ ]+$')
newBugBelowThresholdCount=$(xmllint --shell "${fn}" <<< 'xpath count(/BugCollection/BugInstance[@priority <= "${SPOTBUGS_PRIORITY_THRESHOLD}" or @rank <= "${SPOTBUGS_RANK_THRESHOLD}"])' | grep -oE '[^ ]+$')
else
echo "[TRACE] New XMLLib present, calling 'xmllint --xpath' to get bug instance counts"
newBugTotalCount=$(xmllint --xpath "count(/BugCollection/BugInstance)" "${fn}")
newBugBelowThresholdCount=$(xmllint --xpath "count(/BugCollection/BugInstance[@priority <= ${SPOTBUGS_PRIORITY_THRESHOLD} or @rank <= ${SPOTBUGS_RANK_THRESHOLD}])" "${fn}")
fi
belowThresholdCount=$((belowThresholdCount + newBugBelowThresholdCount))
totalCount=$((totalCount + newBugTotalCount))
if [ "${newBugBelowThresholdCount}" -gt "0" ]; then
echo "[ERROR] There are [${newBugBelowThresholdCount}] new bugs found below threshold in [${componentDir}]."
REPORT_FULL+=("{color:red}-1{color} There are [${newBugBelowThresholdCount}] new bugs found below threshold in [${componentDir}] that must be fixed.")
if [ "${newBugBelowThresholdCount}" -gt "${BUG_LIMIT_PER_PROJECT}" ]; then
REPORT+=("{color:red}-1{color} There are [${newBugBelowThresholdCount}] new bugs found below threshold in [${componentDir}] that must be fixed, listing only the first [${BUG_LIMIT_PER_PROJECT}] ones.")
else
REPORT+=("{color:red}-1{color} There are [${newBugBelowThresholdCount}] new bugs found below threshold in [${componentDir}] that must be fixed.")
fi
echo "[DEBUG] You can find the SpotBugs diff here (look for the red and orange ones): ${htmlFileName}"
REPORT+=("You can find the SpotBugs diff here (look for the red and orange ones): ${htmlFileName}")
REPORT_FULL+=("You can find the SpotBugs diff here (look for the red and orange ones): ${htmlFileName}")
REPORT_FULL+=("The most important SpotBugs errors are:")
if [ "${newBugBelowThresholdCount}" -gt "${BUG_LIMIT_PER_PROJECT}" ]; then
REPORT+=("The top [${BUG_LIMIT_PER_PROJECT}] most important SpotBugs errors are:")
else
REPORT+=("The most important SpotBugs errors are:")
fi
lineCount=0
while IFS= read -r line; do
REPORT_FULL+=( "${line}" );
if [ "${lineCount}" -lt "${BUG_LIMIT_PER_PROJECT}" ]; then
REPORT+=( "${line}" );
fi
lineCount=$((lineCount + 1))
done < <(echo cat "/BugCollection/BugInstance[@priority <= ${SPOTBUGS_PRIORITY_THRESHOLD} or @rank <= ${SPOTBUGS_RANK_THRESHOLD}]/SourceLine/Message/text() | /BugCollection/BugInstance[@priority <= 2 or @rank <= 9]/LongMessage/text()" \
| xmllint --shell "${fn}" | grep -v '\-\-\-\-\-\-\-' | grep -v '/ >' | sed -e 'N;s/\(.*\)\n\(.*\)/\2: \1/')
elif [ "${newBugTotalCount}" -gt "0" ]; then
echo "[WARN] There are [${newBugTotalCount}] new bugs found in [${componentDir}]."
REPORT+=("{color:orange}0{color} There are [${newBugTotalCount}] new bugs found in [${componentDir}] that would be nice to have fixed.")
REPORT_FULL+=("{color:orange}0{color} There are [${newBugTotalCount}] new bugs found in [${componentDir}] that would be nice to have fixed.")
echo "[DEBUG] You can find the SpotBugs diff here: ${htmlFileName}"
REPORT+=("You can find the SpotBugs diff here: ${htmlFileName}")
REPORT_FULL+=("You can find the SpotBugs diff here: ${htmlFileName}")
else
echo "[DEBUG] There are no new bugs found in [${componentDir}]."
REPORT+=("{color:green}+1{color} There are no new bugs found in [${componentDir}].")
REPORT_FULL+=("{color:green}+1{color} There are no new bugs found in [${componentDir}].")
fi
done < <(find "${DIFF_DIR}" -name ${FINDBUGS_DIFF_XML} -print0)
if [ "${belowThresholdCount}" -gt "0" ]; then
echo "[ERROR] There are [${belowThresholdCount}] new bugs found below threshold in total that must be fixed."
summary_both "{color:red}-1{color} There are [${belowThresholdCount}] new bugs found below threshold in total that must be fixed."
elif [ "${totalCount}" -gt "0" ]; then
echo "[WARN] There are [${totalCount}] new bugs found in total that would be nice to have fixed."
summary_both "{color:orange}0{color} There are [${totalCount}] new bugs found in total that would be nice to have fixed."
else
echo "[INFO] There are no new bugs found totally]."
summary_both "{color:green}+1{color} There are no new bugs found in total."
fi
for line in "${REPORT[@]}" ; do
summary ". ${line}"
done
for line in "${REPORT_FULL[@]}" ; do
summary_full ". ${line}"
done
echo "[TRACE] SpotBugs diffs checked and reports created"
}
summary() {
LINE=$1
echo "${LINE}" >> "${SUMMARYFILE}"
}
summary_full() {
LINE=$1
echo "${LINE}" >> "${SUMMARYFILE_FULL}"
}
summary_both() {
LINE=$1
echo "${LINE}" >> "${SUMMARYFILE}"
echo "${LINE}" >> "${SUMMARYFILE_FULL}"
}
parse_args "$@"
case ${OP} in
pre)
verify_and_save_spotbugs_output "${TEMPDIR}/${TASKNAME}/pre" HEAD clean
;;
post)
verify_and_save_spotbugs_output "${TEMPDIR}/${TASKNAME}/post" PATCH patch
;;
report)
create_missing_spotbugs_files "${TEMPDIR}/${TASKNAME}/pre" "${TEMPDIR}/${TASKNAME}/post"
download_and_check_findbugs_diff_jar "${TEMPDIR}/${TASKNAME}/diff"
perform_spotbugs_diffs "${TEMPDIR}/${TASKNAME}/pre" "${TEMPDIR}/${TASKNAME}/post" "${TEMPDIR}/${TASKNAME}/diff"
check_spotbugs_diffs_and_create_reports "${TEMPDIR}/${TASKNAME}/diff"
echo "[TRACE] Summary file size is $(wc -c "${SUMMARYFILE}" | awk '{print $1}') bytes"
echo "[TRACE] Full summary file size is $(wc -c "${SUMMARYFILE_FULL}" | awk '{print $1}') bytes"
;;
esac
cleanup_and_exit 0