diff --git a/backends/arm/scripts/pre-push b/backends/arm/scripts/pre-push index c51138b8ec7..ac0584c6f73 100755 --- a/backends/arm/scripts/pre-push +++ b/backends/arm/scripts/pre-push @@ -4,42 +4,178 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -# Check 1: If commit header contains WIP, everything is ok -git rev-list --format=%s --max-count=1 HEAD | grep -q WIP && exit 0 - -# Check 2: lintunner on latest patches. -lintrunner --revision 'HEAD^' -if [[ $? != 0 ]] - then - echo "Failed linting" - exit 1 +RESET='\e[0m' +RED='\e[31m' +GREEN='\e[32m' +YELLOW='\e[33m' +BLUE='\e[34m' + +INFO="${BLUE}[INFO]${RESET}" +WARNING="${YELLOW}[WARNING]${RESET}" +ERROR="${RED}[ERROR]${RESET}" +SUCCESS="${GREEN}[SUCCESS]${RESET}" + +# This list of imperative verbs was compiled from the entire list of Executorch +# commits. It should be fairly exhaustive, but add more verbs if you find one +# that's missing. +VERBS="Add|Fix|Update|Refactor|Improve|Remove|Change|Implement|Create|Modify|"\ +"Enable|Integrate|Make|Support|Deprecate|Extend|Enhance|Convert|Rewrite|Unify|"\ +"Optimize|Expand|Reorganize|Adjust|Streamline|Clarify|Introduce|Document|"\ +"Polish|Standardize|Revise|Simplify|Restore|Resolve|Replace|Suppress|Migrate|"\ +"Generate|Delete|Exclude|Register|Include|Upgrade|Validate|Verify|Refine|"\ +"Reimplement|Patch|Sync|Revert|Fixup|Enhance|Append|Annotate|Disable|Emit|"\ +"Handle|Ignore|Interpret|Instantiate|Invoke|Limit|Load|Modify|Permit|Print|"\ +"Profile|Recalculate|Reconstruct|Redefine|Redesign|Reevaluate|Relocate|Remap|"\ +"Render|Reposition|Request|Revert|Sanitize|Specify|Strengthen|Stub|Substitute|"\ +"Tag|Tweak|Unify|Unlock|Unset|Use|Validate|Verify" + +# Remote branch +REMOTE=$(git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null) + +if [ -z "$REMOTE" ]; then + echo -e "${WARNING} Could not find upstream branch to compare to." + echo "Please specify the number of commits you are pushing." + echo -n "Enter number of commits to check (default 1): " > /dev/tty + read NUM_COMMITS < /dev/tty + NUM_COMMITS=${NUM_COMMITS:-1} # Default to 1 if empty + RANGE=$(git rev-list HEAD -n "$NUM_COMMITS") + COMMITS=${RANGE} +elif [ "$(git rev-parse --abbrev-ref HEAD)" == "HEAD" ]; then + echo -e "${WARNING} You're in a detached HEAD state." + echo "Please specify the number of commits you are pushing." + echo -n "Enter number of commits to check (default 1): " > /dev/tty + read NUM_COMMITS < /dev/tty + NUM_COMMITS=${NUM_COMMITS:-1} # Default to 1 if empty + RANGE=$(git rev-list HEAD -n "$NUM_COMMITS") + COMMITS=${RANGE} +else + # Determine commits to check + RANGE="$REMOTE..HEAD" + COMMITS=$(git rev-list "$RANGE") +fi + +if [ -z "$COMMITS" ]; then + echo -e "${INFO} No new commits to check." + exit 0 fi -# Check 3: License headers -# We do a simple check of if all committed headers contain "$current_year Arm". -# This does not guarantee OK in ci but should be ok most of the time. +for COMMIT in ${COMMITS}; do + # If commit header contains WIP, everything is ok + git rev-list --format=%s --max-count=1 ${COMMIT} | grep -q WIP && \ + continue + + echo -e "${INFO} Checking commit ${COMMIT}" + + # lintrunner on latest patches. + echo -e "${INFO} Lintrunner" + lintrunner --revision ${COMMIT} + if [[ $? != 0 ]]; then + echo -e "${ERROR} Failed linting" + FAILED=1 + else + echo -e "${SUCCESS} Lintrunner OK" + fi + + # Check license headers + # We do a simple check of if all committed headers contain + # "$current_year Arm". This does not guarantee OK in ci but should be ok + # most of the time. + echo -e "${INFO} License check" + + current_year=$(date +%Y) + failed_license_check=false + commit_files=$(git diff-tree --no-commit-id --name-only \ + --diff-filter=ACMR ${COMMIT} -r) + for commited_file in $commit_files; do + head $commited_file | grep -q "$current_year Arm" + if [[ $? != 0 ]]; then + echo -e "${ERROR} Header in $commited_file did not contain"\ + "'$current_year Arm'" + failed_license_check=true + else + echo -e "${SUCCESS} $commited_file passed license check" + fi + done + + if [[ $failed_license_check == true ]]; then + FAILED=1 + else + echo -e "${SUCCESS} All files passed license check" + fi + + # Check commit message + echo -e "${INFO} Checking commit message" + COMMIT_MSG=$(git log -1 --format=%B "$COMMIT") -current_year=$(date +%Y) -failed_license_check=false -commit_files=$(git diff-tree --no-commit-id --name-only --diff-filter=ACMR HEAD -r) + SUBJECT=$(echo "$COMMIT_MSG" | head -n1) + BODY=$(echo "$COMMIT_MSG" | tail -n +2) + # Check subject length (72 chars) + SUBJECT_MAX_LEN=72 + if [ ${#SUBJECT} -gt ${SUBJECT_MAX_LEN} ]; then + echo -e "${ERROR} Subject exceeds ${SUBJECT_MAX_LEN} characters:"\ + "'${SUBJECT}'" >&2 -for commited_file in $commit_files; do - head $commited_file | grep -q "$current_year Arm" - if [[ $? != 0 ]] - then - echo "Header in $commited_file did not contain '$current_year Arm'" - failed_license_check=true - else - echo "$commited_file passed license check" - fi + FAILED=1 + else + echo -e "${SUCCESS} Commit message subject OK" + fi + + # Check body line length (72 chars) + BODY_MAX_LEN=72 + line_number=2 # Subject + 1 empty line + failed_body_check=false + while IFS= read -r line; do + if [ ${#line} -gt ${BODY_MAX_LEN} ]; then + echo -e "${ERROR} Line ${line_number} in body exceeds"\ + "${BODY_MAX_LEN} characters: '$line'" >&2 + + failed_body_check=true + fi + + ((line_number++)) + done <<< "$BODY" + + if [[ $failed_body_check == true ]]; then + FAILED=1 + else + echo -e "${SUCCESS} Commit message body OK" + fi + + # Check for Signed-off-by + if ! echo "$COMMIT_MSG" | grep -qE "^Signed-off-by: "; then + echo -e "${ERROR} Commit message must contain a 'Signed-off-by'"\ + "footer." >&2 + + FAILED=1 + fi + + # Check subject format, should start with 'Arm backend: ' and be + # imperative mood. + if [[ ! "$SUBJECT" =~ ^"Arm backend":\ (${VERBS}) ]]; then + echo -e "${WARNING} Subject should start with 'Arm backend: '"\ + "followed by an imperative verb." >&2 + echo -n "There are warnings in your commit message. Do you want to"\ + "ignore the warning (y/N): " > /dev/tty + + read USER_INPUT < /dev/tty + + # Check user input for warnings + if [[ ! "$USER_INPUT" =~ ^[Yy]$ ]]; then + FAILED=1 + fi + fi + + echo "" # Newline to visually separate commit processing done -if [[ $failed_license_check == true ]] - then - exit 1 - else - echo "Passed simple license check" +if [[ $FAILED ]]; then + echo -e "${INFO} Fix your commit message errors with"\ + "'git commit --amend' or 'git commit --fixup='" + + exit 1 +else + echo -e "${SUCCESS} All checks passed" fi exit 0