Skip to content

Commit ac7b9d0

Browse files
committed
CI: Address occasional ccache install hangs
1 parent a1cde8e commit ac7b9d0

4 files changed

Lines changed: 87 additions & 14 deletions

File tree

.github/actions/ccache-setup/action.yml

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@ inputs:
2323
description: 'Per-job ccache max size (passed to ccache -M).'
2424
required: false
2525
default: '500M'
26+
ghcr-debs-tag:
27+
description: >
28+
Bundle to install ccache from offline (see install-apt-deps).
29+
'auto' resolves to the runner's ubuntu-<version>-minimal bundle,
30+
which already carries ccache and which most callers pull earlier in
31+
the same job, making the second pull free. 'none' skips the bundle
32+
and uses apt - for callers whose job pulls a different bundle,
33+
where fetching this one costs more than it saves.
34+
required: false
35+
default: 'auto'
2636
read-only:
2737
description: >
2838
When 'true', restore the cache but do NOT save it (no post-job
@@ -36,21 +46,52 @@ inputs:
3646
runs:
3747
using: 'composite'
3848
steps:
39-
- name: Install ccache
49+
- name: Check for ccache
50+
id: check
4051
shell: bash
4152
run: |
4253
if command -v ccache >/dev/null 2>&1; then
4354
echo "ccache already installed: $(ccache --version | head -1)"
44-
elif [ "${{ runner.os }}" = "Linux" ]; then
45-
sudo apt-get update -q
46-
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y \
47-
--no-install-recommends ccache
48-
elif [ "${{ runner.os }}" = "macOS" ]; then
49-
brew install ccache
50-
else
51-
echo "::error::ccache install not supported on ${{ runner.os }}"
52-
exit 1
55+
echo "present=true" >> "$GITHUB_OUTPUT"
56+
exit 0
57+
fi
58+
echo "present=false" >> "$GITHUB_OUTPUT"
59+
case "${{ runner.os }}" in
60+
Linux|macOS) ;;
61+
*) echo "::error::ccache install not supported on ${{ runner.os }}"
62+
exit 1 ;;
63+
esac
64+
tag=""
65+
if [ "${{ runner.os }}" = "Linux" ]; then
66+
tag="${{ inputs.ghcr-debs-tag }}"
67+
if [ "$tag" = "auto" ]; then
68+
# os-release rather than lsb_release: no package needed.
69+
. /etc/os-release
70+
tag="ubuntu-$VERSION_ID-minimal"
71+
elif [ "$tag" = "none" ]; then
72+
tag=""
73+
fi
5374
fi
75+
echo "tag=$tag" >> "$GITHUB_OUTPUT"
76+
77+
# Offline from the ghcr bundle when it has ccache, apt otherwise -
78+
# bounded either way. See install-apt-deps.
79+
- name: Install ccache (Linux)
80+
if: steps.check.outputs.present != 'true' && runner.os == 'Linux'
81+
uses: ./.github/actions/install-apt-deps
82+
with:
83+
packages: ccache
84+
no-install-recommends: 'true'
85+
ghcr-debs-tag: ${{ steps.check.outputs.tag }}
86+
# ccache is an optimization: never spend more of the job
87+
# installing it than a cold compile would have cost.
88+
apt-budget: '120'
89+
apt-install-timeout: '120'
90+
91+
- name: Install ccache (macOS)
92+
if: steps.check.outputs.present != 'true' && runner.os == 'macOS'
93+
shell: bash
94+
run: brew install ccache
5495

5596
# read-only=false (default): restore + post-job save (the run_id in the
5697
# key never hits, so it always saves its contribution).

.github/actions/install-apt-deps/action.yml

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,18 @@ inputs:
1212
description: 'Initial delay between retries (seconds, doubles each attempt)'
1313
required: false
1414
default: '5'
15+
apt-budget:
16+
description: >
17+
Wall-clock budget (seconds) for retrying the apt install.
18+
required: false
19+
default: '600'
20+
apt-install-timeout:
21+
description: >
22+
Per-attempt cap (seconds) on apt-get install. The default suits the
23+
large bundles; callers installing one small package should pass a
24+
much smaller value.
25+
required: false
26+
default: '600'
1527
no-install-recommends:
1628
description: 'Pass --no-install-recommends to apt-get install'
1729
required: false
@@ -85,13 +97,29 @@ runs:
8597
NO_REC="--no-install-recommends"
8698
fi
8799
100+
# A stalled mirror connection returns no error, so the loop below
101+
# cannot see it - apt just waits out its timeout once per index
102+
# file, and the runner fetches around twenty of them. Keep that
103+
# per-connection timeout short so failing over to the next mirror
104+
# is cheap, cap each apt-get so a wedged one is killed, and stop
105+
# retrying once the budget is gone.
106+
APT_OPTS=(-o Acquire::Retries=2 -o Acquire::http::Timeout=10
107+
-o Acquire::https::Timeout=10)
108+
# Enough for a full failover of every index file at the timeout
109+
# above, so a slow-but-recovering update is not cut short.
110+
UPDATE_TIMEOUT=240
111+
deadline=$((SECONDS + ${{ inputs.apt-budget }}))
112+
88113
for i in $(seq 1 $RETRIES); do
89-
if sudo apt-get update -q && \
90-
sudo apt-get install -y $NO_REC ${{ inputs.packages }}; then
114+
if sudo timeout -k 10 $UPDATE_TIMEOUT \
115+
apt-get "${APT_OPTS[@]}" update -q && \
116+
sudo timeout -k 10 ${{ inputs.apt-install-timeout }} \
117+
apt-get "${APT_OPTS[@]}" install -y $NO_REC \
118+
${{ inputs.packages }}; then
91119
exit 0
92120
fi
93-
if [ "$i" -eq "$RETRIES" ]; then
94-
echo "::error::apt-get failed after $RETRIES attempts"
121+
if [ "$i" -eq "$RETRIES" ] || [ "$SECONDS" -ge "$deadline" ]; then
122+
echo "::error::apt-get failed after $i attempt(s)"
95123
exit 1
96124
fi
97125
echo "::warning::apt-get failed (attempt $i/$RETRIES), retrying in ${DELAY}s..."

.github/ci-deps/packages-ubuntu-22.04-minimal.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
autoconf
55
automake
66
build-essential
7+
ccache
78
crossbuild-essential-arm64
89
crossbuild-essential-armel
910
crossbuild-essential-armhf

.github/workflows/fips-dev-no-post.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ jobs:
6969
workflow-id: fips-dev-no-post
7070
read-only: ${{ github.event_name == 'pull_request' }}
7171
max-size: 500M
72+
# This job pulls the linuxkm bundle, not -minimal; fetching the
73+
# latter just for ccache would cost more than the apt path.
74+
ghcr-debs-tag: none
7275

7376
- name: Prepare target kernel for module builds
7477
run: |

0 commit comments

Comments
 (0)