1#!/bin/bash 2set -e 3 4# This script reformats source files using various formatters and linters. 5# 6# Files are changed in-place, so make sure you don't have anything open in an 7# editor, and you may want to commit before formatting in case of awryness. 8# 9# This must be run on a clean repository to succeed 10# 11function display_help() 12{ 13 echo "usage: format-code.sh [-h | --help] " 14 echo " [<path>]" 15 echo 16 echo "Format and lint a repository." 17 echo 18 echo "Arguments:" 19 echo " path Path to git repository (default to pwd)" 20} 21 22eval set -- "$(getopt -o 'h' --long 'help' -n 'format-code.sh' -- "$@")" 23while true; do 24 case "$1" in 25 '-h'|'--help') 26 display_help && exit 0 27 ;; 28 29 '--') 30 shift 31 break 32 ;; 33 34 *) 35 echo "unknown option: $1" 36 display_help && exit 1 37 ;; 38 esac 39done 40 41# Detect tty and set nicer colors. 42if [ -t 1 ]; then 43 BLUE="\e[34m" 44 GREEN="\e[32m" 45 NORMAL="\e[0m" 46 RED="\e[31m" 47 YELLOW="\e[33m" 48else # non-tty, no escapes. 49 BLUE="" 50 GREEN="" 51 NORMAL="" 52 RED="" 53 YELLOW="" 54fi 55 56# Path to default config files for linters. 57CONFIG_PATH="$(git -C "$(dirname "${BASH_SOURCE[0]}")" rev-parse --show-toplevel)/config" 58 59# Find repository root for `pwd` or $1. 60if [ -z "$1" ]; then 61 DIR="$(git rev-parse --show-toplevel || pwd)" 62else 63 DIR="$(git -C "$1" rev-parse --show-toplevel)" 64fi 65if [ ! -d "$DIR/.git" ]; then 66 echo "${RED}Error:${NORMAL} Directory ($DIR) does not appear to be a git repository" 67 exit 1 68fi 69 70cd "${DIR}" 71echo -e " ${BLUE}Formatting code under${NORMAL} $DIR" 72 73ALL_OPERATIONS=( \ 74 commit_gitlint \ 75 commit_spelling \ 76 clang_format \ 77 eslint \ 78 pycodestyle \ 79 shellcheck \ 80 ) 81 82function do_commit_spelling() { 83 if [ ! -e .git/COMMIT_EDITMSG ]; then 84 return 85 fi 86 echo -e " ${BLUE}Running codespell${NORMAL}" 87 88 # Run the codespell with openbmc spcific spellings on the patchset 89 echo "openbmc-dictionary - misspelling count >> " 90 sed "s/Signed-off-by.*//" .git/COMMIT_EDITMSG | \ 91 codespell -D "${CONFIG_PATH}/openbmc-spelling.txt" -d --count - 92 93 # Run the codespell with generic dictionary on the patchset 94 echo "generic-dictionary - misspelling count >> " 95 sed "s/Signed-off-by.*//" .git/COMMIT_EDITMSG | \ 96 codespell --builtin clear,rare,en-GB_to_en-US -d --count - 97} 98 99function do_commit_gitlint() { 100 echo -e " ${BLUE}Running gitlint${NORMAL}" 101 # Check for commit message issues 102 gitlint \ 103 --extra-path "${CONFIG_PATH}/gitlint/" \ 104 --config "${CONFIG_PATH}/.gitlint" 105} 106 107function do_eslint() { 108 if [[ -f ".eslintignore" ]]; then 109 ESLINT_IGNORE="--ignore-path .eslintignore" 110 elif [[ -f ".gitignore" ]]; then 111 ESLINT_IGNORE="--ignore-path .gitignore" 112 fi 113 114 # Get the eslint configuration from the repository 115 if [[ -f ".eslintrc.json" ]]; then 116 echo -e " ${BLUE}Running eslint${NORMAL}" 117 ESLINT_RC="-c .eslintrc.json" 118 else 119 echo -e " ${BLUE}Running eslint using ${YELLOW}the global config${NORMAL}" 120 ESLINT_RC="--no-eslintrc -c ${CONFIG_PATH}/eslint-global-config.json" 121 fi 122 123 ESLINT_COMMAND="eslint . ${ESLINT_IGNORE} ${ESLINT_RC} \ 124 --ext .json --format=stylish \ 125 --resolve-plugins-relative-to /usr/local/lib/node_modules \ 126 --no-error-on-unmatched-pattern" 127 128 # Print eslint command 129 echo "$ESLINT_COMMAND" 130 # Run eslint 131 $ESLINT_COMMAND 132} 133 134function do_pycodestyle() { 135 if [[ -f "setup.cfg" ]]; then 136 echo -e " ${BLUE}Running pycodestyle${NORMAL}" 137 pycodestyle --show-source --exclude=subprojects . 138 rc=$? 139 if [[ ${rc} -ne 0 ]]; then 140 exit ${rc} 141 fi 142 fi 143} 144 145function do_shellcheck() { 146 # If .shellcheck exists, stop on error. Otherwise, allow pass. 147 if [[ -f ".shellcheck" ]]; then 148 local shellcheck_allowfail="false" 149 else 150 local shellcheck_allowfail="true" 151 fi 152 153 # Run shellcheck on any shell-script. 154 shell_scripts="$(git ls-files | xargs -n1 file -0 | \ 155 grep -a "shell script" | cut -d '' -f 1)" 156 if [ -n "${shell_scripts}" ]; then 157 echo -e " ${BLUE}Running shellcheck${NORMAL}" 158 fi 159 for script in ${shell_scripts}; do 160 shellcheck --color=never -x "${script}" || ${shellcheck_allowfail} 161 done 162} 163 164 165do_clang_format() { 166 # Allow called scripts to know which clang format we are using 167 export CLANG_FORMAT="clang-format" 168 IGNORE_FILE=".clang-ignore" 169 declare -a IGNORE_LIST 170 171 if [[ -f "${IGNORE_FILE}" ]]; then 172 readarray -t IGNORE_LIST < "${IGNORE_FILE}" 173 fi 174 175 ignorepaths="" 176 ignorefiles="" 177 178 for path in "${IGNORE_LIST[@]}"; do 179 # Check for comment, line starting with space, or zero-length string. 180 # Checking for [[:space:]] checks all options. 181 if [[ -z "${path}" ]] || [[ "${path}" =~ ^(#|[[:space:]]).*$ ]]; then 182 continue 183 fi 184 185 # All paths must start with ./ for find's path prune expectation. 186 if [[ "${path}" =~ ^\.\/.+$ ]]; then 187 ignorepaths+=" ${path}" 188 else 189 ignorefiles+=" ${path}" 190 fi 191 done 192 193 searchfiles="" 194 while read -r path; do 195 # skip ignorefiles 196 if [[ $ignorefiles == *"$(basename "${path}")"* ]]; then 197 continue 198 fi 199 200 skip=false 201 #skip paths in ingorepaths 202 for pathname in $ignorepaths; do 203 if [[ "./${path}" == "${pathname}"* ]]; then 204 skip=true 205 break 206 fi 207 done 208 209 if [ "$skip" = true ]; then 210 continue 211 fi 212 # shellcheck disable=2089 213 searchfiles+="\"./${path}\" " 214 215 # Get C and C++ files managed by git and skip the mako files 216 done <<<"$(git ls-files | grep -e '\.[ch]pp$' -e '\.[ch]$' | grep -v '\.mako\.')" 217 218 if [[ -f ".clang-format" ]]; then 219 echo -e " ${BLUE}Running clang-format${NORMAL}" 220 # shellcheck disable=SC2090 disable=SC2086 221 echo ${searchfiles} | xargs "${CLANG_FORMAT}" -i 222 fi 223 224} 225 226for op in "${ALL_OPERATIONS[@]}"; do 227 "do_$op" 228done 229 230echo -e " ${BLUE}Result differences...${NORMAL}" 231if ! git --no-pager diff --exit-code ; then 232 echo -e "Format: ${RED}FAILED${NORMAL}" 233 exit 1 234else 235 echo -e "Format: ${GREEN}PASSED${NORMAL}" 236fi 237 238# Sometimes your situation is terrible enough that you need the flexibility. 239# For example, phosphor-mboxd. 240if [[ -f "format-code.sh" ]]; then 241 ./format-code.sh 242 git --no-pager diff --exit-code 243fi 244