[PATCH v2 5/5] hook: add document and example for "proc-receive" hook
From: Jiang Xin <zhiyou.jx@alibaba-inc.com>
Signed-off-by: Jiang Xin <zhiyou.jx@alibaba-inc.com>
---
Documentation/githooks.txt | 22 +++++
t/t5411-proc-receive-hook.sh | 71 ++++++++++++++++
templates/hooks--proc-receive.sample | 119 +++++++++++++++++++++++++++
3 files changed, 212 insertions(+)
create mode 100755 templates/hooks--proc-receive.sample
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 3dccab5375..1af213bad8 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -333,6 +333,28 @@ The default 'update' hook, when enabled--and with
`hooks.allowunannotated` config option unset or set to false--prevents
unannotated tags to be pushed.
+[[proc-receive]]
+proc-receive
+~~~~~~~~~~~~
+This hook is invoked by linkgit:git-receive-pack[1] when it reacts to
+special `git push` command. According to refnames of the commands which
+`git push` sends to 'git-receive-pack', the commands will be devided
+into two groups by matching what the `receive.procReceiveRefs`
+configuration variable defines. One group of the commands will execute
+the internal `execute_commands` function to update the corresponding
+refnames, and the other group of commands which have matching refnames
+will execute this 'proc-receive' hook to create pull requests, etc.
+If there is no `receive.procReceiveRefs` settings, this hook won't
+execute at all, and all commands are sent to the internal
+`execute_commands` function.
+
+Its exit status only determines the success or failure of the group of
+commands with special refnames, unless atomic push is in use.
+
+This hook executes once for the receive operation. It takes no
+arguments, but gets the same information as the
+<<pre-receive,'pre-receive'>> hook does on its standard input.
+
[[post-receive]]
post-receive
~~~~~~~~~~~~
diff --git a/t/t5411-proc-receive-hook.sh b/t/t5411-proc-receive-hook.sh
index 6f35867e85..caedd49f2e 100755
--- a/t/t5411-proc-receive-hook.sh
+++ b/t/t5411-proc-receive-hook.sh
@@ -757,4 +757,75 @@ test_expect_success "(7) push one special ref (show environments)" '
test_cmp expect actual
'
+test_expect_success "(8) install proc-receive from proc-receive.sample" '
+ mv $bare/hooks/pre-receive $bare/hooks/pre-receive.ok &&
+ mv $bare/hooks/post-receive $bare/hooks/post-receive.env &&
+ mv $bare/hooks/proc-receive $bare/hooks/proc-receive.env &&
+ cp ../../templates/hooks--proc-receive.sample $bare/hooks/proc-receive &&
+ chmod a+x $bare/hooks/proc-receive
+'
+
+test_expect_success "(8) proc-receive.sample: show push result" '
+ (
+ cd work &&
+ git push origin \
+ HEAD:refs/for/a/b/c/my/topic
+ ) >out 2>&1 &&
+ grep "^remote:" out | sed -e "s/ *\$//g" >actual &&
+ cat >expect <<-EOF &&
+ remote: [proc-receive] *******************************************************
+ remote: [proc-receive] * Pull request #12345678901 created/updated *
+ remote: [proc-receive] * URL: https://... ... *
+ remote: [proc-receive] *******************************************************
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "(8) proc-receive.sample: show debug info" '
+ (
+ cd work &&
+ git push -o debug=1 -o reviewers=user1,user2 \
+ origin \
+ HEAD:refs/for/a/b/c/my/topic
+ ) >out 2>&1 &&
+ grep "^remote:" out | sed -e "s/ *\$//g" >actual &&
+ cat >expect <<-EOF &&
+ remote: [DEBUG] [proc-receive] push-option: AGIT_DEBUG=1
+ remote: [DEBUG] [proc-receive] push-option: AGIT_REVIEWERS=user1,user2
+ remote: [DEBUG] [proc-receive] command from stdin: $ZERO_OID $B refs/for/a/b/c/my/topic
+ remote: [DEBUG] [proc-receive] call API (AGIT_PR_TARGET=a/b/c, AGIT_PR_TOPIC=)...
+ remote: [DEBUG] [proc-receive] parse API result, and get AGIT_PR_ID, etc.
+ remote: [proc-receive] *******************************************************
+ remote: [proc-receive] * Pull request #12345678901 created/updated *
+ remote: [proc-receive] * URL: https://... ... *
+ remote: [proc-receive] *******************************************************
+ remote: [DEBUG] [proc-receive] output kv pairs to stdout for git to parse.
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "(8) proc-receive.sample: fail to push non-exist branch" '
+ (
+ cd work &&
+ test_must_fail git push -o reviewers=user1,user2 \
+ origin \
+ HEAD:refs/for/a/b/x/my/topic
+ ) >out 2>&1 &&
+ grep "^remote:" out | sed -e "s/ *\$//g" >actual &&
+ cat >expect <<-EOF &&
+ remote: [proc-receive] cannot find target branch from ref: refs/for/a/b/x/my/topic
+ EOF
+ test_cmp expect actual &&
+ (
+ cd $bare &&
+ git show-ref
+ ) >actual &&
+ cat >expect <<-EOF &&
+ $A refs/heads/a/b/c
+ $A refs/heads/maint
+ $A refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
test_done
diff --git a/templates/hooks--proc-receive.sample b/templates/hooks--proc-receive.sample
new file mode 100755
index 0000000000..31fa85ffe1
--- /dev/null
+++ b/templates/hooks--proc-receive.sample
@@ -0,0 +1,119 @@
+#!/bin/sh
+#
+# This is an example hook script, DO NOT use it on production service.
+
+debug() {
+ case "$AGIT_DEBUG" in
+ "yes" | "true" | "1")
+ ;;
+ *)
+ return
+ esac
+
+ echo >&2 "[DEBUG] $@"
+}
+
+# Parse push options
+if test -n "$GIT_PUSH_OPTION_COUNT"
+then
+ i=0
+ while test "$i" -lt "$GIT_PUSH_OPTION_COUNT"
+ do
+ eval "value=\$GIT_PUSH_OPTION_$i"
+ i=$((i + 1))
+
+ k=$(echo ${value%=*} | tr [a-z] [A-Z])
+ v=${value#*=}
+ if test -n "$v" && test -n "$k"
+ then
+ k="AGIT_$k"
+ else
+ continue
+ fi
+ eval "$k=$v"
+ debug "[proc-receive] push-option: $k=$v"
+ done
+fi
+
+# Read push commands.
+count=0
+while read old new refname
+do
+ debug "[proc-receive] command from stdin: $old $new $refname"
+ count=$(( count + 1 ))
+ # Only one special refname is allowed for each push
+ if test $count -gt 1
+ then
+ echo >&2 "[proc-receive]: cannot handle more than one push commands"
+ exit 1
+ fi
+
+ # Parse refname, and set envrionment
+ remains=
+ if test "${refname#refs/for/}" != "$refname"
+ then
+ AGIT_PR_IS_DRAFT=false
+ remains=${refname#refs/for/}
+ elif test "${refname#refs/drafts/}" != "$refname"
+ then
+ AGIT_PR_IS_DRAFT=true
+ remains=${refname#refs/drafts/}
+ else
+ echo >&2 "[proc-receive] unknown refname: $refname"
+ exit 1
+ fi
+
+ ref=
+ found_ref=
+ for i in $(echo $remains | tr "/" "\n")
+ do
+ if test -z "$ref"
+ then
+ ref=$i
+ else
+ ref=$ref/$i
+ fi
+ if git rev-parse --verify $ref -- 2>/dev/null
+ then
+ found_ref=yes
+ break
+ fi
+ done
+ if test -z "$found_ref"
+ then
+ echo >&2 "[proc-receive] cannot find target branch from ref: $refname"
+ exit 1
+ fi
+ AGIT_PR_TARGET=$ref
+ AGIT_PR_SOURCE=${remains#$ref/}
+done
+
+if test -z "$AGIT_PR_TARGET"
+then
+ echo >&2 "[proc-receive] fail to parse refname, no target found"
+ exit 1
+fi
+
+# Call API to generate code review.
+debug "[proc-receive] call API (AGIT_PR_TARGET=$AGIT_PR_TARGET, AGIT_PR_TOPIC=$AGIT_PR_TOPIC)..."
+
+# Parse result of API.
+debug "[proc-receive] parse API result, and get AGIT_PR_ID, etc."
+AGIT_PR_ID="12345678901"
+AGIT_PR_LOCAL_ID="23"
+
+# Show message.
+if test -n "$AGIT_PR_ID"
+then
+ echo >&2 "[proc-receive] *******************************************************"
+ echo >&2 "[proc-receive] * Pull request #$AGIT_PR_ID created/updated *"
+ echo >&2 "[proc-receive] * URL: https://... ... *"
+ echo >&2 "[proc-receive] *******************************************************"
+fi
+
+# Show envs to stdout, and will be exported as envs for "post-receive" hook.
+debug "[proc-receive] output kv pairs to stdout for git to parse."
+echo "AGIT_PR_ID=$AGIT_PR_ID"
+echo "AGIT_PR_LOCAL_ID=$AGIT_PR_LOCAL_ID"
+
+exit 0
--
2.26.0.rc0.5.gb02b988a14.dirty