#!/usr/bin/env bash # # A git commit hook that will automatically run format checking and DCO signoff # checking before the push is successful. # # To enable this hook, run `bootstrap`, or run the following from the root of # the repo. (Note that `bootstrap` will correctly install this even if this code # is located in a submodule, while the following won't.) # # $ ln -s ../../support/hooks/pre-push .git/hooks/pre-push DUMMY_SHA=0000000000000000000000000000000000000000 echo "Running pre-push check; to skip this step use 'push --no-verify'" while read LOCAL_REF LOCAL_SHA REMOTE_REF REMOTE_SHA do if [ "$LOCAL_SHA" = $DUMMY_SHA ] then # Branch deleted. Do nothing. exit 0 else if [ "$REMOTE_SHA" = $DUMMY_SHA ] then # New branch. Verify the last commit, since this is very likely where the new code is # (though there is no way to know for sure). In the extremely uncommon case in which someone # pushes more than 1 new commit to a branch, CI will enforce full checking. RANGE="$LOCAL_SHA~1..$LOCAL_SHA" else # Updating branch. Verify new commits. RANGE="$REMOTE_SHA..$LOCAL_SHA" fi # Verify DCO signoff. We do this before the format checker, since it has # some probability of failing spuriously, while this check never should. # # In general, we can't assume that the commits are signed off by author # pushing, so we settle for just checking that there is a signoff at all. SIGNED_OFF=$(git rev-list --no-merges --grep "^Signed-off-by: " "$RANGE") NOT_SIGNED_OFF=$(git rev-list --no-merges "$RANGE" | grep -Fxv "$SIGNED_OFF") if [ -n "$NOT_SIGNED_OFF" ] then echo >&2 "ERROR: The following commits do not have DCO signoff:" while read -r commit; do echo " $(git log --pretty=oneline --abbrev-commit -n 1 $commit)" done <<< "$NOT_SIGNED_OFF" exit 1 fi # NOTE: The `tools` directory will be the same relative to the support # directory, independent of whether we're in a submodule, so no need to use # our `relpath` helper. SCRIPT_DIR="$(dirname "$(realpath "$0")")/../../tools" # TODO(hausdorff): We should have a more graceful failure story when the # user does not have all the tools set up correctly. This script assumes # `$CLANG_FORMAT` and `$BUILDIFY` are defined, or that the default values it # assumes for these variables correspond to real binaries on the system. If # either of these things aren't true, the check fails. for i in $(git diff --name-only $RANGE --diff-filter=ACMR --ignore-submodules=all 2>&1); do echo -ne " Checking format for $i - " "$SCRIPT_DIR"/code_format/check_format.py check $i if [[ $? -ne 0 ]]; then exit 1 fi echo " Checking spelling for $i" "$SCRIPT_DIR"/spelling/check_spelling_pedantic.py check $i if [[ $? -ne 0 ]]; then exit 1 fi done # TODO(mattklein123): Optimally we would be able to do this on a per-file basis. "$SCRIPT_DIR"/proto_format/proto_format.sh check if [[ $? -ne 0 ]]; then exit 1 fi "$SCRIPT_DIR"/code_format/format_python_tools.sh check if [[ $? -ne 0 ]]; then exit 1 fi # Check correctness of repositories definitions. echo " Checking repositories definitions" if ! "$SCRIPT_DIR"/check_repositories.sh; then exit 1 fi fi done exit 0