1#!/usr/bin/env bash
2# SPDX-License-Identifier: LGPL-2.1-or-later
3
4# The official unmodified version of the script can be found at
5# https://scan.coverity.com/scripts/travisci_build_coverity_scan.sh
6
7set -e
8
9# Declare build command
10COVERITY_SCAN_BUILD_COMMAND="ninja -C cov-build"
11
12# Environment check
13# Use default values if not set
14SCAN_URL=${SCAN_URL:="https://scan.coverity.com"}
15TOOL_BASE=${TOOL_BASE:="/tmp/coverity-scan-analysis"}
16UPLOAD_URL=${UPLOAD_URL:="https://scan.coverity.com/builds"}
17
18# These must be set by environment
19echo -e "\033[33;1mNote: COVERITY_SCAN_PROJECT_NAME and COVERITY_SCAN_TOKEN are available on Project Settings page on scan.coverity.com\033[0m"
20[ -z "$COVERITY_SCAN_PROJECT_NAME" ] && echo "ERROR: COVERITY_SCAN_PROJECT_NAME must be set" && exit 1
21[ -z "$COVERITY_SCAN_NOTIFICATION_EMAIL" ] && echo "ERROR: COVERITY_SCAN_NOTIFICATION_EMAIL must be set" && exit 1
22[ -z "$COVERITY_SCAN_BRANCH_PATTERN" ] && echo "ERROR: COVERITY_SCAN_BRANCH_PATTERN must be set" && exit 1
23[ -z "$COVERITY_SCAN_BUILD_COMMAND" ] && echo "ERROR: COVERITY_SCAN_BUILD_COMMAND must be set" && exit 1
24[ -z "$COVERITY_SCAN_TOKEN" ] && echo "ERROR: COVERITY_SCAN_TOKEN must be set" && exit 1
25
26# Verify this branch should run
27if [[ "${CURRENT_REF^^}" =~ "${COVERITY_SCAN_BRANCH_PATTERN^^}" ]]; then
28    echo -e "\033[33;1mCoverity Scan configured to run on branch ${CURRENT_REF}\033[0m"
29else
30    echo -e "\033[33;1mCoverity Scan NOT configured to run on branch ${CURRENT_REF}\033[0m"
31    exit 1
32fi
33
34# Verify upload is permitted
35AUTH_RES=`curl -s --form project="$COVERITY_SCAN_PROJECT_NAME" --form token="$COVERITY_SCAN_TOKEN" $SCAN_URL/api/upload_permitted`
36if [ "$AUTH_RES" = "Access denied" ]; then
37    echo -e "\033[33;1mCoverity Scan API access denied. Check COVERITY_SCAN_PROJECT_NAME and COVERITY_SCAN_TOKEN.\033[0m"
38    exit 1
39else
40    AUTH=`echo $AUTH_RES | jq .upload_permitted`
41    if [ "$AUTH" = "true" ]; then
42        echo -e "\033[33;1mCoverity Scan analysis authorized per quota.\033[0m"
43    else
44        WHEN=`echo $AUTH_RES | jq .next_upload_permitted_at`
45        echo -e "\033[33;1mCoverity Scan analysis NOT authorized until $WHEN.\033[0m"
46        exit 1
47    fi
48fi
49
50TOOL_DIR=`find $TOOL_BASE -type d -name 'cov-analysis*'`
51export PATH="$TOOL_DIR/bin:$PATH"
52
53# Disable CCACHE for cov-build to compilation units correctly
54export CCACHE_DISABLE=1
55
56# FUNCTION DEFINITIONS
57# --------------------
58_help()
59{
60    # displays help and exits
61    cat <<-EOF
62		USAGE: $0 [CMD] [OPTIONS]
63
64		CMD
65		  build   Issue Coverity build
66		  upload  Upload coverity archive for analysis
67              Note: By default, archive is created from default results directory.
68                    To provide custom archive or results directory, see --result-dir
69                    and --tar options below.
70
71		OPTIONS
72		  -h,--help     Display this menu and exits
73
74		  Applicable to build command
75		  ---------------------------
76		  -o,--out-dir  Specify Coverity intermediate directory (defaults to 'cov-int')
77		  -t,--tar      bool, archive the output to .tgz file (defaults to false)
78
79		  Applicable to upload command
80		  ----------------------------
81		  -d, --result-dir   Specify result directory if different from default ('cov-int')
82		  -t, --tar ARCHIVE  Use custom .tgz archive instead of intermediate directory or pre-archived .tgz
83                         (by default 'analysis-result.tgz'
84	EOF
85    return;
86}
87
88_pack()
89{
90    RESULTS_ARCHIVE=${RESULTS_ARCHIVE:-'analysis-results.tgz'}
91
92    echo -e "\033[33;1mTarring Coverity Scan Analysis results...\033[0m"
93    tar czf $RESULTS_ARCHIVE $RESULTS_DIR
94    SHA=`git rev-parse --short HEAD`
95
96    PACKED=true
97}
98
99
100_build()
101{
102    echo -e "\033[33;1mRunning Coverity Scan Analysis Tool...\033[0m"
103    local _cov_build_options=""
104    #local _cov_build_options="--return-emit-failures 8 --parse-error-threshold 85"
105    eval "${COVERITY_SCAN_BUILD_COMMAND_PREPEND}"
106    COVERITY_UNSUPPORTED=1 cov-build --dir $RESULTS_DIR $_cov_build_options sh -c "$COVERITY_SCAN_BUILD_COMMAND"
107    cov-import-scm --dir $RESULTS_DIR --scm git --log $RESULTS_DIR/scm_log.txt
108
109    if [ $? != 0 ]; then
110	echo -e "\033[33;1mCoverity Scan Build failed: $TEXT.\033[0m"
111	return 1
112    fi
113
114    [ -z $TAR ] || [ $TAR = false ] && return 0
115
116    if [ "$TAR" = true ]; then
117	_pack
118    fi
119}
120
121
122_upload()
123{
124    # pack results
125    [ -z $PACKED ] || [ $PACKED = false ] && _pack
126
127    # Upload results
128    echo -e "\033[33;1mUploading Coverity Scan Analysis results...\033[0m"
129    response=$(curl \
130	           --silent --write-out "\n%{http_code}\n" \
131	           --form project=$COVERITY_SCAN_PROJECT_NAME \
132	           --form token=$COVERITY_SCAN_TOKEN \
133	           --form email=$COVERITY_SCAN_NOTIFICATION_EMAIL \
134	           --form file=@$RESULTS_ARCHIVE \
135	           --form version=$SHA \
136	           --form description="Travis CI build" \
137	           $UPLOAD_URL)
138    printf "\033[33;1mThe response is\033[0m\n%s\n" "$response"
139    status_code=$(echo "$response" | sed -n '$p')
140    # Coverity Scan used to respond with 201 on successfully receiving analysis results.
141    # Now for some reason it sends 200 and may change back in the foreseeable future.
142    # See https://github.com/pmem/pmdk/commit/7b103fd2dd54b2e5974f71fb65c81ab3713c12c5
143    if [ "$status_code" != "200" ]; then
144	TEXT=$(echo "$response" | sed '$d')
145	echo -e "\033[33;1mCoverity Scan upload failed: $TEXT.\033[0m"
146	exit 1
147    fi
148
149    echo -e "\n\033[33;1mCoverity Scan Analysis completed successfully.\033[0m"
150    exit 0
151}
152
153# PARSE COMMAND LINE OPTIONS
154# --------------------------
155
156case $1 in
157    -h|--help)
158	_help
159	exit 0
160	;;
161    build)
162	CMD='build'
163	TEMP=`getopt -o ho:t --long help,out-dir:,tar -n '$0' -- "$@"`
164	_ec=$?
165	[[ $_ec -gt 0 ]] && _help && exit $_ec
166	shift
167	;;
168    upload)
169	CMD='upload'
170	TEMP=`getopt -o hd:t: --long help,result-dir:tar: -n '$0' -- "$@"`
171	_ec=$?
172	[[ $_ec -gt 0 ]] && _help && exit $_ec
173	shift
174	;;
175    *)
176	_help && exit 1 ;;
177esac
178
179RESULTS_DIR='cov-int'
180
181eval set -- "$TEMP"
182if [ $? != 0 ] ; then exit 1 ; fi
183
184# extract options and their arguments into variables.
185if [[ $CMD == 'build' ]]; then
186    TAR=false
187    while true ; do
188	case $1 in
189	    -h|--help)
190		_help
191		exit 0
192		;;
193	    -o|--out-dir)
194		RESULTS_DIR="$2"
195		shift 2
196		;;
197	    -t|--tar)
198		TAR=true
199		shift
200		;;
201	    --) _build; shift ; break ;;
202	    *) echo "Internal error" ; _help && exit 6 ;;
203	esac
204    done
205
206elif [[ $CMD == 'upload' ]]; then
207    while true ; do
208	case $1 in
209	    -h|--help)
210		_help
211		exit 0
212		;;
213	    -d|--result-dir)
214		CHANGE_DEFAULT_DIR=true
215		RESULTS_DIR="$2"
216		shift 2
217		;;
218	    -t|--tar)
219		RESULTS_ARCHIVE="$2"
220		[ -z $CHANGE_DEFAULT_DIR ] || [ $CHANGE_DEFAULT_DIR = false ] && PACKED=true
221		shift 2
222		;;
223	    --) _upload; shift ; break ;;
224	    *) echo "Internal error" ; _help && exit 6 ;;
225	esac
226    done
227
228fi
229