@@ -124,6 +124,7 @@ export -f check_command
124124check_command grep ;
125125check_command python3 ;
126126check_command git ;
127+ check_command sed ;
127128check_command tee ;
128129check_command pip ;
129130# check_command pip-audit ; # optional
@@ -166,13 +167,23 @@ handle_signals
166167
167168# lazy defined variables should be defined now that this is the only script instance.
168169
170+ # set the script-file to check_pip
171+ SCRIPT_FILE=" tests/check_pip"
169172# Set pip-audit options
170173AUDIT_OPTIONS=" --progress-spinner off --desc on --requirement"
171174# 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); "
173176# Set pip-licenses options
174177LICENSE_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 ;
176187# Enable auto-fix if '--fix' argument is provided
177188if [[ " $1 " == " --fix" ]]; then
178189 AUDIT_OPTIONS=" --fix --strict ${AUDIT_OPTIONS} "
@@ -182,62 +193,117 @@ fi
182193# lazy defined functions should be defined now that this is the only script instance.
183194
184195function report_summary() {
196+ printf " ::group::%s\n" " Results" ;
185197 # Improved reporting based on EXIT_CODE
186198 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 ;;
195207 esac
208+ printf " ::endgroup::\n" ;
196209}
197210
198211function navigate_dirs_by_git() {
199212 if _TEST_ROOT_DIR=$( git rev-parse --show-superproject-working-tree 2> /dev/null) ; then
200213 if [ -z " ${_TEST_ROOT_DIR} " ]; then
201214 _TEST_ROOT_DIR=$( git rev-parse --show-toplevel 2> /dev/null)
202215 fi
216+ printf " ::debug::%s\n" " Found ${_TEST_ROOT_DIR} ..." ;
203217 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
205219 EXIT_CODE=40
206220 fi
207221}
208222
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+
209275# THIS IS THE ACTUAL TEST DIR USED (update _TEST_ROOT_DIR as needed)
210276_TEST_ROOT_DIR=$( git rev-parse --show-toplevel 2> /dev/null) ;
211277navigate_dirs_by_git
212278
279+ printf " ::debug::%s\n" " Reading from repository ${_TEST_ROOT_DIR} ..." ;
213280# 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)
215282
216283# THIS IS THE ACTUAL TEST
284+ printf " ::debug::%s\n" " Starting checks ..." ;
217285# Iterate over files and run checks
218286for req_file in $FILES_TO_CHECK ; do
219- printf " \t %s\n" " Checking ${req_file} " ;
287+ printf " ::group:: %s\n" " Checking ${req_file} " ;
220288 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 ..."
222290 { pip-audit $AUDIT_OPTIONS " ${req_file} " || EXIT_CODE=4 ; } ; wait ;
223291 fi ;
224292 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=$?
232297 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 ;
234299 fi
300+ printf " ::endgroup::\n" ;
235301done
236302
237- # summary reporting
303+ printf " ::debug::%s\n " " Summary reporting ... " ;
238304report_summary
239305
240- # cleaning up
306+ printf " ::debug::%s\n " " Cleaning up ... " ;
241307cleanup || rm -f ${LOCK_FILE} 2> /dev/null || : ;
242308
243309# unset when done
@@ -247,5 +313,6 @@ unset ALLOW_LICENSES 2>/dev/null || : ;
247313unset LICENSE_OPTIONS 2> /dev/null || : ;
248314
249315wait ;
316+ printf " ::debug::%s\n" " Check-pip done." ;
250317# Exit with the appropriate code
251318exit ${EXIT_CODE:- 255} ;
0 commit comments