371
371
} ) ( __attrNames flake . devShells ) ) ;
372
372
} // lib . optionalAttrs ( flake ? hydraJobs ) {
373
373
hydraJobs . ${ lib . removeSuffix ":" prefix } = flake . hydraJobs ;
374
+ } // lib . optionalAttrs ( flake ? ciJobs ) {
375
+ ciJobs . ${ lib . removeSuffix ":" prefix } = flake . ciJobs ;
374
376
} ;
375
377
376
378
# Used by `combineFlakes` to combine flakes together
@@ -393,4 +395,149 @@ in {
393
395
# one in the list will be used.
394
396
combineFlakes = sep : prefixAndFlakes : builtins . foldl' addFlakes { }
395
397
( lib . mapAttrsToList ( prefix : flake : prefixFlake prefix sep flake ) prefixAndFlakes ) ;
398
+
399
+ # Make the CI jobs for running code coverage.
400
+ # `project` is the base project without code coverage enabled.
401
+ # `packages` is a selector function that indicates what packages
402
+ # we should run code coverage on (pass haskellLib.selectProjectPackages
403
+ # to run it on the packages).
404
+ # `coverageProjectModule` is applied to `project` and is useful for
405
+ # modifying the project settings when running code coverage (just
406
+ # pass `{}` if you do not need to modify anything).
407
+ # By default the `doCoverage` flag will be set for the packages
408
+ # selected by `packages`.
409
+ projectCoverageCiJobs = project : packages : coverageProjectModule :
410
+ let
411
+ packageNames = project : builtins . attrNames ( packages project . hsPkgs ) ;
412
+ coverageProject = project . appendModule [
413
+ coverageProjectModule
414
+ {
415
+ modules = [ {
416
+ packages = lib . genAttrs ( packageNames project )
417
+ ( _ : { doCoverage = lib . mkDefault true ; } ) ;
418
+ } ] ;
419
+ }
420
+ ] ;
421
+ in
422
+ builtins . listToAttrs ( lib . concatMap ( packageName : [ {
423
+ name = packageName ;
424
+ value = coverageProject . hsPkgs . ${ packageName } . coverageReport ;
425
+ } ] ) ( packageNames coverageProject ) ) ;
426
+
427
+ # Flake package names that are flat and match the cabal component names.
428
+ mkFlakePackages = haskellPackages : builtins . listToAttrs (
429
+ lib . concatLists ( lib . mapAttrsToList ( packageName : package :
430
+ lib . optional ( package . components ? library )
431
+ { name = "${ packageName } :lib:${ packageName } " ; value = package . components . library ; }
432
+ ++ lib . mapAttrsToList ( n : v :
433
+ { name = "${ packageName } :lib:${ n } " ; value = v ; } )
434
+ ( package . components . sublibs )
435
+ ++ lib . mapAttrsToList ( n : v :
436
+ { name = "${ packageName } :exe:${ n } " ; value = v ; } )
437
+ ( package . components . exes )
438
+ ++ lib . mapAttrsToList ( n : v :
439
+ { name = "${ packageName } :test:${ n } " ; value = v ; } )
440
+ ( package . components . tests )
441
+ ++ lib . mapAttrsToList ( n : v :
442
+ { name = "${ packageName } :bench:${ n } " ; value = v ; } )
443
+ ( package . components . benchmarks )
444
+ ) haskellPackages ) ) ;
445
+
446
+ # Flake package names that are flat and match the cabal component names.
447
+ mkFlakeApps = haskellPackages : builtins . listToAttrs (
448
+ lib . concatLists ( lib . mapAttrsToList ( packageName : package :
449
+ lib . mapAttrsToList ( n : v :
450
+ { name = "${ packageName } :exe:${ n } " ; value = { type = "app" ; program = v . exePath ; } ; } )
451
+ ( package . components . exes )
452
+ ++ lib . mapAttrsToList ( n : v :
453
+ { name = "${ packageName } :test:${ n } " ; value = { type = "app" ; program = v . exePath ; } ; } )
454
+ ( package . components . tests )
455
+ ++ lib . mapAttrsToList ( n : v :
456
+ { name = "${ packageName } :benchmark:${ n } " ; value = { type = "app" ; program = v . exePath ; } ; } )
457
+ ( package . components . benchmarks )
458
+ ) haskellPackages ) ) ;
459
+
460
+ # Flatten the result of collectChecks or collectChecks' for use in flake `checks`
461
+ mkFlakeChecks = allChecks : builtins . listToAttrs (
462
+ lib . concatLists ( lib . mapAttrsToList ( packageName : checks :
463
+ # Avoid `recurseForDerivations` issues
464
+ lib . optionals ( lib . isAttrs checks ) (
465
+ lib . mapAttrsToList ( n : v :
466
+ { name = "${ packageName } :test:${ n } " ; value = v ; } )
467
+ ( lib . filterAttrs ( _ : v : lib . isDerivation v ) checks ) )
468
+ ) allChecks ) ) ;
469
+
470
+ removeRecurseForDerivations = x :
471
+ let clean = builtins . removeAttrs x [ "recurseForDerivations" ] ;
472
+ in
473
+ if x . recurseForDerivations or false
474
+ then builtins . mapAttrs ( _ : removeRecurseForDerivations ) clean
475
+ else clean ;
476
+
477
+ mkFlakeCiJobs = project : {
478
+ packages
479
+ , checks
480
+ , coverage
481
+ , devShells
482
+ , checkedProject
483
+ } : {
484
+ # Run all the tests and code coverage
485
+ checks = removeRecurseForDerivations checks ;
486
+ inherit
487
+ coverage
488
+ # Make sure all the packages build
489
+ packages
490
+ # Build and cache any tools in the `devShells`
491
+ devShells ;
492
+ # Build tools and cache tools needed for the project
493
+ inherit ( project ) roots ;
494
+ }
495
+ # Build the plan-nix and check it if materialized
496
+ // lib . optionalAttrs ( checkedProject ? plan-nix ) {
497
+ plan-nix = checkedProject . plan-nix ;
498
+ }
499
+ # Build the stack-nix and check it if materialized
500
+ // lib . optionalAttrs ( checkedProject ? stack-nix ) {
501
+ stack-nix = checkedProject . stack-nix ;
502
+ } ;
503
+
504
+ mkFlake = project : {
505
+ selectPackages ? haskellLib . selectProjectPackages
506
+ , haskellPackages ? selectPackages project . hsPkgs
507
+ , packages ? mkFlakePackages haskellPackages
508
+ , apps ? mkFlakeApps haskellPackages
509
+ , checks ? mkFlakeChecks ( collectChecks' haskellPackages )
510
+ , coverage ? { }
511
+ , devShell ? project . shell
512
+ , devShells ? { default = devShell ; }
513
+ , checkedProject ? project . appendModule { checkMaterialization = true ; }
514
+ , ciJobs ? mkFlakeCiJobs project { inherit checks coverage packages devShells checkedProject ; }
515
+ , hydraJobs ? ciJobs
516
+ } : {
517
+ inherit
518
+ # Used by:
519
+ # `nix build .#pkg-name:lib:pkg-name`
520
+ # `nix build .#pkg-name:lib:sublib-name`
521
+ # `nix build .#pkg-name:exe:exe-name`
522
+ # `nix build .#pkg-name:test:test-name`
523
+ packages
524
+ # Used by:
525
+ # `nix flake check`
526
+ checks
527
+ # `nix run .#pkg-name:exe:exe-name`
528
+ # `nix run .#pkg-name:test:test-name`
529
+ apps
530
+ # Used by hydra.
531
+ hydraJobs
532
+ # Like `hydraJobs` but with `${system}` first so that it the IFDs will not have
533
+ # to run for systems we are not testing (placement of `${system}` is done
534
+ # by `flake-utils.eachSystem` and it treats `hydraJobs` differently from
535
+ # the other flake attributes).
536
+ # See https://github.com/numtide/flake-utils/blob/04c1b180862888302ddfb2e3ad9eaa63afc60cf8/default.nix#L131-L134
537
+ ciJobs
538
+ # Used by:
539
+ # `nix develop`
540
+ devShells
541
+ devShell ; # TODO remove devShell once everyone has nix that supports `devShells.default`
542
+ } ;
396
543
}
0 commit comments