diff --git a/check-style.sh b/check-style.sh index 2f88963..8ee6a00 100755 --- a/check-style.sh +++ b/check-style.sh @@ -41,57 +41,18 @@ case $1 in exit 2 esac -# Filter out symlinks from `affected_files`. We'll check the real files. -# Use bash test operators instead of the 'file' command to avoid issues with -# filenames containing special characters (colons, commas, etc). -filtered_files="" -for f in $affected_files; do - if [ ! -L "$f" ]; then - filtered_files="$filtered_files $f" - fi -done -affected_files=$(echo "$filtered_files" | xargs -r) +# Source shared helper functions. +dev_tools_path=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) +source "${dev_tools_path}/style-helpers.sh" + +# Filter out symlinks. +filter_symlinks # Unset variable would be a sign of programmer error. We are not using '-e' in # this script as we'd like to handle these cases ourselves where relevant, i.e., # allow more than one code check failure per run. set -u -# Work from the git root. This is important to help some tools pick up the -# correct configuration. -git_root=$(git rev-parse --show-toplevel) -cd "${git_root}" - -# Define a cummulative status code for the script. The value is Updated through -# 'run_check' when running checks. A status code creater than 0 will indicate -# that one or more checks failed. -status_code=0 - -# Run a code check command and update the cummulative error code. -run_check() { - $@ - status_code=$(($status_code + $?)) -} - -# Helper function to filter affected files by extension. -get_files_by_extension() { - filter="" - for ext in $@; do - # We want a `.` to be a full stop, not a regexp wildcard. - filter="$(echo $ext | sed -e 's|^\.|\\.|')$|${filter}" - done - filter=$(echo $filter | sed -e 's/|$//') - - echo $affected_files | tr ' ' '\n' | egrep $filter -} - -# Helper function to get a checksum of a file list. -# Calculates a per-file checksum, sorts by filename, and then returns a sum over -# the sums. -calculate_checksum() { - sha256sum $@ | sort -k 2 | sha256sum -} - # Check files that we can clang-format. clang_format_files=$(get_files_by_extension .cc .cpp .h .hpp .proto) if [ ! -z "${clang_format_files}" ]; then @@ -106,7 +67,6 @@ if [ ! -z "${clang_format_files}" ]; then # invoke these scripts differently: # TODO: Consider tidying up this and perhaps solve the above ISSUE in the # process. - dev_tools_path=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) case $mode in "full" ) # For full checking, we can invoke the wrapper script directly. diff --git a/fix-style.sh b/fix-style.sh new file mode 100755 index 0000000..665ad20 --- /dev/null +++ b/fix-style.sh @@ -0,0 +1,74 @@ +#!/bin/bash +# Fix formatting of files with unstaged changes in the git repo. + + +# Source shared helper functions. +dev_tools_path=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) +source "${dev_tools_path}/style-helpers.sh" + +# Get files with unstaged changes only (not staged; we assume that those have +# already been formatted). +affected_files=$(git diff --name-only 2>/dev/null) + +# If there's nothing to do, exit early. +if [ -z "${affected_files}" ]; then + exit 0 +fi + +# Filter out symlinks. +filter_symlinks + +# Filter to only files that actually exist (not deleted). +existing_files="" +for f in $affected_files; do + if [ -f "$f" ]; then + existing_files="$existing_files $f" + fi +done +affected_files=$(echo "$existing_files" | xargs -r) + +if [ -z "${affected_files}" ]; then + exit 0 +fi + +# Fix C/C++/proto files with clang-format. +clang_format_files=$(get_files_by_extension .cc .cpp .h .hpp .proto) +if [ ! -z "${clang_format_files}" ]; then + run_check clang-format -i ${clang_format_files} +fi + +# Fix Bazel files with buildifier. +bazel_files=$(get_files_by_extension .bzl .bazel BUILD WORKSPACE) +if [ ! -z "${bazel_files}" ]; then + run_check buildifier --lint=fix --warnings=all ${bazel_files} +fi + +# Fix Python files. +python_files=$(get_files_by_extension .py) +if [ ! -z "${python_files}" ]; then + run_check ruff check --fix ${python_files} + run_check isort ${python_files} + run_check yapf -i -p ${python_files} +fi + +# Fix all files with prettier. +run_check prettier --write --ignore-unknown \ + --loglevel=warn ${affected_files} + +# Recurse into submodules that have unstaged changes. Only do this from the +# top-level invocation (not from a recursive call inside a submodule) to avoid +# infinite recursion. +if [ -z "${_FIX_STYLE_RECURSING:-}" ]; then + export _FIX_STYLE_RECURSING=1 + dirty_submodules=$(git submodule status --recursive \ + 2>/dev/null | grep '^+' | awk '{print $2}') + for submodule in $dirty_submodules; do + if [ -d "$submodule" ]; then + (cd "$submodule" && \ + "${dev_tools_path}/fix-style.sh") + status_code=$(($status_code + $?)) + fi + done +fi + +exit $status_code diff --git a/style-helpers.sh b/style-helpers.sh new file mode 100755 index 0000000..22927bc --- /dev/null +++ b/style-helpers.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# Shared helper functions for check-style.sh and fix-style.sh. + +# Work from the git root. This is important to help some tools pick +# up the correct configuration. +git_root=$(git rev-parse --show-toplevel) +cd "${git_root}" + +# Define a cumulative status code. The value is updated through +# `run_check` when running checks. A status code greater than 0 +# will indicate that one or more checks failed. +status_code=0 + +# Run a command and update the cumulative error code. +run_check() { + "$@" + status_code=$(($status_code + $?)) +} + +# Filter `affected_files` by extension(s). +get_files_by_extension() { + filter="" + for ext in "$@"; do + # We want a `.` to be a full stop, not a regexp wildcard. + filter="$(echo $ext | sed -e 's|^\.|\\.|')$|${filter}" + done + filter=$(echo $filter | sed -e 's/|$//') + + echo $affected_files | tr ' ' '\n' | egrep $filter +} + +# Get a checksum of a file list. Calculates a per-file checksum, +# sorts by filename, and then returns a sum over the sums. +calculate_checksum() { + sha256sum "$@" | sort -k 2 | sha256sum +} + +# Filter out symlinks from `affected_files`. +filter_symlinks() { + filtered_files="" + for f in $affected_files; do + if [ ! -L "$f" ]; then + filtered_files="$filtered_files $f" + fi + done + affected_files=$(echo "$filtered_files" | xargs -r) +}