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:
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 {