From d1ef1a4516e1adcb5aec775f35aae4713b8f3200 Mon Sep 17 00:00:00 2001 From: xdustinface Date: Sat, 14 Mar 2026 20:48:37 +0700 Subject: [PATCH 1/2] ci: integrate Codecov test analytics via `cargo-nextest` Switch CI test runner from `cargo test` to `cargo-nextest` to produce JUnit XML reports. Results are uploaded to Codecov's new test analytics feature for tracking test duration trends, failure rates, and flaky tests. --- .config/nextest.toml | 5 +++++ .github/scripts/ci_config.py | 23 +++++++++++++++++++++-- .github/workflows/build-and-test.yml | 11 +++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 .config/nextest.toml diff --git a/.config/nextest.toml b/.config/nextest.toml new file mode 100644 index 000000000..6a109e171 --- /dev/null +++ b/.config/nextest.toml @@ -0,0 +1,5 @@ +[profile.ci] +fail-fast = false + +[profile.ci.junit] +path = "junit.xml" diff --git a/.github/scripts/ci_config.py b/.github/scripts/ci_config.py index 3ab0b5ade..8c43c5a3a 100755 --- a/.github/scripts/ci_config.py +++ b/.github/scripts/ci_config.py @@ -12,6 +12,7 @@ import json import os import re +import shutil import subprocess import sys from pathlib import Path @@ -143,22 +144,40 @@ def run_group_tests(args): if coverage: github_output("crate_flags", args.group) + junit_dir = Path("target/test-results") + junit_dir.mkdir(parents=True, exist_ok=True) + for crate in crates: github_group_start(f"Testing {crate}") try: if coverage: - cmd = ["cargo", "llvm-cov", "--no-report", "-p", crate, "--all-features"] + cmd = [ + "cargo", "llvm-cov", "nextest", + "--no-report", "-p", crate, "--all-features", + "--profile", "ci", + ] else: - cmd = ["cargo", "test", "-p", crate, "--all-features"] + cmd = [ + "cargo", "nextest", "run", + "-p", crate, "--all-features", + "--profile", "ci", + ] result = subprocess.run(cmd) + # Collect JUnit XML per crate for Codecov test analytics + src = Path("target/nextest/ci/junit.xml") + if src.exists(): + shutil.copy(src, junit_dir / f"junit-{crate}.xml") + if result.returncode != 0: failed.append(crate) github_error(f"Test failed for {crate} on {args.os}") finally: github_group_end() + github_output("junit_dir", str(junit_dir)) + if failed: print("\n" + "=" * 40) print(f"FAILED TESTS ({args.group} on {args.os}):") diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index aae7f2fb2..2c1c0e47f 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -39,6 +39,9 @@ jobs: with: shared-key: "test-${{ inputs.os }}-${{ matrix.group }}" + - name: Install cargo-nextest + uses: taiki-e/install-action@nextest + - name: Install cargo-llvm-cov if: inputs.coverage uses: taiki-e/install-action@cargo-llvm-cov @@ -82,6 +85,14 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: true + - name: Upload test results to Codecov + if: ${{ !cancelled() }} + uses: codecov/test-results-action@v1 + with: + files: target/test-results/junit-*.xml + flags: ${{ matrix.group }} + token: ${{ secrets.CODECOV_TOKEN }} + - name: Upload failed dashd test logs if: failure() && (matrix.group == 'spv' || matrix.group == 'ffi') uses: actions/upload-artifact@v4 From 1e19c6bfb9e424329c9f9a4d4f3a3fff6e46b77c Mon Sep 17 00:00:00 2001 From: xdustinface Date: Sun, 15 Mar 2026 17:25:43 +0700 Subject: [PATCH 2/2] fix: resolve CI failures in `cargo-nextest` migration - Serialize `dashd_sync` integration tests via nextest test-groups (nextest runs each test as a separate process concurrently, causing dashd resource contention and crashes) - Add `--no-tests=pass` to nextest commands so crates with zero tests (like `dash-fuzz`) don't fail - Output explicit JUnit file list instead of glob pattern to avoid Windows path expansion issues with codecov CLI --- .config/nextest.toml | 9 +++++++++ .github/scripts/ci_config.py | 9 ++++++++- .github/workflows/build-and-test.yml | 4 ++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.config/nextest.toml b/.config/nextest.toml index 6a109e171..9fabc4519 100644 --- a/.config/nextest.toml +++ b/.config/nextest.toml @@ -1,5 +1,14 @@ +[test-groups] +dashd-integration = { max-threads = 1 } + [profile.ci] fail-fast = false [profile.ci.junit] path = "junit.xml" + +# dashd integration tests each start their own dashd process; +# running them concurrently causes resource contention and crashes. +[[profile.ci.overrides]] +filter = 'binary(dashd_sync)' +test-group = 'dashd-integration' diff --git a/.github/scripts/ci_config.py b/.github/scripts/ci_config.py index 8c43c5a3a..ce1412705 100755 --- a/.github/scripts/ci_config.py +++ b/.github/scripts/ci_config.py @@ -156,12 +156,14 @@ def run_group_tests(args): "cargo", "llvm-cov", "nextest", "--no-report", "-p", crate, "--all-features", "--profile", "ci", + "--no-tests=pass", ] else: cmd = [ "cargo", "nextest", "run", "-p", crate, "--all-features", "--profile", "ci", + "--no-tests=pass", ] result = subprocess.run(cmd) @@ -176,7 +178,12 @@ def run_group_tests(args): finally: github_group_end() - github_output("junit_dir", str(junit_dir)) + junit_files = sorted(junit_dir.glob("junit-*.xml")) + if junit_files: + github_output( + "junit_files", + ",".join(str(f).replace("\\", "/") for f in junit_files), + ) if failed: print("\n" + "=" * 40) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 2c1c0e47f..f4b38d5ff 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -86,10 +86,10 @@ jobs: fail_ci_if_error: true - name: Upload test results to Codecov - if: ${{ !cancelled() }} + if: ${{ !cancelled() && steps.tests.outputs.junit_files }} uses: codecov/test-results-action@v1 with: - files: target/test-results/junit-*.xml + files: ${{ steps.tests.outputs.junit_files }} flags: ${{ matrix.group }} token: ${{ secrets.CODECOV_TOKEN }}