diff --git a/.dockerignore b/.dockerignore index f40b94f7b7..6ea3cf4cc2 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,6 +3,14 @@ # Docker contexts -- this file is the canonical list of paths excluded from # `COPY . /src` (used by .github/docker/Dockerfile.rhel8). +# Git metadata. The in-image build does not need it (the CMake submodule init +# is guarded by EXISTS .git, and the submodule working trees are already in +# the context), and its internals (index, packfiles, reflogs) differ between +# checkouts of the same commit, which would invalidate the BuildKit layer +# cache for `COPY . /src` on every nightly image build. +.git +**/.git + # Local CMake build outputs (host-side; can be tens of GB). build/ build_*/ diff --git a/.github/docker/Dockerfile.rhel8 b/.github/docker/Dockerfile.rhel8 index dc5e506ef4..1861014a71 100644 --- a/.github/docker/Dockerfile.rhel8 +++ b/.github/docker/Dockerfile.rhel8 @@ -104,41 +104,39 @@ FROM base AS vcpkg-builder ENV VCPKG_DEFAULT_BINARY_CACHE=/opt/vcpkg-cache -COPY . /src +# Copy only the inputs that determine the dependency set instead of the whole +# source tree. This keys the expensive vcpkg compile below on just these +# paths, so the layer stays valid in the BuildKit registry cache across +# ordinary source commits -- nightly rebuilds reuse the previously compiled +# vcpkg binaries instead of recompiling every port. +COPY ThirdParty/vcpkg /src/ThirdParty/vcpkg +COPY vcpkg.json /src/vcpkg.json +COPY vcpkg-configuration-rhel8.json /src/vcpkg-configuration.json -# Running the ResInsight CMake configure triggers a vcpkg manifest install, -# which compiles every dependency into VCPKG_DEFAULT_BINARY_CACHE. The same -# toolchain is used here and in the final image, so the vcpkg ABI hashes match -# and the cache is reused by CI builds. Later configure steps may fail inside -# the build container; that is tolerated as the dependencies are already built -# by then. +# A direct manifest install compiles every dependency into +# VCPKG_DEFAULT_BINARY_CACHE. The dependency set has no manifest features or +# overlay ports, and CC/CXX below match the ResInsight configure in the +# warmup stage and in CI, so the vcpkg ABI hashes match and the cache is +# reused there. x64-linux is the triplet the vcpkg CMake toolchain defaults +# to on Linux. # -# After configure, verify the cache contains a plausible number of entries. +# After the install, verify the cache contains a plausible number of entries. # vcpkg's `files` binary provider stores one .zip per package under # /<2-hex>/.zip. ResInsight depends on ~30+ packages, so a -# threshold of 20 catches the failure mode where the configure dies early +# threshold of 20 catches the failure mode where the install dies early # (e.g. after building only a handful of leaf deps) while staying robust to # small manifest churn. RUN git config --global --add safe.directory '*' \ - && cp /src/vcpkg-configuration-rhel8.json /src/vcpkg-configuration.json \ && mkdir -p "${VCPKG_DEFAULT_BINARY_CACHE}" \ && source /opt/rh/gcc-toolset-14/enable \ && ( cd /src/ThirdParty/vcpkg && ./bootstrap-vcpkg.sh ) \ && export CC=/opt/rh/gcc-toolset-14/root/usr/bin/gcc \ && export CXX=/opt/rh/gcc-toolset-14/root/usr/bin/g++ \ - && { cmake -S /src -B /tmp/cfg -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DRESINSIGHT_INCLUDE_APPLICATION_UNIT_TESTS=true \ - -DRESINSIGHT_ENABLE_UNITY_BUILD=true \ - -DRESINSIGHT_ENABLE_HDF5=false \ - -DRESINSIGHT_ENABLE_GRPC=false \ - -DRESINSIGHT_GCC_STDCPP_EXP_PATH=/opt/libstdcxx-exp/lib \ - -DCMAKE_TOOLCHAIN_FILE=ThirdParty/vcpkg/scripts/buildsystems/vcpkg.cmake \ - || true; } \ + && ( cd /src && ./ThirdParty/vcpkg/vcpkg install --triplet x64-linux ) \ && vcpkg_zip_count=$(find "${VCPKG_DEFAULT_BINARY_CACHE}" -type f -name '*.zip' | wc -l) \ && echo "vcpkg binary cache entries: ${vcpkg_zip_count}" \ && test "${vcpkg_zip_count}" -ge 20 \ - && rm -rf /tmp/cfg /src + && rm -rf /src # ----------------------------------------------------------------------------- # Stage 3: warm the ResInsight buildcache by performing a full compile at diff --git a/.github/workflows/build-rhel8-image.yml b/.github/workflows/build-rhel8-image.yml index b4dbd34ffe..c0b4169c24 100644 --- a/.github/workflows/build-rhel8-image.yml +++ b/.github/workflows/build-rhel8-image.yml @@ -12,6 +12,7 @@ on: paths: - ".github/docker/Dockerfile.rhel8" - ".github/workflows/build-rhel8-image.yml" + - ".dockerignore" - "vcpkg.json" - "vcpkg-configuration-rhel8.json" - ".gitmodules" @@ -102,5 +103,10 @@ jobs: tags: | ${{ steps.meta.outputs.image }}:latest ${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.date }} - cache-from: type=gha - cache-to: type=gha,mode=max + # Layer cache lives in GHCR instead of the GitHub Actions cache: the + # Actions cache has a 10 GB per-repo limit shared with all other + # workflows, so the multi-GB mode=max export was evicted immediately + # (0% hit rate) and the export itself started failing with + # "not_found" once the repo hit the cap. + cache-from: type=registry,ref=${{ steps.meta.outputs.image }}:buildcache + cache-to: type=registry,ref=${{ steps.meta.outputs.image }}:buildcache,mode=max,image-manifest=true diff --git a/.github/workflows/cleanup-rhel8-image.yml b/.github/workflows/cleanup-rhel8-image.yml index e456e55f84..a0882fe638 100644 --- a/.github/workflows/cleanup-rhel8-image.yml +++ b/.github/workflows/cleanup-rhel8-image.yml @@ -4,7 +4,8 @@ name: Cleanup RHEL8 CI Image Versions # # Retention rules: # * Versions tagged "latest" are always kept (the consumer workflow pulls -# `:latest`, so deleting it would break CI). +# `:latest`, so deleting it would break CI). Versions tagged "buildcache" +# are always kept too (BuildKit layer cache used by the image build). # * The most recent MIN_KEEP versions by updated_at are kept regardless of # age. This is a safety floor: if the last few nightly builds break, we # can still roll back to a working older image. @@ -95,14 +96,14 @@ jobs: echo "Cutoff (UTC) : $(date -u -d @"$cutoff_epoch" -Iseconds)" # jq pipeline: - # 1. drop versions whose tags include "latest" + # 1. drop versions whose tags include "latest" or "buildcache" # 2. sort newest-first # 3. skip the first MIN_KEEP (safety floor) # 4. keep only those older than the cutoff to_delete=$(jq -c \ --argjson keep "$MIN_KEEP" \ --argjson cutoff "$cutoff_epoch" ' - [ .[] | select((.metadata.container.tags // []) | index("latest") | not) ] + [ .[] | select((.metadata.container.tags // []) | (index("latest") or index("buildcache")) | not) ] | sort_by(.updated_at) | reverse | .[$keep:] | [ .[] | select((.updated_at | fromdateiso8601) < $cutoff) ]