From 1b51df0be3b1498ef04c9a4f6a3bd038359e4254 Mon Sep 17 00:00:00 2001
From: Archit Gupta <archit@accelbread.com>
Date: Sat, 13 Jan 2024 18:04:31 -0800
Subject: [PATCH] Add propogationModule feature

The `propagationModule` config option provides a module that can be used
to propagate flakelight configuration into other module systems such as
NixOS or home-manager.
---
 API_GUIDE.md                         | 16 ++++++++--
 builtinModules/propagationModule.nix | 48 ++++++++++++++++++++++++++++
 tests/default.nix                    | 20 ++++++++++++
 3 files changed, 81 insertions(+), 3 deletions(-)
 create mode 100644 builtinModules/propagationModule.nix

diff --git a/API_GUIDE.md b/API_GUIDE.md
index e13102c..ff7ba13 100644
--- a/API_GUIDE.md
+++ b/API_GUIDE.md
@@ -721,15 +721,24 @@ Alternatively, the configurations can be functions, in which case those
 functions will be passed `moduleArgs` and must return a standard
 configuration (this is useful when using autoloads with the `nixDir` feature).
 
+The `propagationModule` config provides a module to apply flakelight
+configuration to other module systems such as NixOS and home-manager. Applying
+this module will give modules in the nested modules system access to a `flake`
+module arg that contains the flakelight module args as well as `inputs'` and
+`outputs'`. Flakelight's packages configuration will also be applied to the pkgs
+of the nested module system (This includes flakelight's additional pkgs values,
+`withOverlays` overlays, and the flake's packages.
+
 For example:
 
 ```nix
 {
   inputs.flakelight.url = "github:nix-community/flakelight";
   outputs = { flakelight, ... }:
-    flakelight ./. ({ lib, ... }: {
+    flakelight ./. ({ lib, config, ... }: {
       nixosConfigurations.system = lib.nixosSystem {
         # nixosSystem arguments
+        modules = [ config.propagationModule ];
       };
     });
 }
@@ -742,11 +751,12 @@ For example:
     home-manger.url = "github:nix-community/home-manager";
   };
   outputs = { flakelight, home-manager, ... }:
-    flakelight ./. {
+    flakelight ./. ({ config, ... }: {
       homeConfigurations.user = home-manager.lib.homeManagerConfiguration {
         # homeManagerConfiguration arguments
+        modules = [ config.propagationModule ];
       };
-    };
+    });
 }
 ```
 
diff --git a/builtinModules/propagationModule.nix b/builtinModules/propagationModule.nix
new file mode 100644
index 0000000..8212926
--- /dev/null
+++ b/builtinModules/propagationModule.nix
@@ -0,0 +1,48 @@
+# flakelight -- Framework for simplifying flake setup
+# Copyright (C) 2023 Archit Gupta <archit@accelbread.com>
+# SPDX-License-Identifier: MIT
+
+# This provides a module that can be added to module systems nested inside of
+# flakelight, for example NixOS or home-manager configurations.
+
+{ lib, config, flakelight, moduleArgs, inputs, outputs, ... }:
+let
+  inherit (lib) composeManyExtensions mapAttrs mkOption optionalAttrs;
+  inherit (flakelight.types) module;
+in
+{
+  options.propagationModule = mkOption { type = module; };
+
+  config.propagationModule =
+    { lib, pkgs, options, ... }:
+    let
+      inherit (pkgs.stdenv.hostPlatform) system;
+    in
+    {
+      config = (optionalAttrs (options ? nixpkgs.overlays) {
+        # Apply flakelight overlays to NixOS/home-manager configurations
+        nixpkgs.overlays = lib.mkOrder 10 [
+          # Avoid re-applying overlays
+          # This can happen when home-manager's pkgs arg already has them
+          (final: prev: optionalAttrs (! prev ? flakelight) (
+            (composeManyExtensions
+              (config.withOverlays ++ [ config.packageOverlay ]))
+              final
+              prev
+          ))
+        ];
+      })
+      // (optionalAttrs (options ? home-manager.sharedModules) {
+        # Propagate module to home-manager when using its nixos module
+        home-manager.sharedModules = [ config.propagationModule ];
+      })
+      // {
+        # Give access to flakelight module args under `flake` arg.
+        # Also include inputs'/outputs' which depend on `pkgs`.
+        _module.args.flake = {
+          inputs' = mapAttrs (_: mapAttrs (_: v: v.${system} or { })) inputs;
+          outputs' = mapAttrs (_: v: v.${system} or { }) outputs;
+        } // moduleArgs;
+      };
+    };
+}
diff --git a/tests/default.nix b/tests/default.nix
index 63f41a2..ee1c85a 100644
--- a/tests/default.nix
+++ b/tests/default.nix
@@ -596,6 +596,26 @@ in
     }))
     (f: f ? nixosConfigurations.test.config.system.build.toplevel);
 
+  nixosConfigurationsWithProp = test
+    (flakelight ./empty ({ lib, config, ... }: {
+      nixosConfigurations.test = nixpkgs.lib.nixosSystem {
+        system = "x86_64-linux";
+        modules = [
+          config.propagationModule
+          ({ flake, ... }: {
+            system.stateVersion = "24.05";
+            environment.variables = {
+              TEST1 = flake.inputs.nixpkgs.legacyPackages.x86_64-linux.hello;
+              TEST2 = flake.inputs'.nixpkgs.legacyPackages.hello;
+            };
+          })
+        ];
+      };
+    }))
+    (f: (f ? nixosConfigurations.test.config.system.build.toplevel)
+      && (f.nixosConfigurations.test.config.environment.variables.TEST1 ==
+      f.nixosConfigurations.test.config.environment.variables.TEST2));
+
   nixosModule = test
     (flakelight ./empty {
       nixosModule = _: { };