@@ -124,6 +124,7 @@ export -f check_command
124
124
check_command grep ;
125
125
check_command python3 ;
126
126
check_command git ;
127
+ check_command sed ;
127
128
check_command tee ;
128
129
check_command pip ;
129
130
# check_command pip-audit ; # optional
@@ -166,13 +167,23 @@ handle_signals
166
167
167
168
# lazy defined variables should be defined now that this is the only script instance.
168
169
170
+ # set the script-file to check_pip
171
+ SCRIPT_FILE=" tests/check_pip"
169
172
# Set pip-audit options
170
173
AUDIT_OPTIONS=" --progress-spinner off --desc on --requirement"
171
174
# List of Allowed Licenses delimited by semicolon ;
172
- ALLOW_LICENSES=" Public Domain;Apache Software License;MIT License;BSD License;Python Software Foundation License"
175
+ ALLOW_LICENSES=" Public Domain;Apache Software License;MIT License;BSD License;Python Software Foundation License;The Unlicense (Unlicense);Mozilla Public License 2.0 (MPL 2.0); "
173
176
# Set pip-licenses options
174
177
LICENSE_OPTIONS=" --from=mixed"
175
-
178
+ # Set pip options
179
+ PIP_COMMON_FLAGS=" --require-virtualenv --use-pep517 --exists-action s --upgrade --upgrade-strategy only-if-needed --quiet"
180
+ # Set Env and OS specific pip options
181
+ if [[ $( \u name -s ) == " *arwin" ]] ; then
182
+ PIP_ENV_FLAGS=" --break-system-packages"
183
+ LICENSE_OPTIONS=" --python python3 ${LICENSE_OPTIONS} --ignore-packages certifi"
184
+ else
185
+ PIP_ENV_FLAGS=" "
186
+ fi ;
176
187
# Enable auto-fix if '--fix' argument is provided
177
188
if [[ " $1 " == " --fix" ]]; then
178
189
AUDIT_OPTIONS=" --fix --strict ${AUDIT_OPTIONS} "
@@ -182,62 +193,117 @@ fi
182
193
# lazy defined functions should be defined now that this is the only script instance.
183
194
184
195
function report_summary() {
196
+ printf " ::group::%s\n" " Results" ;
185
197
# Improved reporting based on EXIT_CODE
186
198
case " ${EXIT_CODE} " in
187
- 0) printf " %s\n" " OK: Found no detected requirements errors." ;;
188
- 1) printf " %s\n" " FAIL: General failure during script execution." >&2 ;;
189
- 3) printf " %s\n" " FAIL: Gathering repostory's requirements failed." >&2 ;; # git ls-tree command failed
190
- 4) printf " %s\n" " FAIL: pip-audit detected security vulnerabilities." >&2 ;;
191
- 5) printf " %s\n" " FAIL: pip-licenses detected license issues." >&2 ;;
192
- 6) printf " %s\n" " FAIL: pip install failed." >&2 ;;
193
- 126) printf " %s\n" " SKIP: Unable to continue script execution." >&2 ;;
194
- * ) printf " %s\n" " FAIL: Detected requirements errors." >&2 ;;
199
+ 0) printf " ::notice title=OK:: %s\n" " OK: Found no detected requirements errors." ;;
200
+ 1) printf " ::error file= ${SCRIPT_FILE} ,line= ${BASH_LINENO :- 0} ,title=CHECK-PIP:: %s\n" " FAIL: General failure during script execution." >&2 ;;
201
+ 3) printf " ::error file= ${SCRIPT_FILE} ,line= ${BASH_LINENO :- 0} ,title=CONFIGURATION:: %s\n" " FAIL: Gathering repostory's requirements failed." >&2 ;; # git ls-tree command failed
202
+ 4) printf " ::error file= ${SCRIPT_FILE} ,line= ${BASH_LINENO :- 0} ,title=SECURITY:: %s\n" " FAIL: pip-audit detected security vulnerabilities." >&2 ;;
203
+ 5) printf " ::error file= ${SCRIPT_FILE} ,line= ${BASH_LINENO :- 0} ,title=LICENSE:: %s\n" " FAIL: pip-licenses detected license issues." >&2 ;;
204
+ 6) printf " ::error file= ${SCRIPT_FILE} ,line= ${BASH_LINENO :- 0} ,title=INSTALL:: %s\n" " FAIL: pip install failed." >&2 ;;
205
+ 126) printf " ::warning file= ${SCRIPT_FILE} ,line= ${BASH_LINENO :- 0} ,title=SKIPPED:: %s\n" " SKIP: Unable to continue script execution." >&2 ;;
206
+ * ) printf " ::error file= ${SCRIPT_FILE} ,line= ${BASH_LINENO :- 0} ,title=FAILED:: %s\n" " FAIL: Detected requirements errors." >&2 ;;
195
207
esac
208
+ printf " ::endgroup::\n" ;
196
209
}
197
210
198
211
function navigate_dirs_by_git() {
199
212
if _TEST_ROOT_DIR=$( git rev-parse --show-superproject-working-tree 2> /dev/null) ; then
200
213
if [ -z " ${_TEST_ROOT_DIR} " ]; then
201
214
_TEST_ROOT_DIR=$( git rev-parse --show-toplevel 2> /dev/null)
202
215
fi
216
+ printf " ::debug::%s\n" " Found ${_TEST_ROOT_DIR} ..." ;
203
217
else
204
- printf " \t %s\n" " FAIL: missing valid repository or source structure" >&2
218
+ printf " ::error file= ${SCRIPT_FILE} ,line= ${BASH_LINENO :- 0} ,title= ${FUNCNAME :- $0 } :: %s\n" " FAIL: missing valid repository or source structure" >&2
205
219
EXIT_CODE=40
206
220
fi
207
221
}
208
222
223
+ function check_license_when_given_req() {
224
+ local SUB_CODE=${EXIT_CODE-0}
225
+ umask 007
226
+ printf " ::debug::%s\n" " need venv ..." ;
227
+ # Create a temporary directory for the virtual environment
228
+ temp_dir=$( mktemp -d)
229
+
230
+ # Enter the temporary directory
231
+ cd " $temp_dir "
232
+
233
+ # Create a virtual environment using venv
234
+ python3 -m venv venv
235
+
236
+ # Activate the virtual environment
237
+ source venv/bin/activate
238
+
239
+ umask 037
240
+ # 2>&1 >/dev/null
241
+ python3 -m pip install $PIP_COMMON_FLAGS $PIP_ENV_FLAGS " pip-licenses>=5.0" || SUB_CODE=6 ;
242
+ wait ;
243
+ printf " ::debug::%s\n" " venv setup ... (${SUB_CODE} )" ;
244
+ # Install the given Python modules using pip
245
+ for module in $@ ; do
246
+ printf " ::debug::%s\n" " Checking license from package '$module ' ..." ;
247
+ REQ_SPEC=$( grep -F " $module " <( cat < " ${_TEST_ROOT_DIR} " /$req_file | sed -E -e ' /^[[:space:]]*$/d' | sed -E -e ' /^[#]+.*$/d' ) | grep -m1 -F " $module " )
248
+ ERR_MSG=" pip install '$module ' failed for $req_file ." ;
249
+ if [[ (" ${SUB_CODE} " -eq 0) ]] && python3 -m pip install $PIP_COMMON_FLAGS $PIP_ENV_FLAGS " ${REQ_SPEC} ;" 2> /dev/null ;
250
+ then
251
+ printf " ::debug::%s\n" " Fetched license from package '$module ' ..." ;
252
+ else
253
+ [[ (" ${SUB_CODE} " -eq 0) ]] && SUB_CODE=6 && \
254
+ printf " ::warning file=${req_file} ,line=1,col=1,title=PIP::%s\n" " ${ERR_MSG} " >&2
255
+ fi
256
+ unset ERR_MSG 2> /dev/null || : ;
257
+ done
258
+
259
+ # Use pip-licenses to list the licenses of the installed packages
260
+ { pip-licenses $LICENSE_OPTIONS --allow-only=" ${ALLOW_LICENSES} " || SUB_CODE=5 ; } ; wait ;
261
+
262
+ # Deactivate the virtual environment
263
+ deactivate
264
+
265
+ # return to starting dir
266
+ cd " ${OLDPWD} " ;
267
+
268
+ # Remove the temporary directory and all of its contents
269
+ rm -rf " ${temp_dir} " || : ;
270
+ umask 137 ;
271
+ wait ;
272
+ return $SUB_CODE
273
+ }
274
+
209
275
# THIS IS THE ACTUAL TEST DIR USED (update _TEST_ROOT_DIR as needed)
210
276
_TEST_ROOT_DIR=$( git rev-parse --show-toplevel 2> /dev/null) ;
211
277
navigate_dirs_by_git
212
278
279
+ printf " ::debug::%s\n" " Reading from repository ${_TEST_ROOT_DIR} ..." ;
213
280
# Get a list of files to check using git ls-tree with filtering (and careful shell globing)
214
- FILES_TO_CHECK=$( git ls-tree -r --full-tree --name-only HEAD -- " ${_TEST_ROOT_DIR} " /** /requirements.txt " ${_TEST_ROOT_DIR} " /* -requirements.txt " ${_TEST_ROOT_DIR} /requirements.txt" 2> /dev/null || EXIT_CODE=3)
281
+ FILES_TO_CHECK=$( git ls-tree -r --full-tree --name-only HEAD -- " ${_TEST_ROOT_DIR} " /test /requirements.txt " ${_TEST_ROOT_DIR} " /* -requirements.txt " ${_TEST_ROOT_DIR} /requirements.txt" 2> /dev/null || EXIT_CODE=3)
215
282
216
283
# THIS IS THE ACTUAL TEST
284
+ printf " ::debug::%s\n" " Starting checks ..." ;
217
285
# Iterate over files and run checks
218
286
for req_file in $FILES_TO_CHECK ; do
219
- printf " \t %s\n" " Checking ${req_file} " ;
287
+ printf " ::group:: %s\n" " Checking ${req_file} " ;
220
288
if [[ ( -x $( command -v pip-audit) ) ]] && [[ (" ${EXIT_CODE} " -eq 0) ]] ; then
221
- printf " \t\t %s\n" " Auditing ${req_file} for security vulnerabilities..."
289
+ printf " ::debug:: %s\n" " Auditing ${req_file} for security vulnerabilities ..."
222
290
{ pip-audit $AUDIT_OPTIONS " ${req_file} " || EXIT_CODE=4 ; } ; wait ;
223
291
fi ;
224
292
if [[ (" ${EXIT_CODE} " -eq 0) ]] ; then
225
- printf " \t\t%s\n" " Checking licenses in $req_file ..." ;
226
- if [[ ( $( pip install -r " $req_file " --quiet 2>&1 > /dev/null || false) ) ]] ; then
227
- { pip-licenses $LICENSE_OPTIONS --allow-only=" ${ALLOW_LICENSES} " || EXIT_CODE=5 ; } ; wait ;
228
- else
229
- [[ (" ${EXIT_CODE} " -eq 0) ]] && EXIT_CODE=6
230
- printf " \t%s\n" " FAIL: pip install failed for $req_file ." >&2
231
- fi
293
+ printf " ::debug::%s\n" " Checking licenses in $req_file ..." ;
294
+ # filter for only pkg from requirements file
295
+ PKG_TO_CHECK=$( { cat < " $req_file " | tr ' ><=' ' =' | cut -d\= -f 1-1 | sed -E -e ' /^[[:space:]]*$/d' | sed -E -e ' /^[#]+.*$/d' | xargs -I{} grep -o -m1 -F " {}" " $req_file " | grep -ovE " ^pip|setuptools|wheel|build|hypothesis|certifi$" | sort -u ; wait ; } 2> /dev/null ) ;
296
+ check_license_when_given_req ${PKG_TO_CHECK} ; EXIT_CODE=$?
232
297
else
233
- printf " \t %s\n" " FAIL: Found requirements errors." >&2 ;
298
+ printf " ::error file= ${req_file} ,line= ${BASH_LINENO :- 1} ,title=REQUIREMENTS:: %s\n" " FAIL: Found requirements errors." >&2 ;
234
299
fi
300
+ printf " ::endgroup::\n" ;
235
301
done
236
302
237
- # summary reporting
303
+ printf " ::debug::%s\n " " Summary reporting ... " ;
238
304
report_summary
239
305
240
- # cleaning up
306
+ printf " ::debug::%s\n " " Cleaning up ... " ;
241
307
cleanup || rm -f ${LOCK_FILE} 2> /dev/null || : ;
242
308
243
309
# unset when done
@@ -247,5 +313,6 @@ unset ALLOW_LICENSES 2>/dev/null || : ;
247
313
unset LICENSE_OPTIONS 2> /dev/null || : ;
248
314
249
315
wait ;
316
+ printf " ::debug::%s\n" " Check-pip done." ;
250
317
# Exit with the appropriate code
251
318
exit ${EXIT_CODE:- 255} ;
0 commit comments