diff --git a/.github/actions/install-deps/action.yml b/.github/actions/install-deps/action.yml new file mode 100644 index 000000000..96947f848 --- /dev/null +++ b/.github/actions/install-deps/action.yml @@ -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 }} diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 6d9f2db91..cfe7865aa 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -116,8 +116,9 @@ jobs: ls -lsha - name: Install dependencies - working-directory: repo - run: ./ci/ci-install.bash coverage + uses: ./repo/.github/actions/install-deps + with: + stage: coverage - name: Download code coverage data uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 3d3f010f4..011a3c373 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -27,7 +27,9 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} - name: Install packages for build - run: ./ci/ci-install.bash format + uses: ./repo/.github/actions/install-deps + with: + stage: format - name: Configure git identity run: | diff --git a/.github/workflows/reusable-build.yml b/.github/workflows/reusable-build.yml index 13b8052c0..9bd051543 100644 --- a/.github/workflows/reusable-build.yml +++ b/.github/workflows/reusable-build.yml @@ -90,7 +90,9 @@ jobs: ${{ env.CACHE_KEY }} - name: Install packages for build - run: ./ci/ci-install.bash build + uses: ./repo/.github/actions/install-deps + with: + stage: build - name: Build run: | diff --git a/.github/workflows/reusable-lint-py.yml b/.github/workflows/reusable-lint-py.yml index c7d718ed1..ca969bad0 100644 --- a/.github/workflows/reusable-lint-py.yml +++ b/.github/workflows/reusable-lint-py.yml @@ -24,7 +24,9 @@ jobs: path: repo - name: Install dependencies - run: ./ci/ci-install.bash lint-py + uses: ./repo/.github/actions/install-deps + with: + stage: lint-py - name: Configure run: | diff --git a/.github/workflows/reusable-rtlmeter-run.yml b/.github/workflows/reusable-rtlmeter-run.yml index 96a75d82d..e038b2284 100644 --- a/.github/workflows/reusable-rtlmeter-run.yml +++ b/.github/workflows/reusable-rtlmeter-run.yml @@ -66,11 +66,14 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: path: repo - sparse-checkout: ci + sparse-checkout: | + .github/actions + ci - name: Install dependencies - working-directory: repo - run: ./ci/ci-install.bash rtlmeter-run + uses: ./repo/.github/actions/install-deps + with: + stage: rtlmeter-run - name: Checkout RTLMeter uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 diff --git a/.github/workflows/reusable-test.yml b/.github/workflows/reusable-test.yml index bd5a76c7c..93000d445 100644 --- a/.github/workflows/reusable-test.yml +++ b/.github/workflows/reusable-test.yml @@ -85,8 +85,9 @@ jobs: key: ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.suite }} - name: Install test dependencies - run: | - ./ci/ci-install.bash test + uses: ./repo/.github/actions/install-deps + with: + stage: test - name: Set up Python venv uses: ./repo/.github/actions/setup-venv