diff options
author | EuAndreh <eu@euandre.org> | 2025-03-09 17:37:09 -0300 |
---|---|---|
committer | EuAndreh <eu@euandre.org> | 2025-03-09 17:37:09 -0300 |
commit | cd93af12b34c17be3bde287e85accc11d821077e (patch) | |
tree | d9787249daa05e004a4c95dc908ca0514c6be4e3 | |
parent | src/cicd: Fix where getopts(1) is called (diff) | |
download | cicd-cd93af12b34c17be3bde287e85accc11d821077e.tar.gz cicd-cd93af12b34c17be3bde287e85accc11d821077e.tar.xz |
Add src/cireport
-rw-r--r-- | deps.mk | 1 | ||||
-rwxr-xr-x | src/cireport | 260 |
2 files changed, 261 insertions, 0 deletions
@@ -3,4 +3,5 @@ sources.sh = \ src/cicd-post-receive \ src/cicd-run \ src/cicdd \ + src/cireport \ diff --git a/src/cireport b/src/cireport new file mode 100755 index 0000000..e14e40a --- /dev/null +++ b/src/cireport @@ -0,0 +1,260 @@ +#!/bin/sh +set -eu + +usage() { + cat <<-'EOF' + Usage: + report [-C REPO] -o DIRECTORY + report -h + EOF +} + +help() { + cat <<-'EOF' + + + Options: + -C REPO change to REPO when doing Git operations (default: $PWD) + -o DIRECTORY the directory where to place the generated files + -h, --help show this message + + + Gather data from Git Notes, and generate an HTML report on CI runs. + + Two refs with notes are expected: + 1. refs/notes/ci-data: contains metadata abount the CI runs, + with timestamps, filenames and exit status; + 2. refs/notes/ci-logs: contains the content of the log. + + When reconstructing the CI run, the $FILENAME present in + the refs/notes/ci-data ref names the file, and its content comes + from refs/notes/ci-logs. + + On a CI run that generated the numbers from 1 to 10, for a file named + 'my-ci-run-2020-01-01-deadbeef.log' that exited successfully, the + expected output on the target directory "public" is: + + $ tree public/ + public/ + index.html + data/ + my-ci-run-2020-01-01-deadbeef.log + ... + logs/ + my-ci-run-2020-01-01-deadbeef.log + ... + + $ cat public/data/my-ci-run-2020-01-01-deadbeef.log + 0 deadbeef my-ci-run-2020-01-01-deadbeef.log + + $ cat public/logs/my-ci-run-2020-01-01-deadbeef.log + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + + The generated 'index.html' is a webpage with the list of all known + CI runs, their status, a link to the commit and a link to the + log file. + + To enable fetching these refs by default, do so in the git config: + + $ git config --add remote.origin.fetch '+refs/notes/*:refs/notes/*' + + + Examples: + + Generate the report on the 'www' directory: + + $ report -o www + EOF +} + + +for flag in "$@"; do + case "$flag" in + --) + break + ;; + --help) + usage + help + exit + ;; + *) + ;; + esac +done + +REPO="$PWD" +while getopts 'C:o:h' flag; do + case "$flag" in + C) + REPO="$OPTARG" + ;; + o) + OUTDIR="$OPTARG" + ;; + h) + usage + help + exit + ;; + *) + exit 2 + ;; + esac +done +shift $((OPTIND - 1)) + +if [ -z "${OUTDIR:-}" ]; then + printf 'Missing -o OUTDIR.\n\n' >&2 + usage >&2 + exit 2 +fi + +if [ -r src/infrastructure/config/conf.env ]; then + CONF=src/infrastructure/config/conf.env +else + CONF=/etc/conf.env +fi + +# shellcheck source=/dev/null +. "$CONF" + + +esc() { + sed \ + -e 's|&|\&|g' \ + -e 's|<|\<|g' \ + -e 's|>|\>|g' \ + -e 's|"|\"|g' \ + -e "s|'|\'|g" +} + +mkdir -p "$OUTDIR" +cd "$OUTDIR" +mkdir -p logs data + +for c in $(git -C "$REPO" notes list | cut -d' ' -f2); do + git -C "$REPO" notes --ref=refs/notes/ci-data show "$c" > data/FILENAME-tmp + FILENAME="$(grep '^filename ' data/FILENAME-tmp | cut -d' ' -f2-)" + mv data/FILENAME-tmp data/"$FILENAME" + git -C "$REPO" notes --ref=refs/notes/ci-logs show "$c" > logs/"$FILENAME" +done + +{ + cat <<-EOF + <!DOCTYPE html> + <html lang="en"> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <meta name="description" content="CI logs for $NAME" /> + <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> + <title>$NAME - CI logs</title> + <style> + body { + max-width: 800px; + margin: 0 auto; + } + + code { + display: block; + margin: 1em 0em 3em 3em; + overflow: auto; + } + + pre { + display: inline; + } + + ol { + list-style-type: disc; + } + + pre, code { + background-color: #ddd; + } + + @media(prefers-color-scheme: dark) { + :root { + color: white; + background-color: black; + } + + a { + color: hsl(211, 100%, 60%); + } + + a:visited { + color: hsl(242, 100%, 80%); + } + + pre, code { + background-color: #222; + } + } + </style> + </head> + <body> + <main> + <h1> + CI logs for + <a href="$HOMEPAGE">$NAME</a> + </h1> + <ol> + EOF + + + PASS='✅' # ✅ + WARN='🐌' # 🐌 + FAIL='❌' # ❌ + find data/ -type f | LANG=C.UTF-8 sort -r | while read -r f; do + STATUS="$( grep '^status ' "$f" | cut -d' ' -f2- | esc)" + SHA="$( grep '^sha ' "$f" | cut -d' ' -f2- | esc)" + FILENAME="$(grep '^filename ' "$f" | cut -d' ' -f2- | esc)" + DURATION="$(grep '^duration ' "$f" | cut -d' ' -f2- | cut -d'"' -f1 | esc)" + MESSAGE="$({ + git -C "$REPO" log -1 --format=%B "$SHA" || { + git fetch origin "$SHA" + git -C "$REPO" log -1 --format=%B "$SHA" + } + } | esc)" + + if [ "$STATUS" = 0 ]; then + if [ "$DURATION" -le 60 ]; then + STATUS_MARKER="$PASS" + else + STATUS_MARKER="$WARN" + fi + else + STATUS_MARKER="$FAIL" + fi + + cat <<-EOF + <li id="$FILENAME"> + <a href="#$FILENAME"><pre>#</pre></a> + $STATUS_MARKER - <pre>${DURATION:-?}s</pre> + <pre>(<a href="${CGIT_URL}${SHA}">commit</a>)</pre> + <a href="logs/$FILENAME"><pre>$FILENAME</pre></a> + <pre>(<a href="data/$FILENAME">data</a>)</pre> + <br /> + <code><pre>$MESSAGE</pre></code> + </li> + EOF + done + + cat <<-EOF + </ol> + </main> + </body> + </html> + EOF +} > index.html |