Skip to content

Commit 7d9251a

Browse files
authored
Merge pull request #353 from JohT/feature/auto-update-conda-environment
Auto update Conda Environment
2 parents b47d682 + 6805999 commit 7d9251a

8 files changed

+291
-49
lines changed

.github/workflows/internal-java-code-analysis.yml

+4
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ jobs:
5656
- name: Checkout GIT Repository
5757
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
5858

59+
- name: Run script tests
60+
id: script-tests
61+
run: ./scripts/runTests.sh
62+
5963
- name: Set Set output variable 'analysis-name'
6064
id: set-analysis-name
6165
run: echo "analysis-name=${{ env.PROJECT_NAME }}-${{ env.AXON_FRAMEWORK_VERSION }}" >> "$GITHUB_OUTPUT"

.github/workflows/public-analyze-code-graph.yml

+2
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ jobs:
9797

9898
# "Setup Python" can be skipped if jupyter notebook analysis-results aren't needed
9999
- name: (Python Setup) Use version ${{ matrix.python }} with Conda package manager Miniforge
100+
id: prepare-conda-environment
100101
uses: conda-incubator/setup-miniconda@505e6394dae86d6a5c7fbb6e3fb8938e3e863830 # v3
101102
with:
102103
python-version: ${{ matrix.python }}
@@ -160,6 +161,7 @@ jobs:
160161
NEO4J_INITIAL_PASSWORD: ${{ steps.generate-neo4j-initial-password.outputs.neo4j-initial-password }}
161162
ENABLE_JUPYTER_NOTEBOOK_PDF_GENERATION: "true"
162163
IMPORT_GIT_LOG_DATA_IF_SOURCE_IS_PRESENT: "" # Options: "none", "aggregated", "full". default = "plugin" or ""
164+
PREPARE_CONDA_ENVIRONMENT: "false" # Had already been done in step with id "prepare-conda-environment".
163165
run: |
164166
TYPESCRIPT_SCAN_HEAP_MEMORY=${{ inputs.typescript-scan-heap-memory }} ./../../scripts/analysis/analyze.sh ${{ inputs.analysis-arguments }}
165167

scripts/activateCondaEnvironment.sh

+29-13
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,35 @@ set -o errexit -o pipefail
1717
# CDPATH reduces the scope of the cd command to potentially prevent unintended directory changes.
1818
# This way non-standard tools like readlink aren't needed.
1919
SCRIPTS_DIR=${SCRIPTS_DIR:-$( CDPATH=. cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P )} # Repository directory containing the shell scripts
20-
echo "activateCondaEnvironment: SCRIPTS_DIR=$SCRIPTS_DIR"
20+
echo "activateCondaEnvironment: SCRIPTS_DIR=${SCRIPTS_DIR}"
2121

2222
# Get the "jupyter" directory by taking the path of this script and going two directory up and then to "jupyter".
2323
JUPYTER_NOTEBOOK_DIRECTORY=${JUPYTER_NOTEBOOK_DIRECTORY:-"${SCRIPTS_DIR}/../jupyter"} # Repository directory containing the Jupyter Notebooks
24-
echo "activateCondaEnvironment: JUPYTER_NOTEBOOK_DIRECTORY=$JUPYTER_NOTEBOOK_DIRECTORY"
24+
echo "activateCondaEnvironment: JUPYTER_NOTEBOOK_DIRECTORY=${JUPYTER_NOTEBOOK_DIRECTORY}"
25+
26+
# Get the file name of the environment description file for the conda package and environment manager
27+
# that contains all dependencies and their versions.
28+
CONDA_ENVIRONMENT_FILE=${CONDA_ENVIRONMENT_FILE:-"${JUPYTER_NOTEBOOK_DIRECTORY}/environment.yml"} # Conda (package manager for Python) environment file path
29+
if [ ! -f "${CONDA_ENVIRONMENT_FILE}" ] ; then
30+
echo "activateCondaEnvironment: Couldn't find environment file ${CONDA_ENVIRONMENT_FILE}."
31+
exit 2
32+
fi
2533

2634
# Define conda environment to use for code structure analysis. Default "codegraph"
2735
CODEGRAPH_CONDA_ENVIRONMENT=${CODEGRAPH_CONDA_ENVIRONMENT:-"codegraph"} # Name of the conda environment to use for code graph analysis
2836
echo "activateCondaEnvironment: CONDA_PREFIX=${CONDA_PREFIX}"
2937
echo "activateCondaEnvironment: Current conda environment=${CONDA_DEFAULT_ENV}"
3038
echo "activateCondaEnvironment: Target conda environment=${CODEGRAPH_CONDA_ENVIRONMENT}"
3139

32-
if [ "${CONDA_DEFAULT_ENV}" = "${CODEGRAPH_CONDA_ENVIRONMENT}" ] ; then
40+
PREPARE_CONDA_ENVIRONMENT=${PREPARE_CONDA_ENVIRONMENT:-"true"} # Wether to prepare then Conda environment if needed (default, "true") or use an already prepared Conda environment ("false")
41+
42+
if [ "${CONDA_DEFAULT_ENV}" = "${CODEGRAPH_CONDA_ENVIRONMENT}" ] && [ "${PREPARE_CONDA_ENVIRONMENT}" = "false" ]; then
3343
echo "activateCondaEnvironment: Skipping activation. Target conda environment ${CODEGRAPH_CONDA_ENVIRONMENT} is already activated."
3444
# "return" needs to be used here instead of "exit".
3545
# This script is included in another script by using "source".
3646
# "exit" would end the main script, "return" just ends this sub script.
3747
return 0
38-
fi
48+
fi
3949

4050
# Include operation system function to for example detect Windows.
4151
source "${SCRIPTS_DIR}/operatingSystemFunctions.sh"
@@ -60,18 +70,24 @@ echo "activateCondaEnvironment: scriptExtension=${scriptExtension}"
6070
eval "$(${pathToConda}conda${scriptExtension} shell.bash hook)"
6171
echo "activateCondaEnvironment: Current conda environment after shell hook=${CONDA_DEFAULT_ENV}"
6272

63-
# Create (if missing) and activate Conda environment for code structure graph analysis
64-
if { "${pathToConda}conda" env list | grep "$CODEGRAPH_CONDA_ENVIRONMENT "; } >/dev/null 2>&1; then
65-
echo "activateCondaEnvironment: Conda environment $CODEGRAPH_CONDA_ENVIRONMENT already created"
66-
else
67-
if [ ! -f "${JUPYTER_NOTEBOOK_DIRECTORY}/environment.yml" ] ; then
68-
echo "activateCondaEnvironment: Couldn't find environment file ${jupyter_notebook_file_path}/environment.yml."
69-
exit 2
73+
# If missing, create Conda environment for code graph analysis
74+
# Note: The curly braces are grouping the outputs of both (piped) operations together to suppress them later (dev/null).
75+
if "${pathToConda}conda" env list | grep "${CODEGRAPH_CONDA_ENVIRONMENT} " >/dev/null 2>&1; then
76+
echo "activateCondaEnvironment: Conda environment ${CODEGRAPH_CONDA_ENVIRONMENT} has already been created."
77+
78+
# Check if the declaration in the environment file matches the actual environment to find out if it needs to be updated.
79+
if "${pathToConda}conda" compare --name "${CODEGRAPH_CONDA_ENVIRONMENT}" "${CONDA_ENVIRONMENT_FILE}" >/dev/null 2>&1; then
80+
echo "activateCondaEnvironment: Conda environment ${CODEGRAPH_CONDA_ENVIRONMENT} is up-to-date."
81+
else
82+
echo "activateCondaEnvironment: Conda environment ${CODEGRAPH_CONDA_ENVIRONMENT} needs to be updated."
83+
"${pathToConda}conda" env update --file "${CONDA_ENVIRONMENT_FILE}" --name ${CODEGRAPH_CONDA_ENVIRONMENT} --prune
7084
fi
71-
echo "activateCondaEnvironment: Creating Conda environment ${CODEGRAPH_CONDA_ENVIRONMENT}"
72-
"${pathToConda}conda" env create --file "${jupyter_notebook_file_path}/environment.yml" --name "${CODEGRAPH_CONDA_ENVIRONMENT}"
85+
else
86+
echo "activateCondaEnvironment: Creating Conda environment ${CODEGRAPH_CONDA_ENVIRONMENT}..."
87+
"${pathToConda}conda" env create --file "${CONDA_ENVIRONMENT_FILE}" --name "${CODEGRAPH_CONDA_ENVIRONMENT}"
7388
fi
7489

90+
# Activate code graph Conda environment
7591
echo "activateCondaEnvironment: Activating Conda environment ${CODEGRAPH_CONDA_ENVIRONMENT}"
7692
"${pathToConda}conda" activate ${CODEGRAPH_CONDA_ENVIRONMENT}
7793

scripts/detectChangedFiles.sh

+84-27
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,17 @@ ARTIFACTS_CHANGE_DETECTION_HASH_FILE=${ARTIFACTS_CHANGE_DETECTION_HASH_FILE:-"ar
2121
CHANGE_DETECTION_HASH_FILE=${CHANGE_DETECTION_HASH_FILE:-"${ARTIFACTS_CHANGE_DETECTION_HASH_FILE}"} # Name of the file that contains the hash code of the file list for change detection
2222
CHANGE_DETECTION_HASH_FILE_PATH=${CHANGE_DETECTION_HASH_FILE_PATH:-"./${ARTIFACTS_DIRECTORY}/${CHANGE_DETECTION_HASH_FILE}"} # Default path of the file that contains the hash code of the file list for change detection. Can be overridden by a command line option.
2323

24+
COLOR_INFO='\033[0;30m' # dark grey
25+
COLOR_ERROR='\033[0;31m' # red
26+
COLOR_DEFAULT='\033[0m'
27+
2428
# Function to display script usage
2529
usage() {
26-
echo "Usage: $0 [--readonly]"
27-
echo " [--paths <comma separated list of file and directory names> (default=artifacts)]"
28-
echo " [--hashfile <path to the file that contains the hash for change detection> (default=env var CHANGE_DETECTION_HASH_FILE_PATH)]"
30+
echo -e "${COLOR_ERROR}" >&2
31+
echo "Usage: $0 [--readonly]" >&2
32+
echo " [--paths <comma separated list of file and directory names> (default=artifacts)]" >&2
33+
echo " [--hashfile <path to the file that contains the hash for change detection> (default=env var CHANGE_DETECTION_HASH_FILE_PATH)]" >&2
34+
echo -e "${COLOR_DEFAULT}" >&2
2935
exit 1
3036
}
3137

@@ -52,25 +58,52 @@ while [[ $# -gt 0 ]]; do
5258
shift
5359
;;
5460
*)
55-
echo "detectChangedFiles: Error: Unknown option: ${key}"
61+
echo -e "${COLOR_ERROR}detectChangedFiles: Error: Unknown option: ${key}${COLOR_DEFAULT}" >&2
5662
usage
5763
;;
5864
esac
5965
shift || true # ignore error when there are no more arguments
6066
done
6167

68+
exit_failed() {
69+
case "$0" in
70+
*/sh) return 1 ;; # Script is sourced
71+
*) exit 1 ;; # Script is executed directly
72+
esac
73+
}
74+
75+
exit_successful() {
76+
case "$0" in
77+
*/sh) return 0 ;; # Script is sourced
78+
*) exit 0 ;; # Script is executed directly
79+
esac
80+
}
81+
6282
if ${readonlyMode}; then
63-
echo "detectChangedFiles: Readonly mode activated. Change detection file won't be created." >&2
83+
echo -e "${COLOR_INFO}detectChangedFiles: Readonly mode activated. Change detection file won't be created.${COLOR_DEFAULT}" >&2
6484
else
65-
echo "detectChangedFiles: ${hashFilePath} will be used as change detection file." >&2
85+
echo -e "${COLOR_INFO}detectChangedFiles: ${hashFilePath} will be used as change detection file.${COLOR_DEFAULT}" >&2
6686
fi
6787

6888
# Check if the paths parameter exist
69-
if [ -z "${paths}" ] ; then
70-
echo 0 # 0=No change detected. The path list is empty. There is nothing to compare. Therefore assume that there are no changes.
71-
exit 0
89+
if [ -z "${paths}" ]; then
90+
echo 0 # 0=No change detected. The path list is empty. There is nothing to compare. Therefore assume that there are no changes.
91+
exit_successful
7292
fi
7393

