diff --git a/flake.nix b/flake.nix index cefc5a2f..65763031 100644 --- a/flake.nix +++ b/flake.nix @@ -148,7 +148,7 @@ services.hyperhive.swarm.controller.swarmctlPackage = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.swarmctl; - services.hyperhive.swarm.ui.package = + services.hyperhive.deploy.swarm-ui.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.swarm-ui; services.hyperhive.swarm.nats.authPackage = diff --git a/nix/host-modules/deploy.nix b/nix/host-modules/deploy.nix index d8e818b8..034c0f28 100644 --- a/nix/host-modules/deploy.nix +++ b/nix/host-modules/deploy.nix @@ -362,6 +362,13 @@ in [ "services" "hyperhive" "swarm" "victoriametrics" "package" ] [ "services" "hyperhive" "deploy" "victoriametrics" "package" ] ) + # With this one `services.hyperhive.swarm.ui` holds nothing at all — it had + # exactly two options and both have moved, so the namespace now exists only + # as this entry and the `enable` one above it. + (lib.mkRenamedOptionModule + [ "services" "hyperhive" "swarm" "ui" "package" ] + [ "services" "hyperhive" "deploy" "swarm-ui" "package" ] + ) ]; # ⚠️ `deploy.forgejo` is declared in ./hive-ci.nix, not here, and it is the diff --git a/nix/host-modules/swarm-ui.nix b/nix/host-modules/swarm-ui.nix index dd389558..c7c7aac1 100644 --- a/nix/host-modules/swarm-ui.nix +++ b/nix/host-modules/swarm-ui.nix @@ -13,7 +13,6 @@ ... }: let - cfg = config.services.hyperhive.swarm.ui; deployCfg = config.services.hyperhive.deploy; gatewayCfg = config.services.hyperhive.gateway; autheliaCfg = config.services.hyperhive.swarm.authelia; @@ -22,11 +21,11 @@ let # this UI was left out of that overlay entirely (an operator with a # stylix-themed host saw the dashboard in their own colours but this # UI still on the default Catppuccin palette), because nothing here - # ever served a themed `colors.css` in place of `cfg.package`'s own. + # ever served a themed `colors.css` in place of `deployCfg.swarm-ui.package`'s own. # Detection + CSS-generation is shared (`./stylix-theme.nix`). # # No package-copy derivation: this vhost has exactly one location - # serving `cfg.package` (unlike hive-c0re's `servedFrontend`, which + # serving `deployCfg.swarm-ui.package` (unlike hive-c0re's `servedFrontend`, which # backs both the dashboard root AND every per-agent gateway route, so # a single swapped tree covers both) — an `= /static/colors.css` # exact-match location overriding just that one file, same idiom @@ -79,22 +78,23 @@ in '') ]; - options.services.hyperhive.swarm.ui = { - package = lib.mkOption { - type = lib.types.package; - defaultText = lib.literalExpression "hyperhive.packages.\${system}.swarm-ui"; - description = '' - Static build of the swarm UI. nginx serves this store path - directly — there is no server-side component beyond the - controller's own API. + # ⚠️ `services.hyperhive.swarm.ui` no longer exists as a namespace. `enable` + # moved in an earlier slice and `package` in this one, and those were its + # only two options — so both paths survive purely as renames in ./deploy.nix. + options.services.hyperhive.deploy.swarm-ui.package = lib.mkOption { + type = lib.types.package; + defaultText = lib.literalExpression "hyperhive.packages.\${system}.swarm-ui"; + description = '' + Static build of the swarm UI. nginx serves this store path + directly — there is no server-side component beyond the + controller's own API. - Wired by default from this flake's own package set (see - `flake.nix`), the same way `swarm.controller.package` is. There - is deliberately **no overlay** in this project, so a - `pkgs.swarm-ui` default here would name an attribute that does - not exist on any real deployment. - ''; - }; + Wired by default from this flake's own package set (see + `flake.nix`), the same way `swarm.controller.package` is. There + is deliberately **no overlay** in this project, so a + `pkgs.swarm-ui` default here would name an attribute that does + not exist on any real deployment. + ''; }; config = lib.mkIf (config.services.hyperhive.enable && deployCfg.swarm-ui.enable) { @@ -178,7 +178,7 @@ in extraConfig = gatewayCfg.lib.securityHeaders; locations = { "/" = { - root = "${cfg.package}"; + root = "${deployCfg.swarm-ui.package}"; extraConfig = '' ${swarmAuthRequest} # SPA: any path the bundle routes client-side is served the @@ -189,7 +189,7 @@ in } // lib.optionalAttrs (stylixThemeColors != null) { # Stylix theming (see the `let` block above): overrides just - # this one file from `cfg.package`'s own static root, rather + # this one file from `deployCfg.swarm-ui.package`'s own static root, rather # than copying the whole tree to change one file inside it — # nginx resolves the more specific `=` exact match over the # `/` prefix root above, so this simply doesn't exist (falling diff --git a/nix/module-eval.nix b/nix/module-eval.nix index 5f098c53..80bd56d5 100644 --- a/nix/module-eval.nix +++ b/nix/module-eval.nix @@ -16,7 +16,7 @@ # # ⚠️ **This evaluates, it does not execute** — a command line, a request or # a certificate needs something that *runs* it. A case needing a rendered -# file must stub what it drags in (`swarm.ui.package = pkgs.emptyDirectory`). +# file must stub what it drags in (`deploy.swarm-ui.package = pkgs.emptyDirectory`). { pkgs, lib, @@ -154,6 +154,14 @@ let swarm.controller.queue.clientSecretFile = "/run/secrets/ctrl-queue.secret"; }; + # The swarm UI, whose namespace this slice empties: `swarm.ui` had exactly + # two options and both have moved, so BOTH old paths are set here and the + # whole namespace now lives or dies by its two rename entries. + uiOldPath = hive { + swarm.ui.enable = true; + swarm.ui.package = pkgs.emptyDirectory; + }; + # The two stores, which had NO old-path fixture at all until this slice — # their `enable` and `retentionPeriod` shims have been uncovered since they # landed, which is precisely the "a missing shim reads as a clean tree" @@ -490,6 +498,31 @@ let # host-side half depends on: no store here, so no bind mount and no unit. # Without it a hive that merely names a token would drag the store's # container config into its evaluation. + # Reads the vhost's rendered `root`, not the option: the UI is served + # straight out of a store path, so a shim that resolves but stops + # reaching the module would leave nginx pointing at the default build. + name = "a config written against the pre-rename swarm-ui paths still serves the operator's build"; + # + # ⚠️ `unsafeDiscardStringContext` on both sides, load-bearing rather than + # tidy: interpolating a derivation carries string *context*, and this + # suite renders its results into a `buildCommand` that may not reference + # store paths. Comparing the paths as plain text is the intent — the case + # asks "does nginx point HERE", not "depend on what it points at". + ok = + let + want = builtins.unsafeDiscardStringContext "${pkgs.emptyDirectory}"; + # `or ""` is NOT enough: nginx's `locations..root` is `nullOr`, so + # a vhost that HAS the attribute set to null skips the default and + # reaches the coercion. Filter nulls, then discard context. + roots = map builtins.unsafeDiscardStringContext ( + lib.filter (r: r != null) ( + map (v: v.locations."/".root or null) (builtins.attrValues uiOldPath.services.nginx.virtualHosts) + ) + ); + in + builtins.elem want roots; + } + { # Reads the package the CONTAINER renders, not the option: a shim that # resolves but stops reaching the module would leave the store running # nixpkgs' default while the operator's override read back fine.