Add tf-a-lts-patch-stack-watcher.yaml
This job will trigger Allow-CI job, Allow-CI+2, by default,
if a new patch/patch stack is submitted to the lts branches.
It also monitors the existing unmerged patch/patch stack,
if the submit requirement is met, it will merge it. [1]
[1]: https://linaro.atlassian.net/browse/TFC-546
Signed-off-by: Arthur She <arthur.she@linaro.org>
Change-Id: I59e62182573ae4ef67989ccb119806890a30da6c
diff --git a/scripts/tf-a-lts-patch-stack-watcher.sh b/scripts/tf-a-lts-patch-stack-watcher.sh
new file mode 100755
index 0000000..a97af63
--- /dev/null
+++ b/scripts/tf-a-lts-patch-stack-watcher.sh
@@ -0,0 +1,131 @@
+#!/bin/bash
+set -ex
+
+echo "########################################################################"
+echo " Gerrit Environment"
+env |grep '^GERRIT'
+echo "########################################################################"
+SSH_PARAMS="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o PubkeyAcceptedKeyTypes=+ssh-rsa -p 29418 -i ${CI_BOT_KEY}"
+GERRIT_URL="review.trustedfirmware.org"
+GERRIT_CHANGE_URL_BASE=${GERRIT_CHANGE_URL%/*}
+GERRIT_QUERY_PARAMS="--dependencies --current-patch-set --format=JSON change:"
+QUERY_DEPENDENCY_CMD="${SSH_PARAMS} ${CI_BOT_USERNAME}@${GERRIT_URL} gerrit query ${GERRIT_QUERY_PARAMS}"
+ALLOW_CI_COMMENT="Trigger Allow-CI job by ${BUILD_URL}"
+VOTE_ALLOW_CI_CMD="${SSH_PARAMS} ${CI_BOT_USERNAME}@${GERRIT_URL} gerrit review --label ${ALLOW_CI_JOB} -m \"${ALLOW_CI_COMMENT}\" ${GERRIT_PATCHSET_REVISION}"
+SUBMIT_COMMENT="Submit patch by ${BUILD_URL}"
+SUBMIT_CMD="${SSH_PARAMS} ${CI_BOT_USERNAME}@${GERRIT_URL} gerrit review -m \"${SUBMIT_COMMENT}\" --submit"
+err_msg=$(mktemp)
+
+function get_top_patch() {
+ # Get the top of the patch stack
+ # return: change_no,commit_revision
+ local change_no=$1
+
+ patch_info=$(ssh ${QUERY_DEPENDENCY_CMD}${change_no} 2>/dev/null| jq -n 'input')
+ revision=$(echo ${patch_info} | jq -r '.currentPatchSet.revision')
+ ret=${change_no},${revision}
+
+ neededBy=$(echo ${patch_info} | jq -c 'select(.neededBy)')
+ while [ -n "${neededBy}" ];
+ do
+ change_no=$(echo ${neededBy} | jq -r '.neededBy[0].number')
+ revision=$(echo ${neededBy} | jq -r '.neededBy[0].revision')
+ ret=${change_no},${revision}
+ neededBy=$(ssh ${QUERY_DEPENDENCY_CMD}${change_no} 2>/dev/null | jq -c 'select(.neededBy)')
+ done
+
+ echo ${ret}
+}
+
+function check_ok_to_submit() {
+ # Check if this patch meets the submit requirement
+ # The submit requirement is
+ # Verified: 1
+ # Code-Owner-Review: 1
+ # Maintainer-Review: 1
+ # return:
+ # "True": if the submit requirement is met
+ # "False: if the submit requirement is not met
+ # "MERGED": if the patch has been merged
+ local change_no=$1
+
+ patch_info=$(ssh ${QUERY_DEPENDENCY_CMD}${change_no} 2>/dev/null | jq -n 'input')
+ patch_votes=$(echo ${patch_info} | jq -c 'select(.currentPatchSet.approvals)')
+
+ if [ $(echo ${patch_info} | jq -r '.status') == "MERGED" ]; then
+ echo "MERGED"
+ elif test -n "${patch_votes}" &&
+ jq -e '.currentPatchSet.approvals[] | select(.type == "Code-Owner-Review" and .value == "1")' <<< "${patch_votes}" > /dev/null &&
+ jq -e '.currentPatchSet.approvals[] | select(.type == "Maintainer-Review" and .value == "1")' <<< "${patch_votes}" > /dev/null &&
+ jq -e '.currentPatchSet.approvals[] | select(.type == "Verified" and .value == "1")' <<< "${patch_votes}" > /dev/null; then
+ echo "True"
+ else
+ echo "${GERRIT_CHANGE_URL_BASE}/${change_no} doesn't meet the submit requirement" >> /dev/stderr
+ echo "False"
+ fi
+}
+
+function submit_patch_stack() {
+ # Check the whole patch stack from top to bottom
+ # to see if all patches meet the submit requirements
+ local change_no=$1
+ local can_merge="True"
+
+ top_patch=$(get_top_patch ${change_no})
+ top_patch_no=$(echo ${top_patch} | cut -d ',' -f 1)
+ top_patch_rev=$(echo ${top_patch} | cut -d ',' -f 2)
+
+ patch_to_be_checked=${top_patch_no}
+ while [ -n "${patch_to_be_checked}" ];
+ do
+ set +x # disable debugging log to prevent comtaminate the message we want to keep
+ can_submit=$(check_ok_to_submit ${patch_to_be_checked} 2>> ${err_msg})
+ set -x
+ # The patch that we just checked was merged, exit the loop
+ [ "${can_submit}" == "MERGED" ] && break
+ [ "${can_merge}" == "True" ] && can_merge=${can_submit}
+ patch_to_be_checked=$(ssh ${QUERY_DEPENDENCY_CMD}${patch_to_be_checked} 2>/dev/null | jq 'select(.dependsOn) | .dependsOn[].number' )
+ done
+ if [ "${can_merge}" == "True" ]; then
+ # Check the patch stack agein to ensure it hasn't been merged yet
+ if [ $(ssh ${QUERY_DEPENDENCY_CMD}${top_patch_no} | jq -r 'select(.status)|.status') != "MERGED" ];then
+ echo "The whole patch stack meets the submit requirements, merge it"
+ ssh ${SUBMIT_CMD} ${top_patch_rev} 2> /dev/null
+ else
+ echo "The whole patch stack has been merged!"
+ fi
+ else
+ echo "The patch stack can not be merged!"
+ cat ${err_msg}
+ fi
+}
+
+function trigger_allow_ci_on_top_of_patch_stack() {
+ # This function will set ${ALLOW_CI_JOB} on the
+ # top of the patch stack or a single patch
+ local change_no=$1
+
+ neededBy=$(ssh ${QUERY_DEPENDENCY_CMD}${change_no} 2>/dev/null | jq -c 'select(.neededBy)')
+ if [ -z "${neededBy}" ]; then
+ echo -e "Trigger Allow-CI job on ${GERRIT_CHANGE_URL}"
+ ssh ${VOTE_ALLOW_CI_CMD} 2>/dev/null
+ else
+ echo "This patch is not on the top of a patch stack"
+ fi
+}
+
+case ${GERRIT_EVENT_TYPE} in
+ "comment-added")
+ echo "Triggered by comment-added"
+ # Check eack patch. if all patches meet the submit requirements
+ # merge it
+ submit_patch_stack ${GERRIT_CHANGE_NUMBER}
+ ;;
+ "patchset-created")
+ echo "Triggered by patchset-created"
+ # New patch / patch stack is created
+ # Set Allow-CI on the top of it
+ trigger_allow_ci_on_top_of_patch_stack ${GERRIT_CHANGE_NUMBER}
+ ;;
+esac
+rm -f ${err_msg}