Skip to content

Instantly share code, notes, and snippets.

@Edudjr
Last active July 11, 2019 19:34
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Edudjr/79a2379842357c33709aecf040d9ae77 to your computer and use it in GitHub Desktop.
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}/*"
#!/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