#!/usr/bin/env bash
set -eufo pipefail
run_lint=false
format_all=false
mode="default"
for arg in "$@"; do
case "$arg" in
--lint) run_lint=true ;;
--all) format_all=true ;;
--pre-commit|--pre-push)
[[ "$mode" == "default" ]] || { echo "Cannot use both --pre-commit and --pre-push" >&2; exit 1; }
mode="${arg#--}"
;;
*)
echo "Unknown option: $arg" >&2
echo "Usage: $0 [--all] [--pre-commit] [--pre-push] [--lint]" >&2
exit 1
;;
esac
done
section() {
echo "- $*" >&2
}
trunk_ref="$(git rev-parse --verify trunk 2>/dev/null || echo "")"
if [[ "$format_all" == "false" && -n "$trunk_ref" ]]; then
base="$(git merge-base HEAD "$trunk_ref" 2>/dev/null || echo "")"
if [[ -n "$base" ]]; then
case "$mode" in
pre-commit)
changed="$(git diff --name-only --cached)"
;;
pre-push)
changed="$(git diff --name-only "$base" HEAD)"
;;
default)
committed="$(git diff --name-only "$base" HEAD)"
staged="$(git diff --name-only --cached)"
unstaged="$(git diff --name-only)"
untracked="$(git ls-files --others --exclude-standard)"
changed="$(printf '%s\n%s\n%s\n%s' "$committed" "$staged" "$unstaged" "$untracked" | sort -u)"
;;
esac
else
format_all=true
fi
elif [[ "$format_all" == "false" ]]; then
format_all=true
fi
changed_matches() {
[[ "$format_all" == "true" ]] || echo "$changed" | grep -qE "$1"
}
WORKSPACE_ROOT="$(bazel info workspace)"
baseline="$(git status --porcelain)"
section "Buildifier"
echo " buildifier" >&2
bazel run //:buildifier
section "Copyright"
echo " update_copyright" >&2
bazel run //scripts:update_copyright
if changed_matches '^java/'; then
section "Java"
echo " google-java-format" >&2
GOOGLE_JAVA_FORMAT="$(bazel run --run_under=echo //scripts:google-java-format)"
find "${WORKSPACE_ROOT}/java" -type f -name '*.java' -exec "$GOOGLE_JAVA_FORMAT" --replace {} +
fi
if changed_matches '^javascript/selenium-webdriver/'; then
section "JavaScript"
echo " prettier" >&2
NODE_WEBDRIVER="${WORKSPACE_ROOT}/javascript/selenium-webdriver"
bazel run //javascript:prettier -- "${NODE_WEBDRIVER}" --write "${NODE_WEBDRIVER}/.prettierrc" --log-level=warn
fi
if changed_matches '^rb/|^rake_tasks/|^Rakefile'; then
section "Ruby"
echo " rubocop -a" >&2
if [[ "$run_lint" == "true" ]]; then
bazel run //rb:rubocop -- -a
else
bazel run //rb:rubocop -- -a --fail-level F
fi
fi
if changed_matches '^rust/'; then
section "Rust"
echo " rustfmt" >&2
bazel run @rules_rust//:rustfmt
fi
if changed_matches '^py/'; then
section "Python"
if [[ "$run_lint" == "true" ]]; then
echo " ruff check" >&2
bazel run //py:ruff-check
fi
echo " ruff format" >&2
bazel run //py:ruff-format
fi
if changed_matches '^dotnet/'; then
section ".NET"
echo " dotnet format" >&2
bazel run //dotnet:format -- style --severity warn
bazel run //dotnet:format -- whitespace
fi
if [[ "$run_lint" == "true" ]]; then
section "Shell/Actions"
echo " actionlint (with shellcheck)" >&2
SHELLCHECK="$(bazel run --run_under=echo @multitool//tools/shellcheck)"
bazel run @multitool//tools/actionlint:cwd -- -shellcheck "$SHELLCHECK"
fi
if [[ "$(git status --porcelain)" != "$baseline" ]]; then
echo "" >&2
echo "Formatters modified files:" >&2
git diff --name-only >&2
exit 1
fi
echo "Format check passed." >&2