diff --git a/.github/actions/conan-cache-guard/action.yml b/.github/actions/conan-cache-guard/action.yml new file mode 100644 index 000000000..4486fe5f0 --- /dev/null +++ b/.github/actions/conan-cache-guard/action.yml @@ -0,0 +1,75 @@ +name: "Conan cache integrity guard" +description: > + Auto-heal a corrupt/incomplete OR wrong-config cached boost package on + self-hosted runners. The self-hosted Linux runners (VM905) reuse ONE + ~/.conan2 across up to 8 concurrent jobs. A partially-failed conan build + can leave an incomplete boost package (missing headers/libs), and an + out-of-band/manual `conan install` can leave an intact-but-wrong-std boost + (compiler.cppstd=gnu17) that PASSES an integrity check yet is ABI-incompatible + with the cppstd=20 c2pool TUs. Conan compatibility fallback then re-selects + that gnu17 boost instead of rebuilding, reddening later jobs intermittently + (the per-job profile cppstd=20 pin only sets what is *requested*, not what is + *selected*). This purges boost on integrity mismatch OR on any cached + cppstd != 20 and rebuilds a clean cppstd=20 boost. On the shared Linux cache + the whole check+purge+rebuild is serialized under an flock so exactly ONE + cold-cache job rebuilds boost while the rest wait and then cache-hit -- without + the lock, every concurrent job purges+rebuilds into the same cache at once and + races ("Reference ... already exists" / boost headers deleted mid-build). + No-op (fast, unlocked-fast) on a healthy cppstd=20 cache. +runs: + using: composite + steps: + - name: Verify boost integrity + cppstd, rebuild-once under lock (shared cache) [posix] + if: runner.os != 'Windows' + shell: bash + run: | + # Serialize check+purge+rebuild across all jobs sharing ~/.conan2 on + # this self-hosted host (mirrors the apt-get flock guard, PR #670). + lock="${HOME}/.conan2/.c2pool-boost-guard.lock" + exec 9>"$lock" 2>/dev/null || exec 9>/tmp/.c2pool-boost-guard.lock + flock 9 + purge="" + if ! conan cache check-integrity "boost/*"; then + purge="integrity check FAILED" + elif conan list "boost/*:*" -f json 2>/dev/null \ + | grep -oE '"compiler.cppstd": "[^"]*"' \ + | grep -qv '"compiler.cppstd": "20"'; then + purge="a cached binary was built with compiler.cppstd != 20 (wrong-std; Conan compatibility fallback would re-select it)" + fi + if [ -n "$purge" ]; then + echo "::warning title=conan-cache-guard::boost $purge -- purging + rebuilding cppstd=20 boost from source under lock" + conan remove "boost/*" -c || true + # Rebuild boost NOW, while holding the lock, using the job's already + # cppstd=20-pinned default profile, so every subsequent (unlocked) + # `conan install` is a clean cache hit and no job ever installs against + # a boost another job is deleting. Log is intentionally not suppressed + # so the from-source rebuild is visible as evidence. + conan install . --build="boost/*" --settings=build_type=Release \ + --output-folder="${RUNNER_TEMP:-/tmp}/conan-boost-warm" + else + echo "conan-cache-guard: boost cache OK (integrity + cppstd=20)" + fi + flock -u 9 + - name: Verify boost integrity + cppstd (auto-purge if corrupt or wrong-std) [windows] + if: runner.os == 'Windows' + shell: cmd + run: | + rem VM217 is a single Windows runner -- no concurrent siblings share its + rem cache, so no thundering-herd race; a purge here is healed by this + rem job's own following conan install. cmd (not powershell) because + rem VM217 execution-policy blocks unsigned script bodies. + set purge= + conan cache check-integrity "boost/*" + if errorlevel 1 set purge=integrity check FAILED + if not defined purge ( + conan list "boost/*:*" -f json > "%TEMP%\cg_boost.json" 2>nul + findstr /C:"compiler.cppstd" "%TEMP%\cg_boost.json" | findstr /V /C:": \"20\"" >nul + if not errorlevel 1 set purge=a cached binary was built with compiler.cppstd ^!= 20 + ) + if defined purge ( + echo ::warning title=conan-cache-guard::boost %purge% -- purging so conan install rebuilds it clean + conan remove "boost/*" -c + ) else ( + echo conan-cache-guard: boost cache OK ^(integrity + cppstd=20^) + ) + exit /b 0 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eda47e2d1..78552ba30 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -84,6 +84,8 @@ jobs: if: runner.environment != 'github-hosted' run: rm -rf build_ci + - name: Conan cache guard (auto-heal corrupt boost) + uses: ./.github/actions/conan-cache-guard - name: Conan install run: conan install . --build=missing --output-folder=build_ci --settings=build_type=Release @@ -216,6 +218,8 @@ jobs: if: runner.environment != 'github-hosted' run: rm -rf build_asan + - name: Conan cache guard (auto-heal corrupt boost) + uses: ./.github/actions/conan-cache-guard - name: Conan install run: conan install . --build=missing --output-folder=build_asan --settings=build_type=Release @@ -339,6 +343,8 @@ jobs: if: runner.environment != 'github-hosted' run: rm -rf build_bch + - name: Conan cache guard (auto-heal corrupt boost) + uses: ./.github/actions/conan-cache-guard - name: Conan install run: conan install . --build=missing --output-folder=build_bch --settings=build_type=Release @@ -428,6 +434,8 @@ jobs: if: runner.environment != 'github-hosted' run: rm -rf build_bch_asan + - name: Conan cache guard (auto-heal corrupt boost) + uses: ./.github/actions/conan-cache-guard - name: Conan install run: conan install . --build=missing --output-folder=build_bch_asan --settings=build_type=Release @@ -802,6 +810,8 @@ jobs: path: ~/.conan2 key: conan2-windows-msvc194-${{ hashFiles('conanfile.txt') }} + - name: Conan cache guard (auto-heal corrupt boost) + uses: ./.github/actions/conan-cache-guard - name: Conan install run: conan install . --build=missing --output-folder=build_ci @@ -903,6 +913,8 @@ jobs: if: runner.environment != 'github-hosted' run: rm -rf build_dgb_auxdoge + - name: Conan cache guard (auto-heal corrupt boost) + uses: ./.github/actions/conan-cache-guard - name: Conan install run: conan install . --build=missing --output-folder=build_dgb_auxdoge --settings=build_type=Release diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 246ab01a7..5b79c6b63 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -90,6 +90,9 @@ jobs: name: Clean stale build dir (self-hosted workspace is reused) run: rm -rf build_codeql + - if: matrix.build-mode == 'manual' + name: Conan cache guard (auto-heal corrupt boost) + uses: ./.github/actions/conan-cache-guard - if: matrix.build-mode == 'manual' name: Install Conan dependencies run: | diff --git a/.github/workflows/coin-matrix.yml b/.github/workflows/coin-matrix.yml index 5e39b4220..cb2a66def 100644 --- a/.github/workflows/coin-matrix.yml +++ b/.github/workflows/coin-matrix.yml @@ -98,6 +98,9 @@ jobs: run: | find ~/.conan2/p -type f \( -name 'conan_package.tgz' -o -name 'conan_sources.tgz' \) -delete 2>/dev/null || true + - name: Conan cache guard (auto-heal corrupt boost) + if: steps.presence.outputs.exists == '1' + uses: ./.github/actions/conan-cache-guard - name: Conan install if: steps.presence.outputs.exists == '1' run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ad5fab59e..01ed9cee0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -130,6 +130,9 @@ jobs: conan2-ubuntu24-gcc13-${{ matrix.coin }}- conan2-ubuntu24-gcc13- + - name: Conan cache guard (auto-heal corrupt boost) + if: steps.presence.outputs.exists == '1' + uses: ./.github/actions/conan-cache-guard - name: Conan install if: steps.presence.outputs.exists == '1' run: | @@ -476,6 +479,9 @@ jobs: path: ~/.conan2 key: conan2-windows-msvc194-${{ hashFiles('conanfile.txt') }} + - name: Conan cache guard (auto-heal corrupt boost) + if: steps.presence.outputs.exists == '1' + uses: ./.github/actions/conan-cache-guard - name: Conan install if: steps.presence.outputs.exists == '1' run: conan install . --build=missing --output-folder=build_ci