commit bd83c4d0d87e16df949ae929d5ed68b13a9f6b07
parent f22d9a92590bc4302b207c0217cf1274a2944b3d
Author: triesap <tyson@radroots.org>
Date: Thu, 20 Aug 2026 18:27:44 +0000
build: add service flake helpers
- centralize supported systems and injected toolchain construction
- compose generic service packages, checks, apps, and shells
- validate helper boundaries with a fail-closed fixture service
- preserve service-domain and later packaging ownership boundaries
Diffstat:
8 files changed, 198 insertions(+), 14 deletions(-)
diff --git a/build/nix/service/compose.nix b/build/nix/service/compose.nix
@@ -0,0 +1,40 @@
+{ lib }:
+{
+ serviceName,
+ package,
+ nativeInputs,
+ extraPackages ? { },
+ checks ? { },
+ apps ? { },
+ devShells ? { },
+}:
+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 package) "package must be a derivation";
+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 (builtins.isAttrs extraPackages) "extraPackages must be an attribute set";
+assert lib.assertMsg (
+ !(builtins.hasAttr "default" extraPackages)
+) "extraPackages must not replace packages.default";
+assert lib.assertMsg (builtins.isAttrs checks) "checks must be an attribute set";
+assert lib.assertMsg (builtins.isAttrs apps) "apps must be an attribute set";
+assert lib.assertMsg (builtins.isAttrs devShells) "devShells must be an attribute set";
+{
+ inherit
+ apps
+ checks
+ devShells
+ nativeInputs
+ serviceName
+ ;
+ packages = {
+ default = package;
+ }
+ // extraPackages;
+}
diff --git a/build/nix/service/default.nix b/build/nix/service/default.nix
@@ -0,0 +1,7 @@
+{ lib, pkgs }:
+{
+ supportedSystems = import ./systems.nix;
+ mkToolchain = import ./toolchain.nix { inherit pkgs; };
+ mkNativeInputs = import ./native-inputs.nix { inherit lib; };
+ mkServiceOutputs = import ./compose.nix { inherit lib; };
+}
diff --git a/build/nix/service/fixture.nix b/build/nix/service/fixture.nix
@@ -0,0 +1,108 @@
+{
+ lib,
+ pkgs,
+ service,
+}:
+let
+ nativeInputs = service.mkNativeInputs {
+ nativeBuildInputs = [ pkgs.coreutils ];
+ environment = {
+ RADROOTS_SERVICE_FIXTURE = "1";
+ };
+ };
+ package = pkgs.writeShellApplication {
+ name = "fixture-service";
+ runtimeInputs = nativeInputs.nativeBuildInputs;
+ text = ''
+ case "''${1:-}" in
+ --help)
+ echo "fixture-service"
+ ;;
+ *)
+ echo "usage: fixture-service --help" >&2
+ exit 2
+ ;;
+ esac
+ '';
+ };
+ smoke =
+ pkgs.runCommand "radroots-service-helper-fixture-smoke"
+ {
+ nativeBuildInputs = [
+ package
+ pkgs.gnugrep
+ ];
+ }
+ ''
+ fixture-service --help > output
+ grep -Fx "fixture-service" output
+ touch "$out"
+ '';
+ outputs = service.mkServiceOutputs {
+ serviceName = "fixture_service";
+ inherit nativeInputs package;
+ checks = {
+ inherit smoke;
+ };
+ apps.default = {
+ type = "app";
+ program = "${package}/bin/fixture-service";
+ };
+ devShells.default = pkgs.mkShell {
+ packages = nativeInputs.nativeBuildInputs;
+ shellHook = ''
+ export RADROOTS_SERVICE_FIXTURE=${lib.escapeShellArg nativeInputs.environment.RADROOTS_SERVICE_FIXTURE}
+ '';
+ };
+ };
+ invalidName = builtins.tryEval (
+ (service.mkServiceOutputs {
+ serviceName = "../fixture";
+ inherit nativeInputs package;
+ }).packages.default.outPath
+ );
+ defaultOverride = builtins.tryEval (
+ (service.mkServiceOutputs {
+ serviceName = "fixture_service";
+ inherit nativeInputs package;
+ extraPackages.default = package;
+ }).packages.default.outPath
+ );
+ invalidPackage = builtins.tryEval (
+ (service.mkServiceOutputs {
+ serviceName = "fixture_service";
+ inherit nativeInputs;
+ package = "not-a-derivation";
+ }).packages.default
+ );
+ invalidNativeInputs = builtins.tryEval (
+ (service.mkServiceOutputs {
+ serviceName = "fixture_service";
+ inherit package;
+ nativeInputs = { };
+ }).nativeInputs
+ );
+in
+assert
+ service.supportedSystems == [
+ "aarch64-darwin"
+ "aarch64-linux"
+ "x86_64-darwin"
+ "x86_64-linux"
+ ];
+assert nativeInputs.nativeBuildInputs == [ pkgs.coreutils ];
+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.apps.default.program == "${package}/bin/fixture-service";
+assert outputs.devShells.default != null;
+assert invalidName.success == false;
+assert defaultOverride.success == false;
+assert invalidPackage.success == false;
+assert invalidNativeInputs.success == false;
+{
+ inherit outputs;
+ check = smoke;
+}
diff --git a/build/nix/service/native-inputs.nix b/build/nix/service/native-inputs.nix
@@ -0,0 +1,14 @@
+{ lib }:
+{
+ nativeBuildInputs ? [ ],
+ buildInputs ? [ ],
+ environment ? { },
+}:
+assert lib.assertMsg (builtins.isList nativeBuildInputs) "nativeBuildInputs must be a list";
+assert lib.assertMsg (builtins.isList buildInputs) "buildInputs must be a list";
+assert lib.assertMsg (builtins.isAttrs environment) "environment must be an attribute set";
+{
+ nativeBuildInputs = lib.unique nativeBuildInputs;
+ buildInputs = lib.unique buildInputs;
+ inherit environment;
+}
diff --git a/build/nix/service/systems.nix b/build/nix/service/systems.nix
@@ -0,0 +1,6 @@
+[
+ "aarch64-darwin"
+ "aarch64-linux"
+ "x86_64-darwin"
+ "x86_64-linux"
+]
diff --git a/build/nix/service/toolchain.nix b/build/nix/service/toolchain.nix
@@ -0,0 +1,4 @@
+{ pkgs }:
+{ rustToolchainFile }:
+assert builtins.pathExists rustToolchainFile;
+pkgs.rust-bin.fromRustupToolchainFile rustToolchainFile
diff --git a/build/nix/toolchains.nix b/build/nix/toolchains.nix
@@ -1,6 +0,0 @@
-{ pkgs }:
-{
- stable = pkgs.rust-bin.fromRustupToolchainFile ../../rust-toolchain.toml;
-
- coverage = pkgs.rust-bin.fromRustupToolchainFile ../../rust-toolchain-coverage.toml;
-}
diff --git a/flake.nix b/flake.nix
@@ -19,12 +19,7 @@
inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
imports = [ inputs.treefmt-nix.flakeModule ];
- systems = [
- "aarch64-darwin"
- "aarch64-linux"
- "x86_64-darwin"
- "x86_64-linux"
- ];
+ systems = import ./build/nix/service/systems.nix;
perSystem =
{
@@ -38,11 +33,24 @@
inherit system;
overlays = [ inputs.rust-overlay.overlays.default ];
};
- toolchains = import ./build/nix/toolchains.nix { inherit pkgs; };
+ service = import ./build/nix/service {
+ inherit lib pkgs;
+ };
+ toolchains = {
+ stable = service.mkToolchain {
+ rustToolchainFile = ./rust-toolchain.toml;
+ };
+ coverage = service.mkToolchain {
+ rustToolchainFile = ./rust-toolchain-coverage.toml;
+ };
+ };
common = import ./build/nix/common.nix {
crane = inputs.crane;
inherit lib pkgs toolchains;
};
+ serviceFixture = import ./build/nix/service/fixture.nix {
+ inherit lib pkgs service;
+ };
in
{
treefmt = import ./treefmt.nix;
@@ -58,8 +66,11 @@
};
checks = lib.filterAttrs (_: value: value != null) (
- import ./build/nix/checks.nix {
+ (import ./build/nix/checks.nix {
inherit common pkgs;
+ })
+ // {
+ service-helper-fixture = serviceFixture.check;
}
);