From 31e86dbafce8e04277e0125a48142b02c84c1781 Mon Sep 17 00:00:00 2001
From: Nicholas Hassan <nick@hassan.host>
Date: Wed, 12 Feb 2025 22:10:25 +1030
Subject: [PATCH] podman: use dependency quadlets directly in build for
 generator

---
 modules/services/podman-linux/containers.nix  | 101 +++++++++---------
 .../services/podman-linux/install-quadlet.nix |  12 ++-
 modules/services/podman-linux/options.nix     |  25 ++++-
 .../services/podman-linux/integration.nix     |   6 +-
 4 files changed, 80 insertions(+), 64 deletions(-)

diff --git a/modules/services/podman-linux/containers.nix b/modules/services/podman-linux/containers.nix
index d5836119a..32e643a5b 100644
--- a/modules/services/podman-linux/containers.nix
+++ b/modules/services/podman-linux/containers.nix
@@ -9,54 +9,46 @@ let
 
   createQuadletSource = name: containerDef:
     let
-      formatServiceNameForType = type: name:
-        {
-          image = "podman-${name}-image.service";
-          build = "podman-${name}-build.service";
-          network = "podman-${name}-network.service";
-          volume = "podman-${name}-volume.service";
-        }."${type}";
+      # formatServiceNameForType = type: name:
+      #   {
+      #     image = "${name}-image.service";
+      #     build = "${name}-build.service";
+      #     network = "${name}-network.service";
+      #     volume = "${name}-volume.service";
+      #   }."${type}";
 
-      dependencyByHomeManagerQuadlet = type: name:
-        let
-          definitionsOfType =
-            filter (q: q.resourceType == type) cfg.internal.quadletDefinitions;
-          matchingName =
-            filter (q: q.serviceName == "podman-${name}") definitionsOfType;
-        in if ((length matchingName) == 1) then
-          [ (formatServiceNameForType type name) ]
+      dependencyBySuffix = type: name:
+        if (hasInfix ".${type}" name) then
+          let
+            baseName = elemAt (splitString ".${type}" name) 0;
+          in
+          if (hasAttr (builtins.trace (baseName) baseName) cfg.internal.builtQuadlets) then
+            [ (cfg.internal.builtQuadlets.${baseName}) ]
+          else
+            [ ]
         else
           [ ];
 
-      forEachValue = type: value:
-        let resolve = v: dependencyByHomeManagerQuadlet type v;
-        in if isList value then
-          concatLists (map resolve value)
-        else
-          resolve value;
-
       withResolverFor = type: value:
-        {
-          "image" = forEachValue "image" value;
-          "build" = forEachValue "build" value;
-          "network" = forEachValue "network" value;
-          "volume" = let
-            a = if isList value then value else [ value ];
-            volumes = map (v: elemAt (splitString ":" v) 0) a;
-          in forEachValue "volume" volumes;
-        }.${type};
+        let
+          resolve = v: dependencyBySuffix type v;
+        in
+        if builtins.isList value
+        then builtins.concatLists (map resolve value)  # Flatten list of lists
+        else resolve value;
 
-      dependencyServices = (withResolverFor "image" containerDef.image)
-        ++ (withResolverFor "build" containerDef.image)
-        ++ (withResolverFor "network" containerDef.network)
-        ++ (withResolverFor "volume" containerDef.volumes);
+      dependencyServices = (withResolverFor "image" containerDef.image) ++
+        (withResolverFor "build" containerDef.image) ++
+        (withResolverFor "network" containerDef.network) ++
+        (withResolverFor "volume" containerDef.volumes);
 
-      resolvedImage = if (builtins.hasAttr containerDef.image cfg.images) then
-        cfg.images."${containerDef.image}".image
-      else if (builtins.hasAttr containerDef.image cfg.builds) then
-        "localhost/homemanager/${containerDef.image}"
-      else
-        containerDef.image;
+      getActualImage =
+        if (builtins.hasAttr containerDef.image cfg.images) then
+          cfg.images."${containerDef.image}".image
+        else if (builtins.hasAttr containerDef.image cfg.builds) then
+          "localhost/homemanager/${containerDef.image}"
+        else
+          containerDef.image;
 
       quadlet = (podman-lib.deepMerge {
         Container = {
@@ -101,29 +93,34 @@ let
           TimeoutStopSec = 30;
         };
         Unit = {
-          After = dependencyServices;
-          Requires = dependencyServices;
           Description = (if (builtins.isString containerDef.description) then
             containerDef.description
           else
             "Service for container ${name}");
         };
       } containerDef.extraConfig);
