Skip to content

Instantly share code, notes, and snippets.

@PhrozenByte
Created January 28, 2021 01:32
Show Gist options
  • Save PhrozenByte/0b10e0dbe4245b38f8ebcd4b17528133 to your computer and use it in GitHub Desktop.
Save PhrozenByte/0b10e0dbe4245b38f8ebcd4b17528133 to your computer and use it in GitHub Desktop.
Creates a coverage.svg badge from a clover.xml.
#!/bin/bash
# coverage-badge.sh - Creates a coverage.svg badge from a clover.xml.
#
# Copyright (C) 2021 Daniel Rudolf (<https://www.daniel-rudolf.de>)
# License: The MIT License <http://opensource.org/licenses/MIT>
#
# SPDX-License-Identifier: MIT
function print_usage {
echo "Usage:"
echo " $(basename "$0") [CLOVER_XML] [BADGE_PATH]"
}
CLOVER_XML=""
BADGE_PATH=""
while [ $# -gt 0 ]; do
if [ "$1" == "--help" ] || [ "$1" == "-h" ]; then
print_usage
echo
echo "Arguments:"
echo " CLOVER_XML path to coverage file to read from (defaults to 'clover.xml')"
echo " BADGE_PATH path to write coverage badge to (defaults to 'coverage.svg')"
echo
echo "Help options:"
echo " -h, --help display this help and exit"
echo " --version output version information and exit"
exit 0
elif [ "$1" == "--version" ]; then
echo "coverage-badge.sh 1.0 (build 20210126)"
echo "Copyright (C) 2021 Daniel Rudolf"
echo "License: The MIT License <http://opensource.org/licenses/MIT>."
echo
echo "Written by Daniel Rudolf <https://www.daniel-rudolf.de/>"
echo "See also: <https://www.daniel-rudolf.de/oss/coverage-badge>"
exit 0
elif [ -z "$CLOVER_XML" ]; then
CLOVER_XML="$1"
shift
elif [ -z "$BADGE_PATH" ]; then
BADGE_PATH="$1"
shift
elif [ "$1" != "--" ]; then
echo "Unknown option: $1" >&2
exit 1
fi
done
if [ -z "$(which xmllint)" ]; then
echo "Missing required dependency: xmllint" >&2
exit 1
fi
if [ -z "$CLOVER_XML" ] && [ ! -e "clover.xml" ]; then
print_usage >&2
exit 1
fi
CLOVER_XML="${CLOVER_XML:-clover.xml}"
if [ ! -e "$CLOVER_XML" ]; then
echo "Unable to read coverage from '$CLOVER_XML': No such file or directory" >&2
exit 1
elif [ ! -f "$CLOVER_XML" ]; then
echo "Unable to read coverage from '$CLOVER_XML': Not a file" >&2
exit 1
elif [ ! -r "$CLOVER_XML" ]; then
echo "Unable to read coverage from '$CLOVER_XML': Permission denied" >&2
exit 1
fi
BADGE_PATH="${BADGE_PATH:-coverage.svg}"
if [ -e "$BADGE_PATH" ]; then
if [ ! -f "$BADGE_PATH" ]; then
echo "Unable to write badge to '$BADGE_PATH': Already exists" >&2
exit 1
elif [ ! -w "$BADGE_PATH" ]; then
echo "Unable to write badge to '$BADGE_PATH': Permission denied" >&2
exit 1
fi
elif [ ! -d "$(dirname "$BADGE_PATH")" ]; then
echo "Unable to write badge to '$BADGE_PATH': Invalid path" >&2
exit 1
elif [ ! -w "$(dirname "$BADGE_PATH")" ] || [ ! -x "$(dirname "$BADGE_PATH")" ]; then
echo "Unable to write badge to '$BADGE_PATH': Permission denied" >&2
exit 1
fi
BADGE_TEMPLATE='
<svg xmlns="http://www.w3.org/2000/svg" width="104" height="20" role="img" aria-label="coverage: {1}">
<title>coverage: {1}</title>
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<clipPath id="r">
<rect width="104" height="20" rx="3" fill="#fff"/>
</clipPath>
<g clip-path="url(#r)">
<rect width="61" height="20" fill="#555"/>
<rect x="61" width="43" height="20" fill="{1}"/>
<rect width="104" height="20" fill="url(#s)"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110">
<text aria-hidden="true" x="315" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="510">coverage</text>
<text x="315" y="140" transform="scale(.1)" fill="#fff" textLength="510">coverage</text>
<text aria-hidden="true" x="815" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="{2}">{0}</text>
<text x="815" y="140" transform="scale(.1)" fill="#fff" textLength="{2}">{0}</text>
</g>
</svg>
'
COLORS=( "#4c1" "#97ca00" "#a4a61d" "#dfb317" "#fe7d37" "#e05d44" )
ELEMENTS="$(xmllint --xpath 'string(/coverage/project/metrics/@elements)' "$CLOVER_XML")"
STATEMENTS="$(xmllint --xpath 'string(/coverage/project/metrics/@statements)' "$CLOVER_XML")"
METHODS="$(xmllint --xpath 'string(/coverage/project/metrics/@methods)' "$CLOVER_XML")"
COVERED_ELEMENTS="$(xmllint --xpath 'string(/coverage/project/metrics/@coveredelements)' "$CLOVER_XML")"
COVERED_STATEMENTS="$(xmllint --xpath 'string(/coverage/project/metrics/@coveredstatements)' "$CLOVER_XML")"
COVERED_METHODS="$(xmllint --xpath 'string(/coverage/project/metrics/@coveredmethods)' "$CLOVER_XML")"
COVERED_METRICS="$((COVERED_ELEMENTS + COVERED_STATEMENTS + COVERED_METHODS))"
TOTAL_METRICS="$((ELEMENTS + STATEMENTS + METHODS))"
COVERAGE="$((1000 * COVERED_METRICS / TOTAL_METRICS))"
COLOR_INDEX="$(((1000 - COVERAGE + 99) / 100))"
if [ "$((COVERAGE % 10))" != "0" ]; then
COVERAGE="$(printf '%.1f\n' "${COVERAGE}e-1")%"
else
COVERAGE="$((COVERAGE / 10))%"
fi
if [ "$COLOR_INDEX" -lt "${#COLORS}" ]; then
COLOR="${COLORS[$COLOR_INDEX]}"
else
COLOR="${COLORS[-1]}"
fi
if [ "${#COVERAGE}" -ge 4 ]; then
TEXT_LENGTH=330
elif [ "${#COVERAGE}" -ge 3 ]; then
TEXT_LENGTH=250
else
TEXT_LENGTH=170
fi
BADGE="$BADGE_TEMPLATE"
BADGE="${BADGE//\{0\}/$COVERAGE}"
BADGE="${BADGE//\{1\}/$COLOR}"
BADGE="${BADGE//\{2\}/$TEXT_LENGTH}"
echo "$BADGE" > "$BADGE_PATH"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment