From 1a3208d43f837768e18219ca4e79fe31bf748865 Mon Sep 17 00:00:00 2001 From: EuAndreh Date: Fri, 31 Mar 2023 20:12:17 -0300 Subject: Revamp CI: simpler variant of the same functionality --- aux/ci/report.sh | 297 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 222 insertions(+), 75 deletions(-) (limited to 'aux/ci/report.sh') diff --git a/aux/ci/report.sh b/aux/ci/report.sh index 6f18f0f..5cffda1 100755 --- a/aux/ci/report.sh +++ b/aux/ci/report.sh @@ -1,17 +1,110 @@ #!/bin/sh set -eu -TLD="$(cat aux/tld.txt)" -. aux/lib.sh +usage() { + cat <<-'EOF' + Usage: + aux/ci/report.sh -n NAME -o OUTDIR + aux/ci/report.sh -h + EOF +} + +help() { + cat <<-'EOF' + + + Options: + -n NAME the name of the project + -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, 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 -while getopts 'n:o:' flag; do +while getopts 'n:o:h' flag; do case "$flag" in n) - PROJECT="$OPTARG" + NAME="$OPTARG" ;; o) OUTDIR="$OPTARG" ;; + h) + usage + help + exit + ;; *) exit 2 ;; @@ -19,83 +112,137 @@ while getopts 'n:o:' flag; do done shift $((OPTIND - 1)) -assert_arg() { - if [ -z "$1" ]; then - echo "Missing $2" >&2 - exit 2 - fi -} +. aux/lib.sh -assert_arg "${PROJECT:-}" '-n PROJECT' -assert_arg "${OUTDIR:-}" '-o OUTDIR' +eval "$(assert_arg "${NAME:-}" '-n NAME')" +eval "$(assert_arg "${OUTDIR:-}" '-o OUTDIR')" -PASS='✅' -FAIL='❌' -mkdir -p "$OUTDIR/ci-logs" "$OUTDIR/ci-data" +esc() { + sed \ + -e 's|&|\&|g' \ + -e 's|<|\<|g' \ + -e 's|>|\>|g' \ + -e 's|"|\"|g' \ + -e "s|'|\'|g" +} -OUT="$(mkstemp)" -chmod 644 "$OUT" +mkdir -p "$OUTDIR" +cd "$OUTDIR" +mkdir -p logs data -for c in $(git notes list | cut -d\ -f2); do - DATA="$(git notes --ref=refs/notes/ci-data show "$c")" - FILENAME="$(echo "$DATA" | cut -d\ -f2)" - echo "$DATA" > "$OUTDIR/ci-data/$FILENAME" - git notes --ref=refs/notes/ci-logs show "$c" \ - > "$OUTDIR/ci-logs/$FILENAME" +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 <> "$OUT" - - - - - - - - $PROJECT - CI logs - - - - -
-

- CI logs for - $PROJECT -

-
    -EOF - -for f in $(find "$OUTDIR/ci-data/" -type f | LANG=C.UTF-8 sort -r); do - DATA="$(cat "$f")" - STATUS="$(echo "$DATA" | cut -d\ -f1)" - FILENAME="$(echo "$DATA" | cut -d\ -f2)" - - if [ "$STATUS" = 0 ]; then - STATUS_MARKER="$PASS" - else - STATUS_MARKER="$FAIL" - fi - - cat <> "$OUT" -
  1. - $STATUS_MARKER
    $FILENAME
    -
  2. -EOF -done +{ + cat <<-EOF + + + + + + + $NAME - CI logs + + + +
    +

    + CI logs for + $NAME +

    +
      + 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 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 <> "$OUT" -
    -
    - - -EOF + cat <<-EOF +
  3. +
    #
    + $STATUS_MARKER -
    ${DURATION:-?}s
    +
    (commit)
    +
    $FILENAME
    +
    (data)
    +
    +
    $MESSAGE
    +
  4. + EOF + done -mv "$OUT" "$OUTDIR/ci.html" + cat <<-EOF +
+
+ + + EOF +} > index.html -- cgit v1.2.3