aboutsummaryrefslogtreecommitdiff
path: root/v2/aux/ci/report.sh
diff options
context:
space:
mode:
authorEuAndreh <eu@euandre.org>2023-04-04 08:33:41 -0300
committerEuAndreh <eu@euandre.org>2023-04-04 08:43:15 -0300
commit08588f9907299b1a927e281d5c65b46b7cefa427 (patch)
tree860f8550c2efee35df9bfa1ef56e338f8331c2d1 /v2/aux/ci/report.sh
parentdynamic.mk: Use serve(1) as is (diff)
downloadeuandre.org-08588f9907299b1a927e281d5c65b46b7cefa427.tar.gz
euandre.org-08588f9907299b1a927e281d5c65b46b7cefa427.tar.xz
Revamp v2/
Diffstat (limited to 'v2/aux/ci/report.sh')
-rwxr-xr-xv2/aux/ci/report.sh250
1 files changed, 250 insertions, 0 deletions
diff --git a/v2/aux/ci/report.sh b/v2/aux/ci/report.sh
new file mode 100755
index 0000000..0a0a0ae
--- /dev/null
+++ b/v2/aux/ci/report.sh
@@ -0,0 +1,250 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ aux/ci/report.sh -o OUTDIR
+ aux/ci/report.sh -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+
+ Options:
+ -o OUTDIR 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, ran for
+ 15 seconds and was deployed to production, the expected output on the
+ target directory "public" is:
+
+ $ tree public/
+ public/
+ index.html
+ data/
+ 2020-01-01T01:00:00-deadbeef.log
+ ...
+ logs/
+ 2020-01-01T01:00:00-deadbeef.log
+ ...
+
+ $ cat public/data/2020-01-01T01:00:00-deadbeef.log
+ status 0
+ sha deadbeef
+ filename deadbeef 2020-01-01T01:00:00-deadbeef.log
+ duration 15
+ timestamp 2020-01-01T01:00:00
+ to-prod true
+ refname refs/heads/main
+
+ $ cat public/logs/2020-01-01T01:00:00-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 and data files.
+
+ 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:
+
+ $ sh aux/ci/report.sh -o www
+ EOF
+}
+
+
+for flag in "$@"; do
+ case "$flag" in
+ --)
+ break
+ ;;
+ --help)
+ usage
+ help
+ exit
+ ;;
+ *)
+ ;;
+ esac
+done
+
+while getopts 'o:h' flag; do
+ case "$flag" in
+ o)
+ OUTDIR="$OPTARG"
+ ;;
+ h)
+ usage
+ help
+ exit
+ ;;
+ *)
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+. aux/lib.sh
+
+eval "$(assert_arg "${OUTDIR:-}" '-o OUTDIR')"
+
+
+esc() {
+ sed \
+ -e 's|&|\&amp;|g' \
+ -e 's|<|\&lt;|g' \
+ -e 's|>|\&gt;|g' \
+ -e 's|"|\&quot;|g' \
+ -e "s|'|\&#39;|g"
+}
+
+mkdir -p "$OUTDIR"
+cd "$OUTDIR"
+mkdir -p logs data
+
+for c in $(git notes list | cut -d' ' -f2); do
+ git 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 notes --ref=refs/notes/ci-logs show "$c" > logs/"$FILENAME"
+done
+
+{
+ cat <<-EOF
+ <!DOCTYPE html>
+ <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="https://$DOMAIN/s/$NAME/">$NAME</a>
+ </h1>
+ <ol>
+ EOF
+
+
+ PASS='&#x2705;' # ✅
+ WARN='&#x1F40C;' # 🐌
+ FAIL='&#x274C;' # ❌
+ 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 log -1 --format=%B "$SHA" || {
+ git fetch origin "$SHA"
+ git 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="https://$DOMAIN/git/$NAME/commit/?id=$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