Skip to content

Instantly share code, notes, and snippets.

@iruca3
Last active June 6, 2017 21:35
Show Gist options
  • Save iruca3/2373af9a16c7bc86f19dad3d812d527d to your computer and use it in GitHub Desktop.
Save iruca3/2373af9a16c7bc86f19dad3d812d527d to your computer and use it in GitHub Desktop.
circleci-coverage-slack
###
# Post coverage rate to Slack
#
# Usage: bash circleci-coverage-slack.sh [cobertura|jacoco]
#
# Required environment variables:
#
# - CIRCLE_TOKEN: project-specific readonly API token (need to access build artifacts for others)
# - SLACK_ENDPOINT: Slack endpoint url
# - COVERAGE_FILE: coverage xml filename (default: coverage.xml)
# - MAX_BUILD_HISTORY: max history num to fetch old rate (default: 5)
#
function calcCoberturaRate() {
local rate=$(ruby -r "rexml/document" -e '
node = REXML::XPath.first(REXML::Document.new(STDIN.read), "coverage")
puts "%.2f" % (node.attributes["line-rate"].to_f * 100)
')
[ "$rate" == "100.00" ] && rate=100
echo $rate
}
function calcJacocoRate() {
local rate=$(ruby -r "rexml/document" -e '
node = REXML::XPath.first(REXML::Document.new(STDIN.read), "report/counter[@type=\"LINE\"]")
missed = node.attributes["missed"].to_f
covered = node.attributes["covered"].to_f
puts "%.2f" % (covered / (covered + missed) * 100)
')
[ "$rate" == "100.00" ] && rate=100
echo $rate
}
function calcRate() {
case "$coverage_type" in
"cobertura" )
calcCoberturaRate
;;
"jacoco" )
calcJacocoRate
;;
esac
}
coverage_type=${1:-cobertura}
coverage=$(find $CIRCLE_ARTIFACTS -type f | grep ${COVERAGE_FILE:-coverage.xml})
url_base="https://circleci.com/api/v1/project/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}"
## calculate rates
rate_new=$(cat $coverage | calcRate)
rate_old=0
for build_num in $(echo $CIRCLE_PREVIOUS_BUILD_NUM; seq $(expr $CIRCLE_BUILD_NUM - 1) -1 1 | head -n ${MAX_BUILD_HISTORY:-5}); do
echo "fetching coverage info for build:${build_num} ..."
artifacts="${url_base}/${build_num}/artifacts"
[ -n "$CIRCLE_TOKEN" ] && artifacts="${artifacts}?circle-token=${CIRCLE_TOKEN}"
coverage=$(curl -s -H 'Accept: application/json' $artifacts | jq -r '.[].url' | grep ${COVERAGE_FILE:-coverage.xml})
[ -n "$coverage" ] && break
done
if [ -n "$coverage" ]; then
[ -n "$CIRCLE_TOKEN" ] && coverage="${coverage}?circle-token=${CIRCLE_TOKEN}"
rate_old=$(curl -s $coverage | calcRate)
fi
## construct messages
author=$(git log --format='%an <%ae>' -1 | sed 's/\\/\\\\/g;s/\"/\\"/g')
log=$(git log --format=%s -1 | sed 's/\\/\\\\/g;s/\"/\\"/g')
mode=$(ruby -e 'puts (ARGV[0].to_f <=> ARGV[1].to_f) + 1' -- $rate_new $rate_old)
mes=("DECREASED" "NOT CHANGED" "INCREASED")
color=("danger" "#a0a0a0" "good")
cat > .slack_payload <<_EOT_
{
"attachments": [
{
"fallback": "Coverage ${mes[$mode]} (${rate_new}%)",
"text": "*${mes[$mode]}* (${rate_old}% -> ${rate_new}%)",
"pretext": "Coverage report: <https://circleci.com/gh/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}|${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}> - <https://circleci.com/gh/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/${CIRCLE_BUILD_NUM}|#${CIRCLE_BUILD_NUM}>",
"color": "${color[$mode]}",
"mrkdwn_in": ["pretext", "text", "fields"],
"fields": [
{
"value": "Commit: <https://github.com/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/commit/${CIRCLE_SHA1}|${CIRCLE_SHA1}>",
"short": false
},
{
"value": "Author: ${author}",
"short": false
},
{
"value": "${log}",
"short": false
},
{
"value": "<https://$CIRCLE_BUILD_NUM-00000000-gh.circle-artifacts.com/0$CIRCLE_ARTIFACTS/coverage/index.html|coverage detail>",
"short": false
}
]
}
]
}
_EOT_
## post to slack
curl -s --data-urlencode payload@.slack_payload ${SLACK_ENDPOINT}
rm .slack_payload
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment