CI: Cache archives downloaded by apt (#8159)

Belt-and-braces in case GitHub apt mirrors start acting up again: On any
successful apt install, cache the deb files downloaded/needed for the
step, so on next run the mirrors need not be consulted. (That is: trade
apt mirror access with GitHub cache access, which is more reliable)
This commit is contained in:
Geza Lore
2026-08-20 11:16:17 +02:00
committed by GitHub
parent e717c513e9
commit 38ae4e617d
7 changed files with 130 additions and 10 deletions
+109
View File
@@ -0,0 +1,109 @@
---
# DESCRIPTION: Github actions composite action
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
name: Install dependencies
description: >-
Install the dependencies of a CI stage with 'ci/ci-install.bash', caching the
'.deb' files apt downloads for that stage, so a package is fetched from the
network at most once per runner image. On hosts that do not use apt this is
just the install, there is nothing to cache.
inputs:
repo:
description: "Verilator checkout holding the ci/ scripts"
required: false
default: repo
stage:
description: "CI stage to install the dependencies of, e.g. 'build'"
required: true
runs:
using: composite
steps:
- name: Compute apt cache key
id: cachekey
if: ${{ runner.os == 'Linux' }}
shell: bash
working-directory: ${{ inputs.repo }}
env:
STAGE: ${{ inputs.stage }}
run: |
source ci/ci-common.bash
host="${DISTRO_ID:-$HOST_OS}${DISTRO_VERSION:+-$DISTRO_VERSION}"
echo "key=apt-${host}-${STAGE}" >> "$GITHUB_OUTPUT"
- name: Restore apt downloads
if: ${{ runner.os == 'Linux' }}
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
with:
path: ${{ runner.temp }}/apt-cache
key: ${{ steps.cachekey.outputs.key }}
restore-keys: |
${{ steps.cachekey.outputs.key }}-
- name: Seed apt archive from cache
if: ${{ runner.os == 'Linux' }}
shell: bash
env:
APT_CACHE_DIR: ${{ runner.temp }}/apt-cache
run: |
set -euo pipefail
# Ask apt where it keeps its downloads, rather than assume
eval "$(apt-config shell ARCHIVES Dir::Cache::archives/d)"
ARCHIVES=${ARCHIVES%/} # '/d' yields a trailing slash
# Wipe the archive first, so that whatever the runner image left behind is not stored
sudo apt-get -o DPkg::Lock::Timeout=60 clean
# The directory is absent when nothing was restored, e.g. on a cold start
if [ -d "$APT_CACHE_DIR" ] &&
[ -n "$(find "$APT_CACHE_DIR" -maxdepth 1 -name '*.deb' -print -quit)" ]; then
sudo cp "$APT_CACHE_DIR"/*.deb "$ARCHIVES"
fi
- name: Install dependencies
shell: bash
working-directory: ${{ inputs.repo }}
env:
STAGE: ${{ inputs.stage }}
run: ./ci/ci-install.bash "$STAGE"
- name: Collect apt downloads
id: collect
if: ${{ runner.os == 'Linux' }}
shell: bash
env:
APT_CACHE_DIR: ${{ runner.temp }}/apt-cache
run: |
set -euo pipefail
# Ask apt where it keeps its downloads, rather than assume
eval "$(apt-config shell ARCHIVES Dir::Cache::archives/d)"
ARCHIVES=${ARCHIVES%/} # '/d' yields a trailing slash
# Sorted names of the '.deb' files in a directory, empty if it has none
list-debs() {
[ -d "$1" ] || return 0
find "$1" -maxdepth 1 -name '*.deb' -printf '%f\n' | sort
}
# Superseded downloads can no longer be fetched, so drop them
sudo apt-get -o DPkg::Lock::Timeout=60 autoclean
downloaded=$(list-debs "$ARCHIVES")
if [ -z "$downloaded" ]; then
echo "Apt kept no '.deb' files, nothing to cache"
exit 0
fi
if [ "$downloaded" = "$(list-debs "$APT_CACHE_DIR")" ]; then
echo "Downloads unchanged, keeping the restored cache entry"
exit 0
fi
# Mirror the archive, rather than add to it, so that the cache cannot
# grow without bound as package versions churn
mkdir -p "$APT_CACHE_DIR"
rm -f "$APT_CACHE_DIR"/*.deb
sudo cp "$ARCHIVES"/*.deb "$APT_CACHE_DIR"
echo "changed=true" >> "$GITHUB_OUTPUT"
- name: Save apt downloads
if: ${{ steps.collect.outputs.changed == 'true' }}
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
with:
path: ${{ runner.temp }}/apt-cache
key: ${{ steps.cachekey.outputs.key }}-${{ github.run_id }}