From 3febcc2568040597e77e7e422667a48843213904 Mon Sep 17 00:00:00 2001 From: frstrtr Date: Wed, 24 Jun 2026 15:41:43 +0000 Subject: [PATCH 01/10] ci(build): run Windows lane on self-hosted c2pool-windows-217 with fork fallback Point the Windows job at the self-hosted runner (VM217, labels self-hosted/Windows/X64/c2pool-build) for internal-branch PRs and pushes; fork PRs fall back to GitHub-hosted windows-2022 so untrusted code never executes on the self-hosted box. Completes the self-hosted build fleet (Linux pending VM905 runner registration; macOS x86_64 already self-hosted via #100). Conan cache retained. --- .github/workflows/build.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3616d2c19..81342704a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -636,7 +636,11 @@ jobs: name: Windows x86_64 # dash-slice campaign: skip Windows for PRs carrying the dash-linux-only label (re-enabled at block-viable gate, ~07-01) if: ${{ !(github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'dash-linux-only')) }} - runs-on: windows-2022 + # Self-hosted fleet (VM217 c2pool-windows-217): internal-branch PRs and + # pushes run on the self-hosted Windows runner for speed; fork PRs fall + # back to GitHub-hosted windows-2022 so untrusted code never lands on the + # self-hosted box (defense-in-depth atop the repo fork-approval gate). + runs-on: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && fromJSON('["self-hosted","Windows","X64","c2pool-build"]') || 'windows-2022' }} steps: - uses: actions/checkout@v6 From bf3f9667f511d06657dd91ff951087813dd40c47 Mon Sep 17 00:00:00 2001 From: frstrtr Date: Wed, 24 Jun 2026 16:47:31 +0000 Subject: [PATCH 02/10] ci: self-hosted macOS arm64 lane (Mac-mini-Alonso-227, c2pool-mac-arm) + sccache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Repoint the macOS arm64 matrix lane to the self-hosted M4 Mac mini (label c2pool-mac-arm) on push and internal-branch PRs, with a GitHub-hosted fork fallback to macos-14 — same fork-gate shape as the Windows lane in this branch. Add sccache compiler launcher (persists across self-hosted runs; no-op-safe on the hosted fallback). Linux (VM905) leg held separately until that runner is registered. --- .github/workflows/build.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 81342704a..35a16f5c6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -546,9 +546,10 @@ jobs: # ════════════════════════════════════════════════════════════════════════════ # ════════════════════════════════════════════════════════════════════════════ - # macOS (matrix: arm64 on GitHub-hosted macos-14, x86_64 on the self-hosted - # Intel runner labeled c2pool-mac-intel). The self-hosted lane resolves the - # macOS x86_64 CI starvation (#42) — macpro-intel-204 standup 2026-06-16. + # macOS (matrix). Both arches self-hosted on push/internal-branch PRs, with + # GitHub-hosted fork fallback: arm64 -> Mac-mini-Alonso-227 (c2pool-mac-arm) + # else macos-14; x86_64 -> macpro-intel-204 (c2pool-mac-intel). Resolves the + # macOS x86_64 CI starvation (#42). sccache caches across self-hosted runs. # ════════════════════════════════════════════════════════════════════════════ macos: name: macOS ${{ matrix.arch }} @@ -560,7 +561,7 @@ jobs: matrix: include: - arch: arm64 - runner: macos-14 + runner: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && fromJSON('["self-hosted","macOS","ARM64","c2pool-mac-arm"]') || 'macos-14' }} - arch: x86_64 runner: [self-hosted, macOS, X64, c2pool-mac-intel] steps: @@ -583,10 +584,10 @@ jobs: fi - name: Install dependencies - run: brew install cmake boost leveldb secp256k1 nlohmann-json yaml-cpp + run: brew install cmake boost leveldb secp256k1 nlohmann-json yaml-cpp sccache - name: Configure - run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release + run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache - name: Build run: cmake --build build --target c2pool -j$(sysctl -n hw.ncpu) From 3d8acd7de9421e2cd314250058d809c3513a3fa6 Mon Sep 17 00:00:00 2001 From: frstrtr Date: Wed, 24 Jun 2026 17:43:04 +0000 Subject: [PATCH 03/10] ci(build): switch 8 Linux lanes to self-hosted c2pool-linux-905 (32c) + ccache Apply the #439 fork-gate to every ubuntu-24.04 job: push/internal-branch PRs run on [self-hosted, Linux, X64, c2pool-build] (VM905, 32 cores); fork PRs fall back to GitHub-hosted ubuntu-24.04. Build stays -j$(nproc) (=32 on the box). Wire ccache as the C/C++ compiler launcher on the six C++ build lanes (linux, linux-asan, coin-bch, coin-bch-asan, qt, dgb-auxdoge) and add the ccache package to their apt lists so the binary is present on both self-hosted and fork-fallback runners. Completes the all-lane self-hosted switch (Linux+macOS+Windows). --- .github/workflows/build.yml | 38 +++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 35a16f5c6..3575568fb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: # ════════════════════════════════════════════════════════════════════════════ test-allowlist-guard: name: Test allowlist drift-guard - runs-on: ubuntu-24.04 + runs-on: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && fromJSON('["self-hosted","Linux","X64","c2pool-build"]') || 'ubuntu-24.04' }} steps: - uses: actions/checkout@v4 - name: Check coin test targets vs build.yml --target allowlist @@ -26,7 +26,7 @@ jobs: # ════════════════════════════════════════════════════════════════════════════ linux: name: Linux x86_64 - runs-on: ubuntu-24.04 + runs-on: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && fromJSON('["self-hosted","Linux","X64","c2pool-build"]') || 'ubuntu-24.04' }} steps: - uses: actions/checkout@v6 @@ -34,7 +34,7 @@ jobs: run: | sudo apt-get update -qq sudo apt-get install -y --no-install-recommends \ - g++ cmake make libleveldb-dev libsecp256k1-dev + g++ ccache cmake make libleveldb-dev libsecp256k1-dev - uses: actions/setup-python@v6 with: { python-version: '3.12' } @@ -57,7 +57,7 @@ jobs: run: conan install . --build=missing --output-folder=build_ci --settings=build_type=Release - name: Configure - run: cmake -S . -B build_ci -DCMAKE_TOOLCHAIN_FILE=build_ci/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release -DCOIN_DGB=ON + run: cmake -S . -B build_ci -DCMAKE_TOOLCHAIN_FILE=build_ci/conan_toolchain.cmake -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_BUILD_TYPE=Release -DCOIN_DGB=ON - name: Build run: cmake --build build_ci --target c2pool -j$(nproc) @@ -142,7 +142,7 @@ jobs: name: Linux x86_64 (AsAN+UBSan) # dash bring-up slices may opt out of sanitizers via dash-skip-sanitizers; KEEP for consensus/payout slices (default = on) if: ${{ !(github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'dash-skip-sanitizers')) }} - runs-on: ubuntu-24.04 + runs-on: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && fromJSON('["self-hosted","Linux","X64","c2pool-build"]') || 'ubuntu-24.04' }} continue-on-error: true steps: - uses: actions/checkout@v6 @@ -151,7 +151,7 @@ jobs: run: | sudo apt-get update -qq sudo apt-get install -y --no-install-recommends \ - g++ cmake make libleveldb-dev libsecp256k1-dev + g++ ccache cmake make libleveldb-dev libsecp256k1-dev - uses: actions/setup-python@v6 with: { python-version: '3.12' } @@ -188,6 +188,7 @@ jobs: run: | cmake -S . -B build_asan \ -DCMAKE_TOOLCHAIN_FILE=build_asan/conan_toolchain.cmake \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_BUILD_TYPE=Release \ -DCOIN_DGB=ON \ -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-sanitize=vptr -fno-omit-frame-pointer -fno-sanitize-recover=undefined -g" \ @@ -249,7 +250,7 @@ jobs: # ════════════════════════════════════════════════════════════════════════════ coin-bch: name: COIN_BCH Linux x86_64 - runs-on: ubuntu-24.04 + runs-on: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && fromJSON('["self-hosted","Linux","X64","c2pool-build"]') || 'ubuntu-24.04' }} steps: - uses: actions/checkout@v6 @@ -257,7 +258,7 @@ jobs: run: | sudo apt-get update -qq sudo apt-get install -y --no-install-recommends \ - g++ cmake make libleveldb-dev libsecp256k1-dev + g++ ccache cmake make libleveldb-dev libsecp256k1-dev - uses: actions/setup-python@v6 with: { python-version: 3.12 } @@ -280,7 +281,7 @@ jobs: run: conan install . --build=missing --output-folder=build_bch --settings=build_type=Release - name: Configure (-DCOIN_BCH=ON) - run: cmake -S . -B build_bch -DCMAKE_TOOLCHAIN_FILE=build_bch/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release -DCOIN_BCH=ON + run: cmake -S . -B build_bch -DCMAKE_TOOLCHAIN_FILE=build_bch/conan_toolchain.cmake -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_BUILD_TYPE=Release -DCOIN_BCH=ON - name: Build c2pool-bch binary run: cmake --build build_bch --target c2pool-bch -j$(nproc) @@ -318,7 +319,7 @@ jobs: # ════════════════════════════════════════════════════════════════════════════ coin-bch-asan: name: COIN_BCH Linux x86_64 (AsAN+UBSan) - runs-on: ubuntu-24.04 + runs-on: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && fromJSON('["self-hosted","Linux","X64","c2pool-build"]') || 'ubuntu-24.04' }} continue-on-error: true steps: - uses: actions/checkout@v6 @@ -327,7 +328,7 @@ jobs: run: | sudo apt-get update -qq sudo apt-get install -y --no-install-recommends \ - g++ cmake make libleveldb-dev libsecp256k1-dev + g++ ccache cmake make libleveldb-dev libsecp256k1-dev - uses: actions/setup-python@v6 with: { python-version: 3.12 } @@ -353,6 +354,7 @@ jobs: run: | cmake -S . -B build_bch_asan \ -DCMAKE_TOOLCHAIN_FILE=build_bch_asan/conan_toolchain.cmake \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_BUILD_TYPE=Release \ -DCOIN_BCH=ON \ -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-sanitize=vptr -fno-omit-frame-pointer -fno-sanitize-recover=undefined -g" \ @@ -399,7 +401,7 @@ jobs: # ════════════════════════════════════════════════════════════════════════════ web-static: name: Web-static verify - runs-on: ubuntu-24.04 + runs-on: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && fromJSON('["self-hosted","Linux","X64","c2pool-build"]') || 'ubuntu-24.04' }} defaults: run: working-directory: web-static/sharechain-explorer @@ -477,7 +479,7 @@ jobs: # ════════════════════════════════════════════════════════════════════════════ qt-webengine: name: Qt WebEngine leak gate - runs-on: ubuntu-24.04 + runs-on: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && fromJSON('["self-hosted","Linux","X64","c2pool-build"]') || 'ubuntu-24.04' }} # Hard ceiling: orphaned QtWebEngine/Chromium helper children survive # the inner `timeout 180`, holding the xvfb pipe open and hanging the # job to the 360-min runner default. Cap it so a hung leak gate is @@ -490,7 +492,7 @@ jobs: run: | sudo apt-get update -qq sudo apt-get install -y --no-install-recommends \ - cmake g++ \ + cmake g++ ccache \ qt6-base-dev qt6-webengine-dev qt6-webchannel-dev \ qt6-tools-dev \ libgl1-mesa-dev libegl1-mesa-dev \ @@ -500,7 +502,7 @@ jobs: xvfb - name: Configure - run: cmake -S ui/c2pool-qt -B build-qt -DC2POOL_QT_BUILD_TESTS=ON + run: cmake -S ui/c2pool-qt -B build-qt -DC2POOL_QT_BUILD_TESTS=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build leak harness run: cmake --build build-qt --target c2pool-qt_test_embedded_leak -j$(nproc) @@ -721,7 +723,7 @@ jobs: # ════════════════════════════════════════════════════════════════════════════ coin-dgb-auxdoge: name: COIN_DGB +AUX_DOGE Linux x86_64 - runs-on: ubuntu-24.04 + runs-on: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && fromJSON('["self-hosted","Linux","X64","c2pool-build"]') || 'ubuntu-24.04' }} steps: - uses: actions/checkout@v6 @@ -729,7 +731,7 @@ jobs: run: | sudo apt-get update -qq sudo apt-get install -y --no-install-recommends \ - g++ cmake make libleveldb-dev libsecp256k1-dev + g++ ccache cmake make libleveldb-dev libsecp256k1-dev - uses: actions/setup-python@v6 with: { python-version: 3.12 } @@ -752,7 +754,7 @@ jobs: run: conan install . --build=missing --output-folder=build_dgb_auxdoge --settings=build_type=Release - name: Configure (-DCOIN_DGB=ON -DAUX_DOGE=ON) - run: cmake -S . -B build_dgb_auxdoge -DCMAKE_TOOLCHAIN_FILE=build_dgb_auxdoge/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release -DCOIN_DGB=ON -DAUX_DOGE=ON + run: cmake -S . -B build_dgb_auxdoge -DCMAKE_TOOLCHAIN_FILE=build_dgb_auxdoge/conan_toolchain.cmake -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_BUILD_TYPE=Release -DCOIN_DGB=ON -DAUX_DOGE=ON - name: Build c2pool-dgb binary (AUX_DOGE seam ON) run: cmake --build build_dgb_auxdoge --target c2pool-dgb -j$(nproc) From b3eed12bdf674f11c1a394ccdc8c8b36326e3dfb Mon Sep 17 00:00:00 2001 From: frstrtr Date: Wed, 24 Jun 2026 17:51:45 +0000 Subject: [PATCH 04/10] ci(build): revert Windows job to GitHub-hosted windows-2022 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VM217 (c2pool-windows-217) is a bare self-hosted runner with no build toolchain — no cmake, no MSVC/VS 2022, no SDK — so the Windows lane fails at configure. GitHub-hosted windows-2022 ships all of it. Keep Linux (8 lanes -> VM905) and macOS (M4/Intel) on the self-hosted fleet with the fork-gate; only the Windows job falls back to github-hosted. Follow-up PR will flip Windows back to the fork-gated self-hosted expr once VM217 has the VS 2022 Build Tools C++ workload + cmake + deps installed and builds c2pool clean. --- .github/workflows/build.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3575568fb..66c9d7822 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -639,11 +639,13 @@ jobs: name: Windows x86_64 # dash-slice campaign: skip Windows for PRs carrying the dash-linux-only label (re-enabled at block-viable gate, ~07-01) if: ${{ !(github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'dash-linux-only')) }} - # Self-hosted fleet (VM217 c2pool-windows-217): internal-branch PRs and - # pushes run on the self-hosted Windows runner for speed; fork PRs fall - # back to GitHub-hosted windows-2022 so untrusted code never lands on the - # self-hosted box (defense-in-depth atop the repo fork-approval gate). - runs-on: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && fromJSON('["self-hosted","Windows","X64","c2pool-build"]') || 'windows-2022' }} + # GitHub-hosted windows-2022 (ships cmake + MSVC/VS 2022 + the SDK). + # VM217 (c2pool-windows-217) is a bare runner with NO build toolchain yet; + # FOLLOW-UP: once vm-fleet installs VS 2022 Build Tools (C++ workload) + + # cmake + deps on VM217 and it builds c2pool clean, a follow-up PR flips + # this back to the fork-gated self-hosted expr. Linux+macOS already on the + # self-hosted fleet on this branch; Windows stays GitHub-hosted meanwhile. + runs-on: windows-2022 steps: - uses: actions/checkout@v6 From 8bd1272eefd7bea45903b67efca59d2e257cfc5f Mon Sep 17 00:00:00 2001 From: frstrtr Date: Wed, 24 Jun 2026 19:04:38 +0000 Subject: [PATCH 05/10] ci(build): cap self-hosted Linux lanes to -j8 (4 runners x 8 = 32 cores) VM905 now hosts 4 parallel self-hosted runners sharing one ccache. Cap per-job cmake/ctest parallelism at -j8 so 4 concurrent Linux jobs total 32 threads on the 32-core box instead of 4x nproc(32)=128 = thrash. macOS (sysctl hw.ncpu) and Windows (NUMBER_OF_PROCESSORS) lanes unchanged; ccache retained for cross-job warm hits. --- .github/workflows/build.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 66c9d7822..29a5236bc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -60,7 +60,7 @@ jobs: run: cmake -S . -B build_ci -DCMAKE_TOOLCHAIN_FILE=build_ci/conan_toolchain.cmake -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_BUILD_TYPE=Release -DCOIN_DGB=ON - name: Build - run: cmake --build build_ci --target c2pool -j$(nproc) + run: cmake --build build_ci --target c2pool -j8 - name: Build tests run: | @@ -85,11 +85,11 @@ jobs: dgb_other_tx_assembler_test dgb_reconstruct_won_block_test dgb_reconstruct_closure_test dgb_gentx_unpack_test dgb_work_source_test dgb_template_builder_test dgb_embedded_coin_node_test dgb_embedded_tx_select_test dgb_template_other_txs_test dgb_coinbase_value_parity_test dgb_submit_classify_test dgb_aux_parent_coinbase_parity_test dgb_template_capture_test dgb_aux_doge_db_commitment_bind_test dgb_aux_doge_mm_commitment_test dgb_aux_doge_dc_proof_test dgb_aux_doge_bind_parsers_test dgb_compact_blocks_bip152_parity_test dgb_aux_dual_target_select_test dgb_aux_broadcast_path_election_test \ rpc_request_test softfork_check_test genesis_check_test algo_select_test digishield_walk_test header_chain_test \ dgb_coin_node_seam_test dgb_block_broadcast_test dgb_won_block_dispatch_test dgb_forced_won_share_dualpath_test dgb_scrypt_pow_test dgb_nonce_grinder_test dgb_regrind_block_test dgb_won_block_finalize_test dgb_share_target_genesis_test dgb_pool_msg_wire_test dgb_get_shares_walk_test dgb_download_stops_test dgb_think_p1_walk_bounds_test dgb_think_p1_desired_emit_test dgb_think_p6_desired_cutoff_test dgb_think_p4_head_keys_test dgb_think_p3_best_head_test dgb_g1_oracle_byte_parity_test dgb_think_p2_walk_bounds_test dgb_expected_time_to_block_test dgb_tail_score_endpoints_test dgb_pool_attempts_per_second_test dgb_pool_efficiency_test dgb_think_p5_best_share_punish_test dgb_auto_ratchet_tail_guard_test dgb_binomial_conf_interval_test dgb_desired_version_tally_test dgb_get_height_and_last_endpoints_test dgb_chain_walk_window_test dgb_redistribute_delegate_ghal_test dgb_share_weight_decay_test dgb_naughty_propagation_test dgb_hash_format_parity_test v37_test \ - -j$(nproc) + -j8 - name: Run tests working-directory: build_ci - run: ctest --output-on-failure -j$(nproc) --exclude-regex "LiveTest\." + run: ctest --output-on-failure -j8 --exclude-regex "LiveTest\." - name: Package release if: startsWith(github.ref, 'refs/tags/v') @@ -219,14 +219,14 @@ jobs: rpc_request_test softfork_check_test genesis_check_test algo_select_test digishield_walk_test header_chain_test \ dgb_coin_node_seam_test dgb_block_broadcast_test dgb_won_block_dispatch_test dgb_forced_won_share_dualpath_test dgb_scrypt_pow_test dgb_nonce_grinder_test dgb_regrind_block_test dgb_won_block_finalize_test dgb_share_target_genesis_test dgb_pool_msg_wire_test dgb_get_shares_walk_test dgb_download_stops_test dgb_think_p1_walk_bounds_test dgb_think_p1_desired_emit_test dgb_think_p6_desired_cutoff_test dgb_think_p4_head_keys_test dgb_think_p3_best_head_test dgb_g1_oracle_byte_parity_test dgb_think_p2_walk_bounds_test dgb_expected_time_to_block_test dgb_tail_score_endpoints_test dgb_pool_attempts_per_second_test dgb_pool_efficiency_test dgb_think_p5_best_share_punish_test dgb_auto_ratchet_tail_guard_test dgb_binomial_conf_interval_test dgb_desired_version_tally_test dgb_get_height_and_last_endpoints_test dgb_chain_walk_window_test dgb_redistribute_delegate_ghal_test dgb_share_weight_decay_test dgb_naughty_propagation_test dgb_hash_format_parity_test test_coin_broadcaster test_multiaddress_pplns test_pplns_stress \ v37_test \ - -j$(nproc) + -j8 - name: Run tests under sanitizers working-directory: build_asan env: ASAN_OPTIONS: detect_leaks=0:check_initialization_order=1:strict_string_checks=1:abort_on_error=0 UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=0 - run: ctest --output-on-failure -j$(nproc) --exclude-regex "LiveTest\." + run: ctest --output-on-failure -j8 --exclude-regex "LiveTest\." - name: Upload sanitizer test results on failure if: failure() @@ -284,7 +284,7 @@ jobs: run: cmake -S . -B build_bch -DCMAKE_TOOLCHAIN_FILE=build_bch/conan_toolchain.cmake -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_BUILD_TYPE=Release -DCOIN_BCH=ON - name: Build c2pool-bch binary - run: cmake --build build_bch --target c2pool-bch -j$(nproc) + run: cmake --build build_bch --target c2pool-bch -j8 - name: Build BCH test targets run: | @@ -303,11 +303,11 @@ jobs: bch_embedded_getwork_test bch_embedded_seam_workview_test \ bch_embedded_block_broadcast_test bch_coin_node_seam_test \ bch_asert_kat_test \ - -j$(nproc) + -j8 - name: Run BCH tests working-directory: build_bch - run: ctest --output-on-failure -j$(nproc) -R "^bch_" + run: ctest --output-on-failure -j8 -R "^bch_" - name: Upload test results on failure if: failure() @@ -379,14 +379,14 @@ jobs: bch_embedded_getwork_test bch_embedded_seam_workview_test \ bch_embedded_block_broadcast_test bch_coin_node_seam_test \ bch_asert_kat_test \ - -j$(nproc) + -j8 - name: Run BCH tests under sanitizers working-directory: build_bch_asan env: ASAN_OPTIONS: detect_leaks=0:check_initialization_order=1:strict_string_checks=1:abort_on_error=0 UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=0 - run: ctest --output-on-failure -j$(nproc) -R "^bch_" + run: ctest --output-on-failure -j8 -R "^bch_" - name: Upload sanitizer test results on failure if: failure() @@ -505,7 +505,7 @@ jobs: run: cmake -S ui/c2pool-qt -B build-qt -DC2POOL_QT_BUILD_TESTS=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build leak harness - run: cmake --build build-qt --target c2pool-qt_test_embedded_leak -j$(nproc) + run: cmake --build build-qt --target c2pool-qt_test_embedded_leak -j8 - name: Run leak harness under xvfb continue-on-error: true @@ -759,7 +759,7 @@ jobs: run: cmake -S . -B build_dgb_auxdoge -DCMAKE_TOOLCHAIN_FILE=build_dgb_auxdoge/conan_toolchain.cmake -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_BUILD_TYPE=Release -DCOIN_DGB=ON -DAUX_DOGE=ON - name: Build c2pool-dgb binary (AUX_DOGE seam ON) - run: cmake --build build_dgb_auxdoge --target c2pool-dgb -j$(nproc) + run: cmake --build build_dgb_auxdoge --target c2pool-dgb -j8 - name: Smoke test (--help) run: | From e0770a9226d091ebd1d7e54652cd999efeb4237d Mon Sep 17 00:00:00 2001 From: frstrtr Date: Wed, 24 Jun 2026 21:07:19 +0000 Subject: [PATCH 06/10] ci(build): skip apt on self-hosted, flock-serialize chromium/qt6 VM905 runs 4 parallel self-hosted runners; concurrent apt-get install collided on /var/lib/dpkg/lock-frontend (exit 100), failing 6/7 Linux lanes. Core build deps are pre-provisioned by vm-fleet, so gate the 5 Install system dependencies steps to runner.environment == github-hosted (skip on self-hosted, still run on fork fallback). Chromium and Qt6 are not in the provisioned set, so keep their apt but flock-serialize across the 4 runners via /tmp/c2pool-apt.lock. --- .github/workflows/build.yml | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 29a5236bc..3a18a032e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,6 +31,7 @@ jobs: - uses: actions/checkout@v6 - name: Install system dependencies + if: runner.environment == 'github-hosted' run: | sudo apt-get update -qq sudo apt-get install -y --no-install-recommends \ @@ -148,6 +149,7 @@ jobs: - uses: actions/checkout@v6 - name: Install system dependencies + if: runner.environment == 'github-hosted' run: | sudo apt-get update -qq sudo apt-get install -y --no-install-recommends \ @@ -255,6 +257,7 @@ jobs: - uses: actions/checkout@v6 - name: Install system dependencies + if: runner.environment == 'github-hosted' run: | sudo apt-get update -qq sudo apt-get install -y --no-install-recommends \ @@ -325,6 +328,7 @@ jobs: - uses: actions/checkout@v6 - name: Install system dependencies + if: runner.environment == 'github-hosted' run: | sudo apt-get update -qq sudo apt-get install -y --no-install-recommends \ @@ -425,10 +429,11 @@ jobs: - name: Install Chrome for pixel-regression harness run: | - sudo apt-get update -qq - sudo apt-get install -y --no-install-recommends \ - google-chrome-stable || \ - sudo apt-get install -y --no-install-recommends chromium-browser + flock /tmp/c2pool-apt.lock bash -e -c ' + sudo apt-get update -qq + sudo apt-get install -y --no-install-recommends google-chrome-stable || \ + sudo apt-get install -y --no-install-recommends chromium-browser + ' # Export the first one that exists so capture.mjs picks it up. for bin in /usr/bin/google-chrome-stable /usr/bin/google-chrome /usr/bin/chromium /usr/bin/chromium-browser; do if [ -x "$bin" ]; then @@ -490,16 +495,18 @@ jobs: - name: Install Qt6 + WebEngine + xvfb run: | - sudo apt-get update -qq - sudo apt-get install -y --no-install-recommends \ - cmake g++ ccache \ - qt6-base-dev qt6-webengine-dev qt6-webchannel-dev \ - qt6-tools-dev \ - libgl1-mesa-dev libegl1-mesa-dev \ - libxkbcommon-dev libnss3 libxcomposite1 libxdamage1 \ - libxrandr2 libxtst6 libatk1.0-0 libatk-bridge2.0-0 \ - libasound2t64 libdrm2 libgbm1 \ - xvfb + flock /tmp/c2pool-apt.lock bash -e -c ' + sudo apt-get update -qq + sudo apt-get install -y --no-install-recommends \ + cmake g++ ccache \ + qt6-base-dev qt6-webengine-dev qt6-webchannel-dev \ + qt6-tools-dev \ + libgl1-mesa-dev libegl1-mesa-dev \ + libxkbcommon-dev libnss3 libxcomposite1 libxdamage1 \ + libxrandr2 libxtst6 libatk1.0-0 libatk-bridge2.0-0 \ + libasound2t64 libdrm2 libgbm1 \ + xvfb + ' - name: Configure run: cmake -S ui/c2pool-qt -B build-qt -DC2POOL_QT_BUILD_TESTS=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache @@ -730,6 +737,7 @@ jobs: - uses: actions/checkout@v6 - name: Install system dependencies + if: runner.environment == 'github-hosted' run: | sudo apt-get update -qq sudo apt-get install -y --no-install-recommends \ From 7705a58d4560909315273144d4fd58a6f92f8270 Mon Sep 17 00:00:00 2001 From: frstrtr Date: Wed, 24 Jun 2026 21:41:06 +0000 Subject: [PATCH 07/10] ci(build): fix self-hosted Linux gtest-discovery + Web-static snap-chromium Two self-hosted-environment gaps on the #439 switch (not code): - Linux x86_64 + ASan twin: reused workspace left a stale build_ci/ build_asan, so ctest re-included empty gtest_discover_tests POST_BUILD files -> Unknown CMake command gtest_discover_tests_impl, exit 8. Add a self-hosted-only clean of each Linux build dir before configure (ccache keeps recompiles fast). Applied to all five Linux C++ lanes. - Web-static verify: VM905 only ships snap chromium, which will not launch under the runner systemd cgroup (not a snap cgroup). Provision a real Chrome-for-Testing via @puppeteer/browsers on self-hosted; keep apt path for github-hosted. Harness already passes --no-sandbox. --- .github/workflows/build.yml | 78 +++++++++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3a18a032e..3e7aaecca 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -54,6 +54,15 @@ jobs: path: ~/.conan2 key: conan2-ubuntu24-gcc13-${{ hashFiles('conanfile.txt') }} + # Self-hosted runners reuse the workspace across runs; a stale build_ci/ + # leaves gtest_discover_tests POST_BUILD artifacts that ctest then + # includes as empty/placeholder files ("Unknown CMake command + # gtest_discover_tests_impl", ctest exit 8). Wipe it for a clean + # configure+discovery. ccache (in ~/.cache/ccache) keeps recompiles fast. + - name: Clean stale build dir (self-hosted workspace is reused) + if: runner.environment != 'github-hosted' + run: rm -rf build_ci + - name: Conan install run: conan install . --build=missing --output-folder=build_ci --settings=build_type=Release @@ -172,6 +181,15 @@ jobs: path: ~/.conan2 key: conan2-ubuntu24-gcc13-${{ hashFiles('conanfile.txt') }} + # Self-hosted runners reuse the workspace across runs; a stale build_asan/ + # leaves gtest_discover_tests POST_BUILD artifacts that ctest then + # includes as empty/placeholder files ("Unknown CMake command + # gtest_discover_tests_impl", ctest exit 8). Wipe it for a clean + # configure+discovery. ccache (in ~/.cache/ccache) keeps recompiles fast. + - name: Clean stale build dir (self-hosted workspace is reused) + if: runner.environment != 'github-hosted' + run: rm -rf build_asan + - name: Conan install run: conan install . --build=missing --output-folder=build_asan --settings=build_type=Release @@ -280,6 +298,15 @@ jobs: path: ~/.conan2 key: conan2-ubuntu24-gcc13-bch-${{ hashFiles('conanfile.txt') }} + # Self-hosted runners reuse the workspace across runs; a stale build_bch/ + # leaves gtest_discover_tests POST_BUILD artifacts that ctest then + # includes as empty/placeholder files ("Unknown CMake command + # gtest_discover_tests_impl", ctest exit 8). Wipe it for a clean + # configure+discovery. ccache (in ~/.cache/ccache) keeps recompiles fast. + - name: Clean stale build dir (self-hosted workspace is reused) + if: runner.environment != 'github-hosted' + run: rm -rf build_bch + - name: Conan install run: conan install . --build=missing --output-folder=build_bch --settings=build_type=Release @@ -351,6 +378,15 @@ jobs: path: ~/.conan2 key: conan2-ubuntu24-gcc13-bch-${{ hashFiles('conanfile.txt') }} + # Self-hosted runners reuse the workspace across runs; a stale build_bch_asan/ + # leaves gtest_discover_tests POST_BUILD artifacts that ctest then + # includes as empty/placeholder files ("Unknown CMake command + # gtest_discover_tests_impl", ctest exit 8). Wipe it for a clean + # configure+discovery. ccache (in ~/.cache/ccache) keeps recompiles fast. + - name: Clean stale build dir (self-hosted workspace is reused) + if: runner.environment != 'github-hosted' + run: rm -rf build_bch_asan + - name: Conan install run: conan install . --build=missing --output-folder=build_bch_asan --settings=build_type=Release @@ -429,18 +465,27 @@ jobs: - name: Install Chrome for pixel-regression harness run: | - flock /tmp/c2pool-apt.lock bash -e -c ' - sudo apt-get update -qq - sudo apt-get install -y --no-install-recommends google-chrome-stable || \ - sudo apt-get install -y --no-install-recommends chromium-browser - ' - # Export the first one that exists so capture.mjs picks it up. - for bin in /usr/bin/google-chrome-stable /usr/bin/google-chrome /usr/bin/chromium /usr/bin/chromium-browser; do - if [ -x "$bin" ]; then - echo "CHROME_BIN=$bin" >> "$GITHUB_ENV" - break - fi - done + # GitHub-hosted: apt google-chrome/chromium is a real .deb and launches. + # Self-hosted VM905 only offers snap chromium, which refuses to launch + # under the runner systemd cgroup ("not a snap cgroup for tag + # snap.chromium.chromium"). Provision a real Chrome-for-Testing build + # via @puppeteer/browsers (a puppeteer-core dep, present after npm ci); + # it unpacks under ~/.cache/puppeteer with no snap confinement. The + # harness already launches with --no-sandbox (tests/visual/capture.mjs). + if [ "${{ runner.environment }}" = "github-hosted" ]; then + flock /tmp/c2pool-apt.lock bash -e -c ' + sudo apt-get update -qq + sudo apt-get install -y --no-install-recommends google-chrome-stable || \ + sudo apt-get install -y --no-install-recommends chromium-browser + ' + for bin in /usr/bin/google-chrome-stable /usr/bin/google-chrome /usr/bin/chromium /usr/bin/chromium-browser; do + if [ -x "$bin" ]; then echo "CHROME_BIN=$bin" >> "$GITHUB_ENV"; break; fi + done + else + CHROME_BIN="$(npx --yes @puppeteer/browsers install chrome@stable | tail -1 | awk '{print $NF}')" + test -x "$CHROME_BIN" || { echo "puppeteer chrome install failed"; exit 1; } + echo "CHROME_BIN=$CHROME_BIN" >> "$GITHUB_ENV" + fi - name: Visual pixel-diff harness run: npm run visual @@ -760,6 +805,15 @@ jobs: path: ~/.conan2 key: conan2-ubuntu24-gcc13-dgb-auxdoge-${{ hashFiles('conanfile.txt') }} + # Self-hosted runners reuse the workspace across runs; a stale build_dgb_auxdoge/ + # leaves gtest_discover_tests POST_BUILD artifacts that ctest then + # includes as empty/placeholder files ("Unknown CMake command + # gtest_discover_tests_impl", ctest exit 8). Wipe it for a clean + # configure+discovery. ccache (in ~/.cache/ccache) keeps recompiles fast. + - name: Clean stale build dir (self-hosted workspace is reused) + if: runner.environment != 'github-hosted' + run: rm -rf build_dgb_auxdoge + - name: Conan install run: conan install . --build=missing --output-folder=build_dgb_auxdoge --settings=build_type=Release From 85e58f2f8d781ae7ed05a043ca71bcfb1063bd4d Mon Sep 17 00:00:00 2001 From: frstrtr Date: Thu, 25 Jun 2026 04:39:08 +0000 Subject: [PATCH 08/10] ci(test): hoist include(GoogleTest) before first gtest_discover_tests (PRE_TEST exit-8 fix) The plain Linux x86_64 and AsAN+UBSan lanes failed at ctest with exit 8: CMake Error at build_*/test/test_dgb_subsidy[1]_include.cmake:6 Unknown CMake command "gtest_discover_tests_impl". preceded by "include() given empty file name (ignored)". include(GoogleTest) sets _GOOGLETEST_DISCOVER_TESTS_SCRIPT (the path to GoogleTestAddTests.cmake). It was at line ~176, AFTER the first three gtest_discover_tests() calls (test_dgb_subsidy/coinbase_value/utxo, all DISCOVERY_MODE PRE_TEST). In PRE_TEST the generated [1]_include.cmake includes that script at ctest time to define gtest_discover_tests_impl; with the var unset the include path was empty and ctest aborted. The clean-build-dir step (8d05ef2b) removed the cached _tests.cmake that had masked the broken else-branch, surfacing it on the first PRE_TEST target. Fix: hoist include(GoogleTest) to the top of the BUILD_TESTING block. --- test/CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7656d6a4a..2687901e2 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,4 +1,13 @@ if (BUILD_TESTING AND GTest_FOUND) + # include(GoogleTest) MUST precede any gtest_discover_tests() call below: + # it sets _GOOGLETEST_DISCOVER_TESTS_SCRIPT (path to GoogleTestAddTests.cmake). + # In PRE_TEST mode the generated [1]_include.cmake includes that script + # to define gtest_discover_tests_impl; if the var is unset the include path is + # empty and ctest dies "Unknown CMake command gtest_discover_tests_impl" (exit + # 8). The clean-build-dir step (8d05ef2b) removed the cached _tests.cmake that + # had masked this, surfacing it on test_dgb_subsidy (the first PRE_TEST call). + include(GoogleTest) + # DGB block subsidy oracle-conformance (card #156). config_coin.hpp # subsidy() is header-only/static; no dgb runtime link required. add_executable(test_dgb_subsidy test_dgb_subsidy.cpp) From 2a06fede9ca7294625fcc8fda44334573fa568fb Mon Sep 17 00:00:00 2001 From: frstrtr Date: Fri, 26 Jun 2026 05:07:30 +0000 Subject: [PATCH 09/10] ci(visual): use domcontentloaded in pixel harness (self-hosted CfT load hang) Self-hosted runner provisions a fresh Chrome-for-Testing build via @puppeteer/browsers; under it page.goto(waitUntil:load) never fires the load event within 15s because a non-essential subresource (favicon) stays pending, failing Web-static verify on #439 only. The four dashboard scripts are synchronous head scripts (executed by DOMContentLoaded) and #defrag-canvas is gated by the explicit waitForSelector, so switching to domcontentloaded is render-correct and cannot regress the GitHub-hosted apt-chrome run on master. --- .../sharechain-explorer/tests/visual/capture.mjs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/web-static/sharechain-explorer/tests/visual/capture.mjs b/web-static/sharechain-explorer/tests/visual/capture.mjs index 726b42523..0affcd01b 100644 --- a/web-static/sharechain-explorer/tests/visual/capture.mjs +++ b/web-static/sharechain-explorer/tests/visual/capture.mjs @@ -56,10 +56,17 @@ async function capture(mode, outfile) { // Suppress console errors from unused endpoints (Mock returns {} for // a handful of dashboards we don't care about.) page.on('pageerror', (err) => console.error(`[${mode}] pageerror`, err.message)); - // `load` rather than `networkidle0` — bundled mode opens a - // persistent SSE connection that keeps "network" non-idle. - await page.goto(url, { waitUntil: 'load', timeout: 15000 }); - await page.waitForSelector('#defrag-canvas', { timeout: 5000 }); + // `domcontentloaded` rather than `load`/`networkidle0`. The four + // dashboard scripts (d3, multipool, highcharts*) are synchronous + // classic scripts, so they have downloaded + executed by + // DOMContentLoaded and #defrag-canvas already exists — the explicit + // waitForSelector below gates render deterministically. `load` is + // fragile: a fresh Chrome-for-Testing build (self-hosted runner) + // leaves a non-essential subresource (favicon/image) pending and + // never fires the load event within the timeout; `networkidle0` + // never settles because bundled mode opens a persistent SSE conn. + await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 15000 }); + await page.waitForSelector('#defrag-canvas', { timeout: 8000 }); // Generous fixed delay lets both paths complete their initial // fetch + render. Deterministic against the mock server (no live // data, no SSE pushes). From 40e4eb0453c5dca14796bf8eeef841f6d9b36f66 Mon Sep 17 00:00:00 2001 From: frstrtr Date: Fri, 26 Jun 2026 14:24:03 +0000 Subject: [PATCH 10/10] ci(build): keep Web-static verify on GitHub-hosted (not self-hosted) The self-hosted switch routed the sub-minute Web-static verify job onto the c2pool-build Linux runner along with the heavy C++ lanes. The puppeteer pixel-regression harness cannot complete page navigation there: page.goto times out at 15s under both load and domcontentloaded waitUntil, because the self-hosted runner environment never fires the navigation-complete event for dashboard.html. The job is green on ubuntu-24.04 and gains nothing from the 32-core box, so pin it back to GitHub-hosted. Dashboard-owned web-static assets and the visual harness are unchanged and not the cause. --- .github/workflows/build.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3e7aaecca..8b143b370 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -441,7 +441,10 @@ jobs: # ════════════════════════════════════════════════════════════════════════════ web-static: name: Web-static verify - runs-on: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && fromJSON('["self-hosted","Linux","X64","c2pool-build"]') || 'ubuntu-24.04' }} + # Pin to GitHub-hosted: sub-minute JS job needs no self-hosted compute, + # and the puppeteer pixel harness cannot complete page navigation in the + # self-hosted runner env (DOMContentLoaded never fires within 15s). + runs-on: ubuntu-24.04 defaults: run: working-directory: web-static/sharechain-explorer