check_format: Introduce shfmt tooling for .sh style enforcement
Add new dev tool for enforcing proper formatting of the Bash code across the entire repo. This is done in order of defining a common set of good practices to follow when writing .sh|Bash code. As powerful as shfmt may be, it allows only for some specific rules to be enforced, hence it still needs to work side by side with shellcheck syntax-wise. If it comes to style, following rules are being enforced: * indent_style = tab - Lines must be indented with tabs. The exception from this rule is the use of heredocs with <<BASH redirect operator. Spaces can be used to format the line only if it's already preceded with a tab. * binary_next_line = true - Lines can start with logical operators. E.g: if [[ -v foo ]] \ && [[ -v bar ]]; then ... fi * switch_case_indent = true - case|esac patterns are indented with tabs. * space_redirects = true - redirect operators are followed with a space. E.g: > foo over >foo. In addition, shfmt will enforce its own Bash-style for different parts of the code as well. Examples and more details can be found here: https://github.com/mvdan/sh Change-Id: I6e5c8d79e6dba9c6471010f3d0f563dd34e62fd6 Signed-off-by: Michal Berger <michalx.berger@intel.com> Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/1418 Reviewed-by: Karol Latecki <karol.latecki@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Reviewed-by: Darek Stojaczyk <dariusz.stojaczyk@intel.com> Community-CI: Mellanox Build Bot Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
This commit is contained in:
parent
844c8ec383
commit
d596ba87b2
@ -227,6 +227,84 @@ else
|
||||
echo "You do not have pycodestyle or pep8 installed so your Python style is not being checked!"
|
||||
fi
|
||||
|
||||
shfmt="shfmt-3.1.0"
|
||||
if hash "$shfmt" 2> /dev/null; then
|
||||
shfmt_cmdline=() silly_plural=()
|
||||
|
||||
silly_plural[1]="s"
|
||||
|
||||
commits=() sh_files=() sh_files_repo=() sh_files_staged=()
|
||||
|
||||
mapfile -t sh_files_repo < <(git ls-files '*.sh')
|
||||
# Fetch .sh files only from the commits that are targeted for merge
|
||||
while read -r _ commit; do
|
||||
commits+=("$commit")
|
||||
done < <(git cherry -v origin/master)
|
||||
|
||||
mapfile -t sh_files < <(git diff --name-only HEAD origin/master "${sh_files_repo[@]}")
|
||||
# In case of a call from a pre-commit git hook
|
||||
mapfile -t sh_files_staged < <(
|
||||
IFS="|"
|
||||
git diff --cached --name-only "${sh_files_repo[@]}" | grep -v "${sh_files[*]}"
|
||||
)
|
||||
|
||||
if ((${#sh_files[@]})); then
|
||||
printf 'Checking .sh formatting style...\n\n'
|
||||
printf ' Found %u updated|new .sh file%s from the following commits:\n' \
|
||||
"${#sh_files[@]}" "${silly_plural[${#sh_files[@]} > 1 ? 1 : 0]}"
|
||||
printf ' * %s\n' "${commits[@]}"
|
||||
|
||||
if ((${#sh_files_staged[@]})); then
|
||||
printf ' Found %u .sh file%s in staging area:\n' \
|
||||
"${#sh_files_staged[@]}" "${silly_plural[${#sh_files_staged[@]} > 1 ? 1 : 0]}"
|
||||
printf ' * %s\n' "${sh_files_staged[@]}"
|
||||
sh_files+=("${sh_files_staged[@]}")
|
||||
fi
|
||||
|
||||
printf ' Running %s against the following file%s:\n' "$shfmt" "${silly_plural[${#sh_files[@]} > 1 ? 1 : 0]}"
|
||||
printf ' * %s\n' "${sh_files[@]}"
|
||||
|
||||
shfmt_cmdline+=(-i 0) # indent_style = tab|indent_size = 0
|
||||
shfmt_cmdline+=(-bn) # binary_next_line = true
|
||||
shfmt_cmdline+=(-ci) # switch_case_indent = true
|
||||
shfmt_cmdline+=(-ln bash) # shell_variant = bash (default)
|
||||
shfmt_cmdline+=(-d) # diffOut - print diff of the changes and exit with != 0
|
||||
shfmt_cmdline+=(-sr) # redirect operators will be followed by a space
|
||||
|
||||
diff=$PWD/$shfmt.patch
|
||||
|
||||
# Explicitly tell shfmt to not look for .editorconfig. .editorconfig is also not looked up
|
||||
# in case any formatting arguments has been passed on its cmdline.
|
||||
if ! SHFMT_NO_EDITORCONFIG=true "$shfmt" "${shfmt_cmdline[@]}" "${sh_files[@]}" > "$diff"; then
|
||||
# In case shfmt detects an actual syntax error it will write out a proper message on
|
||||
# its stderr, hence the diff file should remain empty.
|
||||
if [[ -s $diff ]]; then
|
||||
diff_out=$(< "$diff")
|
||||
fi
|
||||
|
||||
cat <<- ERROR_SHFMT
|
||||
|
||||
* Errors in style formatting have been detected.
|
||||
${diff_out:+* Please, review the generated patch at $diff
|
||||
|
||||
# _START_OF_THE_DIFF
|
||||
|
||||
${diff_out:-ERROR}
|
||||
|
||||
# _END_OF_THE_DIFF
|
||||
}
|
||||
|
||||
ERROR_SHFMT
|
||||
rc=1
|
||||
else
|
||||
rm -f "$diff"
|
||||
printf 'OK\n'
|
||||
fi
|
||||
fi
|
||||
else
|
||||
printf '%s not detected, Bash style formatting check is skipped\n' "$shfmt"
|
||||
fi
|
||||
|
||||
if hash shellcheck 2> /dev/null; then
|
||||
echo -n "Checking Bash style..."
|
||||
|
||||
|
@ -28,6 +28,53 @@ function install_all_dependencies() {
|
||||
INSTALL_DOCS=true
|
||||
}
|
||||
|
||||
function install_shfmt() {
|
||||
# Fetch version that has been tested
|
||||
local shfmt_version=3.1.0
|
||||
local shfmt=shfmt-$shfmt_version
|
||||
local shfmt_dir=${SHFMT_DIR:-/opt/shfmt}
|
||||
local shfmt_dir_out=${SHFMT_DIR_OUT:-/usr/bin}
|
||||
local shfmt_url
|
||||
local os
|
||||
|
||||
if hash "$shfmt" && [[ $("$shfmt" --version) == "v$shfmt_version" ]]; then
|
||||
echo "$shfmt already installed"
|
||||
return 0
|
||||
fi 2> /dev/null
|
||||
|
||||
os=$(uname -s)
|
||||
|
||||
case "$os" in
|
||||
Linux) shfmt_url=https://github.com/mvdan/sh/releases/download/v$shfmt_version/shfmt_v${shfmt_version}_linux_amd64 ;;
|
||||
FreeBSD) shfmt_url=https://github.com/mvdan/sh/releases/download/v$shfmt_version/shfmt_v${shfmt_version}_freebsd_amd64 ;;
|
||||
*)
|
||||
echo "Not supported OS (${os:-Unknown}), skipping"
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
mkdir -p "$shfmt_dir"
|
||||
mkdir -p "$shfmt_dir_out"
|
||||
|
||||
echo "Fetching ${shfmt_url##*/}"...
|
||||
local err
|
||||
if err=$(curl -f -Lo"$shfmt_dir/$shfmt" "$shfmt_url" 2>&1); then
|
||||
chmod +x "$shfmt_dir/$shfmt"
|
||||
ln -sf "$shfmt_dir/$shfmt" "$shfmt_dir_out"
|
||||
else
|
||||
cat <<- CURL_ERR
|
||||
|
||||
* Fetching $shfmt_url failed, $shfmt will not be available for format check.
|
||||
* Error:
|
||||
|
||||
$err
|
||||
|
||||
CURL_ERR
|
||||
return 0
|
||||
fi
|
||||
echo "$shfmt installed"
|
||||
}
|
||||
|
||||
INSTALL_CRYPTO=false
|
||||
INSTALL_DEV_TOOLS=false
|
||||
INSTALL_PMEM=false
|
||||
@ -119,6 +166,7 @@ if [ -s /etc/redhat-release ]; then
|
||||
yum install -y git astyle sg3_utils pciutils
|
||||
# Additional (optional) dependencies for showing backtrace in logs
|
||||
yum install -y libunwind-devel || true
|
||||
install_shfmt
|
||||
fi
|
||||
if [[ $INSTALL_PMEM == "true" ]]; then
|
||||
# Additional dependencies for building pmem based backends
|
||||
@ -167,6 +215,7 @@ elif [ -f /etc/debian_version ]; then
|
||||
apt-get install -y libunwind-dev || true
|
||||
# Additional dependecies for nvmf performance test script
|
||||
apt-get install -y python3-paramiko
|
||||
install_shfmt
|
||||
fi
|
||||
if [[ $INSTALL_PMEM == "true" ]]; then
|
||||
# Additional dependencies for building pmem based backends
|
||||
@ -205,6 +254,7 @@ elif [ -f /etc/SuSE-release ] || [ -f /etc/SUSE-brand ]; then
|
||||
pciutils ShellCheck
|
||||
# Additional (optional) dependencies for showing backtrace in logs
|
||||
zypper install libunwind-devel || true
|
||||
install_shfmt
|
||||
fi
|
||||
if [[ $INSTALL_PMEM == "true" ]]; then
|
||||
# Additional dependencies for building pmem based backends
|
||||
@ -232,6 +282,7 @@ elif [ $(uname -s) = "FreeBSD" ]; then
|
||||
# Tools for developers
|
||||
pkg install -y devel/astyle bash py27-pycodestyle \
|
||||
misc/e2fsprogs-libuuid sysutils/sg3_utils nasm
|
||||
install_shfmt
|
||||
fi
|
||||
if [[ $INSTALL_DOCS == "true" ]]; then
|
||||
# Additional dependencies for building docs
|
||||
@ -272,6 +323,7 @@ elif [ -f /etc/arch-release ]; then
|
||||
makepkg -si --needed --noconfirm;
|
||||
cd .. && rm -rf lcov-git;
|
||||
popd"
|
||||
install_shfmt
|
||||
fi
|
||||
if [[ $INSTALL_PMEM == "true" ]]; then
|
||||
# Additional dependencies for building pmem based backends
|
||||
|
Loading…
Reference in New Issue
Block a user