94+
# Check all paths if they are valid files or valid directories
95+
for path in ${paths//,/ }; do
96+
pathWithoutProtocolPrefix=${path/#*::/}
97+
if [ -f "${pathWithoutProtocolPrefix}" ] ; then
98+
continue # Valid file
99+
elif [ -d "${pathWithoutProtocolPrefix}" ] ; then
100+
continue # Valid directory
101+
fi
102+
# Neither a valid directory and file
103+
echo -e "${COLOR_ERROR}detectChangedFiles: Error: Invalid path: ${pathWithoutProtocolPrefix}${COLOR_DEFAULT}" >&2
104+
exit_failed
105+
done
106+
74107
# Function to get file size
75108
get_file_size() {
76109
if [ -f "$1" ]; then
@@ -80,23 +113,46 @@ get_file_size() {
80113
fi
81114
}
82115

116+
isMacOS() {
117+
[ "$(uname -s)" = "Darwin" ]
118+
}
119+
83120
# Function to process a single path
84121
file_names_and_sizes() {
85122
if [ -d "$1" ]; then
123+
# TODO Remove after debugging
124+
echo "detectChangedFiles: Checking directory $1" >&2
125+
86126
# If it's a directory, list all files inside
87127
# except for "node_modules", "target", "temp" and the change detection file itself
88-
find -L "$1" \
89-
-type d -name "node_modules" -prune -o \
90-
-type d -name "target" -prune -o \
91-
-type d -name "temp" -prune -o \
92-
-type d -name ".reports" -prune -o \
93-
-not -path "${hashFilePath}" \
94-
-type f \
95-
-exec stat -f "%N %z" {} + \
96-
| sort
128+
if isMacOS; then
129+
find -L "$1" \
130+
-type d -name "node_modules" -prune -o \
131+
-type d -name "target" -prune -o \
132+
-type d -name "temp" -prune -o \
133+
-type d -name ".reports" -prune -o \
134+
-not -path "${hashFilePath}" \
135+
-type f \
136+
-exec stat -f "%N %z" {} + \
137+
| sort
138+
else
139+
find -L "$1" \
140+
-type d -name "node_modules" -prune -o \
141+
-type d -name "target" -prune -o \
142+
-type d -name "temp" -prune -o \
143+
-type d -name ".reports" -prune -o \
144+
-not -path "${hashFilePath}" \
145+
-type f \
146+
-exec stat --printf="%n %s\n" {} + \
147+
| sort
148+
fi
97149
elif [ -f "$1" ]; then
98-
# If it's a file, just echo the file path
99-
stat -f "%N %z" < "$1"
150+
# The path is a file. Print its path and size.
151+
if isMacOS; then
152+
stat -f "%N %z" "$1"
153+
else
154+
stat --printf="%n %s\n" "$1"
155+
fi
100156
fi
101157
}
102158

@@ -110,10 +166,11 @@ get_md5_checksum_of_all_file_names_and_sizes() {
110166
local processed_paths=0
111167

112168
for path in ${paths//,/ }; do
113-
local files_and_their_size; files_and_their_size=$(file_names_and_sizes "${path}")
169+
pathWithoutProtocolPrefix=${path/#*::/}
170+
local files_and_their_size; files_and_their_size=$(file_names_and_sizes "${pathWithoutProtocolPrefix}")
114171
all_files_and_sizes="${all_files_and_sizes}${files_and_their_size}"
115172
processed_paths=$((processed_paths + 1))
116-
echo -ne "detectChangedFiles: Calculate checksum progress: ($processed_paths/$total_paths)\r" >&2
173+
echo -ne "${COLOR_INFO}detectChangedFiles: Calculate checksum progress: ($processed_paths/$total_paths)\r${COLOR_DEFAULT}" >&2
117174
done
118175
echo "" >&2
119176
echo "${all_files_and_sizes}" | openssl md5 | awk '{print $2}'
@@ -133,12 +190,12 @@ if [ ! -f "${hashFilePath}" ] ; then
133190
mkdir -p "${hash_file_directory}"
134191
# Create the file containing the hash of the files list to a new file for the next call
135192
echo "${CURRENT_FILES_HASH}" > "${hashFilePath}"
136-
echo "detectChangedFiles: Change detection file created" >&2
193+
echo -e "${COLOR_INFO}detectChangedFiles: Change detection file created.${COLOR_DEFAULT}" >&2
137194
else
138-
echo "detectChangedFiles: Skipping file creation with content (=hash) ${CURRENT_FILES_HASH}" >&2
195+
echo -e "${COLOR_INFO}detectChangedFiles: Skipping file creation with content (=hash) ${CURRENT_FILES_HASH}${COLOR_DEFAULT}" >&2
139196
fi
140197
echo 1 # 1=Change detected and change detection file created
141-
exit 0
198+
exit_successful
142199
fi
143200

144201
# Assume that there is no change if the saved hash is equal to the current one.
@@ -149,9 +206,9 @@ else
149206
if ! ${readonlyMode}; then
150207
# Write the updated hash into the file containing the hash of the files list for the next call
151208
echo "${CURRENT_FILES_HASH}" > "${hashFilePath}"
152-
echo "detectChangedFiles: Change detection file updated" >&2
209+
echo -e "${COLOR_INFO}detectChangedFiles: Change detection file updated.${COLOR_DEFAULT}" >&2
153210
else
154-
echo "detectChangedFiles: Skipping file update with content (=hash) ${CURRENT_FILES_HASH}" >&2
211+
echo -e "${COLOR_INFO}detectChangedFiles: Skipping file update with content (=hash) ${CURRENT_FILES_HASH}.${COLOR_DEFAULT}" >&2
155212
fi
156213
echo 2 # 2=Change detected and change detection file updated
157214
fi

scripts/download.sh

+8-5
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,6 @@ if [[ -z ${downloadUrl} ]]; then
4545
exit 1
4646
fi
4747

48-
if ! curl --head --fail ${downloadUrl} >/dev/null 2>&1; then
49-
echo "download: Error: Invalid URL: ${downloadUrl}"
50-
exit 1
51-
fi
52-
5348
if [[ -z ${filename} ]]; then
5449
filename=$(basename -- "${downloadUrl}")
5550
fi
@@ -65,6 +60,14 @@ fi
6560
if [ ! -f "${SHARED_DOWNLOADS_DIRECTORY}/${filename}" ] ; then
6661
echo "download: Downloading ${filename} from ${downloadUrl} into ${SHARED_DOWNLOADS_DIRECTORY}"
6762

63+
# Check if the URL is valid
64+
# The check is deferred and not done in the input validation block at the beginning.
65+
# This is because the check needs a network connection which shouldn't be required when the file had already been downloaded.
66+
if ! curl --head --fail ${downloadUrl} >/dev/null 2>&1; then
67+
echo "download: Error: Invalid URL: ${downloadUrl}"
68+
exit 1
69+
fi
70+
6871
# Download the file
6972
if ! curl -L --fail-with-body -o "${SHARED_DOWNLOADS_DIRECTORY}/${filename}" "${downloadUrl}"; then
7073
echo "download: Error: Failed to download ${filename}"

scripts/runTests.sh

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/usr/bin/env bash
2+
3+
# Runs all test scripts (no Python and Chromium required).
4+
# It only considers scripts in the "scripts" directory.
5+
6+
# Fail on any error ("-e" = exit on first error, "-o pipefail" exist on errors within piped commands)
7+
set -o errexit -o pipefail
8+
9+
# Overrideable Constants (defaults also defined in sub scripts)
10+
LOG_GROUP_START=${LOG_GROUP_START:-"::group::"} # Prefix to start a log group. Defaults to GitHub Actions log group start command.
11+
LOG_GROUP_END=${LOG_GROUP_END:-"::endgroup::"} # Prefix to end a log group. Defaults to GitHub Actions log group end command.
12+
13+
## Get this "scripts" directory if not already set
14+
# Even if $BASH_SOURCE is made for Bourne-like shells it is also supported by others and therefore here the preferred solution.
15+
# CDPATH reduces the scope of the cd command to potentially prevent unintended directory changes.
16+
# This way non-standard tools like readlink aren't needed.
17+
SCRIPTS_DIR=${SCRIPTS_DIR:-$( CDPATH=. cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P )} # Repository directory containing the shell scripts
18+
echo "runTests: SCRIPTS_DIR=${SCRIPTS_DIR}" >&2
19+
20+
# Run all report scripts
21+
for test_script_file in "${SCRIPTS_DIR}"/test*.sh; do
22+
test_script_filename=$(basename -- "${test_script_file}");
23+
test_script_filename="${test_script_filename%.*}" # Remove file extension
24+
25+
echo "${LOG_GROUP_START}Run ${test_script_filename}";
26+
echo "runTests: $(date +'%Y-%m-%dT%H:%M:%S%z') Starting ${test_script_filename}...";
27+
28+
source "${test_script_file}"
29+
30+
echo "runTests: $(date +'%Y-%m-%dT%H:%M:%S%z') Finished ${test_script_filename}";
31+
echo "${LOG_GROUP_END}";
32+
done

scripts/scanTypescript.sh

-4
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,10 @@ is_valid_scan_result() {
115115
}
116116

117117
is_change_detected() {
118-
local COLOR_DARK_GREY='\033[0;30m'
119-
local COLOR_DEFAULT='\033[0m'
120118
local source_directory_name; source_directory_name=$(basename "${source_directory}");
121119

122-
echo -e "${COLOR_DARK_GREY}"
123120
changeDetectionHashFilePath="./${SOURCE_DIRECTORY}/typescriptScanChangeDetection-${source_directory_name}.sha"
124121
changeDetectionReturnCode=$( source "${SCRIPTS_DIR}/detectChangedFiles.sh" --readonly --hashfile "${changeDetectionHashFilePath}" --paths "${source_directory}")
125-
echo -e "${COLOR_DEFAULT}"
126122

127123
if [ "${changeDetectionReturnCode}" == "0" ] && [ "${TYPESCRIPT_SCAN_CHANGE_DETECTION}" = true ]; then
128124
true

0 commit comments

Comments
 (0)