Last active
September 1, 2023 21:56
-
-
Save colinmollenhour/1248871 to your computer and use it in GitHub Desktop.
gitolite update hooks to reject CRLF line endings and require formatted commit messages
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# see https://github.com/kahseng/redmine_gitolite_hook/blob/master/README.rdoc | |
# | |
# The "post-receive" script is run after receive-pack has accepted a pack | |
# and the repository has been updated. It is passed arguments in through | |
# stdin in the form | |
# <oldrev> <newrev> <refname> | |
# For example: | |
# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master | |
REDMINE_SERVER=https://www.example.com/redmine | |
FETCH_URL=sys/fetch_changesets | |
APIKEY=YOUR_REDMINE_WS_API_KEY | |
# Project identifier defaults to repo name, but can be mapped | |
PROJECT="$GL_REPO" | |
MAP=( "foo:foo2" | |
"bar:baz" ) | |
# Above can be overridden in ~/.redmine file | |
if [ -f ~/.redmine ]; then | |
. ~/.redmine | |
fi | |
for repomap in ${MAP[@]}; do | |
REPO=${repomap%%:*} | |
_PROJECT=${repomap#*:} | |
if [ "$GL_REPO" = "$REPO" ]; then | |
PROJECT="$_PROJECT" | |
break | |
fi | |
done | |
echo "Updating redmine project for $GL_REPO: $PROJECT" | |
lockfile=/tmp/redmine-update-${PROJECT//\//-} | |
if [ -f $lockfile ]; then | |
echo "$PROJECT is already being updated.." | |
echo "$PROJECT is already being updated.." >> /tmp/redmine-update.log | |
exit | |
fi | |
update_redmine() { | |
touch $lockfile | |
curl -k -s "$REDMINE_SERVER/$FETCH_URL?id=$PROJECT&key=$APIKEY" > /dev/null | |
rm $lockfile | |
echo $(date) Update $PROJECT completed >> /tmp/redmine-update.log | |
} | |
update_redmine & | |
disown -h |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
$GL_BINDIR/hooks/update-reject-crlf $@ || exit 1 | |
$GL_BINDIR/hooks/update-require-issue $@ || exit 1 | |
exit 0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# An "update" hook which rejects any update containing CRLF. | |
# | |
# Author: Gerhard Gappmeier, ascolab GmbH | |
# Author: Colin Mollenhour | |
# | |
# This script is based on the update.sample in git/contrib/hooks. | |
# You are free to use this script for whatever you want. | |
# | |
BINARY_REGEX='\.(pdb|dll|exe|png|gif|jpg|mwb|zip|tgz|gz|bz2|7z|rar|swf|fla|jar|mo|ttf|foo|mp3|icc|pdf|bmp|wmf|otf|eot|woff|phar|ico)$' | |
NO_EXTENSION_REGEX='/\w+$' | |
OVERRIDE_REGEX='Contains CRLF' | |
refname="$1" | |
oldrev="$2" | |
newrev="$3" | |
BADREV="0000000000000000000000000000000000000000" | |
[ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ] && \ | |
{ echo "Usage: $0 <ref> <oldrev> <newrev>" >&2; exit 1; } | |
# Skip checks on branch deletions | |
[ "$newrev" = "$BADREV" ] && exit 0 | |
# Skip checks on tag pushes | |
[[ $refname =~ ^refs\/tags\/ ]] && exit 0 | |
FIRST="" | |
ERRORS="" | |
CR=$(printf "\r") | |
# Enable case-insensitive matching for file extensions | |
shopt -s nocasematch | |
for rev in $(git rev-list --reverse "$newrev" $(git for-each-ref --format='%(refname)' "refs/heads/*" | sed 's/^/\^/')) | |
do | |
# skip when commit mesage contains override | |
MESSAGE=$(git show -s --format=format:"%s" $rev) | |
[[ $MESSAGE =~ $OVERRIDE_REGEX ]] && continue | |
IS_BAD=0 | |
while read old_mode new_mode old_sha1 new_sha1 status name | |
do | |
# skip lines showing parent commit and deletions | |
[[ -z "$new_sha1" ]] || [ "$new_sha1" = "$BADREV" ] && continue | |
# don't do a CRLF check for binary files | |
[[ $name =~ $BINARY_REGEX ]] && continue | |
# Skip pngs and executables without extensions | |
if [[ $name =~ $NO_EXTENSION_REGEX ]]; then | |
tmp=$(mktemp -t git-crlf.XXXXXXX) | |
git cat-file blob $new_sha1 > $tmp | |
<$tmp head -n 1 | grep -Fq "PNG" && { rm -f $tmp; continue; } | |
file $tmp | grep -Fq "executable" && { rm -f $tmp; continue; } | |
rm -f $tmp | |
fi | |
# check for CRLF | |
if git cat-file blob $new_sha1 | grep -Eq "$CR$"; then | |
if [ "$FIRST" = "" ]; then FIRST=$rev; fi | |
if [ $IS_BAD -eq 0 ]; then IS_BAD=1; ERRORS="${ERRORS}CRLF DETECTED IN ${rev:0:7}:\n"; fi | |
ERRORS="${ERRORS} $name\n" | |
fi | |
done < <(git diff-tree -r "$rev") | |
done | |
if [ "$FIRST" != "" ]; then | |
echo "###################################################################" | |
echo "# One or more files contained CRLF (Windows) line endings which are" | |
echo "# not allowed. Activate the autocrlf feature and/or change the line" | |
echo "# endings to LF before committing and before trying to push. You " | |
echo "# will have to amend your existing commits to not contain CRLF. " | |
echo "# \"git config core.autocrlf true\" to activate CRLF conversion." | |
echo "# \"git commit --amend\" to change your most recent commit." | |
echo "# \"git rebase -i ${FIRST:0:7}^\" to change all commits." | |
echo "###################################################################" | |
echo -e "$ERRORS" | |
exit 1 | |
fi | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# An "update" hook which rejects any update with poor commit messages. | |
# | |
# Author: Colin Mollenhour | |
# | |
ISSUE_REGEX='\s*(refs|references|ticket|issue|feature|bug|task|fixes|closes|fixed) \#([0-9]+)[.:,]?\s*' | |
NO_ISSUE_REGEX='noref' | |
NO_ISSUE_GL_USER_REGEX='^(www-data)$' | |
NO_ISSUE_AUTHOR_EMAIL_REGEX='^(colin@.*mollenhour\.com|matt\.davenport@bss-llc\.com)$' | |
DEVELOP_MESSAGE_REGEX="into '?develop'?|develop'? into" | |
DEVELOP_REFNAME_REGEX='develop$' | |
MYSQL='mysql' | |
# Add authentication or put authentication in ~/.my.cnf | |
#MYSQL="$MYSQL -u redmine -pPASSWORD" | |
refname="$1" | |
oldrev="$2" | |
newrev="$3" | |
#echo "DEBUG - refname: $1, oldrev: $2, newrev: $3" | |
sep="---------------------------------------------" | |
function die { echo -e "$1" 1>&2; exit 1; } | |
[ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ] && \ | |
die "Usage: $0 <ref> <oldrev> <newrev>" | |
[ -z "$GL_USER" ] && \ | |
die "Gitolite user is not defined." | |
# Skip checks on branch deletions | |
[ "$newrev" = "0000000000000000000000000000000000000000" ] && exit 0 | |
# Skip checks on tag pushes | |
[[ $refname =~ ^refs\/tags\/ ]] && exit 0 | |
function reject { | |
die " | |
Your commit message was rejected for the following reason: | |
$1 | |
$sep | |
$SHORT: $MESSAGE | |
$sep | |
HINT: Use 'git commit --amend' to modify your last commit or 'git rebase -i $SHORT^' to | |
modify all commits up to and including the offending one. | |
" | |
} | |
for REF in $(git rev-list --reverse "$newrev" $(git for-each-ref --format='%(refname)' "refs/heads/*" | sed 's/^/\^/')); do | |
SHORT=${REF:0:7} | |
MESSAGE=$(git show -s --format=format:"%s%+b" $REF | tr '\n' ' ') | |
AUTHOR_EMAIL=$(git show -s --format=format:"%ae" $REF) | |
shopt -s nocasematch | |
# Make sure the author email is populated | |
[ -z $AUTHOR_EMAIL ] && \ | |
reject "You must configure your name and email address." | |
# Make sure that the log message contains some text. | |
[[ $MESSAGE =~ [a-zA-Z0-9]+ ]] || \ | |
reject "You must supply a descriptive commit message." | |
# Allow an issue reference to be omitted if "noref" is added to message | |
if [[ $MESSAGE =~ $NO_ISSUE_REGEX ]]; then | |
[ -z $NO_ISSUE_GL_USER_REGEX ] && [ -z $NO_ISSUE_AUTHOR_EMAIL_REGEX ] && \ | |
continue | |
[ -n $NO_ISSUE_GL_USER_REGEX ] && [[ $GL_USER =~ $NO_ISSUE_GL_USER_REGEX ]] && \ | |
continue | |
[ -n $NO_ISSUE_AUTHOR_EMAIL_REGEX ] && [[ $AUTHOR_EMAIL =~ $NO_ISSUE_AUTHOR_EMAIL_REGEX ]] && \ | |
continue | |
fi | |
# Reject merging develop into other branches | |
[[ $MESSAGE =~ $DEVELOP_MESSAGE_REGEX ]] && \ | |
[[ ! $refname =~ $DEVELOP_REFNAME_REGEX ]] && \ | |
reject "Develop branch may not be merged into other branches." | |
# Skip merge commits and commits that do nothing | |
NUMPARENTS=$(git cat-file -p $REF | grep '^parent ' | wc -l) | |
[ $NUMPARENTS -gt 1 -o "$(git diff --stat $REF $REF^)" == "" ] && \ | |
continue | |
# Make sure that the log message references a Redmine issue. | |
[[ $MESSAGE =~ $ISSUE_REGEX ]] || \ | |
reject "You must specify a Redmine issue number matching the following pattern:\n$ISSUE_REGEX\nExample: Issue #123, Refs #123, Fixes #123" | |
# Make sure the referenced issue actually exists | |
REDMINE_ISSUE=${BASH_REMATCH[2]} | |
REDMINE_ISSUE_EXISTS=$(${MYSQL} -N redmine -e "SELECT COUNT(*) FROM issues I WHERE I.id = ${REDMINE_ISSUE};") | |
[[ ${REDMINE_ISSUE_EXISTS} -ne 0 ]] || \ | |
reject "Issue #${REDMINE_ISSUE} does not exist." | |
# Check message length, ignoring issue refs | |
MSGNOREF=${MESSAGE/${BASH_REMATCH[0]}} | |
[[ ${#MSGNOREF} -gt 10 ]] || \ | |
reject "Your commit message \"$MSGNOREF\" is too short. Be more descriptive." | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment