diff --git a/.github/workflows/ci_linux.yml b/.github/workflows/ci_linux.yml index 870fd207..b7d17bcb 100644 --- a/.github/workflows/ci_linux.yml +++ b/.github/workflows/ci_linux.yml @@ -25,7 +25,22 @@ jobs: - name: Install Doxygen run: sudo apt-get update && sudo apt-get install -y doxygen - # 4️⃣ Build all targets + # 4️⃣ Prefetch external repos (emsdk via //..., LLVM); retry network flakes only + - name: Prefetch external repos + run: | + max=3 + for i in $(seq 1 "$max"); do + if bazel fetch //...; then + exit 0 + fi + if [ "$i" -lt "$max" ]; then + echo "Fetch failed (attempt $i/$max). Retrying in 20s..." + sleep 20 + fi + done + exit 1 + + # 5️⃣ Build all targets - name: Build all targets run: bazel build --verbose_failures //... @@ -55,6 +70,20 @@ jobs: with: bazelisk-version: "1.29.0" + - name: Prefetch external repos + run: | + max=3 + for i in $(seq 1 "$max"); do + if bazel fetch --config=asan //library/tests/...; then + exit 0 + fi + if [ "$i" -lt "$max" ]; then + echo "Fetch failed (attempt $i/$max). Retrying in 20s..." + sleep 20 + fi + done + exit 1 + - name: Run tests (AddressSanitizer) run: bazel test --config=asan --verbose_failures --test_output=errors //library/tests/... @@ -79,6 +108,20 @@ jobs: with: bazelisk-version: "1.29.0" + - name: Prefetch external repos + run: | + max=3 + for i in $(seq 1 "$max"); do + if bazel fetch --config=tsan //library/tests/system/...; then + exit 0 + fi + if [ "$i" -lt "$max" ]; then + echo "Fetch failed (attempt $i/$max). Retrying in 20s..." + sleep 20 + fi + done + exit 1 + - name: Run system tests (ThreadSanitizer) run: bazel test --config=tsan --verbose_failures --test_output=errors //library/tests/system/... diff --git a/.github/workflows/ci_macos.yml b/.github/workflows/ci_macos.yml index 248d2993..50c23e9b 100644 --- a/.github/workflows/ci_macos.yml +++ b/.github/workflows/ci_macos.yml @@ -24,6 +24,21 @@ jobs: - name: Install Doxygen run: brew install doxygen + # Prefetch external repos (emsdk via //..., LLVM); retry network flakes only + - name: Prefetch external repos + run: | + max=3 + for i in $(seq 1 "$max"); do + if bazelisk fetch //...; then + exit 0 + fi + if [ "$i" -lt "$max" ]; then + echo "Fetch failed (attempt $i/$max). Retrying in 20s..." + sleep 20 + fi + done + exit 1 + # Build all targets - name: Build all targets run: bazelisk build --verbose_failures //... @@ -54,6 +69,21 @@ jobs: with: bazelisk-version: "1.29.0" + - name: Prefetch external repos + run: | + max=3 + for i in $(seq 1 "$max"); do + if bazelisk fetch --config=asan //library/tests/system/... \ + && bazelisk fetch --config=tsan //library/tests/system/...; then + exit 0 + fi + if [ "$i" -lt "$max" ]; then + echo "Fetch failed (attempt $i/$max). Retrying in 20s..." + sleep 20 + fi + done + exit 1 + - name: Run system tests (AddressSanitizer) run: bazelisk test --config=asan --verbose_failures --test_output=errors //library/tests/system/... diff --git a/.github/workflows/ci_wasm.yml b/.github/workflows/ci_wasm.yml index 7b8bd042..6a2b8871 100644 --- a/.github/workflows/ci_wasm.yml +++ b/.github/workflows/ci_wasm.yml @@ -34,18 +34,46 @@ jobs: with: bazelisk-version: "1.29.0" - # 4️⃣ Unit tests and WASM builds (hermetic emsdk toolchain downloaded by Bazel) + # 4️⃣ Prefetch emsdk / toolchain archives (retry transient network timeouts) + - name: Prefetch external repos + run: | + max=3 + for i in $(seq 1 "$max"); do + if bazel fetch //web/... //wasm/...; then + exit 0 + fi + if [ "$i" -lt "$max" ]; then + echo "Fetch failed (attempt $i/$max). Retrying in 20s..." + sleep 20 + fi + done + exit 1 + + # 5️⃣ Unit tests and WASM builds (hermetic emsdk toolchain downloaded by Bazel) - name: Test web helpers, system tests, e2e, and CalcDDtablePBN run: bazel test --verbose_failures //web:web_tests //web:web_system_tests //wasm:all - name: Build WASM targets - run: bazel build --verbose_failures //wasm:all_examples_wasm + run: | + max=3 + for i in $(seq 1 "$max"); do + if bazel build --verbose_failures //wasm:all_examples_wasm; then + exit 0 + fi + if [ "$i" -lt "$max" ]; then + echo "Build failed (attempt $i/$max). Retrying in 20s..." + sleep 20 + # Restart server only — keep downloaded external repos / disk cache. + bazel shutdown || true + fi + done + exit 1 - # 5️⃣ Smoke test: run solve_board under Node.js — pass if it does not crash + # 6️⃣ Smoke test: run solve_board under Node.js — pass if it does not crash - name: Smoke test solve_board_wasm run: node bazel-bin/wasm/solve_board.js - # 6️⃣ Upload test logs + # 7️⃣ Upload test logs - name: Upload test logs - WASM if: always() uses: actions/upload-artifact@v6 diff --git a/.github/workflows/ci_windows.yml b/.github/workflows/ci_windows.yml index fda2dc27..a69cea9f 100644 --- a/.github/workflows/ci_windows.yml +++ b/.github/workflows/ci_windows.yml @@ -20,11 +20,39 @@ jobs: with: bazelisk-version: "1.29.0" - # Build all targets - - name: Build all targets - run: bazel build --verbose_failures //... + # Warm external deps (emsdk, LLVM, …); retry transient network fetch failures + - name: Prefetch external repos + shell: pwsh + run: | + $max = 3 + for ($i = 1; $i -le $max; $i++) { + bazel fetch //... + if ($LASTEXITCODE -eq 0) { exit 0 } + if ($i -lt $max) { + Write-Host "Fetch failed (attempt $i/$max). Retrying in 20s..." + Start-Sleep -Seconds 20 + } + } + exit 1 - # Run all tests (including Python) + # Build all targets (retry: fetch flakes can still surface on first analysis) + - name: Bazel build (retry on transient fetch failure) + shell: pwsh + run: | + $max = 3 + for ($i = 1; $i -le $max; $i++) { + bazel build --verbose_failures //... + if ($LASTEXITCODE -eq 0) { exit 0 } + if ($i -lt $max) { + Write-Host "Build failed (attempt $i/$max). Retrying in 20s..." + Start-Sleep -Seconds 20 + # Restart server only — keep downloaded external repos / disk cache. + bazel shutdown + } + } + exit 1 + + # Run all tests (including Python) — no retry so real test failures surface quickly - name: Run all tests run: bazel test --verbose_failures //... @@ -36,4 +64,4 @@ jobs: name: bazel-test-logs-windows path: bazel-testlogs/ if-no-files-found: ignore - retention-days: 30 \ No newline at end of file + retention-days: 30 diff --git a/MODULE.bazel b/MODULE.bazel index 2f3d1ba0..796ce3eb 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -31,6 +31,18 @@ bazel_dep(name = "rules_java", version = "9.6.1") # install into ~/.m2 (Phase 1; no remote/public publishing yet). bazel_dep(name = "rules_jvm_external", version = "7.0") +# Prefer GitHub, fall back to codeload CDN on transient timeouts (see CI flakes). +# Integrity and strip_prefix match BCR emsdk 5.0.7 source.json. +archive_override( + module_name = "emsdk", + integrity = "sha256-Jm30uWRN3hgwOvBdzn0EhUJzsLtSckbLf7aglZF3TM8=", + strip_prefix = "emsdk-5.0.7/bazel", + urls = [ + "https://github.com/emscripten-core/emsdk/archive/refs/tags/5.0.7.tar.gz", + "https://codeload.github.com/emscripten-core/emsdk/tar.gz/refs/tags/5.0.7", + ], +) + llvm = use_extension("@toolchains_llvm//toolchain/extensions:llvm.bzl", "llvm") llvm.toolchain( name = "llvm_toolchain", diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 6f6ff2a0..5f82f03c 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -82,8 +82,6 @@ "https://bcr.bazel.build/modules/bazel_worker_java/0.0.8/source.json": "9395c4679444bc47bf7e51a710366a4480aa371c6f6bed01868e2fabcf11acec", "https://bcr.bazel.build/modules/buildozer/8.5.1/MODULE.bazel": "a35d9561b3fc5b18797c330793e99e3b834a473d5fbd3d7d7634aafc9bdb6f8f", "https://bcr.bazel.build/modules/buildozer/8.5.1/source.json": "e3386e6ff4529f2442800dee47ad28d3e6487f36a1f75ae39ae56c70f0cd2fbd", - "https://bcr.bazel.build/modules/emsdk/5.0.7/MODULE.bazel": "c9464a9a8c07bdef4bf2ddcd67c20bcb3c71915da818679756b1e85a04a0b9c0", - "https://bcr.bazel.build/modules/emsdk/5.0.7/source.json": "0c4e55e4b706d842045b1b8583efc060945432722c81a8f9a7f0855c986d6321", "https://bcr.bazel.build/modules/gawk/5.3.2.bcr.1/MODULE.bazel": "cdf8cbe5ee750db04b78878c9633cc76e80dcf4416cbe982ac3a9222f80713c8", "https://bcr.bazel.build/modules/gawk/5.3.2.bcr.1/source.json": "fa7b512dfcb5eafd90ce3959cf42a2a6fe96144ebbb4b3b3928054895f2afac2", "https://bcr.bazel.build/modules/gazelle/0.32.0/MODULE.bazel": "b499f58a5d0d3537f3cf5b76d8ada18242f64ec474d8391247438bf04f58c7b8", @@ -314,7 +312,7 @@ "@@emsdk+//:emscripten_cache.bzl%emscripten_cache": { "general": { "bzlTransitiveDigest": "uqDvXmpTNqW4+ie/Fk+xC3TrFrKvL+9hNtoP51Kt2oo=", - "usagesDigest": "VPgNmvM2UoqCG4eFVWZb0w4XPfP0v8NIzb+KfzDwqVE=", + "usagesDigest": "pWzD9zlSoLlTg9KHHx+GNAE4f/zuf6Hiv5i/aMApD/U=", "recordedInputs": [], "generatedRepoSpecs": { "emscripten_cache": { @@ -451,7 +449,7 @@ "@@rules_nodejs+//nodejs:extensions.bzl%node": { "general": { "bzlTransitiveDigest": "4pUxCNc22K4I+6+4Nxu52Hur12tFRfa1JMsN5mdDv60=", - "usagesDigest": "FvBWrdNTUrXyEUQ4cLS6ZvBDMhUc1qMqDU70KQC+LHY=", + "usagesDigest": "vZ6azLX/KFnBVNfNqvqrt+aTI+qpRIspF5DmoxPiuUs=", "recordedInputs": [], "generatedRepoSpecs": { "nodejs_linux_amd64": {