Add slack hook
[gitolite-hooks.git] / slack
1 #!/bin/bash
2 #
3 # Slack (slack.com) notification post-receive hook.
4 #
5 # Based on: https://github.com/joemiller/git-hooks Campfire notification post-receive hook. Author: Joe Miller
6 # (http://joemiller.me)
7 #
8 # Based on post-receive.irc by Mikael Fridh <frimik@gmail.com> https://gist.github.com/1821358
9 #
10
11 function help() {
12   echo "Required config settings:"
13   echo " git config hooks.slack.webhook-url 'https://hooks.slack.com/services/...'"
14   echo " git config hooks.slack.channel 'general'"
15   echo " git config hooks.slack.show-only-last-commit 'true' #optional"
16   echo " git config hooks.slack.username 'git' #optional"
17   echo " git config hooks.slack.icon-url 'http://imgur/icon.png' #optional"
18   echo " git config hooks.slack.icon-emoji ':twisted_rightwards_arrows:' #optional"
19   echo " git config hooks.slack.repo-nice-name 'MyRepo' #optional"
20   echo " git config hooks.slack.repos-root '/path/to/repos' #optional"
21   echo " git config hooks.slack.changeset-url-pattern 'http://yourserver/%repo_path%/changeset/%rev_hash
22 %' #optional"
23   echo " git config hooks.slack.compare-url-pattern 'http://yourserver/%repo_path%/changeset/%old_rev_ha
24 sh%..%new_rev_hash%' #optional"
25 }
26
27 function notify_slack() {
28   oldrev=$(git rev-parse $1)
29   newrev=$(git rev-parse $2)
30   refname="$3"
31
32   # --- Interpret
33   # 0000->1234 (create)
34   # 1234->2345 (update)
35   # 2345->0000 (delete)
36   if expr "$oldrev" : '0*$' >/dev/null
37   then
38     change_type="create"
39   else
40     if expr "$newrev" : '0*$' >/dev/null
41     then
42       change_type="delete"
43     else
44       change_type="update"
45     fi
46   fi
47
48   # --- Get the revision types
49   newrev_type=$(git cat-file -t $newrev 2> /dev/null)
50   oldrev_type=$(git cat-file -t "$oldrev" 2> /dev/null)
51   case "$change_type" in
52     create|update)
53       rev="$newrev"
54       rev_type="$newrev_type"
55       ;;
56     delete)
57       rev="$oldrev"
58       rev_type="$oldrev_type"
59       ;;
60   esac
61
62   # The revision type tells us what type the commit is, combined with
63   # the location of the ref we can decide between
64   #  - working branch
65   #  - tracking branch
66   #  - unannoted tag
67   #  - annotated tag
68   case "$refname","$rev_type" in
69     refs/tags/*,commit)
70       # un-annotated tag
71       refname_type="tag"
72       short_refname=${refname##refs/tags/}
73       ;;
74     refs/tags/*,tag)
75       # annotated tag
76       refname_type="annotated tag"
77       short_refname=${refname##refs/tags/}
78       # change recipients
79       if [ -n "$announcerecipients" ]; then
80         recipients="$announcerecipients"
81       fi
82       ;;
83     refs/heads/*,commit)
84       # branch
85       refname_type="branch"
86       short_refname=${refname##refs/heads/}
87       ;;
88     refs/remotes/*,commit)
89       # tracking branch
90       refname_type="tracking branch"
91       short_refname=${refname##refs/remotes/}
92       echo >&2 "*** Push-update of tracking branch, $refname"
93       echo >&2 "***  - no notification generated."
94       return 0
95       ;;
96     *)
97       # Anything else (is there anything else?)
98       echo >&2 "*** Unknown type of update to $refname ($rev_type)"
99       echo >&2 "***  - no notification generated"
100       return 0
101       ;;
102   esac
103   # plural suffix, default "", changed to "s" if commits > 1
104   s=""
105
106   # Repo name, either Gitolite or normal repo.
107   if [ -n "$GL_REPO" ]; then
108     # it's a gitolite repo
109     repodir=$(basename `pwd`)
110     repo=$GL_REPO
111   else
112     repodir=$(basename `pwd`)
113     if [ "$repodir" == ".git" ]; then
114       repodir=`dirname $PWD`
115       repodir=`basename $repodir`
116     fi
117     repo=${repodir%.git}
118   fi
119
120   repoprefix=$(git config hooks.slack.repo-nice-name || git config hooks.irc.prefix || git config hooks.emailprefix || echo "$repo")
121   onlylast=`git config --get hooks.slack.show-only-last-commit`
122   onlylast=$onlylast && [ -n "$onlylast" ]
123
124   # Get the user information
125   # If $GL_USER is set we're running under gitolite.
126   if [ -n "$GL_USER" ]; then
127     user=$GL_USER
128   else
129     user=$USER
130   fi
131
132   case ${change_type} in
133     "create")
134       header="New ${refname_type} *${short_refname}* has been created in ${repoprefix}"
135       single_commit_suffix="commit"
136       ;;
137     "delete")
138       header="$(tr '[:lower:]' '[:upper:]' <<< ${refname_type:0:1})${refname_type:1} *$short_refname* has been deleted from ${repoprefix}"
139       single_commit_suffix="commit"
140       ;;
141     "update")
142       num=$(git log --pretty=oneline ${1}..${2}|wc -l|tr -d ' ')
143       branch=${3/refs\/heads\//}
144
145       if [ ${num} -gt 1 ]; then
146         header="${num} new commits *pushed* to *${short_refname}* in ${repoprefix}"
147         single_commit_suffix="one"
148         s="s"
149       else
150         header="A new commit has been *pushed* to *${short_refname}* in ${repoprefix}"
151         single_commit_suffix="one"
152       fi
153
154       ;;
155     *)
156       # most weird ... this should never happen
157       echo >&2 "*** Unknown type of update to $refname ($rev_type)"
158       echo >&2 "***  - notifications will probably screw up."
159       ;;
160   esac
161   if $onlylast && [[ "${change_type}" != "delete" ]]; then
162     header="$header, showing last $single_commit_suffix:"
163   fi
164
165
166   if [[ "${change_type}" != "delete" && "${refname_type}" == "branch" ]]; then
167     changeseturlpattern=`git config --get hooks.slack.changeset-url-pattern`
168     compareurlpattern=`git config --get hooks.slack.compare-url-pattern`
169     reporoot=`git config --get hooks.slack.repos-root`
170
171     urlformat=
172     if [ -n "$changeseturlpattern" -a -n "$reporoot" ]; then
173       if [[ $PWD == ${reporoot}* ]]; then
174         repopath=$PWD
175         base=`basename $PWD`
176         if [ "$base" == ".git" ]; then
177           repopath=`dirname $repopath`
178         fi
179         idx=`echo $reporoot | wc -c | tr -d ' '`
180         repopath=`echo $repopath | cut -c$idx-`
181         urlformat=`echo $changeseturlpattern | sed "s|%repo_path%|$repopath|g" | sed "s/%rev_hash%/%H/g"`
182
183         if [ -n "$compareurlpattern" ]; then
184           comparelink=`echo $compareurlpattern | sed "s|%repo_path%|$repopath|g"  | sed "s|%old_rev_hash%|$oldrev|g" | sed "s|%new_rev_hash%|$newrev|g"`
185           header=`echo $header | sed -e "s|\([a-zA-Z0-9]\{1,\} new commit[s]\{0,1\}\)|\<$comparelink\|\\1\>|"`
186         fi
187       else
188         echo >&2 "$PWD is not in $reporoot. Not creating hyperlinks."
189       fi
190     fi
191
192     formattedurl=""
193     if [ -n "$urlformat" ]; then
194       formattedurl="<${urlformat}|%h> "
195     fi
196
197
198     nl="\\\\n"
199
200     if [[ "${change_type}" == "update" ]]; then
201       start="${1}"
202     else
203       start="HEAD"
204     fi
205
206     end="${2}"
207
208     # merge `git log` output with $header
209     if $onlylast; then
210       countarg="-n 1"
211     else
212       countarg=""
213     fi
214
215     # Process the log and escape double quotes; assuming for now that committer names don't have five semicolons in them
216     log_out=$( git log --pretty=format:"%cN;;;;;${formattedurl}%s" $countarg ${start}..${end} \
217              | sed -e 's/\\/\\\\/g' \
218              | sed -e 's/"/\\"/g' \
219              | sed -e 's/\(.*\);;;;;\(.*\)/{"title":"\1","value":"\2","short":false},/' )
220
221     fields=${log_out%?}
222
223     attachments="[{ \"fallback\" : \"${header}\", \"color\" : \"good\", \"fields\" : [${fields}]}]"
224   fi
225
226   if [ -n "${attachments}" ] && [[ "${attachments}" != "" ]]; then
227     msg=$(echo -e "\"text\":\"${header}\", \"attachments\" : $attachments")
228   else
229     msg=$(echo -e "\"text\":\"${header}\"")
230   fi
231
232   # slack API uses \n substitution for newlines
233   # msg=$(echo "${msg}" | perl -p -e 's/\+/&#43;/mg')
234
235   webhook_url=$(git config --get hooks.slack.webhook-url)
236   channel=$(git config --get hooks.slack.channel)
237   username=$(git config --get hooks.slack.username)
238   iconurl=$(git config --get hooks.slack.icon-url)
239   iconemoji=$(git config --get hooks.slack.icon-emoji)
240
241   if [ -z "$webhook_url" ] || [ -z "$channel" ]; then
242     echo "ERROR: config settings not found"
243     help
244     exit 1
245   fi
246
247   payload="{${msg}, \"channel\": \"#$channel\""
248
249   if [ -n "$username" ]; then
250     payload="$payload, \"username\": \"$username\""
251   fi
252
253   if [ -n "$iconurl" ]; then
254     payload="$payload, \"icon_url\": \"$iconurl\""
255   elif [ -n "$iconemoji" ]; then
256     payload="$payload, \"icon_emoji\": \"$iconemoji\""
257   fi
258
259   payload="$payload}"
260
261   if [ -n "$DEBUG" ]; then
262     echo "POST $webhook_url"
263     echo "payload=$payload"
264     return
265   fi
266
267   curl -s \
268       -d "payload=$payload" \
269       "$webhook_url" \
270       >/dev/null
271 }
272
273 webhook_url=$(git config --get hooks.slack.webhook-url)
274
275 while read line; do
276   set -- $line
277   if [ -n "$webhook_url" ]; then
278       notify_slack $*
279   fi
280
281   RET=$?
282 done
283
284 exit $RET