CI: install ccache from the staged bundle and bound every apt call

ccache-setup ran a raw apt-get update, so jobs whose ghcr .deb bundle had
already installed cleanly still reached the mirror. On 2026-08-19 that step
stalled on archive.ubuntu.com for 9-43 min and took out 11 jobs. The .deb is
already in /var/cache/apt/archives - install-apt-deps stages the whole bundle
- so install it with --no-download first and only fall back to the mirror.
ccache was missing from the 22.04-minimal and linuxkm bundles; add it.

Neither the fallback in install-apt-deps nor any of the raw apt sites had a
timeout, so a wedged mirror hung instead of failing and the retry loops never
fired. ci-deps-image already solved this for itself (Acquire timeouts plus
`timeout`); apply the same to the consumers, and to the linuxkm producer job
that was still missing it. Also recover from a dpkg interrupted by a kill,
and bound the bundle pull.
pull/11219/head
Juliusz Sosinowicz 2026-08-19 19:07:30 +00:00
parent 437bca85a7
commit ccd810bb73
8 changed files with 154 additions and 26 deletions

View File

@ -42,9 +42,37 @@ runs:
if command -v ccache >/dev/null 2>&1; then
echo "ccache already installed: $(ccache --version | head -1)"
elif [ "${{ runner.os }}" = "Linux" ]; then
sudo apt-get update -q
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y \
--no-install-recommends ccache
export DEBIAN_FRONTEND=noninteractive
# install-apt-deps stages the WHOLE ghcr bundle into
# /var/cache/apt/archives, and ccache is in the -minimal/-full
# lists, so in a job that ran it first the .deb is already on disk.
# Take it offline (--no-download): no apt-get update, nothing to
# stall on. Every other path here reaches the mirror, which is what
# used to hang these jobs for 10-40 min after the bundle had
# already installed cleanly.
if sudo apt-get install -y --no-install-recommends \
--no-download ccache; then
echo "ccache installed offline from the staged .deb bundle"
else
# Same defence in depth as install-apt-deps: Acquire timeouts drop
# a stalled connection, `timeout` hard-kills a wedged apt-get, and
# only then does the retry loop get a non-zero exit to act on.
APT_OPTS=(-o Acquire::Retries=3 -o Acquire::http::Timeout=30
-o Acquire::https::Timeout=30)
ok=""
for i in 1 2 3; do
sudo dpkg --configure -a >/dev/null 2>&1 || true
if sudo timeout -k 10 120 apt-get "${APT_OPTS[@]}" update -q && \
sudo timeout -k 10 300 apt-get "${APT_OPTS[@]}" install -y \
--no-install-recommends ccache; then
ok=1
break
fi
echo "::warning::ccache apt install failed (attempt $i/3)"
sleep $((5 * i))
done
[ -n "$ok" ] || { echo "::error::could not install ccache"; exit 1; }
fi
elif [ "${{ runner.os }}" = "macOS" ]; then
brew install ccache
else

View File

@ -50,7 +50,7 @@ runs:
# PRs read the public upstream image too rather than a nonexistent
# ghcr.io/<fork>/wolfssl-ci-debs.
IMG="ghcr.io/wolfssl/wolfssl-ci-debs:${{ inputs.ghcr-debs-tag }}"
if ! docker pull -q "$IMG" >/dev/null 2>&1; then
if ! timeout -k 10 300 docker pull -q "$IMG" >/dev/null 2>&1; then
echo "::notice::ghcr bundle $IMG unavailable; using apt"
exit 0
fi
@ -85,9 +85,21 @@ runs:
NO_REC="--no-install-recommends"
fi
# A wedged mirror hangs apt rather than failing it, so the retry loop
# below never fired and the job burned its whole budget instead.
# Defend in depth: apt drops a stalled connection after 30s and retries
# it (Acquire timeouts), `timeout` hard-kills an apt-get that wedged
# anyway, then the loop re-runs - re-reading apt-mirrors.txt, so a
# retry can land on a different mirror.
APT_OPTS=(-o Acquire::Retries=3 -o Acquire::http::Timeout=30
-o Acquire::https::Timeout=30)
for i in $(seq 1 $RETRIES); do
if sudo apt-get update -q && \
sudo apt-get install -y $NO_REC ${{ inputs.packages }}; then
# A previous attempt killed mid-unpack leaves dpkg needing this.
sudo dpkg --configure -a >/dev/null 2>&1 || true
if sudo timeout -k 10 120 apt-get "${APT_OPTS[@]}" update -q && \
sudo timeout -k 10 900 apt-get "${APT_OPTS[@]}" install -y \
$NO_REC ${{ inputs.packages }}; then
exit 0
fi
if [ "$i" -eq "$RETRIES" ]; then

