Last active
July 11, 2019 19:34
-
-
Save Edudjr/79a2379842357c33709aecf040d9ae77 to your computer and use it in GitHub Desktop.
Script used for running Swift projects in Sonarqube. There is just a small change to the original: includedCommandLineFlags=" --include .*/${currentDirectory}/${word}/*"
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# backelite-sonar-swift-plugin - Enables analysis of Swift and Objective-C projects into SonarQube. | |
# Copyright © 2015 Backelite (${email}) | |
# | |
# This program is free software: you can redistribute it and/or modify | |
# it under the terms of the GNU Lesser General Public License as published by | |
# the Free Software Foundation, either version 3 of the License, or | |
# (at your option) any later version. | |
# | |
# This program is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU Lesser General Public License for more details. | |
# | |
# You should have received a copy of the GNU Lesser General Public License | |
# along with this program. If not, see <http://www.gnu.org/licenses/>. | |
# | |
## INSTALLATION: Copy this script somewhere in your PATH | |
## USAGE: ./run-sonar-swift.sh | |
## DEBUG: ./run-sonar-swift.sh -v | |
## WARNING: edit your project parameters in sonar-project.properties rather than modifying this script | |
# | |
# Global parameters | |
SLATHER_CMD=slather | |
SWIFTLINT_CMD=swiftlint | |
TAILOR_CMD=tailor | |
XCPRETTY_CMD=xcpretty | |
LIZARD_CMD=lizard | |
XCODEBUILD_CMD=xcodebuild | |
trap "echo 'Script interrupted by Ctrl+C'; stopProgress; exit 1" SIGHUP SIGINT SIGTERM | |
function startProgress() { | |
while true | |
do | |
echo -n "." | |
sleep 5 | |
done | |
} | |
function stopProgress() { | |
if [ "$vflag" = "" -a "$nflag" = "" ]; then | |
kill $PROGRESS_PID &>/dev/null | |
fi | |
} | |
function testIsInstalled() { | |
hash $1 2>/dev/null | |
if [ $? -eq 1 ]; then | |
echo >&2 "ERROR - $1 is not installed or not in your PATH"; exit 1; | |
fi | |
} | |
function readParameter() { | |
variable=$1 | |
shift | |
parameter=$1 | |
shift | |
eval $variable="\"$(sed '/^\#/d' sonar-project.properties | grep $parameter | tail -n 1 | cut -d '=' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')\"" | |
} | |
# Run a set of commands with logging and error handling | |
function runCommand() { | |
# 1st arg: redirect stdout | |
# 2nd arg: command to run | |
# 3rd..nth arg: args | |
redirect=$1 | |
shift | |
command=$1 | |
shift | |
if [ "$nflag" = "on" ]; then | |
# don't execute command, just echo it | |
echo | |
if [ "$redirect" = "/dev/stdout" ]; then | |
if [ "$vflag" = "on" ]; then | |
echo "+" $command "$@" | |
else | |
echo "+" $command "$@" "> /dev/null" | |
fi | |
elif [ "$redirect" != "no" ]; then | |
echo "+" $command "$@" "> $redirect" | |
else | |
echo "+" $command "$@" | |
fi | |
elif [ "$vflag" = "on" ]; then | |
echo | |
if [ "$redirect" = "/dev/stdout" ]; then | |
set -x #echo on | |
$command "$@" | |
returnValue=$? | |
set +x #echo off | |
elif [ "$redirect" != "no" ]; then | |
set -x #echo on | |
$command "$@" > $redirect | |
returnValue=$? | |
set +x #echo off | |
else | |
set -x #echo on | |
$command "$@" | |
returnValue=$? | |
set +x #echo off | |
fi | |
if [[ $returnValue != 0 && $returnValue != 5 ]] ; then | |
stopProgress | |
echo "ERROR - Command '$command $@' failed with error code: $returnValue" | |
exit $returnValue | |
fi | |
else | |
if [ "$redirect" = "/dev/stdout" ]; then | |
$command "$@" > /dev/null | |
elif [ "$redirect" != "no" ]; then | |
$command "$@" > $redirect | |
else | |
$command "$@" | |
fi | |
returnValue=$? | |
if [[ $returnValue != 0 && $returnValue != 5 ]] ; then | |
stopProgress | |
echo "ERROR - Command '$command $@' failed with error code: $returnValue" | |
exit $returnValue | |
fi | |
echo | |
fi | |
} | |
## COMMAND LINE OPTIONS | |
vflag="" | |
nflag="" | |
unittests="on" | |
swiftlint="on" | |
tailor="on" | |
lizard="on" | |
oclint="on" | |
fauxpas="on" | |
sonarscanner="" | |
while [ $# -gt 0 ] | |
do | |
case "$1" in | |
-v) vflag=on;; | |
-n) nflag=on;; | |
-nounittests) unittests="";; | |
-noswiftlint) swiftlint="";; | |
-notailor) tailor="";; | |
-usesonarscanner) sonarscanner="on";; | |
--) shift; break;; | |
-*) | |
echo >&2 "Usage: $0 [-v]" | |
exit 1;; | |
*) break;; # terminate while loop | |
esac | |
shift | |
done | |
# Usage OK | |
echo "Running run-sonar-swift.sh..." | |
## CHECK PREREQUISITES | |
# sonar-project.properties in current directory | |
if [ ! -f sonar-project.properties ]; then | |
echo >&2 "ERROR - No sonar-project.properties in current directory"; exit 1; | |
fi | |
## READ PARAMETERS from sonar-project.properties | |
#.xcodeproj filename | |
projectFile=''; readParameter projectFile 'sonar.swift.project' | |
workspaceFile=''; readParameter workspaceFile 'sonar.swift.workspace' | |
# Count projects | |
if [[ ! -z "$projectFile" ]]; then | |
projectCount=$(echo $projectFile | sed -n 1'p' | tr ',' '\n' | wc -l | tr -d '[[:space:]]') | |
if [ "$vflag" = "on" ]; then | |
echo "Project count is [$projectCount]" | |
fi | |
fi | |
# Source directories for .swift files | |
srcDirs=''; readParameter srcDirs 'sonar.sources' | |
# The name of your application scheme in Xcode | |
appScheme=''; readParameter appScheme 'sonar.swift.appScheme' | |
# The app configuration to use for the build | |
appConfiguration=''; readParameter appConfiguration 'sonar.swift.appConfiguration' | |
# The name of your test scheme in Xcode | |
testScheme=''; readParameter testScheme 'sonar.swift.testScheme' | |
# The name of your binary file (application) | |
binaryName=''; readParameter binaryName 'sonar.swift.appName' | |
# Get the path of plist file | |
plistFile=`xcodebuild -showBuildSettings -project "${projectFile}" | grep -i 'PRODUCT_SETTINGS_PATH' -m 1 | sed 's/[ ]*PRODUCT_SETTINGS_PATH = //'` | |
# Number version from plist if no sonar.projectVersion | |
numVerionFromPlist=`defaults read ${plistFile} CFBundleShortVersionString` | |
# Read destination simulator | |
destinationSimulator=''; readParameter destinationSimulator 'sonar.swift.simulator' | |
# Read tailor configuration | |
tailorConfiguration=''; readParameter tailorConfiguration 'sonar.swift.tailor.config' | |
# The file patterns to exclude from coverage report | |
excludedPathsFromCoverage=''; readParameter excludedPathsFromCoverage 'sonar.swift.excludedPathsFromCoverage' | |
# Check for mandatory parameters | |
if [ -z "$projectFile" -o "$projectFile" = " " ] && [ -z "$workspaceFile" -o "$workspaceFile" = " " ]; then | |
echo >&2 "ERROR - sonar.swift.project or/and sonar.swift.workspace parameter is missing in sonar-project.properties. You must specify which projects (comma-separated list) are application code or which workspace and project to use." | |
exit 1 | |
elif [ ! -z "$workspaceFile" ] && [ -z "$projectFile" ]; then | |
echo >&2 "ERROR - sonar.swift.workspace parameter is present in sonar-project.properties but sonar.swift.project and is not. You must specify which projects (comma-separated list) are application code or which workspace and project to use." | |
exit 1 | |
fi | |
if [ -z "$srcDirs" -o "$srcDirs" = " " ]; then | |
echo >&2 "ERROR - sonar.sources parameter is missing in sonar-project.properties. You must specify which directories contain your .swift source files (comma-separated list)." | |
exit 1 | |
fi | |
if [ -z "$appScheme" -o "$appScheme" = " " ]; then | |
echo >&2 "ERROR - sonar.swift.appScheme parameter is missing in sonar-project.properties. You must specify which scheme is used to build your application." | |
exit 1 | |
fi | |
if [ "$unittests" = "on" ]; then | |
if [ -z "$destinationSimulator" -o "$destinationSimulator" = " " ]; then | |
echo >&2 "ERROR - sonar.swift.simulator parameter is missing in sonar-project.properties. You must specify which simulator to use." | |
exit 1 | |
fi | |
fi | |
# if the appConfiguration is not specified then set to Debug | |
if [ -z "$appConfiguration" -o "$appConfiguration" = " " ]; then | |
appConfiguration="Debug" | |
fi | |
if [ "$vflag" = "on" ]; then | |
echo "Xcode project file is: $projectFile" | |
echo "Xcode workspace file is: $workspaceFile" | |
echo "Xcode application scheme is: $appScheme" | |
echo "Number version from plist is: $numVerionFromPlist" | |
if [ -n "$unittests" ]; then | |
echo "Destination simulator is: $destinationSimulator" | |
echo "Excluded paths from coverage are: $excludedPathsFromCoverage" | |
else | |
echo "Unit surefire are disabled" | |
fi | |
fi | |
## SCRIPT | |
# Start progress indicator in the background | |
if [ "$vflag" = "" -a "$nflag" = "" ]; then | |
startProgress & | |
# Save PID | |
PROGRESS_PID=$! | |
fi | |
# Create sonar-reports/ for reports output | |
if [ "$vflag" = "on" ]; then | |
echo 'Creating directory sonar-reports/' | |
fi | |
rm -rf sonar-reports | |
mkdir sonar-reports | |
# Extracting project information needed later | |
echo -n 'Extracting Xcode project information' | |
if [[ "$workspaceFile" != "" ]] ; then | |
buildCmdPrefix="-workspace $workspaceFile" | |
else | |
buildCmdPrefix="-project $projectFile" | |
fi | |
buildCmd=($XCODEBUILD_CMD clean build $buildCmdPrefix -scheme $appScheme) | |
if [[ ! -z "$destinationSimulator" ]]; then | |
buildCmd+=(-destination "$destinationSimulator" -destination-timeout 360 COMPILER_INDEX_STORE_ENABLE=NO) | |
fi | |
runCommand xcodebuild.log "${buildCmd[@]}" | |
#oclint-xcodebuild # Transform the xcodebuild.log file into a compile_command.json file | |
cat xcodebuild.log | $XCPRETTY_CMD -r json-compilation-database -o compile_commands.json | |
# Objective-C code detection | |
hasObjC="no" | |
compileCmdFile=compile_commands.json | |
minimumSize=3 | |
actualSize=$(stat -f%z "$compileCmdFile") | |
echo "actual = $actualSize, min = $minimumSize" | |
if [ $actualSize -ge $minimumSize ]; then | |
hasObjC="yes" | |
fi | |
# Unit surefire and coverage | |
if [ "$unittests" = "on" ]; then | |
# Put default xml files with no surefire and no coverage... | |
echo "<?xml version='1.0' encoding='UTF-8' standalone='yes'?><testsuites name='AllTestUnits'></testsuites>" > sonar-reports/TEST-report.xml | |
echo "<?xml version='1.0' ?><!DOCTYPE coverage SYSTEM 'http://cobertura.sourceforge.net/xml/coverage-03.dtd'><coverage><sources></sources><packages></packages></coverage>" > sonar-reports/coverage-swift.xml | |
echo -n 'Running surefire' | |
buildCmd=($XCODEBUILD_CMD clean build test) | |
if [[ ! -z "$workspaceFile" ]]; then | |
buildCmd+=(-workspace "$workspaceFile") | |
elif [[ ! -z "$projectFile" ]]; then | |
buildCmd+=(-project "$projectFile") | |
fi | |
buildCmd+=( -scheme "$appScheme" -configuration "$appConfiguration" -enableCodeCoverage YES) | |
if [[ ! -z "$destinationSimulator" ]]; then | |
buildCmd+=(-destination "$destinationSimulator" -destination-timeout 60) | |
fi | |
runCommand sonar-reports/xcodebuild.log "${buildCmd[@]}" | |
cat sonar-reports/xcodebuild.log | $XCPRETTY_CMD -t --report junit | |
mv build/reports/junit.xml sonar-reports/TEST-report.xml | |
echo '\nComputing coverage report\n' | |
# Build the --exclude flags | |
excludedCommandLineFlags="" | |
if [ ! -z "$excludedPathsFromCoverage" -a "$excludedPathsFromCoverage" != " " ]; then | |
echo $excludedPathsFromCoverage | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh2 | |
while read word; do | |
excludedCommandLineFlags+=" -i $word" | |
done < tmpFileRunSonarSh2 | |
rm -rf tmpFileRunSonarSh2 | |
fi | |
if [ "$vflag" = "on" ]; then | |
echo "Command line exclusion flags for slather is:$excludedCommandLineFlags" | |
fi | |
firstProject=$(echo $projectFile | sed -n 1'p' | tr ',' '\n' | head -n 1) | |
slatherCmd=($SLATHER_CMD coverage) | |
if [[ ! -z "$binaryName" ]]; then | |
slatherCmd+=( --binary-basename "$binaryName") | |
fi | |
slatherCmd+=( --input-format profdata $excludedCommandLineFlags --cobertura-xml --output-directory sonar-reports) | |
if [[ ! -z "$workspaceFile" ]]; then | |
slatherCmd+=( --workspace "$workspaceFile") | |
fi | |
slatherCmd+=( --scheme "$appScheme" "$firstProject") | |
echo "${slatherCmd[@]}" | |
runCommand /dev/stdout "${slatherCmd[@]}" | |
mv sonar-reports/cobertura.xml sonar-reports/coverage-swift.xml | |
fi | |
# SwiftLint | |
if [ "$swiftlint" = "on" ]; then | |
if hash $SWIFTLINT_CMD 2>/dev/null; then | |
echo -n 'Running SwiftLint...' | |
# Build the --include flags | |
currentDirectory=${PWD##*/} | |
echo "$srcDirs" | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh | |
while read word; do | |
# Run SwiftLint command | |
$SWIFTLINT_CMD lint --path "$word" > sonar-reports/"$(echo $word | sed 's/\//_/g')"-swiftlint.txt | |
done < tmpFileRunSonarSh | |
rm -rf tmpFileRunSonarSh | |
else | |
echo "Skipping SwiftLint (not installed!)" | |
fi | |
else | |
echo 'Skipping SwiftLint (test purposes only!)' | |
fi | |
# Tailor | |
if [ "$tailor" = "on" ]; then | |
if hash $TAILOR_CMD 2>/dev/null; then | |
echo -n 'Running Tailor...' | |
# Build the --include flags | |
currentDirectory=${PWD##*/} | |
echo "$srcDirs" | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh | |
while read word; do | |
# Run tailor command | |
$TAILOR_CMD $tailorConfiguration "$word" > sonar-reports/"$(echo $word | sed 's/\//_/g')"-tailor.txt | |
done < tmpFileRunSonarSh | |
rm -rf tmpFileRunSonarSh | |
else | |
echo "Skipping Tailor (not installed!)" | |
fi | |
else | |
echo 'Skipping Tailor!' | |
fi | |
if [ "$oclint" = "on" ] && [ "$hasObjC" = "yes" ]; then | |
echo -n 'Running OCLint...' | |
# Options | |
maxPriority=10000 | |
longLineThreshold=250 | |
# Build the --include flags | |
currentDirectory=${PWD##*/} | |
echo "$srcDirs" | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh | |
while read word; do | |
includedCommandLineFlags=" --include .*/${currentDirectory}/${word}/*" | |
if [ "$vflag" = "on" ]; then | |
echo | |
echo -n "Path included in oclint analysis is:$includedCommandLineFlags" | |
fi | |
# Run OCLint with the right set of compiler options | |
runCommand no oclint-json-compilation-database -v $includedCommandLineFlags -- -rc LONG_LINE=$longLineThreshold -max-priority-1 $maxPriority -max-priority-2 $maxPriority -max-priority-3 $maxPriority -report-type pmd -o sonar-reports/$(echo $word | sed 's/\//_/g')-oclint.xml | |
done < tmpFileRunSonarSh | |
rm -rf tmpFileRunSonarSh | |
else | |
echo 'Skipping OCLint (test purposes only!)' | |
fi | |
#FauxPas | |
if [ "$fauxpas" = "on" ] && [ "$hasObjC" = "yes" ]; then | |
hash fauxpas 2>/dev/null | |
if [ $? -eq 0 ]; then | |
echo -n 'Running FauxPas...' | |
if [ "$projectCount" = "1" ] | |
then | |
fauxpas -o json check $projectFile --workspace $workspaceFile --scheme $appScheme > sonar-reports/fauxpas.json | |
else | |
echo $projectFile | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh | |
while read projectName; do | |
$XCODEBUILD_CMD -list -project $projectName | sed -n '/Schemes/,$p' | while read scheme | |
do | |
if [ "$scheme" = "" ] | |
then | |
exit | |
fi | |
if [ "$scheme" == "${scheme/Schemes/}" ] | |
then | |
if [ "$scheme" != "$testScheme" ] | |
then | |
projectBaseDir=$(dirname $projectName) | |
workspaceRelativePath=$(python -c "import os.path; print os.path.relpath('$workspaceFile', '$projectBaseDir')") | |
fauxpas -o json check $projectName --workspace $workspaceRelativePath --scheme $scheme > sonar-reports/$(basename $projectName .xcodeproj)-$scheme-fauxpas.json | |
fi | |
fi | |
done | |
done < tmpFileRunSonarSh | |
rm -rf tmpFileRunSonarSh | |
fi | |
else | |
echo 'Skipping FauxPas (not installed)' | |
fi | |
else | |
echo 'Skipping FauxPas' | |
fi | |
# Lizard Complexity | |
if [ "$lizard" = "on" ]; then | |
if hash $LIZARD_CMD 2>/dev/null; then | |
echo -n 'Running Lizard...' | |
$LIZARD_CMD --xml "$srcDirs" > sonar-reports/lizard-report.xml | |
else | |
echo 'Skipping Lizard (not installed!)' | |
fi | |
else | |
echo 'Skipping Lizard (test purposes only!)' | |
fi | |
# The project version from properties file | |
numVersionSonarRunner=''; readParameter numVersionSonarRunner 'sonar.projectVersion' | |
if [ -z "$numVersionSonarRunner" -o "$numVersionSonarRunner" = " " ]; then | |
numVersionSonarRunner=" --define sonar.projectVersion=$numVerionFromPlist" | |
else | |
#if we have version number in properties file, we don't overide numVersion for sonar-runner/sonar-scanner command | |
numVersionSonarRunner=''; | |
fi | |
# SonarQube | |
if [ "$sonarscanner" = "on" ]; then | |
echo -n 'Running SonarQube using SonarQube Scanner' | |
if hash /dev/stdout sonar-scanner 2>/dev/null; then | |
runCommand /dev/stdout sonar-scanner $numVersionSonarRunner | |
else | |
echo 'Skipping sonar-scanner (not installed!)' | |
fi | |
else | |
echo -n 'Running SonarQube using SonarQube Runner' | |
if hash /dev/stdout sonar-runner 2>/dev/null; then | |
runCommand /dev/stdout sonar-runner $numVersionSonarRunner | |
else | |
runCommand /dev/stdout sonar-scanner $numVersionSonarRunner | |
fi | |
fi | |
# Kill progress indicator | |
stopProgress | |
exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment