pre-commit-src/pre-commit
changeset 25707 2a19e3a985b6
child 25708 d3198dc01a79
equal deleted inserted replaced
25706:4cbb4d574255 25707:2a19e3a985b6
       
     1 #!/bin/bash
       
     2 
       
     3 # Improve error handling, option e is not picked because it's expected for functions
       
     4 # to return non-0 statuses.
       
     5 set -Eeuo pipefail
       
     6 
       
     7 # Allow user input during commit
       
     8 exec < /dev/tty
       
     9 
       
    10 templates_content='{}'
       
    11 templates_names=()
       
    12 unique_json_files=()
       
    13 unique_filterlists_to_include=()
       
    14 all_domains_variables='[]'
       
    15 all_domains_variables_names='{}'
       
    16 all_json_files_contents='{}'
       
    17 variables_in_json_files='{}'
       
    18 variables_in_filterlists='{}'
       
    19 all_domain_variables_matches_in_filterlists='[]'
       
    20 last_error=''
       
    21 testing=false
       
    22 
       
    23 error_handler() {
       
    24     local exit_code=$?
       
    25     local line_number=$1
       
    26     echo "Error: Script failed with exit code $exit_code at line $line_number"
       
    27     if [ "$BASH_COMMAND" = "return 1" ]; then
       
    28         echo -e "Last error message:\n$last_error"
       
    29     else
       
    30         echo -e "\n\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
       
    31         echo "THIS ERROR SHOULDN'T HAPPEN, PLEASE REPORT IT TO AFB TEAM OR KRIS"
       
    32         echo -e "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n"
       
    33         echo "Last executed command: $BASH_COMMAND"
       
    34     fi
       
    35     if [ "$testing" = true ]; then
       
    36         exit 0
       
    37     else
       
    38         exit $exit_code
       
    39     fi
       
    40 }
       
    41 
       
    42 # Set up trap to catch errors and invoke the error handler
       
    43 trap 'error_handler $LINENO' ERR
       
    44 
       
    45 check_git_status() {
       
    46     local status=$(git status)
       
    47 
       
    48     if grep -q "Changes not staged for commit" <<< $status || grep -q "Untracked files" <<< $status; then
       
    49         read -p "There are changes not staged for commit. The script will check only the staged version. Do you want to continue? (y/n): " choice
       
    50         case "$choice" in 
       
    51             # Echo empty line
       
    52             y|Y ) echo "";;
       
    53             n|N ) echo "Aborting."; exit 1;;
       
    54             * ) echo "Invalid choice. Aborting."; exit 1;;
       
    55         esac
       
    56     fi
       
    57 }
       
    58 
       
    59 check_if_jq_is_installed() {
       
    60     if ! command -v jq &>/dev/null; then
       
    61         last_error="ERROR: jq is not installed. Please install jq to continue."
       
    62         return 1
       
    63     fi
       
    64 }
       
    65 
       
    66 check_if_file_exists() {
       
    67     local file_path="$1"
       
    68     if ! [ -f "$file_path" ]; then
       
    69         last_error="ERROR: File $file_path does not exist"
       
    70         return 1
       
    71     fi
       
    72 }
       
    73 
       
    74 get_staged_version_of_a_file() {
       
    75     local file_name="$1"
       
    76 
       
    77     # Using name reference in order to not create subshells for each assignment and allow to use global variables
       
    78     # and global error handling
       
    79     local -n staged_file_content_nameref=$2
       
    80 
       
    81     check_if_file_exists "$file_name"
       
    82     if git show :"$file_name" >/dev/null 2>&1; then
       
    83         staged_file_content_nameref="$(git show :"$file_name")"
       
    84     else
       
    85         last_error="ERROR: File $file_name was requested by a template but it's not tracked neither staged."
       
    86         return 1
       
    87     fi
       
    88 }
       
    89 parse_template_data() {
       
    90     local template="$1"
       
    91     local -n file_data_nameref=$2
       
    92     local staged_template
       
    93 
       
    94     get_staged_version_of_a_file "$template" staged_template
       
    95 
       
    96     # Extract lines starting with %domainsVariables exceptionrules: and process them with jq
       
    97     local json_files_in_template
       
    98     json_files_in_template=$(grep "^%domainsVariables exceptionrules:" <<< "$staged_template" | sed 's/^%domainsVariables exceptionrules://; s/%$//' | jq -R -s 'split("\n") | map(select(length > 0))') || json_files_in_template="[]"
       
    99     # Extract lines starting with %include exceptionrules: and process them with jq
       
   100     local included_filterlists_files_in_template
       
   101     included_filterlists_files_in_template=$(grep "^%include exceptionrules:" <<< "$staged_template" | sed 's/^%include exceptionrules://; s/%$//' | jq -R -s 'split("\n") | map(select(length > 0))') || included_filterlists_files_in_template="[]"
       
   102 
       
   103     if [ "$included_filterlists_files_in_template" = "[]" ]; then
       
   104         last_error="ERROR: There is no list included in template $template_name"
       
   105         return 1
       
   106     fi
       
   107 
       
   108     # Create a JSON object with the template name as the key and the extracted domainsVariables and include data as values
       
   109     file_data_nameref=$(jq -n --arg template "$template" --argjson json_files_in_template "$json_files_in_template" --argjson included_filterlists_files_in_template "$included_filterlists_files_in_template" '
       
   110         {
       
   111             $template: {
       
   112                 "domainsVariables": $json_files_in_template,
       
   113                 "include": $included_filterlists_files_in_template
       
   114             }
       
   115         }')
       
   116 }
       
   117 
       
   118 update_templates_content() {
       
   119     local file_template="$1"
       
   120     # Merge the new template data into the existing templates_content JSON
       
   121     templates_content=$(jq -n --argjson templates_content "$templates_content" --argjson file_template "$file_template" '$templates_content + $file_template')
       
   122 }
       
   123 
       
   124 update_unique_json_files() {
       
   125     local template_name="$1"
       
   126     local file_data="$2"
       
   127     # Extract the list of domainsVariables files from the template data
       
   128     local json_files_list=$(jq -r --arg template_name "$template_name" '.[$template_name].domainsVariables[]' <<< "$file_data")
       
   129     for json_file in $json_files_list; do
       
   130         if ! grep -qwF "$json_file" <<< "${unique_json_files[@]}"; then
       
   131             unique_json_files+=("$json_file")
       
   132         fi
       
   133     done
       
   134 }
       
   135 
       
   136 update_unique_includes() {
       
   137     local template_name="$1"
       
   138     local file_data="$2"
       
   139     # Extract the list of included files from the template data
       
   140     local included_files_list=$(jq -r --arg file "$template_name" '.[$file].include[]' <<< "$file_data")
       
   141 
       
   142     for included_filterlist in $included_files_list; do
       
   143         if ! grep -qwF "$included_filterlist" <<< "${unique_filterlists_to_include[@]}"; then
       
   144             unique_filterlists_to_include+=("$included_filterlist")
       
   145         fi
       
   146     done
       
   147 }
       
   148 
       
   149 check_if_valid_json() {
       
   150     local json_file_path="$1"
       
   151     if ! jq -e . >/dev/null 2>&1 <<< "$(cat "$json_file_path")"; then
       
   152         last_error="ERROR: Invalid JSON content in $json_file_path"
       
   153         return 1
       
   154     fi
       
   155 }
       
   156 
       
   157 check_if_correct_domains_variables_json_structure() {
       
   158     local json_file_content="$1"
       
   159     # Check if the JSON structure is valid and matches the expected format:
       
   160     # { "variable1": ["domain1", "domain2" (...)], "variable2": ["domain1", "domain3" (...)], (...)}
       
   161     if ! echo $json_file_content | jq -e '
       
   162         type == "object" and
       
   163         ([keys[] as $k | .[$k] | type == "array" and all(.[]; type == "string")] | all)
       
   164         '   >/dev/null 2>&1;
       
   165     then
       
   166         last_error="ERROR: JSON structure is invalid in $json_file_path"
       
   167         return 1
       
   168     fi
       
   169 }
       
   170 
       
   171 check_if_duplicated_domains_variable_name_in_single_file() {
       
   172     local json_file_path="$1"
       
   173     local domains_variables_names="$2"
       
   174     if [ "$(echo "$domains_variables_names" | sort | uniq -d)" ]; then
       
   175         last_error="ERROR: Duplicate domains_variables_names found in $json_file_path"
       
   176         return 1
       
   177     fi
       
   178 }
       
   179 
       
   180 check_if_valid_domains_variable_name() {
       
   181     local domains_variable_name="$1"
       
   182     if ! [[ "$domains_variable_name" =~ ^[[:alnum:]_]+$ ]]; then
       
   183         last_error="ERROR: Invalid domains variable name: $domains_variable_name, only alphanumeric characters and underscores are allowed"
       
   184         return 1
       
   185     fi
       
   186 }
       
   187 
       
   188 check_if_duplicated_domains() {
       
   189     local json_file_path="$1"
       
   190     local key="$2"
       
   191     local value="$3"
       
   192     # Value of domainsVariable is an array of strings representing domains,
       
   193     # therefore the error message mentions domains.
       
   194     if [ -n "$(echo "$value" | sort | uniq -di)" ]; then
       
   195         last_error="ERROR: There are duplicated domains in $key in file $json_file_path:\n"
       
   196         last_error+="$(echo "$value" | sort | uniq -di)"
       
   197         return 1
       
   198     fi
       
   199 }
       
   200 
       
   201 check_if_correct_domain() {
       
   202     local json_file_path="$1"
       
   203     local domains_variable_name="$2"
       
   204     local domains="$3"
       
   205     for domain in $domains; do
       
   206         # Check if the domain matches the expected pattern
       
   207         if ! echo "$domain" | grep -qP "^(?:(?:(?!-)[A-Za-z0-9-]{1,63}(?<!-)\.)*(?:[A-Za-z]{2,})$)|(?:^(?:(?!-)[A-Za-z0-9-]{1,63}(?<!-)\.)+\*)$"; then
       
   208             last_error="ERROR: Invalid domain in $domains_variable_name: $domain in file $json_file_path"
       
   209             return 1
       
   210         fi
       
   211     done
       
   212 }
       
   213 
       
   214 check_if_correct_domains_variables() {
       
   215     local json_file_path="$1"
       
   216     local domains_variables_names="$2"
       
   217     local json_file_content="$3"
       
   218     check_if_duplicated_domains_variable_name_in_single_file "$json_file_path" "$domains_variables_names"
       
   219 
       
   220     for domains_variable_name in $domains_variables_names; do
       
   221         check_if_valid_domains_variable_name "$domains_variable_name"
       
   222         # Extract the value associated with the domains variable name from the JSON file
       
   223         local domains=$(jq -r --arg key "$domains_variable_name" '.[$key][]' <<< "$json_file_content")
       
   224 
       
   225         check_if_duplicated_domains "$json_file_path" "$domains_variable_name" "$domains"
       
   226         check_if_correct_domain "$json_file_path" "$domains_variable_name" "$domains"
       
   227     done
       
   228 }
       
   229 
       
   230 update_domains_variables_data() {
       
   231     local domains_variables_names="$1"
       
   232     local json_file_path="$2"
       
   233     local json_file_content=$(jq -c . < "$json_file_path")
       
   234     
       
   235     # Merge the new domains variables into the existing all_domains_variables array
       
   236     all_domains_variables=$(jq -n --argjson all_domains_variables "$all_domains_variables" --argjson domains_variables_names "$(echo "$domains_variables_names" | jq -R -s 'split("\n") | map(select(length > 0))')" '$all_domains_variables + $domains_variables_names | unique')
       
   237 
       
   238     # Add the JSON file content to the all_json_files_contents object
       
   239     all_json_files_contents=$(echo "$all_json_files_contents" | jq --arg json_file_path "$json_file_path" --argjson json_file_content "$json_file_content" '.[$json_file_path] = $json_file_content')
       
   240 
       
   241     # Update the variables_in_json_files object with the keys from the JSON file
       
   242     variables_in_json_files=$(echo "$variables_in_json_files" | jq --arg key "$json_file_path" --argjson value "$(echo "$all_json_files_contents" | jq 'to_entries | map({key: .key, value: (.value | keys)}) | from_entries' | jq --arg key "$json_file_path" '.[$key]')" '. * {($key): $value}')
       
   243 }
       
   244 
       
   245 check_if_duplicated_domains_variable_name_between_files() {
       
   246     local json_file_path="$1"
       
   247     local domains_variables_names="$2"
       
   248     for domains_variable_name in $domains_variables_names; do
       
   249 
       
   250         # Check if the domains variable name exists in the all_domains_variables array
       
   251         if jq -e --arg name "$domains_variable_name" 'index($name) != null' <<< "$all_domains_variables" >/dev/null 2>&1; then
       
   252             # If a duplicate is found, identify the file containing the duplicate and report an error
       
   253             local file_with_duplicate=$(jq -r --arg name "$domains_variable_name" '
       
   254             to_entries | map(select(.value | has($name))) | .[0].key
       
   255             ' <<< "$all_json_files_contents")
       
   256             last_error="ERROR: Duplicate domains variable found in $json_file_path and $file_with_duplicate file: $domains_variable_name"
       
   257             return 1
       
   258         fi
       
   259     done
       
   260 }
       
   261 
       
   262 find_domain_variables_in_filterlist() {
       
   263     local filterlist_content="$1"
       
   264     local filterlist_path="$2"
       
   265     local -n all_lines_with_domain_variables_in_filterlist_nameref=$3
       
   266     # Find lines containing domain variables in the filterlist
       
   267     all_lines_with_domain_variables_in_filterlist_nameref=$(grep -P '%<\{.*\}>%' <<< "$filterlist_content") || all_lines_with_domain_variables_in_filterlist_nameref=''
       
   268 }
       
   269 
       
   270 process_filters() {
       
   271     local all_lines_with_domain_variables_in_filterlist="$1"
       
   272     local -n domains_variables_collected_from_filterlist_nameref="$2"
       
   273     domains_variables_collected_from_filterlist_nameref='[]'
       
   274 
       
   275     for filter in $all_lines_with_domain_variables_in_filterlist; do
       
   276         # Extract the domain variable from the filter
       
   277         local domains_variable_match=$(grep -oP '(?<=%<\{).*?(?=\}>%)' <<< "$filter")
       
   278         if [ "$(echo "$domains_variable_match" | wc -l)" -gt 1 ]; then
       
   279             last_error="ERROR: More than 2 domain variables found in filter: $filter"
       
   280             return 1
       
   281         fi
       
   282         # Ensure the domain variable is correctly formatted in the filter
       
   283         local true_matches=$(grep -P '(%<{(\w+)}>%(?:,~?[a-zA-Z0-9*.~-]+)*#[?@$]?#)|([,$]domain=(?:[a-zA-Z0-9*.~-]+\|)*%<{(\w+)}>%)' <<< "$filter")
       
   284 
       
   285         if [ -z "$true_matches" ]; then
       
   286             last_error="ERROR: Domain variable added in a wrong way in filter: $filter"
       
   287             return 1
       
   288         fi
       
   289         domains_variables_collected_from_filterlist_nameref=$(jq --arg domains_variable_match "$domains_variable_match" '. + [$domains_variable_match]' <<< "$domains_variables_collected_from_filterlist_nameref")
       
   290     done
       
   291 }
       
   292 
       
   293 update_matches_and_variables() {
       
   294     local domains_variables_collected_from_filterlist="$1"
       
   295     local file_path="$2"
       
   296     
       
   297     # Update the list of all domain variable matches in filterlists
       
   298     all_domain_variables_matches_in_filterlists=$(jq -n --argjson all_domain_variables_matches_in_filterlists "$all_domain_variables_matches_in_filterlists" --argjson matches "$domains_variables_collected_from_filterlist" '$all_domain_variables_matches_in_filterlists + $matches | unique')
       
   299 
       
   300     # Update the variables_in_filterlists object with the matches from the current filterlist
       
   301     variables_in_filterlists=$(jq --arg key "$file_path" --argjson value "$domains_variables_collected_from_filterlist" '.[$key] = $value' <<< "$variables_in_filterlists")
       
   302 }
       
   303 
       
   304 extract_domains_variables_in_included_filterlists() {
       
   305     local template_name="$1"
       
   306     local -n domains_variables_in_included_filterlists_nameref=$2
       
   307     # Extract the list of included filterlists from the template
       
   308     local included_filterlists=$(jq -r --arg template_name "$template_name" '.[$template_name].include[]' <<< "$templates_content")
       
   309     domains_variables_in_included_filterlists_nameref=()
       
   310 
       
   311     for included_filterlist in $included_filterlists; do
       
   312         # Extract the domain variables from each included filterlist
       
   313         local domains_variables=$(jq -r --arg key "$included_filterlist" '.[$key][]' <<< "$variables_in_filterlists")
       
   314         for domain_variable in $domains_variables; do
       
   315             domains_variables_in_included_filterlists_nameref+=("$domain_variable")
       
   316         done
       
   317     done
       
   318 
       
   319 }
       
   320 
       
   321 extract_domains_variables_in_included_json_files() {
       
   322     local template_name="$1"
       
   323     local -n domains_variables_in_included_json_files_nameref=$2
       
   324 
       
   325     # Extract the list of included JSON files from the template
       
   326     local included_json_files=$(jq -r --arg template_name "$template_name" '.[$template_name].domainsVariables[]' <<< "$templates_content")
       
   327     domains_variables_in_included_json_files_nameref=()
       
   328 
       
   329     for included_json_file in $included_json_files; do
       
   330         # Extract the domain variables from each included JSON file
       
   331         local domains_variables=$(jq -r --arg key "$included_json_file" '.[$key][]' <<< "$variables_in_json_files")
       
   332         for domain_variable in $domains_variables; do
       
   333             domains_variables_in_included_json_files_nameref+=("$domain_variable")
       
   334         done
       
   335     done
       
   336 }
       
   337 
       
   338 check_domain_variables_in_filterlists() {
       
   339 
       
   340     local template_name="$1"
       
   341     local domains_variables_in_included_filterlists=()
       
   342     local domains_variables_in_included_json_files=()
       
   343 
       
   344     # When for example $2 was empty, then the array had one element with empty string
       
   345     if [ -n "$2" ]; then
       
   346         domains_variables_in_included_filterlists=($2)
       
   347     fi
       
   348     if [ -n "$3" ]; then
       
   349         domains_variables_in_included_json_files=($3)
       
   350     fi
       
   351 
       
   352     # Extract the list of included filterlists and JSON files from the template
       
   353     local included_filterlists=$(jq -r --arg template_name "$template_name" '.[$template_name].include[]' <<< "$templates_content")
       
   354     local included_json_files=$(jq -r --arg template_name "$template_name" '.[$template_name].domainsVariables[]' <<< "$templates_content")
       
   355 
       
   356     for domain_variable_in_filterlist in ${domains_variables_in_included_filterlists[@]}; do
       
   357         local found=false
       
   358         for domain_variable_in_json_file in ${domains_variables_in_included_json_files[@]}; do
       
   359             if [ "$domain_variable_in_filterlist" = "$domain_variable_in_json_file" ]; then
       
   360                 found=true
       
   361                 break
       
   362             fi
       
   363         done
       
   364         if ! $found; then
       
   365             last_error="Error: One of the filterlists:\n\n"
       
   366             last_error+="$included_filterlists\n\n"
       
   367             last_error+="included in the template $template_name contain a domain variable $domain_variable_in_filterlist "
       
   368             last_error+="which wasn't found in any of the domains variables files included in that template:\n\n"
       
   369             last_error+="$included_json_files"
       
   370             return 1
       
   371         fi
       
   372     done
       
   373 }
       
   374 
       
   375 check_if_domains_variables_are_identical_in_lists_and_jsons() {
       
   376     if [ "$all_domains_variables" != "$all_domain_variables_matches_in_filterlists" ]; then
       
   377         last_error="Error: the domain variables in domain-variables file and the filter list are not the same\n"
       
   378         last_error+="Extra variables in domain-variables files:\n"
       
   379         last_error+="$(jq -n --argjson all_domains_variables "$all_domains_variables" --argjson all_domain_variables_matches_in_filterlists "$all_domain_variables_matches_in_filterlists" '$all_domains_variables - $all_domain_variables_matches_in_filterlists')\n"
       
   380         last_error+="Extra variables in filter lists:\n"
       
   381         last_error+=$(jq -n --argjson all_domains_variables "$all_domains_variables" --argjson all_domain_variables_matches_in_filterlists "$all_domain_variables_matches_in_filterlists" '$all_domain_variables_matches_in_filterlists - $all_domains_variables')
       
   382         return 1
       
   383     fi
       
   384 }
       
   385 
       
   386 main() {
       
   387     check_git_status
       
   388     check_if_jq_is_installed
       
   389     for template_name in *.txt; do
       
   390         templates_names+=("$template_name")
       
   391         # To avoid creating a subshell, the variable is passed as a reference to parse_template_data function
       
   392         # That helps with the error handling and allows to use global variables
       
   393         local file_data
       
   394         # Parse data from the template
       
   395         parse_template_data "$template_name" file_data
       
   396         # Update the templates_content JSON with the data from the file
       
   397         update_templates_content "$file_data"
       
   398 
       
   399         update_unique_json_files "$template_name" "$file_data"
       
   400         update_unique_includes "$template_name" "$file_data"
       
   401     done
       
   402 
       
   403     for domains_variables_path in ${unique_json_files[@]}; do
       
   404         check_if_file_exists "$domains_variables_path"
       
   405         local staged_domains_variables_file
       
   406         get_staged_version_of_a_file "$domains_variables_path" staged_domains_variables_file
       
   407         check_if_correct_domains_variables_json_structure "$staged_domains_variables_file"
       
   408 
       
   409         # If jq would be used the duplicates would be automatically removed, therefore I used perl
       
   410         local domains_variables_names=$(perl -0777 -ne 'print "$1\n" while /"([^"]+?)"(?=[\s\r\n]*:)/g' <<< $staged_domains_variables_file)
       
   411         check_if_correct_domains_variables "$domains_variables_path" "$domains_variables_names" "$staged_domains_variables_file"
       
   412         check_if_duplicated_domains_variable_name_between_files "$domains_variables_path" "$domains_variables_names"
       
   413         update_domains_variables_data "$domains_variables_names" "$domains_variables_path"
       
   414     done
       
   415 
       
   416     for filterlist_path in ${unique_filterlists_to_include[@]}; do
       
   417         check_if_file_exists "$filterlist_path"
       
   418         local filterlist_content
       
   419         get_staged_version_of_a_file "$filterlist_path" filterlist_content
       
   420 
       
   421         local all_lines_with_domain_variables_in_filterlist
       
   422         # This regex check is simpler than in filterlist delivery to also catch domains variables in the wrong place
       
   423         # without starting with a complex regex. The full regex is in one of the next steps
       
   424 
       
   425         find_domain_variables_in_filterlist "$filterlist_content" "$filterlist_path" all_lines_with_domain_variables_in_filterlist
       
   426 
       
   427         if [ -z "$all_lines_with_domain_variables_in_filterlist" ]; then
       
   428             # In case of lack of matches, the value of all_lines_with_domain_variables_in_filterlist should have just
       
   429             # a message to show.
       
   430             local domains_variables_collected_from_filterlist='[]'
       
   431         else
       
   432             local domains_variables_collected_from_filterlist
       
   433             process_filters "$all_lines_with_domain_variables_in_filterlist" domains_variables_collected_from_filterlist
       
   434         fi
       
   435 
       
   436         update_matches_and_variables "$domains_variables_collected_from_filterlist" "$filterlist_path"
       
   437     done
       
   438 
       
   439     local domains_variables_in_included_filterlists=()
       
   440     for template_name in ${templates_names[@]}; do
       
   441         extract_domains_variables_in_included_filterlists "$template_name" domains_variables_in_included_filterlists
       
   442 
       
   443         local domains_variables_in_included_json_files
       
   444         extract_domains_variables_in_included_json_files "$template_name" domains_variables_in_included_json_files
       
   445         check_domain_variables_in_filterlists "$template_name" "$(echo ${domains_variables_in_included_filterlists[@]})" "$(echo ${domains_variables_in_included_json_files[@]})"
       
   446     done
       
   447 
       
   448     check_if_domains_variables_are_identical_in_lists_and_jsons
       
   449 }
       
   450 
       
   451 # For testing purposes only if the script has no arguments or the argument is main the process should run
       
   452 # thanks to that the script can be tested without running the main function
       
   453 if [ -z "${1:-}" ] || [ "$1" = "main" ]; then
       
   454     main
       
   455 
       
   456     # Check if this file has unstaged changes:
       
   457     pre_commit_git_status=$(git status :pre-commit-src/pre-commit)
       
   458 
       
   459     if grep -q "Changes not staged for commit" <<< $pre_commit_git_status || grep -q "Untracked files" <<< $pre_commit_git_status; then
       
   460         echo "Unstaged changes detected in pre-commit file. Stage pre-commit changes before continuing."
       
   461         exit 1
       
   462     fi
       
   463 
       
   464     pre_commit_tests_git_status=$(git status :pre-commit-src/tests/pre-commit-tests.sh)
       
   465     if grep -q "Changes not staged for commit" <<< $pre_commit_tests_git_status || grep -q "Untracked files" <<< $pre_commit_tests_git_status; then
       
   466         echo "Unstaged changes detected in pre-commit-tests file. Stage pre-commit changes before continuing."
       
   467         exit 1
       
   468     fi
       
   469 
       
   470     # Run unit tests. The workaround with exit_code is meant to avoid failed test to trigger error_handling
       
   471     exit_code=0
       
   472     ./pre-commit-src/tests/pre-commit-tests.sh || exit_code=1
       
   473     function_exit_code=$?
       
   474     if [ $exit_code -ne 1 ]; then
       
   475         exit_code=$function_exit_code
       
   476     fi
       
   477 
       
   478     if [ $exit_code -ne 0 ]; then
       
   479         echo "Tests failed with exit code $exit_code"
       
   480     else
       
   481         echo "Tests passed successfully"
       
   482     fi
       
   483     exit $exit_code
       
   484 
       
   485 elif [ "$1" = "--load-only" ]; then
       
   486     testing=true
       
   487     echo "Script loaded successfully"
       
   488 else
       
   489     "$@"
       
   490 fi