From 51c0e6bc63ace7655329fb32d15fe5d9d5842dd6 Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Fri, 8 Aug 2025 12:05:01 +0300 Subject: [PATCH] hydra: Restore coverage job Sometime ago we lost the coverage job in the midst of meson migration. Until we have something like codecov it'd be very useful to restore this job with the html reports and historical metrics. As a bonus we get more coverage metrics by switching to LLVM tooling from LCOV. --- ci/gha/tests/default.nix | 52 +++++++++++++++++++++++++++------------- packaging/hydra.nix | 15 ++++++++---- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/ci/gha/tests/default.nix b/ci/gha/tests/default.nix index d2bee699b..5dbb3f407 100644 --- a/ci/gha/tests/default.nix +++ b/ci/gha/tests/default.nix @@ -2,6 +2,12 @@ nixFlake ? builtins.getFlake ("git+file://" + toString ../../..), system ? builtins.currentSystem, pkgs ? nixFlake.inputs.nixpkgs.legacyPackages.${system}, + nixComponents ? ( + nixFlake.lib.makeComponents { + inherit pkgs; + inherit getStdenv; + } + ), getStdenv ? p: p.stdenv, componentTestsPrefix ? "", withSanitizers ? false, @@ -64,18 +70,13 @@ let in rec { - nixComponents = - (nixFlake.lib.makeComponents { - inherit pkgs; - inherit getStdenv; - }).overrideScope - ( - final: prev: { - nix-store-tests = prev.nix-store-tests.override { withBenchmarks = true; }; + nixComponentsInstrumented = nixComponents.overrideScope ( + final: prev: { + nix-store-tests = prev.nix-store-tests.override { withBenchmarks = true; }; - mesonComponentOverrides = lib.composeManyExtensions componentOverrides; - } - ); + mesonComponentOverrides = lib.composeManyExtensions componentOverrides; + } + ); /** Top-level tests for the flake outputs, as they would be built by hydra. @@ -120,15 +121,15 @@ rec { lib.concatMapAttrs (testName: test: { "${componentTestsPrefix}${pkgName}-${testName}" = test; }) (pkg.tests or { }) - ) nixComponents) + ) nixComponentsInstrumented) // lib.optionalAttrs (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) { - "${componentTestsPrefix}nix-functional-tests" = nixComponents.nix-functional-tests; + "${componentTestsPrefix}nix-functional-tests" = nixComponentsInstrumented.nix-functional-tests; }; codeCoverage = let componentsTestsToProfile = - (builtins.mapAttrs (n: v: nixComponents.${n}.tests.run) { + (builtins.mapAttrs (n: v: nixComponentsInstrumented.${n}.tests.run) { "nix-util-tests" = { }; "nix-store-tests" = { }; "nix-fetchers-tests" = { }; @@ -136,7 +137,7 @@ rec { "nix-flake-tests" = { }; }) // { - inherit (nixComponents) nix-functional-tests; + inherit (nixComponentsInstrumented) nix-functional-tests; }; coverageProfileDrvs = lib.mapAttrs ( @@ -170,12 +171,13 @@ rec { coverageReports = let - nixComponentDrvs = lib.filter (lib.isDerivation) (lib.attrValues nixComponents); + nixComponentDrvs = lib.filter (lib.isDerivation) (lib.attrValues nixComponentsInstrumented); in pkgs.runCommand "code-coverage-report" { nativeBuildInputs = [ pkgs.llvmPackages.libllvm + pkgs.jq ]; __structuredAttrs = true; nixComponents = nixComponentDrvs; @@ -201,6 +203,24 @@ rec { echo } >> $out/index.txt + llvm-cov export $arguments -instr-profile ${mergedProfdata} -format=text > $out/coverage.json + + mkdir -p $out/nix-support + + coverageTotals=$(jq ".data[0].totals" $out/coverage.json) + + # Mostly inline from pkgs/build-support/setup-hooks/make-coverage-analysis-report.sh [1], + # which we can't use here, because we rely on LLVM's infra for source code coverage collection. + # [1]: https://github.com/NixOS/nixpkgs/blob/67bb48c4c8e327417d6d5aa7e538244b209e852b/pkgs/build-support/setup-hooks/make-coverage-analysis-report.sh#L16 + declare -A metricsArray=(["lineCoverage"]="lines" ["functionCoverage"]="functions" ["branchCoverage"]="branches") + + for metricName in "''\${!metricsArray[@]}"; do + key="''\${metricsArray[$metricName]}" + metric=$(echo "$coverageTotals" | jq ".$key.percent * 10 | round / 10") + echo "$metricName $metric %" >> $out/nix-support/hydra-metrics + done + + echo "report coverage $out" >> $out/nix-support/hydra-build-products ''; in assert withCoverage; diff --git a/packaging/hydra.nix b/packaging/hydra.nix index 7a7569fa3..9f9749bde 100644 --- a/packaging/hydra.nix +++ b/packaging/hydra.nix @@ -223,10 +223,17 @@ in dockerImage = lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage); # # Line coverage analysis. - # coverage = nixpkgsFor.x86_64-linux.native.nix.override { - # pname = "nix-coverage"; - # withCoverageChecks = true; - # }; + coverage = + (import ./../ci/gha/tests rec { + withCoverage = true; + pkgs = nixpkgsFor.x86_64-linux.nativeForStdenv.clangStdenv; + nixComponents = pkgs.nixComponents2; + nixFlake = null; + getStdenv = p: p.clangStdenv; + }).codeCoverage.coverageReports.overrideAttrs + { + name = "nix-coverage"; # For historical consistency + }; # Nix's manual manual = nixpkgsFor.x86_64-linux.native.nixComponents2.nix-manual;