blob: 0000189d9ee7341a42cc0ac17796f6e62f753b02 [file] [log] [blame]
Andrew Geissler9b0105c2018-01-10 10:58:35 -08001#!/bin/bash
Patrick Williamsc1513c82022-11-27 17:37:08 -06002set -e
Andrew Geissler9b0105c2018-01-10 10:58:35 -08003
Patrick Williamsc1513c82022-11-27 17:37:08 -06004# This script reformats source files using various formatters and linters.
Andrew Geissler9b0105c2018-01-10 10:58:35 -08005#
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#
Patrick Williamsc1513c82022-11-27 17:37:08 -060011function display_help()
12{
Patrick Williamsd27ab4c2022-11-27 17:51:35 -060013 echo "usage: format-code.sh [-h | --help] [--no-diff]"
Patrick Williamsc1513c82022-11-27 17:37:08 -060014 echo " [<path>]"
15 echo
16 echo "Format and lint a repository."
17 echo
18 echo "Arguments:"
Patrick Williamsd27ab4c2022-11-27 17:51:35 -060019 echo " --no-diff Don't show final diff output"
Patrick Williamsc1513c82022-11-27 17:37:08 -060020 echo " path Path to git repository (default to pwd)"
21}
Andrew Geissler9b0105c2018-01-10 10:58:35 -080022
Patrick Williamsd27ab4c2022-11-27 17:51:35 -060023eval set -- "$(getopt -o 'h' --long 'help,no-diff' -n 'format-code.sh' -- "$@")"
Patrick Williamsc1513c82022-11-27 17:37:08 -060024while true; do
25 case "$1" in
26 '-h'|'--help')
27 display_help && exit 0
28 ;;
Andrew Geissler9b0105c2018-01-10 10:58:35 -080029
Patrick Williamsd27ab4c2022-11-27 17:51:35 -060030 '--no-diff')
31 OPTION_NO_DIFF=1
32 shift
33 ;;
34
Patrick Williamsc1513c82022-11-27 17:37:08 -060035 '--')
36 shift
37 break
38 ;;
39
40 *)
41 echo "unknown option: $1"
42 display_help && exit 1
43 ;;
44 esac
45done
46
47# Detect tty and set nicer colors.
48if [ -t 1 ]; then
49 BLUE="\e[34m"
50 GREEN="\e[32m"
51 NORMAL="\e[0m"
52 RED="\e[31m"
53 YELLOW="\e[33m"
54else # non-tty, no escapes.
55 BLUE=""
56 GREEN=""
57 NORMAL=""
58 RED=""
59 YELLOW=""
60fi
61
62# Path to default config files for linters.
63CONFIG_PATH="$(git -C "$(dirname "${BASH_SOURCE[0]}")" rev-parse --show-toplevel)/config"
64
65# Find repository root for `pwd` or $1.
66if [ -z "$1" ]; then
67 DIR="$(git rev-parse --show-toplevel || pwd)"
68else
69 DIR="$(git -C "$1" rev-parse --show-toplevel)"
70fi
71if [ ! -d "$DIR/.git" ]; then
72 echo "${RED}Error:${NORMAL} Directory ($DIR) does not appear to be a git repository"
73 exit 1
74fi
Andrew Jeffery0cce8482018-04-30 11:37:01 +093075
Manojkiran Edae6f120a2021-03-20 11:13:43 +053076cd "${DIR}"
Patrick Williamsc1513c82022-11-27 17:37:08 -060077echo -e " ${BLUE}Formatting code under${NORMAL} $DIR"
Andrew Geissler9b0105c2018-01-10 10:58:35 -080078
Patrick Williams38515a32022-11-27 06:58:56 -060079ALL_OPERATIONS=( \
Patrick Williams38515a32022-11-27 06:58:56 -060080 commit_gitlint \
81 commit_spelling \
Patrick Williamsc1513c82022-11-27 17:37:08 -060082 clang_format \
Patrick Williams38515a32022-11-27 06:58:56 -060083 eslint \
84 pycodestyle \
85 shellcheck \
86 )
Manojkiran Eda87111bb2021-08-14 11:26:16 +053087
Patrick Williams38515a32022-11-27 06:58:56 -060088function do_commit_spelling() {
Patrick Williamsc1513c82022-11-27 17:37:08 -060089 if [ ! -e .git/COMMIT_EDITMSG ]; then
90 return
91 fi
92 echo -e " ${BLUE}Running codespell${NORMAL}"
Ed Tanousac5915f2022-03-10 08:32:54 -080093
Patrick Williams38515a32022-11-27 06:58:56 -060094 # Run the codespell with openbmc spcific spellings on the patchset
95 echo "openbmc-dictionary - misspelling count >> "
Patrick Williamsc1513c82022-11-27 17:37:08 -060096 sed "s/Signed-off-by.*//" .git/COMMIT_EDITMSG | \
97 codespell -D "${CONFIG_PATH}/openbmc-spelling.txt" -d --count -
Patrick Williams38515a32022-11-27 06:58:56 -060098
99 # Run the codespell with generic dictionary on the patchset
100 echo "generic-dictionary - misspelling count >> "
Patrick Williamsc1513c82022-11-27 17:37:08 -0600101 sed "s/Signed-off-by.*//" .git/COMMIT_EDITMSG | \
Patrick Williams38515a32022-11-27 06:58:56 -0600102 codespell --builtin clear,rare,en-GB_to_en-US -d --count -
103}
104
105function do_commit_gitlint() {
Patrick Williamsc1513c82022-11-27 17:37:08 -0600106 echo -e " ${BLUE}Running gitlint${NORMAL}"
Patrick Williams38515a32022-11-27 06:58:56 -0600107 # Check for commit message issues
108 gitlint \
Patrick Williamsc1513c82022-11-27 17:37:08 -0600109 --extra-path "${CONFIG_PATH}/gitlint/" \
110 --config "${CONFIG_PATH}/.gitlint"
Patrick Williams38515a32022-11-27 06:58:56 -0600111}
112
113function do_eslint() {
114 if [[ -f ".eslintignore" ]]; then
115 ESLINT_IGNORE="--ignore-path .eslintignore"
116 elif [[ -f ".gitignore" ]]; then
117 ESLINT_IGNORE="--ignore-path .gitignore"
118 fi
119
120 # Get the eslint configuration from the repository
121 if [[ -f ".eslintrc.json" ]]; then
Patrick Williamsc1513c82022-11-27 17:37:08 -0600122 echo -e " ${BLUE}Running eslint${NORMAL}"
Patrick Williams38515a32022-11-27 06:58:56 -0600123 ESLINT_RC="-c .eslintrc.json"
124 else
Patrick Williamsc1513c82022-11-27 17:37:08 -0600125 echo -e " ${BLUE}Running eslint using ${YELLOW}the global config${NORMAL}"
126 ESLINT_RC="--no-eslintrc -c ${CONFIG_PATH}/eslint-global-config.json"
Patrick Williams38515a32022-11-27 06:58:56 -0600127 fi
128
129 ESLINT_COMMAND="eslint . ${ESLINT_IGNORE} ${ESLINT_RC} \
Ed Tanous0c4153b2022-08-17 10:20:26 -0700130 --ext .json --format=stylish \
Ed Tanousac5915f2022-03-10 08:32:54 -0800131 --resolve-plugins-relative-to /usr/local/lib/node_modules \
132 --no-error-on-unmatched-pattern"
133
Patrick Williams38515a32022-11-27 06:58:56 -0600134 # Print eslint command
135 echo "$ESLINT_COMMAND"
136 # Run eslint
137 $ESLINT_COMMAND
138}
Manojkiran Eda87111bb2021-08-14 11:26:16 +0530139
Patrick Williams38515a32022-11-27 06:58:56 -0600140function do_pycodestyle() {
141 if [[ -f "setup.cfg" ]]; then
Patrick Williamsc1513c82022-11-27 17:37:08 -0600142 echo -e " ${BLUE}Running pycodestyle${NORMAL}"
Patrick Williams38515a32022-11-27 06:58:56 -0600143 pycodestyle --show-source --exclude=subprojects .
144 rc=$?
145 if [[ ${rc} -ne 0 ]]; then
146 exit ${rc}
147 fi
James Feistf1665d62019-11-22 09:06:33 -0800148 fi
Patrick Williams38515a32022-11-27 06:58:56 -0600149}
James Feistf1665d62019-11-22 09:06:33 -0800150
Patrick Williams38515a32022-11-27 06:58:56 -0600151function do_shellcheck() {
152 # If .shellcheck exists, stop on error. Otherwise, allow pass.
153 if [[ -f ".shellcheck" ]]; then
154 local shellcheck_allowfail="false"
155 else
156 local shellcheck_allowfail="true"
157 fi
James Feistf1665d62019-11-22 09:06:33 -0800158
Patrick Williams38515a32022-11-27 06:58:56 -0600159 # Run shellcheck on any shell-script.
160 shell_scripts="$(git ls-files | xargs -n1 file -0 | \
161 grep -a "shell script" | cut -d '' -f 1)"
Patrick Williamsc1513c82022-11-27 17:37:08 -0600162 if [ -n "${shell_scripts}" ]; then
163 echo -e " ${BLUE}Running shellcheck${NORMAL}"
164 fi
Patrick Williams38515a32022-11-27 06:58:56 -0600165 for script in ${shell_scripts}; do
166 shellcheck --color=never -x "${script}" || ${shellcheck_allowfail}
167 done
168}
James Feistf1665d62019-11-22 09:06:33 -0800169
Patrick Williams38515a32022-11-27 06:58:56 -0600170
171do_clang_format() {
172 # Allow called scripts to know which clang format we are using
173 export CLANG_FORMAT="clang-format"
174 IGNORE_FILE=".clang-ignore"
175 declare -a IGNORE_LIST
176
177 if [[ -f "${IGNORE_FILE}" ]]; then
178 readarray -t IGNORE_LIST < "${IGNORE_FILE}"
179 fi
180
181 ignorepaths=""
182 ignorefiles=""
183
184 for path in "${IGNORE_LIST[@]}"; do
185 # Check for comment, line starting with space, or zero-length string.
186 # Checking for [[:space:]] checks all options.
187 if [[ -z "${path}" ]] || [[ "${path}" =~ ^(#|[[:space:]]).*$ ]]; then
188 continue
189 fi
190
191 # All paths must start with ./ for find's path prune expectation.
192 if [[ "${path}" =~ ^\.\/.+$ ]]; then
193 ignorepaths+=" ${path}"
194 else
195 ignorefiles+=" ${path}"
196 fi
197 done
198
199 searchfiles=""
200 while read -r path; do
201 # skip ignorefiles
202 if [[ $ignorefiles == *"$(basename "${path}")"* ]]; then
203 continue
204 fi
205
206 skip=false
207 #skip paths in ingorepaths
208 for pathname in $ignorepaths; do
209 if [[ "./${path}" == "${pathname}"* ]]; then
210 skip=true
211 break
212 fi
213 done
214
215 if [ "$skip" = true ]; then
216 continue
217 fi
218 # shellcheck disable=2089
219 searchfiles+="\"./${path}\" "
220
221 # Get C and C++ files managed by git and skip the mako files
222 done <<<"$(git ls-files | grep -e '\.[ch]pp$' -e '\.[ch]$' | grep -v '\.mako\.')"
223
224 if [[ -f ".clang-format" ]]; then
Patrick Williamsc1513c82022-11-27 17:37:08 -0600225 echo -e " ${BLUE}Running clang-format${NORMAL}"
Patrick Williams38515a32022-11-27 06:58:56 -0600226 # shellcheck disable=SC2090 disable=SC2086
227 echo ${searchfiles} | xargs "${CLANG_FORMAT}" -i
Patrick Williams38515a32022-11-27 06:58:56 -0600228 fi
229
230}
231
232for op in "${ALL_OPERATIONS[@]}"; do
233 "do_$op"
234done
Andrew Jeffery457b6d12018-03-09 15:28:14 +1030235
Patrick Williamsd27ab4c2022-11-27 17:51:35 -0600236if [ -z "$OPTION_NO_DIFF" ]; then
237 echo -e " ${BLUE}Result differences...${NORMAL}"
238 if ! git --no-pager diff --exit-code ; then
239 echo -e "Format: ${RED}FAILED${NORMAL}"
240 exit 1
241 else
242 echo -e "Format: ${GREEN}PASSED${NORMAL}"
243 fi
Patrick Williamsc1513c82022-11-27 17:37:08 -0600244fi
245
Andrew Jeffery457b6d12018-03-09 15:28:14 +1030246# Sometimes your situation is terrible enough that you need the flexibility.
247# For example, phosphor-mboxd.
Patrick Williams330b0772022-11-28 06:14:06 -0600248for formatter in "format-code.sh" "format-code"; do
249 if [[ -x "${formatter}" ]]; then
250 echo -e " ${BLUE}Calling secondary formatter:${NORMAL} ${formatter}"
251 "./${formatter}"
252 if [ -z "$OPTION_NO_DIFF" ]; then
253 git --no-pager diff --exit-code
254 fi
Patrick Williamsd27ab4c2022-11-27 17:51:35 -0600255 fi
Patrick Williams330b0772022-11-28 06:14:06 -0600256done