blob: 11abe19abe38a73e07f2d94ecc6e22f977318f99 [file] [log] [blame]
Luigi Santivettife5f9882020-10-22 01:07:21 +01001#!/bin/bash
2#
3# remote-update hook
4#
5# Copyright 2020 Luigi Santivetti <luigi.santivetti@gmail.com>
6
7# Permission is hereby granted, free of charge, to any person obtaining a
8# copy of this software and associated documentation files (the "Software"),
9# to deal in the Software without restriction, including without limitation
10# the rights to use, copy, modify, merge, publish, distribute, sublicense,
11# and/or sell copies of the Software, and to permit persons to whom the
12# Software is furnished to do so, subject to the following conditions:
13
14# The above copyright notice and this permission notice (including the next
15# paragraph) shall be included in all copies or substantial portions of the
16# Software.
17
18# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21# ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
22# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25# remote-update supports calling from two server side hooks with the following
26# list of command line arguments (we only care about --project).
27#
28# 1. commit-received:
29#
30# --project <project name>
31# --refname <refname>
32# --uploader <uploader>
33# --uploader-username <username>
34# --oldrev <sha1>
35# --newrev <sha1>
36# --cmdref <refname>
37#
38# 2. submit:
39#
40# --project <project name>
41# --branch <branch>
42# --submitter <submitter>
43# --patchset <patchset id>
44# --commit <sha1>
45#
46# RATIONALE: remote-update can reject one change either pushed for review or
47# submitted for a merge into Gerrit. It performs a remote update, importing
48# into Gerrit all refs/heads/* merged outside Gerrit from an external mirror.
49# This allows Gerrit to sit in the middle and act like a reviewing tool, but
50# having to follow the line of develpment potentially done also elsewhere.
51#
52# ` remote-update hook ` TODO
53# ` `
54# ` `
55# (1) push/submit ` (2) fetch ` (3) fetch
56# +--------+ ··········> +--------+ ··········> +--------+ ··········>
57# | Change | | Gerrit | | Mirror |
58# +--------+ <·········· +--------+ <·········· +--------+ <··········
59# reject/accept (6) ` update (5) ` update (4)
60# ` `
61# ` `
62# ` `
63#
64# The assumption is that `Mirror' does not have other remotes set, so there
65# no need to `git remote update' it. The only changes to mirror either come
66# from Gerrit itself (with change-merged hook) or via direct push for who's
67# access granted to it.
68
69echo "Gerrit Code Review: remote-update hook"
70
71# File to dump onto the file system storing this hook stdout and stderr
72LOG_FILE=${HOOKS_LOG_DIR:-/tmp}
73if [ ! -w "$LOG_FILE" ]; then
74 echo " ****** warning: skip log" >&2
75 LOG_FILE="/dev/null" # avoid permission denied
76else
77 LOG_FILE="$LOG_FILE/hooks.remote-update-$(date +'%d%m%Y%H%M%S').txt"
78fi
79
80# Host service where replication mirrors and deploy engine live
81HOOK_HOST=${HOOKS_REMOTE_HOST:-}
82if [ ! -n "$HOOK_HOST" ]; then
83 echo " ****** warning: no host, skip hook" >&2
84 exit 0
85fi
86
87# User allowed t othe target host to push and eventually deploy changes
88HOOK_USER=${HOOKS_REMOTE_USER:-}
89if [ ! -n "$HOOK_USER" ]; then
90 echo " ****** warning: no user, skip hook" >&2
91 exit 0
92fi
93
94# Directory where this hook expects the project mirror to be located
95HOOK_PATH=${HOOKS_REMOTE_PATH:-}
96if [ ! -n "$HOOK_PATH" ]; then
97 echo " ****** warning: no path, skip hook" >&2
98 exit 0
99fi
100
101# Alias for the remote tracking a given mirror
102GERRIT_R=${HOOKS_REMOTE_ALIAS:-}
103if [ ! -n "$GERRIT_R" ]; then
104 echo " ****** warning: no remote alias, skip hook" >&2
105 exit 0
106fi
107
108SSH_RSA_ID=${HOOKS_REMOTE_RSAID:-}
109if [ ! -n "$SSH_RSA_ID" ]; then
110 echo " ****** error: no rsa id file found" >&2
111 exit 1
112fi
113
114SSH_PORT=${HOOKS_REMOTE_PORT:-22}
115if [ ! -n "$SSH_PORT" ]; then
116 echo " ****** warning: no ssh port, default to 22" >&2
117fi
118
119/bin/bash <(/bin/cat <<EOF
120set -x
121args='${*}'
122
123declare -A CHANGE=(
124 [project]=""
125 [refname]=""
126 [uploader]=""
127 [uploader-username]=""
128 [oldrev]=""
129 [newrev]=""
130 [cmdref]=""
131 [branch]=""
132 [submitter]=""
133 [patchset]=""
134 [commit]=""
135)
136
137while read -a line; do
138 for K in \${!CHANGE[@]}; do
139 if [ "\${line[0]}" == "--\$K" ]; then
140 CHANGE[\$K]="\${line[@]:1}"
141 break
142 fi
143 done
144done <<< "\$(echo -e "\${args// --/\\\\n--}")"
145
146remote_url="ssh://$HOOK_USER@$HOOK_HOST:$HOOK_PATH/\${CHANGE[project]}.git"
147
148# Does Gerrit have \\\$remote_url remote set up?
149git remote get-url "$GERRIT_R" | grep -q -- "\$remote_url"
150if [ "\$?" -ne 0 ]; then
151 echo >&2 " ****** info: \${CHANGE[project]} clean url for $GERRIT_R"
152 git remote rm $GERRIT_R
153
154 echo >&2 " ****** info: \${CHANGE[project]} adding remote: $GERRIT_R"
155 git remote add $GERRIT_R \$remote_url
156 if [ "\$?" -ne 0 ]; then
157 echo >&2 " ****** error ¯\\_(ツ)_/¯ : add remote $GERRIT_R failed"
158 exit 1
159 fi
160fi
161
162git remote show | grep -q -- "$GERRIT_R"
163if [ "\$?" -ne 0 ]; then
164 echo >&2 " ****** info: \${CHANGE[project]} clean remote: $GERRIT_R"
165 git remote rm $GERRIT_R
166
167 echo >&2 " ****** info: \${CHANGE[project]} adding remote: $GERRIT_R"
168 git remote add $GERRIT_R \$remote_url
169 if [ "\$?" -ne 0 ]; then
170 echo >&2 " ****** error ¯\\_(ツ)_/¯ : add remote $GERRIT_R failed"
171 exit 1
172 fi
173fi
174
175GIT_SSH_COMMAND="ssh"
176GIT_SSH_COMMAND+=" -o StrictHostKeyChecking=no"
177GIT_SSH_COMMAND+=" -o UserKnownHostsFile=/dev/null"
178GIT_SSH_COMMAND+=" -p $SSH_PORT"
179GIT_SSH_COMMAND+=" -i $SSH_RSA_ID"
180
181# RATIONALE: \`+refs' in order to allow non-fast-forward fetch. This has two
182# consequences. Gerrit's refs/heads/* for \$CHANGE[project] is always hard
183# reset to match the refs/heads/* on the remote, which is distruptive from
184# a Gerrit point of view. Secondly, \$CHANGE[newref] may not merge against
185# non-fast-forwardable heads. In such a case, \$CHANGE[newref] needs to be
186# rebased on top of refs/heads/*.
187GIT_SSH_COMMAND="\$GIT_SSH_COMMAND" git fetch -v "$GERRIT_R" +refs/heads/*:refs/heads/*
188if [ "\$?" -ne 0 ]; then
189 echo >&2 " ****** error ¯\\_(ツ)_/¯ : failed to update remote: $GERRIT_R"
190 exit 1
191fi
192
193echo " ****** success: remote update done"
194set +x
195EOF
196 ) &> ${LOG_FILE}; RET=$?
197
198if [ -r "${LOG_FILE}" ]; then
199 grep -E "^ \*\*\*\*\*\* " ${LOG_FILE} 2>/dev/null
200fi
201
202[ "$RET" -ne 0 ] || rm -f ${LOG_FILE}
203
204exit "$RET"