winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

pre-commit.sh (5891B)


      1 #!/bin/sh
      2 #
      3 # Copyright (c) Microsoft Corporation.
      4 # Licensed under the MIT License.
      5 #
      6 # A hook script to verify what is about to be committed.
      7 # Called by "git commit" with no arguments.  The hook should
      8 # exit with non-zero status after issuing an appropriate message if
      9 # it wants to stop the commit.
     10 # To avoid having this script called, use "git commit -n"
     11 
     12 # Get files that need to be checked against clang-format into an array
     13 
     14 # To understand the regex format in bash, take a look at https://tldp.org/LDP/abs/html/x17129.html
     15 # For example, this is how the regex (\.cpp|\.h)$ matches the example file client/include/SFSClient.h:
     16 #   (\.cpp|\.h): Match either .cpp or .h. The . character is escaped because it has a special meaning in regex.
     17 #   $: Match end of string
     18 pattern_to_include_in_clang_format='(\.cpp|\.h)$'
     19 pattern_to_include_in_cmake_format='CMakeLists.txt'
     20 
     21 # Get the names of staged files (--cached) that are not deleted (--diff-filter=d)
     22 all_staged_files=$(git diff --cached --name-only --diff-filter=d)
     23 mapfile -t filtered_files <<<"$all_staged_files"
     24 
     25 declare -A files_with_unstaged_changes
     26 
     27 clang_format_files=()
     28 cmake_format_files=()
     29 
     30 # Only those that match a pattern will be analyzed
     31 max_index=${#filtered_files[@]}
     32 for ((i = 0; i < max_index; i++)); do
     33     if [[ "${filtered_files[$i]}" =~ $pattern_to_include_in_clang_format || "${filtered_files[$i]}" =~ $pattern_to_include_in_cmake_format ]]; then
     34         if [[ "${filtered_files[$i]}" =~ $pattern_to_include_in_clang_format ]]; then
     35             clang_format_files+=("${filtered_files[$i]}")
     36         elif [[ "${filtered_files[$i]}" =~ $pattern_to_include_in_cmake_format ]]; then
     37             cmake_format_files+=("${filtered_files[$i]}")
     38         fi
     39 
     40         # Collect files that will be analyzed but have unstaged changes, as we don't want to overwrite these changes
     41         # `git diff` is empty if a file has no unstaged changes
     42         if [[ -n "$(git diff "${filtered_files[$i]}")" ]]; then
     43             files_with_unstaged_changes["${filtered_files[$i]}"]=1
     44         fi
     45     fi
     46 done
     47 
     48 # Early stop if no files to check
     49 if [[ ${clang_format_files[*]} == "" && ${cmake_format_files[*]} == "" ]]; then
     50     exit 0
     51 fi
     52 
     53 RED='\033[0;31m'
     54 YELLOW='\033[0;33m'
     55 NC='\033[0m' # No color
     56 
     57 #
     58 # clang-format
     59 #
     60 
     61 # Check that clang-format is installed
     62 python_dir="$(python -c 'import os,sysconfig;print(sysconfig.get_path("scripts",f"{os.name}_user"))')"
     63 clang_format="$python_dir/clang-format.exe"
     64 
     65 if [[ ! -x "$(command -v "$clang_format")" ]]; then
     66     echo -e "${RED}There are staged changes that need to be checked by the formatter."
     67     echo -e "${RED}clang-format toolset was not found. Re-run scripts\Setup.ps1 to install it.${NC}"
     68     exit 1
     69 fi
     70 
     71 formatted_files=()
     72 unformatted_files=()
     73 for file in "${clang_format_files[@]}"; do
     74     # Check if file will be modified so we can properly inform the user of modified files
     75     # Use `clang-format -n` which does a dry run and outputs a non-empty response if there's something to format
     76     # Pass contents through stdin to get only the staged changes. --assume-filename is used to find the closest .clang-format
     77     # `git show :<file>` outputs the staged version of a file
     78     if [[ -n $(git show ":$file" | "$clang_format" -n --assume-filename "$file" 2>&1) ]]; then
     79 
     80         # If the file has unstaged changes, we can't format it
     81         if [[ -v "files_with_unstaged_changes["$file"]" ]] ; then
     82             unformatted_files+=("$file")
     83         else
     84             # Collect file that will be formatted and then actually format in-place
     85             formatted_files+=("$file")
     86             "$clang_format" -i "$file"
     87         fi
     88     fi
     89 done
     90 
     91 
     92 # Report modified files at end so we can run both formatters and report all changes at once
     93 
     94 #
     95 # cmake-format
     96 #
     97 cmake_format="$python_dir/cmake-format.exe"
     98 
     99 if [[ ! -x "$(command -v "$cmake_format")" ]]; then
    100     echo -e "${RED}There are staged changes that need to be checked by the formatter."
    101     echo -e "${RED}cmake-format toolset was not found. Re-run scripts\Setup.ps1 to install it.${NC}"
    102     exit 1
    103 fi
    104 
    105 tmp_formatted_name="tmp_formatted"
    106 
    107 for file in "${cmake_format_files[@]}"; do
    108     # Check if file will be modified so we can properly inform the user of modified files
    109     # First create a temporary file with only staged changes since cmake-format does not support stdin
    110     git show ":$file" > $tmp_formatted_name
    111     # Use `cmake-format --check` which does a dry run and returns 1 if there's something to format
    112     if ! "$cmake_format" --check "$tmp_formatted_name" > /dev/null 2>&1; then
    113 
    114         # If the file has unstaged changes, we can't format it
    115         if [[ -v "files_with_unstaged_changes["$file"]" ]] ; then
    116             unformatted_files+=("$file")
    117         else
    118             # Collect file that will be formatted and then actually format in-place
    119             formatted_files+=("$file")
    120             "$cmake_format" -i "$file"
    121         fi
    122     fi
    123     rm $tmp_formatted_name
    124 done
    125 
    126 IFS=$'\n' sorted_formatted_files=($(sort <<<"${formatted_files[*]}"))
    127 IFS=$'\n' sorted_unformatted_files=($(sort <<<"${unformatted_files[*]}"))
    128 unset IFS
    129 
    130 exit_code=0
    131 
    132 # If there were formatted files, we report the changes
    133 if [[ ${sorted_formatted_files[*]} != "" ]]; then
    134     echo -e "${YELLOW}There are files that have been automatically formatted. Review and stage the changes before committing:"
    135     for file in "${sorted_formatted_files[@]}"; do
    136         echo -e "M\t$file"
    137     done
    138     echo -e "${NC}"
    139     exit_code=1
    140 fi
    141 
    142 # If there were files that could not be formatted, we report them
    143 if [[ ${unformatted_files[*]} != "" ]]; then
    144     echo -e "${RED}There are files that need to be formatted but have unstaged changes. Either stage the files or stash/discard the unstaged changes:"
    145     for file in "${unformatted_files[@]}"; do
    146         echo -e "M\t$file"
    147     done
    148     echo -e "${NC}"
    149     exit_code=1
    150 fi
    151 
    152 exit $exit_code