dbb8f6e006713f43b7dbd6100d3b5c024dc72766
braney
  Fri Sep 18 11:11:15 2026 -0700
weeklybld: let the docker instance scripts run a past release, refs #38377

We could not reproduce a bug against the release a user is actually on.
hgwdev runs tip and the current release, and nothing older. Docker Hub
already has a per-release image, so the missing piece was a way to run one.

The lifecycle scripts now take a release name as well as tip, beta and rel.
run-instance.sh v503 starts the published v503 image as container kent-v503
on port 8503; the port is 8000 plus the release number, so the mapping needs
no table and no allocator. A release instance gets no CGI overlay, for the
same reason rel gets none: it has to be the code that shipped.
refresh-instance.sh, remove-instance.sh and smoke-instance.sh take the same
name. smoke-instance.sh checks a release instance against the version in its
own name, so no --version flag is needed. start-all.sh and stop-all.sh now
read the release containers that exist rather than a fixed list, so the set
of releases we keep can change without editing them.

tunnel-release.sh is one script for every release rather than one per
release, since that set changes.

v499 through v503 are running on hgwdev on ports 8499 through 8503 and pass
smoke-instance.sh. Five instances cost about 1.5 GB of memory, 10 GB of
docker disk, and no measurable CPU. They read tables from
genome-mysql.soe.ucsc.edu and files from hgdownload.soe.ucsc.edu, so they
put no load on the hgwdev MySQL or /gbdb.

diff --git src/utils/qa/weeklybld/run-instance.sh src/utils/qa/weeklybld/run-instance.sh
index ffa26f1c612..5e94863a85a 100755
--- src/utils/qa/weeklybld/run-instance.sh
+++ src/utils/qa/weeklybld/run-instance.sh
@@ -1,48 +1,65 @@
 #!/bin/bash
 #
-# run-instance.sh <tip|beta|rel>
+# run-instance.sh <tip|beta|rel|vNNN>
 #
-# Start one of the three docker browser instances on hgwdev. The image is fully
+# Start one of the docker browser instances on hgwdev. The image is fully
 # self-contained: MariaDB, Apache and the CGIs were baked in at build time by
 # browserSetup.sh, so NOTHING from the hgwdev filesystem is bind-mounted into
 # the container. Anything that must survive a refresh (the MariaDB data dir and
 # /gbdb) lives on Docker-managed named volumes, which seed from the image's
 # baked content on first use and persist across refresh-instance cycles. For
 # tip and beta, the matching hgwdev CGIs are copied in on top afterward (see
 # overlay-cgi.sh) -- a copy, not a mount.
+#
+# A release name (v499, v503, ...) starts a past release from its published
+# image on Docker Hub. That instance is deliberately NOT overlaid: the point of
+# it is to run the exact code a user on that release is running, so a bug can be
+# reproduced against it. refs #38377
 # refs #37655
 #
 set -eEu -o pipefail
 
 selfDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
 
 usage() {
-    echo "usage: $(basename "$0") tip|beta|rel" >&2
+    echo "usage: $(basename "$0") tip|beta|rel|vNNN" >&2
     exit 1
 }
 
 [[ $# -eq 1 ]] || usage
 name="$1"
 platform=""
 case "$name" in
     tip)        port=8081; image=kent:tip ;;
     beta)       port=8082; image=kent:beta ;;
     rel)        port=8083; image=genomebrowser/server:latest ;;
     # beta-arm64 is an arm64 image running emulated on this amd64 host, so pin
     # the platform explicitly; it needs the QEMU binfmt handlers registered.
     beta-arm64) port=8084; image=kent:beta-arm64; platform="--platform linux/arm64" ;;
+    # A past release. The port encodes the release number (v503 -> 8503), so the
+    # mapping needs neither a table nor an allocator, and the tag is the
+    # multi-arch manifest buildReleaseDocker.sh pushes, so docker picks the
+    # image for this architecture. refs #38377
+    v[0-9][0-9][0-9])
+        port=$(( 8000 + 10#${name#v} ))
+        image="genomebrowser/server:$name"
+        if (( port <= 8084 )); then
+            echo "$name would want port $port, which is inside the tip/beta/rel block" >&2
+            exit 1
+        fi
+        ;;
     *)          usage ;;
 esac
 container="kent-$name"
 
 # -p 127.0.0.1:PORT:80 binds to loopback only, so the container is reachable
 #   from hgwdev itself (reverse-proxy vhosts or an ssh tunnel), not the network.
 # --restart=unless-stopped brings the container back after a daemon restart or
 #   reboot without manual intervention.
 # --memory/--cpus cap resource use so a runaway query can't starve hgwdev.
 docker run -d --name "$container" \
     $platform \
     --restart=unless-stopped \
     --memory=8g --cpus=2 \
     -p "127.0.0.1:${port}:80" \
     -v "kent-${name}-mysql:/var/lib/mysql" \