Skip to content

[WIP]Add git hooks for Cloudberry Database development #221

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions hooks/install

This file was deleted.

47 changes: 0 additions & 47 deletions hooks/pre-push

This file was deleted.

2 changes: 1 addition & 1 deletion .gitmessage → src/tools/hooks/.gitmessage
Original file line number Diff line number Diff line change
@@ -44,4 +44,4 @@ Add your commit body here
#
#
#
#
#
49 changes: 49 additions & 0 deletions src/tools/hooks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
## Configure the Git hooks for local CloudberryDB development

Git hooks are scripts that run automatically every time a particular
Git event occurs in a Git repository. We customize our CloudberryDB
Git’s behavior by these hooks to check or remind us how to improve our
code and commit.

## List of hooks

| Type | Description |
|--|--|
| `pre-commit` | This hook is to verify what is about to be committed and executed every time you run git commit before writing a commit message, including if the code has been properly indented and if has a trailing whitespace. |
| `prepare-commit-msg` | This hook is called after the `pre-commit` hook to populate the text editor with a commit message template. |
| `commit-msg` | This hook is to check the commit log message and called after the user enters a commit message. |
| `pre-push` | This hook is to verify what is about to be pushed, and called by `git push` after it has checked the remote status, but before anything has been pushed. |

## Install

You can configure these Git hooks in one of the following three ways
as you like:

* Run the command to configure the file path for Git hooks:

```
# Run the command under src/tools/hooks dir
git config core.hooksPath $PWD
```

* Or link the hook files to the `.git/hooks` relative files:

```
# Run the command under src/tool/hooks dir
ln -sf ./pre-commit ../../../.git/hooks/pre-commit
ln -sf ./prepare-commit-msg ../../../.git/hooks/prepare-commit-msg
ln -sf ./commit-msg ../../../.git/hooks/commit-msg
ln -sf ./pre-push ../../../.git/hooks/pre-push
```

* Or you can copy these hooks to the `.git/hooks` directory directly.

> [!NOTE]
> If you want to bypass these hooks, you can run the `git commit`/`git
> push` command with `--no-verify` or `-n` option.
## Useful links

- [Git Hooks](https://git-scm.com/docs/githooks)
91 changes: 91 additions & 0 deletions src/tools/hooks/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#!/bin/sh
#
# A hook script to check the commit log message. Called by "git commit" with
# one argument, the name of the file that has the commit message. The hook
# should exit with non-zero status after issuing an appropriate message if it
# wants to stop the commit. The hook is allowed to edit the commit message
# file.
#
# Define the color
readonly reset=$(tput sgr0)
readonly red=$(tput bold; tput setaf 1)
readonly green=$(tput bold; tput setaf 2)

# Initialize exit code as 0
exit_code=0

# Function to check if a string starts with a specific prefix
startsWith() {
case $1 in
"$2"*) return 0;;
*) return 1;;
esac
}

# Read the commit message from the commit-msg hook input
commit_msg_file=$1
commit_msg=$(cat "$commit_msg_file")

echo -n "Check if the commit message is written... "
if [ -z "$commit_msg" ]; then
echo -e "\n ${red}Error: Commit message is empty. Please write a commit message."
exit_code=1
else
echo "${green}Done"
fi
echo "${reset}"

