Files
github-action/tools/quality-checks.sh

123 lines
3.6 KiB
Bash

#!/usr/bin/env bash
# Steps of the reusable workflow .gitea/workflows/quality-checks.yml (ADR-0005).
# The workflow checks out the calling repository, fetches this file into
# shared-scripts/ and runs one command per step:
#
# quality-checks.sh validate check the inputs, before anything is installed
# quality-checks.sh install install the pinned tools into a virtual environment
# quality-checks.sh run run every selected tool on the tracked files
#
# Settings come from the environment, never from interpolated expressions:
# LANGUAGES, WORKING_DIRECTORY, RUFF_VERSION, MYPY_VERSION,
# SHELLCHECK_PY_VERSION, SHFMT_PY_VERSION, QC_VENV (and QC_BIN for tests).
# Every failure is printed as ::error::[category] message.
validate_inputs() {
set -u
IFS=',' read -ra langs <<< "${LANGUAGES// /}"
if [ "${#langs[@]}" -eq 0 ]; then
echo "::error::[configuration] languages is empty; use python, shell or both, comma separated."
exit 1
fi
for lang in "${langs[@]}"; do
case "$lang" in
python | shell) ;;
*)
echo "::error::[configuration] Unknown language '$lang'; use python and shell, comma separated."
exit 1
;;
esac
done
case "$WORKING_DIRECTORY" in
"" | /* | .. | ../* | */.. | */../*)
echo "::error::[configuration] working-directory must be a relative path inside the repository."
exit 1
;;
esac
echo "Checking ${LANGUAGES} in ${WORKING_DIRECTORY}"
}
install_tools() {
set -eu
IFS=',' read -ra langs <<< "${LANGUAGES// /}"
packages=()
for lang in "${langs[@]}"; do
case "$lang" in
python) packages+=("ruff==${RUFF_VERSION}" "mypy==${MYPY_VERSION}") ;;
shell) packages+=("shellcheck-py==${SHELLCHECK_PY_VERSION}" "shfmt-py==${SHFMT_PY_VERSION}") ;;
esac
done
python3 -m venv "$QC_VENV"
"$QC_VENV/bin/pip" install --quiet "${packages[@]}"
}
run_checks() {
set -u
QC_BIN="${QC_BIN:-$QC_VENV/bin}"
if [ ! -d "$WORKING_DIRECTORY" ]; then
echo "::error::[configuration] working-directory '$WORKING_DIRECTORY' does not exist."
exit 1
fi
cd "$WORKING_DIRECTORY"
failed=()
run_tool() {
local name="$1"
shift
echo "::group::$name"
if "$@"; then
echo "::endgroup::"
echo "PASS $name"
else
echo "::endgroup::"
echo "FAIL $name"
failed+=("$name")
fi
}
wanted() {
local lang
IFS=',' read -ra langs <<< "${LANGUAGES// /}"
for lang in "${langs[@]}"; do
[ "$lang" = "$1" ] && return 0
done
return 1
}
if wanted python; then
mapfile -t files < <(git ls-files -- '*.py')
if [ "${#files[@]}" -eq 0 ]; then
echo "::notice::No python files in '$WORKING_DIRECTORY'; nothing was checked."
else
run_tool "ruff check" "$QC_BIN/ruff" check "${files[@]}"
run_tool "ruff format --check" "$QC_BIN/ruff" format --check "${files[@]}"
run_tool "mypy --strict" "$QC_BIN/mypy" --strict "${files[@]}"
fi
fi
if wanted shell; then
mapfile -t files < <(git ls-files -- '*.sh')
if [ "${#files[@]}" -eq 0 ]; then
echo "::notice::No shell files in '$WORKING_DIRECTORY'; nothing was checked."
else
run_tool "shellcheck" "$QC_BIN/shellcheck" "${files[@]}"
run_tool "shfmt -d" "$QC_BIN/shfmt" -d "${files[@]}"
fi
fi
if [ "${#failed[@]}" -gt 0 ]; then
echo "::error::Quality checks failed: ${failed[*]}"
exit 1
fi
echo "All selected checks passed."
}
case "${1:-}" in
validate) validate_inputs ;;
install) install_tools ;;
run) run_checks ;;
*)
echo "::error::[configuration] usage: quality-checks.sh validate|install|run" >&2
exit 2
;;
esac