blob: c84fcca176c56f26e02b9c1c8115bcce8aa06e4a [file] [log] [blame]
#!/usr/bin/env bash
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# no public APIs here
# SHELLDOC-IGNORE
# This bug system provides github integration
add_bugsystem github
# personalities can override the following settings:
# Web interface URL.
GITHUB_BASE_URL="https://github.com"
# API interface URL.
GITHUB_API_URL="https://api.github.com"
# user/repo
GITHUB_REPO=""
# user settings
GITHUB_PASSWD=""
GITHUB_TOKEN=""
GITHUB_USER=""
GITHUB_ISSUE=""
# private globals...
GITHUB_BRIDGED=false
GITHUB_COMMITSHA=""
# Simple function to set a default GitHub user after PROJECT_NAME has been set
function github_set_github_user
{
if [[ -n "${PROJECT_NAME}" && ! "${PROJECT_NAME}" = unknown ]]; then
GITHUB_USER=${GITHUB_USER:-"${PROJECT_NAME}qa"}
fi
}
function github_usage
{
github_set_github_user
yetus_add_option "--github-api-url=<url>" "The URL of the API for github (default: '${GITHUB_API_URL}')"
yetus_add_option "--github-base-url=<url>" "The URL of the github server (default:'${GITHUB_BASE_URL}')"
yetus_add_option "--github-password=<pw>" "Github password"
yetus_add_option "--github-repo=<repo>" "github repo to use (default:'${GITHUB_REPO}')"
yetus_add_option "--github-token=<token>" "The token to use to write to github"
yetus_add_option "--github-user=<user>" "Github user [default: ${GITHUB_USER}]"
}
function github_parse_args
{
declare i
github_set_github_user
for i in "$@"; do
case ${i} in
--github-api-url=*)
GITHUB_API_URL=${i#*=}
;;
--github-base-url=*)
GITHUB_BASE_URL=${i#*=}
;;
--github-repo=*)
GITHUB_REPO=${i#*=}
;;
--github-token=*)
GITHUB_TOKEN=${i#*=}
;;
--github-password=*)
GITHUB_PASSWD=${i#*=}
;;
--github-user=*)
GITHUB_USER=${i#*=}
;;
esac
done
}
## @description this gets called when JIRA thinks this
## @description issue is just a pointer to github
## @description WARNING: Called from JIRA plugin!
function github_jira_bridge
{
declare fileloc=$1
declare jsonloc=$2
declare urlfromjira
# shellcheck disable=SC2016
urlfromjira=$(${AWK} "match(\$0,\"${GITHUB_BASE_URL}/[^ ]*patch[ &\\\"]\"){url=substr(\$0,RSTART,RLENGTH-1)}
END{if (url) print url}" "${jsonloc}" )
if [[ -z $urlfromjira ]]; then
# This is currently the expected path, as github pull requests are not common
return 1
fi
# we use this to prevent loops later on
GITHUB_BRIDGED=true
yetus_debug "github_jira_bridge: Checking url ${urlfromjira}"
github_breakup_url "${urlfromjira}"
github_locate_patch GH:"${GITHUB_ISSUE}" "${fileloc}"
}
## @description given a URL, break it up into github plugin globals
## @description this will *override* any personality or yetus defaults
## @param url
function github_breakup_url
{
declare url=$1
declare count
declare pos1
declare pos2
count=${url//[^\/]}
count=${#count}
((pos2=count-3))
((pos1=pos2))
GITHUB_BASE_URL=$(echo "${url}" | cut -f1-${pos2} -d/)
((pos1=pos1+1))
((pos2=pos1+1))
GITHUB_REPO=$(echo "${url}" | cut -f${pos1}-${pos2} -d/)
((pos1=pos2+2))
unset pos2
GITHUB_ISSUE=$(echo "${url}" | cut -f${pos1}-${pos2} -d/ | cut -f1 -d.)
}
## @description based upon a github PR, attempt to link back to JIRA
function github_find_jira_title
{
declare title
declare maybe
declare retval
if [[ ! -f "${PATCH_DIR}/github-pull.json" ]]; then
return 1
fi
title=$(${GREP} title "${PATCH_DIR}/github-pull.json" \
| cut -f4 -d\")
# people typically do two types: JIRA-ISSUE: and [JIRA-ISSUE]
# JIRA_ISSUE_RE is pretty strict so we need to chop that stuff
# out first
maybe=$(echo "${title}" | cut -f2 -d\[ | cut -f1 -d\])
jira_determine_issue "${maybe}"
retval=$?
if [[ ${retval} == 0 ]]; then
return 0
fi
maybe=$(echo "${title}" | cut -f1 -d:)
jira_determine_issue "${maybe}"
retval=$?
if [[ ${retval} == 0 ]]; then
return 0
fi
return 1
}
function github_determine_issue
{
declare input=$1
if [[ ${input} =~ ^[0-9]+$
&& -n ${GITHUB_REPO} ]]; then
# shellcheck disable=SC2034
ISSUE=${input}
if [[ -z ${GITHUB_ISSUE} ]]; then
GITHUB_ISSUE=${input}
fi
fi
# if JIRA didn't call us, should we call it?
if [[ ${GITHUB_BRIDGED} == false ]]; then
github_find_jira_title
if [[ $? == 0 ]]; then
return 0
fi
fi
if [[ -n ${GITHUB_ISSUE} ]]; then
return 0
fi
return 1
}
## @description Try to guess the branch being tested using a variety of heuristics
## @audience private
## @stability evolving
## @replaceable no
## @return 0 on success, with PATCH_BRANCH updated appropriately
## @return 1 on failure
function github_determine_branch
{
if [[ ! -f "${PATCH_DIR}/github-pull.json" ]]; then
return 1
fi
# shellcheck disable=SC2016
PATCH_BRANCH=$(${AWK} 'match($0,"\"ref\": \""){print $2}' "${PATCH_DIR}/github-pull.json"\
| cut -f2 -d\"\
| tail -1 )
yetus_debug "Github determine branch: starting with ${PATCH_BRANCH}"
verify_valid_branch "${PATCH_BRANCH}"
}
## @description Given input = GH:##, download a patch to output.
## @description Also sets GITHUB_ISSUE to the raw number.
## @audience private
## @stability evolving
## @replaceable no
## @param input
## @param output
## @return 0 on success
## @return 1 on failure
function github_locate_patch
{
declare input=$1
declare output=$2
declare githubauth
if [[ "${OFFLINE}" == true ]]; then
yetus_debug "github_locate_patch: offline, skipping"
return 1
fi
if [[ ! "${input}" =~ ^GH: ]]; then
return 1
fi
input=${input#GH:}
# https://github.com/your/repo/pull/##
if [[ ${input} =~ ^${GITHUB_BASE_URL}.*/pull/[0-9]+$ ]]; then
github_breakup_url "${input}.patch"
input=${GITHUB_ISSUE}
fi
# https://github.com/your/repo/pulls/##.patch
if [[ ${input} =~ ^${GITHUB_BASE_URL}.*patch$ ]]; then
github_breakup_url "${input}"
input=${GITHUB_ISSUE}
fi
# https://github.com/your/repo/pulls/##.diff
if [[ ${input} =~ ^${GITHUB_BASE_URL}.*diff$ ]]; then
github_breakup_url "${input}"
input=${GITHUB_ISSUE}
fi
# if it isn't a number at this point, no idea
# how to process
if [[ ! ${input} =~ ^[0-9]+$ ]]; then
yetus_debug "github: ${input} is not a pull request #"
return 1
fi
# we always pull the .patch version (even if .diff was given)
# with the assumption that this way binary files work.
# The downside of this is that the patch files are
# significantly larger and therefore take longer to process
PATCHURL="${GITHUB_BASE_URL}/${GITHUB_REPO}/pull/${input}.patch"
echo "GITHUB PR #${input} is being downloaded at $(date) from"
echo "${GITHUB_BASE_URL}/${GITHUB_REPO}/pull/${input}"
if [[ -n "${GITHUB_USER}"
&& -n "${GITHUB_PASSWD}" ]]; then
githubauth="${GITHUB_USER}:${GITHUB_PASSWD}"
elif [[ -n "${GITHUB_TOKEN}" ]]; then
githubauth="Authorization: token ${GITHUB_TOKEN}"
else
githubauth="X-ignore-me: fake"
fi
# Let's pull the PR JSON for later use
${CURL} --silent --fail \
-H "Accept: application/vnd.github.v3.full+json" \
-H "${githubauth}" \
--output "${PATCH_DIR}/github-pull.json" \
--location \
"${GITHUB_API_URL}/repos/${GITHUB_REPO}/pulls/${input}"
echo "Patch from GITHUB PR #${input} is being downloaded at $(date) from"
echo "${PATCHURL}"
# the actual patch file
${CURL} --silent --fail \
--output "${output}" \
--location \
-H "${githubauth}" \
"${PATCHURL}"
if [[ $? != 0 ]]; then
yetus_debug "github_locate_patch: not a github pull request."
return 1
fi
GITHUB_ISSUE=${input}
# github will translate this to be #(xx) !
add_footer_table "GITHUB PR" "${GITHUB_BASE_URL}/${GITHUB_REPO}/pull/${input}"
return 0
}
function github_linecomments
{
declare plugin=$1
declare file=$2
# shellcheck disable=SC2034
declare realline=$3
declare uniline=$4
declare text=$5
declare tempfile="${PATCH_DIR}/ghcomment.$$.${RANDOM}"
declare githubauth
if [[ "${file}" =~ ^./ ]]; then
file=${file##./}
fi
if [[ -z "${GITHUB_COMMITSHA}" ]]; then
GITHUB_COMMITSHA=$(${GREP} \"sha\" "${PATCH_DIR}/github-pull.json" 2>/dev/null \
| head -1 \
| cut -f4 -d\")
fi
if [[ -z "${uniline}" ]]; then
return
fi
# build our REST post
{
printf "{\"body\":\""
echo "${plugin}: ${text}" \
| ${SED} -e 's,\\,\\\\,g' \
-e 's,\",\\\",g' \
-e 's,$,\\r\\n,g' \
| tr -d '\n'
echo "\","
echo "\"commit_id\":\"${GITHUB_COMMITSHA}\","
echo "\"path\":\"${file}\","
echo "\"position\":${uniline}"
echo "}"
} > "${tempfile}"
if [[ -n "${GITHUB_USER}"
&& -n "${GITHUB_PASSWD}" ]]; then
githubauth="${GITHUB_USER}:${GITHUB_PASSWD}"
elif [[ -n "${GITHUB_TOKEN}" ]]; then
githubauth="Authorization: token ${GITHUB_TOKEN}"
else
return 0
fi
${CURL} -X POST \
-H "Accept: application/vnd.github.v3.full+json" \
-H "Content-Type: application/json" \
-H "${githubauth}" \
-d @"${tempfile}" \
--silent --location \
"${GITHUB_API_URL}/repos/${GITHUB_REPO}/pulls/${GITHUB_ISSUE}/comments" \
>/dev/null
rm "${tempfile}"
}
## @description Write the contents of a file to github
## @param filename
## @stability stable
## @audience public
function github_write_comment
{
declare -r commentfile=${1}
declare retval=0
declare restfile="${PATCH_DIR}/ghcomment.$$"
declare githubauth
if [[ "${OFFLINE}" == true ]]; then
echo "Github Plugin: Running in offline, comment skipped."
return 0
fi
{
printf "{\"body\":\""
${SED} -e 's,\\,\\\\,g' \
-e 's,\",\\\",g' \
-e 's,$,\\r\\n,g' "${commentfile}" \
| tr -d '\n'
echo "\"}"
} > "${restfile}"
if [[ -n "${GITHUB_USER}"
&& -n "${GITHUB_PASSWD}" ]]; then
githubauth="${GITHUB_USER}:${GITHUB_PASSWD}"
elif [[ -n "${GITHUB_TOKEN}" ]]; then
githubauth="Authorization: token ${GITHUB_TOKEN}"
else
echo "Github Plugin: no credentials provided to write a comment."
return 0
fi
${CURL} -X POST \
-H "Accept: application/vnd.github.v3.full+json" \
-H "Content-Type: application/json" \
-H "${githubauth}" \
-d @"${restfile}" \
--silent --location \
"${GITHUB_API_URL}/repos/${GITHUB_REPO}/issues/${GITHUB_ISSUE}/comments" \
>/dev/null
retval=$?
rm "${restfile}"
return ${retval}
}
## @description Print out the finished details to the Github PR
## @audience private
## @stability evolving
## @replaceable no
## @param runresult
function github_finalreport
{
declare result=$1
declare i
declare commentfile=${PATCH_DIR}/gitcommentfile.$$
declare comment
rm "${commentfile}" 2>/dev/null
if [[ ${ROBOT} = "false"
|| -z ${GITHUB_ISSUE} ]] ; then
return 0
fi
big_console_header "Adding comment to Github"
if [[ ${result} == 0 ]]; then
echo ":confetti_ball: **+1 overall**" >> "${commentfile}"
else
echo ":broken_heart: **-1 overall**" >> "${commentfile}"
fi
printf "\n\n\n\n" >> "${commentfile}"
i=0
until [[ ${i} -eq ${#TP_HEADER[@]} ]]; do
printf "%s\n\n" "${TP_HEADER[${i}]}" >> "${commentfile}"
((i=i+1))
done
{
printf "\n\n"
echo "| Vote | Subsystem | Runtime | Comment |"
echo "|:----:|----------:|--------:|:--------|"
} >> "${commentfile}"
i=0
until [[ ${i} -eq ${#TP_VOTE_TABLE[@]} ]]; do
ourstring=$(echo "${TP_VOTE_TABLE[${i}]}" | tr -s ' ')
vote=$(echo "${ourstring}" | cut -f2 -d\| | tr -d ' ')
comment=$(echo "${ourstring}" | cut -f5 -d\|)
if [[ "${vote}" = "H" ]]; then
echo "||| _${comment}_ |" >> "${commentfile}"
else
echo "${TP_VOTE_TABLE[${i}]}" >> "${commentfile}"
fi
((i=i+1))
done
if [[ ${#TP_TEST_TABLE[@]} -gt 0 ]]; then
{
printf "\n\n"
echo "| Reason | Tests |"
echo "|-------:|:------|"
} >> "${commentfile}"
i=0
until [[ ${i} -eq ${#TP_TEST_TABLE[@]} ]]; do
echo "${TP_TEST_TABLE[${i}]}" >> "${commentfile}"
((i=i+1))
done
fi
{
printf "\n\n"
echo "| Subsystem | Report/Notes |"
echo "|----------:|:-------------|"
} >> "${commentfile}"
i=0
until [[ $i -eq ${#TP_FOOTER_TABLE[@]} ]]; do
comment=$(echo "${TP_FOOTER_TABLE[${i}]}" |
${SED} -e "s,@@BASE@@,${BUILD_URL}${BUILD_URL_ARTIFACTS},g")
printf "%s\n" "${comment}" >> "${commentfile}"
((i=i+1))
done
printf "\n\nThis message was automatically generated.\n\n" >> "${commentfile}"
github_write_comment "${commentfile}"
}