From 9b8e0a0e3dc5d76f69dbe1604f011a08fb20e217 Mon Sep 17 00:00:00 2001 From: abrichr Date: Wed, 19 Aug 2026 15:50:49 -0400 Subject: [PATCH] ci: bound the apt install and prefer the canonical Ubuntu archive The hosted runner resolves its Ubuntu mirror through /etc/apt/apt-mirrors.txt, which points at azure.archive.ubuntu.com. That mirror fails intermittently and apt spends minutes of retries before it falls back, so an unbounded package step can consume a whole run: in openadapt-flow a TeX install ran 3h11m and hit the run limit, and in openadapt-capture a test job ran over an hour on main. The dump job has a 30-minute budget but the age install had no bound of its own, so one hung apt call could spend the whole budget and no recovery point would be taken that day. Bound the step to 5 minutes and prefer the canonical archive. Five minutes is deliberately tight. `age` is a single small package, and a tighter bound on the install leaves 25 of the 30 job minutes for the dump, encryption, checksum, and upload, which are the actual recovery point. A slow mirror now fails this step fast instead of starving the backup. Only the apt step changes. The dump, the age encryption, the manifest, the checksum, the S3 upload, and every credential path are untouched, and the package name and its flags are identical, so the encryption tool is the same build it has always been. Three failed update attempts fail the step closed rather than continuing toward a backup that cannot be encrypted. Co-Authored-By: Claude Opus 5 --- .github/workflows/db-backup.yml | 35 ++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/.github/workflows/db-backup.yml b/.github/workflows/db-backup.yml index 8424072..6668f26 100644 --- a/.github/workflows/db-backup.yml +++ b/.github/workflows/db-backup.yml @@ -91,8 +91,41 @@ jobs: version: 2.75.0 - name: Install age + # The dump, encrypt, and upload step is the recovery point and needs the + # job budget. Without a step bound, one hung apt call consumes all 30 + # minutes and no backup is taken. `age` is a single small package, so 5 + # minutes is a wide margin over its normal install and it leaves 25 of + # the 30 job minutes for the backup itself. + timeout-minutes: 5 run: | - sudo apt-get update -qq + set -uo pipefail + # The hosted runner resolves its Ubuntu mirror through + # /etc/apt/apt-mirrors.txt, which points at azure.archive.ubuntu.com. + # That mirror fails intermittently, and each failure costs minutes of + # apt retries. Prefer the canonical archive. Best-effort: an absent or + # already-canonical file changes nothing. + # + # The canonical archive is the source azure.archive.ubuntu.com mirrors, + # so the same suite resolves the same `age` package. The package name + # and its flags below are unchanged, so the encryption tool this backup + # depends on is the same build it has always been. + sudo sed -i \ + 's|http://azure.archive.ubuntu.com/ubuntu|http://archive.ubuntu.com/ubuntu|g' \ + /etc/apt/apt-mirrors.txt 2>/dev/null || true + update_ok="" + for attempt in 1 2 3; do + if sudo apt-get update -qq; then + update_ok=1 + break + fi + echo "::warning::apt-get update failed (attempt ${attempt}/3); retrying" + sleep $((attempt * 10)) + done + if [ -z "$update_ok" ]; then + echo "::error::apt-get update failed three times; the Ubuntu mirror is unreachable" + exit 1 + fi + set -e sudo apt-get install -y -qq age - name: Validate the exact source, private target, and recipient