View File

@ -4,6 +4,7 @@
autoconf
automake
build-essential
ccache
crossbuild-essential-arm64
crossbuild-essential-armel
crossbuild-essential-armhf

View File

@ -201,7 +201,13 @@ echo "==> [container] Exporting Zephyr..."
west zephyr-export
echo "==> [container] Installing host packages (newlib, python3-venv)..."
sudo apt-get update -qq && sudo apt-get install -y -qq python3-venv libnewlib-dev >/dev/null 2>&1 || true
# `|| true` keeps this best-effort, but without a timeout a wedged mirror
# stalls here silently until the job budget runs out.
APT_OPTS=(-o Acquire::Retries=3 -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30)
sudo timeout -k 10 120 apt-get "${APT_OPTS[@]}" update -qq >/dev/null 2>&1 \
&& sudo timeout -k 10 300 apt-get "${APT_OPTS[@]}" install -y -qq \
python3-venv libnewlib-dev >/dev/null 2>&1 \
|| echo "==> [container] host package install skipped (apt unavailable)"
python3 -m venv .venv
source .venv/bin/activate
pip3 install west

View File

@ -183,21 +183,25 @@ jobs:
set -euo pipefail
K="${{ steps.check.outputs.kernel }}"
# linuxkm.yml installs only the headers; the membrowse linuxkm targets
# also need the build toolchain. Bundle the union - each consumer
# installs its own subset offline.
PKGS=(build-essential autoconf automake libtool "linux-headers-$K")
# also need the build toolchain, and ccache-setup installs ccache
# offline from whatever this bundle staged. Bundle the union - each
# consumer installs its own subset offline.
PKGS=(build-essential autoconf automake libtool ccache
"linux-headers-$K")
echo "Packages: ${PKGS[*]}"
export DEBIAN_FRONTEND=noninteractive
rm -rf debs && mkdir -p debs
sudo apt-get clean
APT_OPTS=(-o Acquire::Retries=3 -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30)
retry() { local i; for i in 1 2 3 4 5; do "$@" && return 0; sleep $((2**i)); done; "$@"; }
retry sudo apt-get update -q
retry sudo timeout -k 10 120 apt-get "${APT_OPTS[@]}" update -q
# The whole set is required and this bundle is small, so resolve it as
# one closure and let any download failure fail the job. We push only
# on success, so a transient mirror error keeps the last good bundle
# rather than publishing a partial one - which the kernel-label skip
# would then pin in place until the kernel next changes (~monthly).
retry sudo apt-get install -y --download-only "${PKGS[@]}"
retry sudo timeout -k 10 600 apt-get "${APT_OPTS[@]}" install -y \
--download-only "${PKGS[@]}"
sudo cp /var/cache/apt/archives/*.deb debs/ 2>/dev/null || true
echo "Bundled $(ls debs/*.deb 2>/dev/null | wc -l) .deb files"
test -n "$(ls debs/*.deb 2>/dev/null)" # headers are never preinstalled

View File

@ -64,10 +64,23 @@ jobs:
run: |
set -eux
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y --no-install-recommends \
build-essential autoconf automake libtool pkg-config \
git ca-certificates ${{ inputs.apt_packages }}
# A wedged mirror hangs apt instead of failing it. Acquire timeouts
# drop a stalled connection, `timeout` hard-kills apt-get if it
# wedges anyway, and the loop then retries against a fresh mirror.
APT_OPTS=(-o Acquire::Retries=3 -o Acquire::http::Timeout=30
-o Acquire::https::Timeout=30)
for i in 1 2 3; do
if timeout -k 10 120 apt-get "${APT_OPTS[@]}" update -q && \
timeout -k 10 900 apt-get "${APT_OPTS[@]}" install -y \
--no-install-recommends \
build-essential autoconf automake libtool pkg-config \
git ca-certificates ${{ inputs.apt_packages }}; then
break
fi
test "$i" -lt 3 || { echo "::error::apt-get failed after 3 attempts"; exit 1; }
echo "::warning::apt-get failed (attempt $i/3)"
sleep $((5 * i))
done
# Building only needs the commit under test, not history. The break check
# that needs history runs in the compile job, not here.
@ -129,10 +142,23 @@ jobs:
run: |
set -eux
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y --no-install-recommends \
build-essential autoconf automake libtool pkg-config \
git ca-certificates ${{ inputs.apt_packages }}
# A wedged mirror hangs apt instead of failing it. Acquire timeouts
# drop a stalled connection, `timeout` hard-kills apt-get if it
# wedges anyway, and the loop then retries against a fresh mirror.
APT_OPTS=(-o Acquire::Retries=3 -o Acquire::http::Timeout=30
-o Acquire::https::Timeout=30)
for i in 1 2 3; do
if timeout -k 10 120 apt-get "${APT_OPTS[@]}" update -q && \
timeout -k 10 900 apt-get "${APT_OPTS[@]}" install -y \
--no-install-recommends \
build-essential autoconf automake libtool pkg-config \
git ca-certificates ${{ inputs.apt_packages }}; then
break
fi
test "$i" -lt 3 || { echo "::error::apt-get failed after 3 attempts"; exit 1; }
echo "::warning::apt-get failed (attempt $i/3)"
sleep $((5 * i))
done
# This job does not build wolfSSL, but the latest leg still checks out
# wolfSSL history because check-break.sh scans commit messages here. The

View File

@ -91,8 +91,25 @@ jobs:
steps:
- name: Install build tools
run: |
sudo apt-get update
sudo apt-get install -y ninja-build
# A wedged mirror hangs apt instead of failing it. Acquire timeouts
# drop a stalled connection, `timeout` hard-kills apt-get if it
# wedges anyway, and the loop then retries against a fresh mirror.
APT_OPTS=(-o Acquire::Retries=3 -o Acquire::http::Timeout=30
-o Acquire::https::Timeout=30)
apt_retry() {
local i
for i in 1 2 3; do
if sudo timeout -k 10 120 apt-get "${APT_OPTS[@]}" update -q && \
sudo timeout -k 10 900 apt-get "${APT_OPTS[@]}" install -y "$@"; then
return 0
fi
echo "::warning::apt-get failed (attempt $i/3)"
sleep $((5 * i))
done
echo "::error::apt-get failed after 3 attempts"
return 1
}
apt_retry ninja-build
# Check out wolfSSL first: actions/checkout runs "git clean -ffdx", which
# would delete an untracked oqs-install/ placed in the workspace by the
@ -161,8 +178,25 @@ jobs:
steps:
- name: Install build tools
run: |
sudo apt-get update
sudo apt-get install -y autoconf automake libtool
# A wedged mirror hangs apt instead of failing it. Acquire timeouts
# drop a stalled connection, `timeout` hard-kills apt-get if it
# wedges anyway, and the loop then retries against a fresh mirror.
APT_OPTS=(-o Acquire::Retries=3 -o Acquire::http::Timeout=30
-o Acquire::https::Timeout=30)
apt_retry() {
local i
for i in 1 2 3; do
if sudo timeout -k 10 120 apt-get "${APT_OPTS[@]}" update -q && \
sudo timeout -k 10 900 apt-get "${APT_OPTS[@]}" install -y "$@"; then
return 0
fi
echo "::warning::apt-get failed (attempt $i/3)"
sleep $((5 * i))
done
echo "::error::apt-get failed after 3 attempts"
return 1
}
apt_retry autoconf automake libtool
- name: Checkout wolfSSL
uses: actions/checkout@v5

View File

@ -822,7 +822,24 @@ jobs:
- name: Install build deps + SBOM validators
run: |
sudo apt-get update
# A wedged mirror hangs apt instead of failing it. Acquire timeouts
# drop a stalled connection, `timeout` hard-kills apt-get if it
# wedges anyway, and the loop then retries against a fresh mirror.
APT_OPTS=(-o Acquire::Retries=3 -o Acquire::http::Timeout=30
-o Acquire::https::Timeout=30)
apt_retry() {
local i
for i in 1 2 3; do
if sudo timeout -k 10 120 apt-get "${APT_OPTS[@]}" update -q && \
sudo timeout -k 10 900 apt-get "${APT_OPTS[@]}" install -y "$@"; then
return 0
fi
echo "::warning::apt-get failed (attempt $i/3)"
sleep $((5 * i))
done
echo "::error::apt-get failed after 3 attempts"
return 1
}
# bison + autotools-dev are required by strace's ./bootstrap.
# gcc-multilib + g++-multilib give strace's --enable-mpers=check
# the 32-bit/x32 compilers it needs - without them mpers is
@ -830,7 +847,7 @@ jobs:
# syscalls, diverging from what bomsh's devcontainer produces.
# The rest mirror bomsh's .devcontainer/Dockerfile bomtrace3
# stage.
sudo apt-get install -y build-essential autoconf automake libtool \
apt_retry build-essential autoconf automake libtool \
bison autotools-dev gcc-multilib g++-multilib \
python3 python3-pip git
python3 -m pip install --user --upgrade pip