echo -n "Check the commit title format... "
commit_title=$(echo "$commit_msg" | head -n 1)
if [[ ! "$commit_title" =~ ^[A-Z] ]]; then
echo -e "\n ${red}Error: Commit title must start with an uppercase letter!"
exit_code=1
elif [[ "$commit_title" =~ [^\x00-\x7F] ]]; then
echo -e "\n ${red}Error: Commit title cannot contain fullwidth characters."
exit_code=1
elif [ ${#commit_title} -gt 50 ]; then
echo -e "\n ${red}Error: Commit title length cannot exceeds 50 characters."
exit_code=1
else
echo "${green}Done"
fi
echo "${reset}"

echo -n "Check the commit body... "
commit_body=$(echo "$commit_msg" | sed -n '2,$p')
while IFS= read -r line; do
if [ ${#line} -gt 72 ]; then
echo -e "\n ${red}Error: Commit body line length cannot exceed 72 characters."
echo -e "\n ${red}For Vim editor, you can use `:set textwidth=72` to autowrap. Then you can input `:normal gqG` to apply the warp rule for existing body text."
exit_code=1
elif [[ "$line" =~ [^\x00-\x7F] ]]; then
echo -e "\n ${red}Error: Commit body line cannot contain fullwidth characters."
exit_code=1
else
echo "${green}Done"
fi
done
echo "${reset}"

# Check the commit tailer and allow its length longer than 72
commit_tailer=$(echo "$commit_msg" | tail -n 2)
if startsWith "$commit_tailer" "See:" && [ ${#commit_tailer} -gt 72 ]; then
exit ${exit_code}
fi

# If any checks fail, exit the commit process.
# If all checks pass, go with no exit.
if [[ "${exit_code}" != 0 ]]; then
echo "${red}---------------------------------------------------"
echo "All Checks have been performed. Warnings/Errors happened!"
echo "Update your commit message before running `git commit` again!"
echo "---------------------------------------------------"
exit ${exit_code}
echo ${reset}
else
echo "${green}---------------------------------------------------"
echo "All Checks have passed. Bingo!"
echo "---------------------------------------------------"
exit ${exit_code}
echo ${reset}
fi
89 changes: 89 additions & 0 deletions src/tools/hooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/bin/sh
#
# A hook script to verify what is about to be committed. Called by
# "git commit" with no arguments. The hook should exit with non-zero
# status after issuing an appropriate message if it wants to stop the
# commit.

readonly reset=$(tput sgr0)
readonly red=$(tput bold; tput setaf 1)
readonly green=$(tput bold; tput setaf 2)

exit_code=0

echo -n "Checking if new commits exits... "
if git rev-parse --verify HEAD >/dev/null 2>&1
then
against=HEAD
else
# Initial commit: diff against an empty tree object
against=$(git hash-object -t tree /dev/null)
fi
echo "${green}Done"
echo "${reset}"

# The following script is copied from pre-commit.sample to avoid
# non-ASCII filenames from being added to the repository.

echo -n "Running non-ASCII filenames check..."
allownonascii=$(git config --type=bool hooks.allownonascii)
exec 1>&2
if [ "$allownonascii" != "true" ] &&
test $(git diff --cached --name-only --diff-filter=A -z $against |
LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
then
cat <<\EOF
${red}Error: Attempt to add a non-ASCII file name.
This can cause problems if you want to work with people on other platforms.
To be portable it is advisable to rename the file.
If you know what you are doing you can disable this check using:
git config hooks.allownonascii true
EOF
echo "${reset}"
exit_code=1
else
echo "${green}Done"
fi
echo "${reset}"

# If there are whitespace errors, print the offending file names and fail.
echo -n "Running whitespace checks... "
exec git diff-index --check --cached $against --
echo "${green}Done"
echo "${reset}"

# This following hook is modified from PostgreSQL wiki, which is used
# to check the changed code by pgindent. See the original link:
# https://wiki.postgresql.org/wiki/Working_with_Git#Using_git_hooks

set -u
: ${PGAUTOINDENT:=no}

branch=$(git rev-parse --abbrev-ref HEAD)
files=$(git diff --cached --name-only --diff-filter=ACMR)

check_indent () {
# no need to filter files - pgindent ignores everything that isn't a
# .c or .h file
src/tools/pgindent/pgindent --silent-diff $files && return 0
exec 2>&1
if [ "$PGAUTOINDENT" = yes ] ; then
echo "Running pgindent on changed files... "
src/tools/pgindent/pgindent $files
echo "${red}Commit abandoned! Rerun git commit to adopt pgindent changes."
else
echo 'You need a pgindent run, e.g:'
echo -n 'src/tools/pgindent/pgindent '
echo '`git diff --name-only --diff-filter=ACMR`'
fi
exit_code=1
}

# nothing to do if there are no files
test -z "$files" && exit 0
check_indent

if [[ "${exit_code}" != 0 ]]; then
echo "${red}Please update your commits again per the errors.${reset}"
fi
exit ${exit_code}
29 changes: 29 additions & 0 deletions src/tools/hooks/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/bash
# A hook for the reminder before pushing to the original repo.

remote="$1"
url="$2"

z40=0000000000000000000000000000000000000000

function user_input_to_proceed() {
read -p 'Proceed with push? (y/N): ' yesno </dev/tty
case "$yesno" in
y|Y|yes|Yes|YES) ;;
*) echo "Aborting push" >&2; exit 1 ;;
esac
}

while read local_ref local_sha remote_ref remote_sha ; do
if [ "$local_sha" = $z40 ] ; then
# Handle delete
continue
fi
if [ "$remote_sha" = $z40 ] ; then
# New branch
continue
fi
# Update to existing branch, examine new commits
range="$remote_sha..$local_sha"

exit 0
10 changes: 10 additions & 0 deletions src/tools/hooks/prepare-commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/sh
#
# A hook script to prepare the commit log message. Called by "git commit" with
# the name of the file that has the commit message, followed by the
# description of the commit message's source. The hook's purpose is to edit
# the commit message file. If the hook fails with a non-zero status, the
# commit is aborted.
#

cat ./src/tools/hooks/.gitmessage > $1