2023-05-15 22:31:21 +01:00
|
|
|
#!/usr/bin/env bash
|
2026-02-03 12:51:23 -06:00
|
|
|
# Code formatter - runs targeted formatters based on what changed from trunk.
|
|
|
|
|
# Usage: format.sh [--all] [--pre-commit] [--pre-push] [--lint]
|
|
|
|
|
# (default) Check all changes relative to trunk including uncommitted work
|
|
|
|
|
# --all Format everything, skip change detection (previous behavior)
|
|
|
|
|
# --pre-commit Only check staged changes
|
|
|
|
|
# --pre-push Only check committed changes relative to trunk
|
|
|
|
|
# --lint Also run linters before formatting
|
2023-05-15 22:31:21 +01:00
|
|
|
set -eufo pipefail
|
|
|
|
|
|
2026-02-03 12:51:23 -06:00
|
|
|
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
|
2026-01-31 14:26:06 -06:00
|
|
|
|
2023-05-15 22:31:21 +01:00
|
|
|
section() {
|
|
|
|
|
echo "- $*" >&2
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-03 12:51:23 -06:00
|
|
|
# Find what's changed compared to trunk (skip if --all)
|
|
|
|
|
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
|
|
|
|
|
# No trunk ref found, format everything
|
|
|
|
|
format_all=true
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Helper to check if a pattern matches changed files
|
|
|
|
|
changed_matches() {
|
|
|
|
|
[[ "$format_all" == "true" ]] || echo "$changed" | grep -qE "$1"
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-28 09:42:56 -04:00
|
|
|
WORKSPACE_ROOT="$(bazel info workspace)"
|
2024-01-10 12:00:37 +00:00
|
|
|
|
2026-02-03 12:51:23 -06:00
|
|
|
# Capture baseline to detect formatter-introduced changes (allows pre-existing uncommitted work)
|
|
|
|
|
baseline="$(git status --porcelain)"
|
2023-05-15 22:31:21 +01:00
|
|
|
|
2026-02-03 12:51:23 -06:00
|
|
|
# Always run buildifier and copyright
|
2023-06-19 16:21:57 +01:00
|
|
|
section "Buildifier"
|
|
|
|
|
echo " buildifier" >&2
|
|
|
|
|
bazel run //:buildifier
|
|
|
|
|
|
2026-02-03 12:51:23 -06:00
|
|
|
section "Copyright"
|
|
|
|
|
echo " update_copyright" >&2
|
|
|
|
|
bazel run //scripts:update_copyright
|
|
|
|
|
|
|
|
|
|
# Run language formatters only if those files changed
|
|
|
|
|
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
|
2023-09-11 13:02:40 +01:00
|
|
|
|
2026-02-03 12:51:23 -06:00
|
|
|
if changed_matches '^dotnet/'; then
|
|
|
|
|
section ".NET"
|
|
|
|
|
echo " dotnet format" >&2
|
|
|
|
|
bazel run //dotnet:format -- style --severity warn
|
|
|
|
|
bazel run //dotnet:format -- whitespace
|
|
|
|
|
fi
|
2024-01-10 12:00:37 +00:00
|
|
|
|
2026-02-03 12:51:23 -06:00
|
|
|
# Run shellcheck and actionlint when --lint is passed
|
|
|
|
|
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
|
2024-03-15 16:43:18 -07:00
|
|
|
|
2026-02-03 12:51:23 -06:00
|
|
|
# Check if formatting introduced new changes (comparing to baseline)
|
|
|
|
|
if [[ "$(git status --porcelain)" != "$baseline" ]]; then
|
|
|
|
|
echo "" >&2
|
|
|
|
|
echo "Formatters modified files:" >&2
|
|
|
|
|
git diff --name-only >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
2024-01-29 10:00:22 -05:00
|
|
|
|
2026-02-03 12:51:23 -06:00
|
|
|
echo "Format check passed." >&2
|