name: "Cached apt install" description: > Install apt packages, caching the downloaded .deb files so a cache hit installs them from disk without contacting an apt mirror. inputs: packages: description: "Space-separated list of apt packages to install." required: true cache-key: description: > Namespace for the package cache. Unrelated installs should pass distinct values so they get separate cache entries. required: false default: "apt" runs: using: "composite" steps: - name: 🧮 Resolve apt cache identity id: norm shell: bash env: PACKAGES: ${{ inputs.packages }} run: | # A cache-key-safe id derived from the package set, plus the runner # image id, so the key changes when either the package list or the base # image changes. ImageOS is set in the runner environment but does not # resolve through ${{ env.ImageOS }} inside a composite action, so read # it from the shell environment here. echo "id=$(printf '%s' "$PACKAGES" | tr ' ' '-')" >> "$GITHUB_OUTPUT" echo "image=${ImageOS:-unknown}" >> "$GITHUB_OUTPUT" - name: 💾 Cache apt packages id: cache uses: actions/cache@v6 with: path: ~/.cache/apt-deb/${{ inputs.cache-key }} key: apt-deb-${{ inputs.cache-key }}-${{ runner.os }}-${{ steps.norm.outputs.image }}-${{ steps.norm.outputs.id }} - name: 📦 Install apt packages shell: bash env: PACKAGES: ${{ inputs.packages }} CACHE_HIT: ${{ steps.cache.outputs.cache-hit }} CACHE_KEY: ${{ inputs.cache-key }} run: | archives="$HOME/.cache/apt-deb/$CACHE_KEY" mkdir -p "$archives/partial" if [ "$CACHE_HIT" = "true" ]; then # Cache hit: install the cached .deb files directly with dpkg. dpkg # installs from file paths, so it needs no package index and no # mirror; the dependency closure was downloaded on the miss that # populated the cache. An empty cache means the packages are already # on the runner image. shopt -s nullglob debs=("$archives"/*.deb) if [ "${#debs[@]}" -gt 0 ]; then sudo dpkg -i "${debs[@]}" else echo "No cached .deb files; requested packages are already on the runner image." fi else # Cache miss: refresh the package lists and download the .deb files # (and their dependency closure) into the cache directory, then # install them from there. sudo apt-get update sudo apt-get install -y -d -o Dir::Cache::Archives="$archives" $PACKAGES sudo apt-get install -y -o Dir::Cache::Archives="$archives" --no-download $PACKAGES # actions/cache reads the directory as the runner user on save. sudo chown -R "$(id -u):$(id -g)" "$archives" fi