-    in ''
-      # Automatically generated by home-manager podman container configuration
-      # DO NOT EDIT THIS FILE DIRECTLY
-      #
-      # ${name}.container
-      ${podman-lib.toQuadletIni quadlet}
-    '';
+    in 
+    {
+      dependencies = dependencyServices;
+      text = ''
+        # Automatically generated by home-manager podman container configuration
+        # DO NOT EDIT THIS FILE DIRECTLY
+        #
+        # ${name}.container
+        ${podman-lib.toQuadletIni quadlet}
+      '';
+    };
 
-  toQuadletInternal = name: containerDef: {
+  toQuadletInternal = name: containerDef: let 
+    quadletSrc = createQuadletSource name containerDef;
+  in {
     assertions = podman-lib.buildConfigAsserts name containerDef.extraConfig;
+    dependencies = quadletSrc.dependencies;
     resourceType = "container";
     serviceName =
       "podman-${name}"; # quadlet service name: 'podman-<name>.service'
     source =
-      podman-lib.removeBlankLines (createQuadletSource name containerDef);
+      podman-lib.removeBlankLines quadletSrc.text;
   };
 
   # Define the container user type as the user interface
diff --git a/modules/services/podman-linux/install-quadlet.nix b/modules/services/podman-linux/install-quadlet.nix
index d0157020c..2f909ae45 100644
--- a/modules/services/podman-linux/install-quadlet.nix
+++ b/modules/services/podman-linux/install-quadlet.nix
@@ -17,12 +17,14 @@ let
 
       buildInputs = [ cfg.package ];
 
-      dontUnpack = true;
+      # dontUnpack = true;
+
+      unpackPhase = ''
+        mkdir -p $out/quadlets
+        ${concatStringsSep "\n" (map (v: "cp ${v.out}/quadlets/${v.quadletData.serviceName}.${v.quadletData.resourceType} $out/quadlets") quadlet.dependencies)}
+      '';
 
       installPhase = ''
-        mkdir $out
-        # Directory for the quadlet file
-        mkdir -p $out/quadlets
         # Directory for systemd unit files
         mkdir -p $out/units
 
@@ -84,5 +86,7 @@ in {
     home.activation.podmanQuadletCleanup =
       lib.mkIf (lib.length builtQuadlets >= 1)
       (lib.hm.dag.entryAfter [ "reloadSystemd" ] activationCleanupScript);
+
+    services.podman.internal.builtQuadlets = listToAttrs (map (pkg: { name = removePrefix "podman-" pkg.passthru.quadletData.serviceName; value = pkg; }) builtQuadlets);
   };
 }
diff --git a/modules/services/podman-linux/options.nix b/modules/services/podman-linux/options.nix
index 242028aea..850660c00 100644
--- a/modules/services/podman-linux/options.nix
+++ b/modules/services/podman-linux/options.nix
@@ -11,6 +11,13 @@ let
         description = "List of Nix type assertions.";
       };
 
+      dependencies = lib.mkOption {
+        type = with lib.types; listOf package;
+        default = [ ];
+        internal = true;
+        description = "List of systemd service dependencies.";
+      };
+
       resourceType = lib.mkOption {
         type = lib.types.str;
         default = "";
@@ -33,11 +40,19 @@ let
   };
 in {
   options.services.podman = {
-    internal.quadletDefinitions = lib.mkOption {
-      type = lib.types.listOf quadletInternalType;
-      default = { };
-      internal = true;
-      description = "List of quadlet source file content and service names.";
+    internal = {
+      quadletDefinitions = lib.mkOption {
+        type = lib.types.listOf quadletInternalType;
+        default = { };
+        internal = true;
+        description = "List of quadlet source file content and service names.";
+      };
+      builtQuadlets = lib.mkOption {
+        type = with lib.types; attrsOf package;
+        default = { };
+        internal = true;
+        description = "All built quadlets.";
+      };
     };
 
     package = lib.mkOption {
diff --git a/tests/modules/services/podman-linux/integration.nix b/tests/modules/services/podman-linux/integration.nix
index 26aef7019..565c22cca 100644
--- a/tests/modules/services/podman-linux/integration.nix
+++ b/tests/modules/services/podman-linux/integration.nix
@@ -17,9 +17,9 @@
     };
     containers = {
       "my-container" = {
-        image = "my-img";
-        network = [ "my-net" "externalnet" ];
-        volumes = [ "my-vol:/data" ];
+        image = "my-img.image";
+        network = [ "my-net.network" "externalnet" ];
+        volumes = [ "my-vol.volume:/data" ];
       };
       "my-container-bld" = { image = "my-bld"; };
     };