lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

commit dd548294ec25f66a61165f1d7b916958b997f745
parent b4506dc6e677378e0f3b04509e574d9f2c7856d2
Author: triesap <tyson@radroots.org>
Date:   Thu, 20 Aug 2026 19:10:03 +0000

build: add service check graph

- add validated first-class Cargo formatting, checking, testing, linting, and documentation derivations
- require direct SQLx, configuration, source-lock, integration, and package check outputs
- qualify missing, malformed, weakening, and override inputs through the service fixture
- expose the complete service check graph without routing checks through flake applications

Diffstat:
Abuild/nix/service/checks.nix | 116+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mbuild/nix/service/default.nix | 1+
Mbuild/nix/service/fixture.nix | 144++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mflake.nix | 4+---
4 files changed, 257 insertions(+), 8 deletions(-)

diff --git a/build/nix/service/checks.nix b/build/nix/service/checks.nix @@ -0,0 +1,116 @@ +{ + crane, + lib, + pkgs, +}: +{ + serviceName, + toolchain, + source, + cargoLock, + nativeInputs, + package, + hooks, + extraChecks ? { }, +}: +assert lib.assertMsg ( + builtins.isString serviceName && builtins.match "^[a-z][a-z0-9_]*$" serviceName != null +) "serviceName must be a lowercase snake-case identifier"; +assert lib.assertMsg (lib.isDerivation toolchain) "toolchain must be a derivation"; +assert lib.assertMsg (builtins.pathExists source) "source must exist"; +assert lib.assertMsg (builtins.pathExists cargoLock) "cargoLock must exist"; +assert lib.assertMsg ( + builtins.isAttrs nativeInputs + && builtins.isList (nativeInputs.nativeBuildInputs or null) + && builtins.isList (nativeInputs.buildInputs or null) + && builtins.isAttrs (nativeInputs.environment or null) +) "nativeInputs must come from mkNativeInputs"; +assert lib.assertMsg (lib.all (name: !(builtins.hasAttr name nativeInputs.environment)) [ + "CARGO_PROFILE" + "RUSTDOCFLAGS" + "RUSTFLAGS" +]) "nativeInputs.environment must not weaken the standard check policy"; +assert lib.assertMsg (lib.isDerivation package) "package must be a derivation"; +assert lib.assertMsg (builtins.isAttrs hooks) "hooks must be an attribute set"; +assert lib.assertMsg ( + builtins.attrNames hooks == [ + "config" + "integration" + "source-lock" + "sqlx" + ] +) "hooks must provide exactly config, integration, source-lock, and sqlx"; +assert lib.assertMsg (lib.all (name: lib.isDerivation hooks.${name}) ( + builtins.attrNames hooks +)) "every service-specific hook must be a derivation"; +assert lib.assertMsg (builtins.isAttrs extraChecks) "extraChecks must be an attribute set"; +assert lib.assertMsg (lib.all + (name: builtins.match "^[a-z][a-z0-9-]*$" name != null && lib.isDerivation extraChecks.${name}) + (builtins.attrNames extraChecks) +) "extraChecks must contain lowercase check names bound to derivations"; +let + standardNames = [ + "check" + "clippy" + "config" + "docs" + "fmt" + "integration" + "package" + "source-lock" + "sqlx" + "test" + ]; +in +assert lib.assertMsg ( + builtins.length ( + builtins.attrNames (builtins.intersectAttrs extraChecks (lib.genAttrs standardNames (_: null))) + ) == 0 +) "extraChecks must not replace a standard check"; +let + craneLib = (crane.mkLib pkgs).overrideToolchain toolchain; + commonArgs = { + pname = "${serviceName}-checks"; + version = "1"; + src = craneLib.cleanCargoSource source; + inherit cargoLock; + strictDeps = true; + nativeBuildInputs = nativeInputs.nativeBuildInputs; + buildInputs = nativeInputs.buildInputs; + env = nativeInputs.environment; + doCheck = false; + }; + cargoArtifacts = craneLib.buildDepsOnly commonArgs; + mkCargoCheck = + name: command: extraArgs: + craneLib.mkCargoDerivation ( + commonArgs + // extraArgs + // { + inherit cargoArtifacts; + pname = "${serviceName}-${name}"; + buildPhaseCargoCommand = command; + installPhaseCommand = "mkdir -p $out"; + } + ); + standardChecks = { + fmt = craneLib.cargoFmt ( + commonArgs + // { + pname = "${serviceName}-fmt"; + } + ); + check = mkCargoCheck "check" "cargo check --workspace --all-targets --locked" { }; + test = mkCargoCheck "test" "cargo test --workspace --all-targets --locked" { }; + clippy = mkCargoCheck "clippy" "cargo clippy --workspace --all-targets --locked -- -D warnings" { }; + docs = mkCargoCheck "docs" "cargo doc --workspace --no-deps --locked" { + RUSTDOCFLAGS = "-D warnings"; + }; + sqlx = hooks.sqlx; + config = hooks.config; + source-lock = hooks."source-lock"; + package = package; + integration = hooks.integration; + }; +in +standardChecks // extraChecks diff --git a/build/nix/service/default.nix b/build/nix/service/default.nix @@ -8,5 +8,6 @@ mkToolchain = import ./toolchain.nix { inherit pkgs; }; mkNativeInputs = import ./native-inputs.nix { inherit lib; }; mkServicePackage = import ./package.nix { inherit crane lib pkgs; }; + mkServiceChecks = import ./checks.nix { inherit crane lib pkgs; }; mkServiceOutputs = import ./compose.nix { inherit lib; }; } diff --git a/build/nix/service/fixture.nix b/build/nix/service/fixture.nix @@ -20,6 +20,27 @@ let binaryName = "fixture-service"; releaseProfile = "release"; }; + hooks = { + sqlx = pkgs.runCommand "fixture-service-sqlx" { } '' + if grep -F "sqlx" ${fixtureSource}/Cargo.toml; then + echo "fixture unexpectedly acquired a SQLx dependency" >&2 + exit 1 + fi + touch "$out" + ''; + config = pkgs.runCommand "fixture-service-config" { } '' + grep -Fx 'publish = false' ${fixtureSource}/Cargo.toml + touch "$out" + ''; + source-lock = pkgs.runCommand "fixture-service-source-lock" { } '' + grep -Fx 'version = 4' ${fixtureSource}/Cargo.lock + touch "$out" + ''; + integration = pkgs.runCommand "fixture-service-integration" { nativeBuildInputs = [ package ]; } '' + fixture-service --help | grep -Fx "fixture-service" + touch "$out" + ''; + }; smoke = pkgs.runCommand "radroots-service-helper-fixture-smoke" { @@ -46,12 +67,22 @@ let fi touch "$out" ''; + checks = service.mkServiceChecks { + serviceName = "fixture_service"; + inherit + hooks + nativeInputs + package + toolchain + ; + source = fixtureSource; + cargoLock = fixtureSource + "/Cargo.lock"; + extraChecks.smoke = smoke; + }; outputs = service.mkServiceOutputs { serviceName = "fixture_service"; inherit nativeInputs package; - checks = { - inherit smoke; - }; + inherit checks; apps.default = { type = "app"; program = "${package}/bin/fixture-service"; @@ -127,6 +158,84 @@ let }; }).outPath ); + checkArgs = { + serviceName = "fixture_service"; + inherit + hooks + nativeInputs + package + toolchain + ; + source = fixtureSource; + cargoLock = fixtureSource + "/Cargo.lock"; + }; + invalidHookResults = map ( + hookName: + builtins.tryEval ( + (service.mkServiceChecks ( + checkArgs + // { + hooks = hooks // { + ${hookName} = "not-a-derivation"; + }; + } + )).check.outPath + ) + ) (builtins.attrNames hooks); + missingHook = builtins.tryEval ( + (service.mkServiceChecks ( + checkArgs + // { + hooks = builtins.removeAttrs hooks [ "sqlx" ]; + } + )).check.outPath + ); + unexpectedHook = builtins.tryEval ( + (service.mkServiceChecks ( + checkArgs + // { + hooks = hooks // { + other = smoke; + }; + } + )).check.outPath + ); + weakenedPolicyResults = + map + ( + variable: + builtins.tryEval ( + (service.mkServiceChecks ( + checkArgs + // { + nativeInputs = service.mkNativeInputs { + environment.${variable} = "override"; + }; + } + )).check.outPath + ) + ) + [ + "CARGO_PROFILE" + "RUSTDOCFLAGS" + "RUSTFLAGS" + ]; + invalidExtraCheck = builtins.tryEval ( + (service.mkServiceChecks ( + checkArgs + // { + extraChecks.other = "not-a-derivation"; + } + )).check.outPath + ); + standardOverride = builtins.tryEval ( + (service.mkServiceChecks ( + checkArgs + // { + extraChecks.test = smoke; + } + )).check.outPath + ); in assert service.supportedSystems == [ @@ -140,7 +249,27 @@ assert nativeInputs.buildInputs == [ ]; assert nativeInputs.environment.RADROOTS_SERVICE_FIXTURE == "1"; assert outputs.serviceName == "fixture_service"; assert outputs.packages.default == package; -assert outputs.checks.smoke == smoke; +assert outputs.checks == checks; +assert + builtins.attrNames checks == [ + "check" + "clippy" + "config" + "docs" + "fmt" + "integration" + "package" + "smoke" + "source-lock" + "sqlx" + "test" + ]; +assert checks.package == package; +assert checks.sqlx == hooks.sqlx; +assert checks.config == hooks.config; +assert checks.source-lock == hooks.source-lock; +assert checks.integration == hooks.integration; +assert checks.smoke == smoke; assert outputs.apps.default.program == "${package}/bin/fixture-service"; assert outputs.devShells.default != null; assert invalidName.success == false; @@ -151,7 +280,12 @@ assert invalidServicePackage.success == false; assert invalidBinaryName.success == false; assert invalidReleaseProfile.success == false; assert profileOverride.success == false; +assert lib.all (result: result.success == false) invalidHookResults; +assert missingHook.success == false; +assert unexpectedHook.success == false; +assert lib.all (result: result.success == false) weakenedPolicyResults; +assert invalidExtraCheck.success == false; +assert standardOverride.success == false; { inherit outputs; - check = smoke; } diff --git a/flake.nix b/flake.nix @@ -71,9 +71,7 @@ (import ./build/nix/checks.nix { inherit common pkgs; }) - // { - service-helper-fixture = serviceFixture.check; - } + // serviceFixture.outputs.checks ); devShells = import ./build/nix/devshells.nix {