Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 59 additions & 17 deletions .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,24 @@ jobs:
# Unbounded, this step could not fail — it could only stall. On 2026-08-19 a slow mirror
# held apt past the job's 30-minute budget on four main-branch runs and several unrelated
# PRs, which reported as a red check on branches that never ran a line of project code.
# Two bounds fix that: apt retries and times out each mirror request itself (a blip
# self-heals without a manual re-run), and `timeout-minutes` is the backstop for a mirror
# that drips bytes slowly enough to defeat the socket timeout.
# On 2026-10-22 it recurred: the step took 20-60s normally, but 10 of 73 runs hit the
# 6-minute backstop. The Azure mirror appears to be a pull-through cache. A package nobody
# else requested recently (poppler-data, fonts-urw-base35, pulled in by gnome-calculator)
# is fetched from a slow upstream on first request, and the download crawls or stalls. The
# retry that warmed the mirror then succeeded in seconds, which is why re-runs were green.
# Three bounds fix that: the downloaded .debs are cached so most runs never ask the mirror
# for them, a download that does stall is dropped and retried under a per-attempt deadline
# (apt resumes with the files already fetched), and `timeout-minutes` stays as the
# backstop. dpkg itself never runs under a deadline.
- name: Restore Linux desktop package archives
id: apt-archives
if: steps.impact.outputs.run != 'false'
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.2.3
with:
path: ~/apt-archives
key: apt-archives-${{ runner.os }}-${{ hashFiles('.github/workflows/linux.yml') }}
restore-keys: apt-archives-${{ runner.os }}-

- name: Install Linux desktop dependencies
if: steps.impact.outputs.run != 'false'
timeout-minutes: 6
Expand All @@ -79,22 +94,49 @@ jobs:
# The runner's own unattended-upgrades timer holds the dpkg lock; wait, don't hang.
-o DPkg::Lock::Timeout=60
)
printf 'APT update start (UTC): %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
sudo apt-get update "${APT_OPTS[@]}"
printf 'APT update complete (UTC): %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
printf 'APT install start (UTC): %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
sudo apt-get install -y "${APT_OPTS[@]}" \
xvfb \
xdotool \
scrot \
at-spi2-core \
python3-gi \
gir1.2-atspi-2.0 \
libatk-adaptor \
dbus-x11 \
gnome-calculator \
PACKAGES=(
xvfb
xdotool
scrot
at-spi2-core
python3-gi
gir1.2-atspi-2.0
libatk-adaptor
dbus-x11
gnome-calculator
wmctrl
)
# Retry a network phase under a per-attempt deadline.
apt_bounded() {
local seconds="$1"
shift
for attempt in 1 2 3; do
printf 'APT %s attempt %s start (UTC): %s\n' "$1" "$attempt" "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
if sudo timeout --kill-after=5 "$seconds" apt-get "$@" "${APT_OPTS[@]}"; then
return 0
fi
done
echo "::error::apt-get $1 did not finish within ${seconds}s on 3 attempts"
return 1
}
mkdir -p ~/apt-archives
sudo cp -n ~/apt-archives/*.deb /var/cache/apt/archives/ 2>/dev/null || true
apt_bounded 30 update
apt_bounded 75 install -y --no-install-recommends --download-only "${PACKAGES[@]}"
printf 'APT install start (UTC): %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
sudo apt-get install -y --no-install-recommends "${APT_OPTS[@]}" "${PACKAGES[@]}"
printf 'APT install complete (UTC): %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
# Drop archives for versions the index no longer offers so the cache cannot grow.
sudo apt-get autoclean -q
rm -f ~/apt-archives/*.deb
cp /var/cache/apt/archives/*.deb ~/apt-archives/

- name: Save Linux desktop package archives
if: steps.impact.outputs.run != 'false' && steps.apt-archives.outputs.cache-hit != 'true'
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.2.3
with:
path: ~/apt-archives
key: apt-archives-${{ runner.os }}-${{ hashFiles('.github/workflows/linux.yml') }}

- name: Start Xvfb and D-Bus
if: steps.impact.outputs.run != 'false'
Expand Down
Loading