nix: stop three defaults from consulting hyperhive.enable

hive-forge's publicUrl and behindGateway, and swarm-controller's
forgeTokenFile, no longer gate their default on
config.services.hyperhive.enable — none of the three has a reader that
depends on hyperhive being enabled to make sense of the value, so the
extra condition only added a hidden coupling. module-eval.nix gains a
centralToggleOff fixture plus four cases asserting each affected
default now resolves identically whether the toggle is on or off.

Refs #4500
This commit is contained in:
atlas 2026-09-19 02:20:35 +02:00 committed by mara
commit c5f60fd58f
4 changed files with 63 additions and 27 deletions

View file

@ -167,8 +167,7 @@ in
services.hyperhive.deploy.forgejo.behindGateway = true. services.hyperhive.deploy.forgejo.behindGateway = true.
The CI container runs with a private network namespace and The CI container runs with a private network namespace and
reaches the forge through the gateway vhost on the bridge IP. reaches the forge through the gateway vhost on the bridge IP.
Set behindGateway = true (it defaults to true alongside Set behindGateway = true (it is the default).
services.hyperhive.enable).
''; '';
} }
]; ];

View file

@ -200,11 +200,7 @@ in
publicUrl = lib.mkOption { publicUrl = lib.mkOption {
type = lib.types.nullOr lib.types.str; type = lib.types.nullOr lib.types.str;
default = default = if deployCfg.forgejo.behindGateway then "https://${cfg.domain}" else null;
if config.services.hyperhive.enable && deployCfg.forgejo.behindGateway then
"https://${cfg.domain}"
else
null;
defaultText = lib.literalExpression '' defaultText = lib.literalExpression ''
if behindGateway then "https://''${domain}" else null if behindGateway then "https://''${domain}" else null
''; '';
@ -303,8 +299,7 @@ in
behindGateway = lib.mkOption { behindGateway = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = config.services.hyperhive.enable; default = true;
defaultText = lib.literalExpression "config.services.hyperhive.enable";
description = '' description = ''
Serve forgejo through the hive-gateway nginx as a sub-domain Serve forgejo through the hive-gateway nginx as a sub-domain
vhost (`server_name = cfg.domain`) instead of directly on vhost (`server_name = cfg.domain`) instead of directly on
@ -318,8 +313,8 @@ in
- `gateway.localHostsEntry = true` extends `/etc/hosts` to - `gateway.localHostsEntry = true` extends `/etc/hosts` to
include `cfg.domain 127.0.0.1` for local dev. include `cfg.domain 127.0.0.1` for local dev.
Defaults to `services.hyperhive.enable` (the gateway always runs Defaults to `true` (the gateway always runs alongside
alongside hyperhive, so forge auto-routes through it). Set `false` hyperhive, so forge auto-routes through it). Set `false`
explicitly to keep forge on the direct port even though the explicitly to keep forge on the direct port even though the
gateway is running (e.g. an external git client that doesn't gateway is running (e.g. an external git client that doesn't
traverse the gateway). traverse the gateway).

View file

@ -502,21 +502,16 @@ in
forgeTokenFile = lib.mkOption { forgeTokenFile = lib.mkOption {
type = lib.types.nullOr lib.types.str; type = lib.types.nullOr lib.types.str;
# The forge has no `enable` of its own to check — neither half of # The forge has no `enable` of its own to condition this on —
# its split namespace carries one — the module activates on the general # neither half of its split namespace carries one — and it deploys
# `config.services.hyperhive.enable` instead (see # unconditionally wherever the rest of the stack does, so its own
# `hive-forge/default.nix`'s own `config = lib.mkIf # delivery path is simply the right default. The controller's units
# config.services.hyperhive.enable { ... }`), so that's the # are the thing that decides whether the file is ever read: they only
# condition to match here too. Referencing a `forge.enable` that # exist under `deploy.swarm-controller.enable`, and a host whose forge
# doesn't exist threw `attribute 'enable' missing` on every host # lives elsewhere overrides this (or sets `null`) explicitly.
# that turns swarm-controller on — caught in review, not by default = deployCfg.forgejo.hostSwarmControllerTokenFile;
# `nix flake check` (nothing in its checked combinations forced
# this particular default to actually evaluate).
default =
if config.services.hyperhive.enable then deployCfg.forgejo.hostSwarmControllerTokenFile else null;
defaultText = lib.literalExpression '' defaultText = lib.literalExpression ''
forge's own `hostSwarmControllerTokenFile` when this host runs config.services.hyperhive.deploy.forgejo.hostSwarmControllerTokenFile
hyperhive at all (forge has no separate enable), else null
''; '';
example = "/var/lib/secrets/swarm-controller-forge.token"; example = "/var/lib/secrets/swarm-controller-forge.token";
description = '' description = ''
@ -525,8 +520,8 @@ in
`forgejo-swarm-controller-account` + `hive-forge-swarm-controller-token` `forgejo-swarm-controller-account` + `hive-forge-swarm-controller-token`
units, which mint and collect it onto forge's own host). units, which mint and collect it onto forge's own host).
Defaults to forge's own delivery path on every host running Defaults to forge's own delivery path (forge deploys
hyperhive (forge deploys unconditionally alongside it see unconditionally alongside the rest of the stack see
`hive-forge/default.nix`, it has no `enable` of its own). `hive-forge/default.nix`, it has no `enable` of its own).
Override explicitly if forge's actual token file ends up Override explicitly if forge's actual token file ends up
somewhere else copy it out of forge's somewhere else copy it out of forge's

View file

@ -97,6 +97,15 @@ let
allLocal = hive { deploy.singleHostSwarm = true; }; allLocal = hive { deploy.singleHostSwarm = true; };
bare = hive { }; bare = hive { };
# The same stub with the central toggle off. Paired with `bare` below to pin
# the defaults that used to read `services.hyperhive.enable` and no longer
# do: each is asserted to hold the SAME literal in both, so a future edit
# that quietly re-introduces the dependency — or that changes what the
# default renders for a hive with the toggle on — fails here. Reading an
# option off this fixture forces that option only, not the config, so the
# toggle being off costs nothing.
centralToggleOff = hive { enable = false; };
withCi = hive { deploy.forgejo.ci.enable = true; }; withCi = hive { deploy.forgejo.ci.enable = true; };
# A host configured against the pre-rename option path. `mkRenamedOptionModule` # A host configured against the pre-rename option path. `mkRenamedOptionModule`
@ -781,6 +790,44 @@ let
# Each case: a name stating the property, and `ok`. # Each case: a name stating the property, and `ok`.
cases = [ cases = [
{
# Both halves matter. The equality is the "no longer consults the central
# toggle" half; the literal is the "and still renders what it always
# did" half, which an equality on its own would let drift to `false` in
# lockstep.
name = "the forge's behindGateway default is true regardless of the central toggle";
ok =
bare.services.hyperhive.deploy.forgejo.behindGateway == true
&& centralToggleOff.services.hyperhive.deploy.forgejo.behindGateway == true;
}
{
# Downstream of the one above — publicUrl reads `behindGateway`, so it
# tracked the central toggle transitively as well as directly. The domain
# is the stub's swarm domain, which both fixtures share.
name = "the forge's publicUrl default follows behindGateway alone, not the central toggle";
ok =
bare.services.hyperhive.swarm.forge.publicUrl == "https://forge.t.local"
&& centralToggleOff.services.hyperhive.swarm.forge.publicUrl == "https://forge.t.local";
}
{
# And that it still tracks `behindGateway` at all: without this arm the
# case above passes just as well for a default hardcoded to the URL.
name = "the forge's publicUrl default is still null with behindGateway off";
ok =
(hive { deploy.forgejo.behindGateway = false; }).services.hyperhive.swarm.forge.publicUrl == null;
}
{
# The controller's token path defaulted to forge's delivery path only on
# a host with the central toggle on, and to `null` otherwise. Forge
# deploys unconditionally, so the path is now unconditional too.
name = "the swarm controller's forgeTokenFile defaults to forge's delivery path regardless of the central toggle";
ok =
let
forgePath = "/var/lib/hyperhive-forge/swarm-controller.token";
in
bare.services.hyperhive.deploy.swarm-controller.forgeTokenFile == forgePath
&& centralToggleOff.services.hyperhive.deploy.swarm-controller.forgeTokenFile == forgePath;
}
{ {
# `ctl` is in no deny list — it is reserved *because it is the subject a # `ctl` is in no deny list — it is reserved *because it is the subject a
# cert-auth role accepts*, which is a value an operator sets, so a # cert-auth role accepts*, which is a value an operator sets, so a