966c49bdf93dada183133e06522bfb0152de6168
braney
  Wed Sep 23 13:38:34 2026 -0700
docent: the nightly writes a status page, refs #38252

nightlyStatus.py reads the nightly logs, flips.log and each committed script's proof:
lines, and writes one page with a row per script, anchored by its name so a ticket can
link to one row. Each row has the current state and the date it has held since, the last
14 days, the strongest proof: level in plain words, and links to the script, the ticket
and any before/after picture.

Each row also has a link to try it on genome-test and on hgwbeta: the script's steps as
far as one URL can rebuild them, with track: steps resolved by DOCENT_DERIVE. Every link
is fetched with a fresh cart first, and the page calls it verified only when the fetch
draws what the expect: step names.

A night is a log written by an --update run, which is what cron does, not a log named
_0410: the 09-14 and 09-15 runs started late and a name match dropped them.

nightly.sh runs it after the log is complete and adds one line to the mail, the page URL
or the error. It changes nothing else about the night's verdict or exit status.

diff --git src/hg/utils/docent/tests/regress/nightly.sh src/hg/utils/docent/tests/regress/nightly.sh
index 2d1de12ae40..6cb1bcebcd8 100755
--- src/hg/utils/docent/tests/regress/nightly.sh
+++ src/hg/utils/docent/tests/regress/nightly.sh
@@ -34,30 +34,32 @@
 # --update will only ever touch a checkout that is a pristine mirror of origin/master.
 # A working tree with uncommitted edits, or one holding a commit that has not been
 # pushed, is left alone and reported, because a `reset --hard` there would throw away
 # work.  That is what makes it safe for the flag to exist in a script that also sits in
 # a working tree.
 
 set -u
 export PATH=/usr/bin:/bin:/usr/local/bin:$PATH
 
 HERE=$(cd "$(dirname "$0")" && pwd)
 PW=/hive/groups/browser/uiTest/pw
 PW_ENV="env PLAYWRIGHT_BROWSERS_PATH=$PW/browsers NODE_PATH=$PW/node_modules"
 TO=${DOCENT_NIGHTLY_TO:-braney@ucsc.edu}
 LOGDIR=${DOCENT_NIGHTLY_LOGS:-/hive/users/braney/docentNightly/logs}
 FLIPS=${DOCENT_NIGHTLY_FLIPS:-/hive/users/braney/docentNightly/flips.log}
+STATUSDIR=${DOCENT_NIGHTLY_STATUS:-/hive/users/braney/public_html/docentStatus}
+STATUSURL=${DOCENT_NIGHTLY_STATUS_URL:-https://hgwdev.gi.ucsc.edu/~braney/docentStatus/}
 STAMP=$(date +%Y-%m-%d_%H%M)
 # Only a label for the flips.log line.  Every script here says `target: genome-test`
 # itself; this is not read from them, so override it if that ever stops being true.
 TARGET=${DOCENT_NIGHTLY_TARGET:-genome-test}
 
 mkdir -p "$LOGDIR"
 OUT="$LOGDIR/$STAMP.txt"
 VERDICTS="$LOGDIR/.$STAMP.verdicts"
 trap 'rm -f "$VERDICTS"' EXIT
 
 # A job that could not start has to arrive looking like the others, or a night when
 # nothing ran reads as a quiet night.  Same subject shape, same log file, exit 0.
 bail() {
 {
     echo "Docent regression run, $(date)"
@@ -176,23 +178,35 @@
           "$(date +%F)" "$t" "$TARGET" \
           "$(git -C "$HERE" rev-parse --short HEAD 2>/dev/null)" "$STAMP.txt" >> "$FLIPS"
         echo "  $t -- recorded in $FLIPS"
       fi
     done
     echo
     echo "  To close one out: drop the .xfail from the script's name, and add a"
     echo "  server-flip line to its proof: key naming the commit that shipped the fix."
     echo "  Then \`make proof\` counts it.  Until that commit is pushed this stays red."
   fi
 
   echo
   echo "full log kept at $OUT"
 } > "$OUT" 2>&1
 
+# The status page: one row per script, with its history, its proof and a link to try it.
+# Written after the log, because the log is what it reads.  A failure here is reported in
+# the mail and changes nothing else, since the page only reports what the log says.
+if [ -n "${TESTS// /}" ]; then
+  if st=$(cd "$HERE" && $PW_ENV python3 nightlyStatus.py --logs "$LOGDIR" \
+            --flips "$FLIPS" --out "$STATUSDIR" 2>&1); then
+    echo "status page: $STATUSURL ($st)" >> "$OUT"
+  else
+    printf 'status page NOT updated:\n%s\n' "$st" >> "$OUT"
+  fi
+fi
+
 # subject_state is set inside the block above, which runs in this shell, so it survives.
 n=$(grep -c '^  ok$' "$OUT" 2>/dev/null || echo 0)
 mail -s "docent regression: ${subject_state:-unknown} ($n ok) $(date +%F)" "$TO" < "$OUT"
 
 # Keep two months of logs and no more.
 find "$LOGDIR" -name '*.txt' -mtime +60 -delete 2>/dev/null
 
 exit 0