nix/hive-forge: unify on cfg.domain as full hostname, drop subdomain label (mara #754:9684)
mara on PR #754: "would it be better to specify full forge domain in options instead?" Drops the awkward `cfg.subdomain` label option. Now `cfg.domain` is the single source of truth for both the forgejo `DOMAIN` setting (existing semantics) AND the gateway vhost server-name (new). ## Before / after ```nix # before: separate label + cfg.domain juggling services.hyperhive.forge.subdomain = "forge"; # → forge.<hive> services.hyperhive.forge.domain = "localhost"; # unused for vhost # after: full domain, single option services.hyperhive.forge.domain = "forge.darkest.space"; # ← used for ROOT_URL + vhost ``` ## Default `cfg.domain` default auto-derives: - `forge.<services.hyperhive.domain>` when hive-domain is set - `"localhost"` otherwise (pre-#749 direct-on-port shape) So the common case (hive-domain set) gets `forge.<hive>` for free, operators with a bespoke shape (`git.example.com`) set the full hostname directly. ## Assertions - `cfg.domain != ""` — empty would render `.<hive>` shaped garbage in both server_name + /etc/hosts. - `cfg.behindGateway → gateway.enable` — can't route through a gateway that isn't running. (The previous "subdomain = empty" assertion is dropped — that edge case is gone with the rename.) ## Verified - default with `hyperhive.domain = "test.local"` → `forge.test.local`, `ROOT_URL = http://forge.test.local/`, vhost present - `forge.domain = "git.example.com"` → `git.example.com`, `ROOT_URL = http://git.example.com/`, vhost = `["_", "git.example.com"]` - `gateway.enable = false` → `forge.domain` falls back to `localhost`, `ROOT_URL = http://localhost:3000/`, no gateway vhost (`behindGateway = false`) - `/etc/hosts` (when `localHostsEntry = true`) → unique entries for hive-domain + forge.domain (de-duped via `lib.unique` for the edge case where forge.domain = hive-domain) - full container toplevel builds clean ## PR title (Will fix the PR title separately — still says "/forge/" which is wrong since the rewrite to sub-domain shape.)
This commit is contained in:
parent
de67002c94
commit
9c27c4076f
2 changed files with 107 additions and 82 deletions
|
|
@ -9,26 +9,22 @@ let
|
||||||
gatewayCfg = config.services.hyperhive.gateway;
|
gatewayCfg = config.services.hyperhive.gateway;
|
||||||
hyperhiveDomain = config.services.hyperhive.domain;
|
hyperhiveDomain = config.services.hyperhive.domain;
|
||||||
|
|
||||||
# Sub-domain forgejo lives at when served behind the gateway (#749,
|
|
||||||
# mara verdict at issue:9609 — sub-domain over sub-path). Defaults
|
|
||||||
# to `forge.<hive-domain>`; set to `null` to opt out of subdomain
|
|
||||||
# routing (forge stays direct on `cfg.httpPort`). Empty string is
|
|
||||||
# rejected at assertion time.
|
|
||||||
subdomain =
|
|
||||||
if cfg.subdomain == null then null else "${cfg.subdomain}.${hyperhiveDomain}";
|
|
||||||
|
|
||||||
# ROOT_URL forgejo advertises in clone links + outbound URLs. When
|
# ROOT_URL forgejo advertises in clone links + outbound URLs. When
|
||||||
# served behind the gateway (#749), use the sub-domain so generated
|
# served behind the gateway (#749 — mara verdict at issue:9609,
|
||||||
# URLs resolve cleanly through the per-subdomain server-block. When
|
# sub-domain over sub-path), `cfg.domain` doubles as both the
|
||||||
# direct (gateway off, or operator nulled `cfg.subdomain`), keep the
|
# forgejo `DOMAIN` setting AND the gateway vhost server-name, so
|
||||||
# original port-3000 shape. Operators can override via `cfg.rootUrl`
|
# ROOT_URL just uses it directly (drops the port suffix when the
|
||||||
# for TLS / non-default gateway ports / bespoke sub-domains.
|
# gateway is on the canonical port 80). When direct (gateway off
|
||||||
|
# or `behindGateway = false`), keep the host:port shape so direct
|
||||||
|
# browser access on `:httpPort` still produces correct links.
|
||||||
|
# Operators can override via `cfg.rootUrl` for TLS / non-default
|
||||||
|
# gateway ports / bespoke shapes.
|
||||||
defaultRootUrl =
|
defaultRootUrl =
|
||||||
if subdomain != null && gatewayCfg.enable or false then
|
if cfg.behindGateway then
|
||||||
let
|
let
|
||||||
portSuffix = if gatewayCfg.port == 80 then "" else ":${toString gatewayCfg.port}";
|
portSuffix = if gatewayCfg.port == 80 then "" else ":${toString gatewayCfg.port}";
|
||||||
in
|
in
|
||||||
"http://${subdomain}${portSuffix}/"
|
"http://${cfg.domain}${portSuffix}/"
|
||||||
else
|
else
|
||||||
"http://${cfg.domain}:${toString cfg.httpPort}/";
|
"http://${cfg.domain}:${toString cfg.httpPort}/";
|
||||||
effectiveRootUrl = if cfg.rootUrl != null then cfg.rootUrl else defaultRootUrl;
|
effectiveRootUrl = if cfg.rootUrl != null then cfg.rootUrl else defaultRootUrl;
|
||||||
|
|
@ -86,13 +82,30 @@ in
|
||||||
|
|
||||||
domain = lib.mkOption {
|
domain = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
default = "localhost";
|
default = if hyperhiveDomain != null then "forge.${hyperhiveDomain}" else "localhost";
|
||||||
example = "forge.internal";
|
defaultText = lib.literalExpression ''
|
||||||
|
if services.hyperhive.domain != null then
|
||||||
|
"forge.''${services.hyperhive.domain}"
|
||||||
|
else
|
||||||
|
"localhost"
|
||||||
|
'';
|
||||||
|
example = "git.example.com";
|
||||||
description = ''
|
description = ''
|
||||||
Hostname used in repo clone URLs the forge advertises. The
|
Public hostname for the forge. Doubles as both the forgejo
|
||||||
container shares host netns so `localhost` works for any
|
`DOMAIN` setting (clone URLs forgejo advertises) AND the
|
||||||
agent on the same host; set a real hostname when you want
|
gateway vhost server-name when `behindGateway = true`
|
||||||
clones from outside the host to look canonical.
|
(#749, mara verdict at issue:9609 — sub-domain over sub-path).
|
||||||
|
|
||||||
|
Defaults to `forge.''${services.hyperhive.domain}` when the
|
||||||
|
hive-domain is set (idiomatic sub-domain shape — `forge`
|
||||||
|
labelled under the hive's bare domain), falling back to
|
||||||
|
`localhost` otherwise (pre-#749 direct-on-port behaviour).
|
||||||
|
|
||||||
|
Set to a full hostname (`git.example.com`,
|
||||||
|
`forge.internal.lan`, etc.) for a bespoke vhost shape — the
|
||||||
|
full domain goes here, no separate sub-domain-label option
|
||||||
|
(mara on #754:9684 — "specify full forge domain in options
|
||||||
|
instead").
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -111,29 +124,29 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
subdomain = lib.mkOption {
|
behindGateway = lib.mkOption {
|
||||||
type = lib.types.nullOr lib.types.str;
|
type = lib.types.bool;
|
||||||
default = "forge";
|
default = gatewayCfg.enable or false;
|
||||||
example = "git";
|
defaultText = lib.literalExpression "config.services.hyperhive.gateway.enable";
|
||||||
description = ''
|
description = ''
|
||||||
Sub-domain label for the gateway vhost that serves forgejo
|
Serve forgejo through the hive-gateway nginx as a sub-domain
|
||||||
(#749). The gateway adds a `server { server_name ''${subdomain}.''${hyperhive.domain}; }`
|
vhost (`server_name = cfg.domain`) instead of directly on
|
||||||
block that proxies all `/` → `http://127.0.0.1:''${httpPort}/`.
|
`httpPort` (#749, mara verdict at issue:9609 — sub-domain
|
||||||
Forgejo's `ROOT_URL` auto-flips to
|
over sub-path).
|
||||||
`http://''${subdomain}.''${hyperhive.domain}/` so clone-links +
|
|
||||||
asset references resolve cleanly through the sub-domain.
|
|
||||||
|
|
||||||
Defaults to `"forge"` (→ `forge.''${hyperhive.domain}`).
|
When `true`:
|
||||||
Set to `null` to opt out — forge stays direct on `httpPort`,
|
- The gateway adds a `server { server_name = ''${cfg.domain}; }`
|
||||||
no gateway vhost. Empty-string `""` is rejected (would render
|
block that proxies all `/` → `http://127.0.0.1:''${httpPort}/`.
|
||||||
`.''${hyperhive.domain}` — nginx treats that as a wildcard
|
- Forgejo's `ROOT_URL` flips to `http(s)://''${cfg.domain}/`
|
||||||
catch-all, not a bare-domain server block, so the behaviour
|
(sub-domain root, no port suffix when gateway is on 80).
|
||||||
is surprising; bare-domain landing is what the dashboard
|
- `gateway.localHostsEntry = true` extends `/etc/hosts` to
|
||||||
already serves anyway).
|
include `cfg.domain → 127.0.0.1` for local dev.
|
||||||
|
|
||||||
Requires `services.hyperhive.domain` to be set. Requires
|
Defaults to `services.hyperhive.gateway.enable` — flipping
|
||||||
`services.hyperhive.gateway.enable = true` for the vhost to
|
the gateway on/off auto-routes forge through it. Set `false`
|
||||||
actually exist.
|
explicitly to keep forge on the direct port even when the
|
||||||
|
gateway is running (e.g. an external git client that doesn't
|
||||||
|
traverse the gateway).
|
||||||
|
|
||||||
The mara-call on #749:9609 picks sub-domain over sub-path for
|
The mara-call on #749:9609 picks sub-domain over sub-path for
|
||||||
forge + matrix (both are external standard apps with sub-domain-
|
forge + matrix (both are external standard apps with sub-domain-
|
||||||
|
|
@ -149,17 +162,17 @@ in
|
||||||
example = "https://forge.example.com/";
|
example = "https://forge.example.com/";
|
||||||
description = ''
|
description = ''
|
||||||
Override the auto-derived forgejo `ROOT_URL`. When `null`
|
Override the auto-derived forgejo `ROOT_URL`. When `null`
|
||||||
(default), `ROOT_URL` is derived from `subdomain` + gateway
|
(default), `ROOT_URL` is derived from `cfg.domain` + gateway
|
||||||
state:
|
state:
|
||||||
|
|
||||||
- gateway on + `subdomain != null` → `http://<subdomain>.<hive>/`
|
- `behindGateway = true` → `http://''${cfg.domain}/` (uses
|
||||||
(uses `services.hyperhive.gateway.port` when non-80)
|
`services.hyperhive.gateway.port` when non-80)
|
||||||
- otherwise → `http://<domain>:<httpPort>/` (direct)
|
- `behindGateway = false` → `http://''${cfg.domain}:''${cfg.httpPort}/`
|
||||||
|
|
||||||
Set this to a fully-qualified URL when running behind TLS
|
Set this to a fully-qualified URL when running behind TLS
|
||||||
termination, a non-default gateway port, or a bespoke
|
termination (`https://...`), a non-default gateway port, or
|
||||||
sub-domain shape (e.g. `https://forge.example.com/`). Must
|
a bespoke shape. Must end with `/` per forgejo's `ROOT_URL`
|
||||||
end with `/` per forgejo's `ROOT_URL` contract.
|
contract.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -198,32 +211,35 @@ in
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
assertion =
|
# `cfg.domain` can't be empty — would render `.<hive>` shaped
|
||||||
cfg.subdomain == null
|
# garbage as both server_name (nginx wildcard catch-all) and
|
||||||
|| hyperhiveDomain != null
|
# /etc/hosts entry (invalid). Default rejects this case (lands
|
||||||
|| cfg.rootUrl != null;
|
# `"localhost"` when hive-domain is unset), but operator-set
|
||||||
|
# empty strings should fail loud.
|
||||||
|
assertion = cfg.domain != "";
|
||||||
message = ''
|
message = ''
|
||||||
services.hyperhive.forge.subdomain = "${toString cfg.subdomain}"
|
services.hyperhive.forge.domain = "" is rejected. The
|
||||||
requires services.hyperhive.domain to be set (sub-domain is
|
rendered URLs would be invalid (nginx wildcard catch-all
|
||||||
rendered as "<subdomain>.<hive-domain>"). Either set
|
for an empty server_name, /etc/hosts rejects empty entries).
|
||||||
services.hyperhive.domain, override services.hyperhive.forge.rootUrl
|
Either leave at default (auto-derives to
|
||||||
directly, or set services.hyperhive.forge.subdomain = null to opt
|
"forge.<services.hyperhive.domain>" when set, else
|
||||||
out of sub-domain routing.
|
"localhost"), or set a non-empty hostname like "forge.example.com"
|
||||||
|
or "git.internal".
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
# `subdomain = ""` would render `.<hive-domain>` as both the
|
# behindGateway requires the gateway module to actually be on.
|
||||||
# nginx server_name (treated as wildcard catch-all, surprising)
|
# Otherwise the configured `ROOT_URL` flips to a sub-domain
|
||||||
# and the /etc/hosts entry (invalid hostname). argus 🟡 on
|
# shape that has no nginx vhost backing it → broken on the
|
||||||
# #754 v2 — fail loud here rather than ship the surprising
|
# rebuild.
|
||||||
# behaviour.
|
assertion = !cfg.behindGateway || (gatewayCfg.enable or false);
|
||||||
assertion = cfg.subdomain != "";
|
|
||||||
message = ''
|
message = ''
|
||||||
services.hyperhive.forge.subdomain = "" is rejected: the
|
services.hyperhive.forge.behindGateway = true requires
|
||||||
rendered sub-domain ".<hive-domain>" is invalid (nginx
|
services.hyperhive.gateway.enable = true (the gateway vhost
|
||||||
treats it as a wildcard catch-all, /etc/hosts rejects it).
|
serving forge needs the gateway container to actually be
|
||||||
Use `null` to opt out of sub-domain routing entirely, or
|
running). Either turn the gateway on, or set
|
||||||
set a non-empty label like "forge" or "git".
|
services.hyperhive.forge.behindGateway = false to keep forge
|
||||||
|
on its direct port.
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -398,14 +398,20 @@ in
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
# Forge sub-domain vhost (#749, mara verdict at issue:9609 —
|
# Forge vhost (#749, mara verdict at issue:9609 —
|
||||||
# sub-domain over sub-path). When forgejo runs behind the
|
# sub-domain over sub-path). When forgejo runs behind the
|
||||||
# gateway, it gets its own `server { server_name ...; }`
|
# gateway (`forge.behindGateway = true`), it gets its own
|
||||||
# block keyed on `<forge.subdomain>.<hive-domain>`. The
|
# `server { server_name = forge.domain; }` block. The
|
||||||
# block proxies all `/` → `http://127.0.0.1:<forge.httpPort>/`
|
# block proxies all `/` → `http://127.0.0.1:<forge.httpPort>/`
|
||||||
# so forgejo handles requests at root (default deploy shape
|
# so forgejo handles requests at root (default deploy shape
|
||||||
# — no `ROOT_URL`-prefix translation needed).
|
# — no `ROOT_URL`-prefix translation needed).
|
||||||
#
|
#
|
||||||
|
# `forge.domain` is the full hostname (e.g.
|
||||||
|
# `forge.darkest.space`, `git.example.com`) — single source
|
||||||
|
# of truth for both the forgejo `DOMAIN` setting and the
|
||||||
|
# gateway vhost name (mara on #754:9684 — "specify full
|
||||||
|
# forge domain in options instead").
|
||||||
|
#
|
||||||
# `client_max_body_size 1G` — git pushes + LFS uploads can
|
# `client_max_body_size 1G` — git pushes + LFS uploads can
|
||||||
# be large; nginx's default 1M would 413 most real commits.
|
# be large; nginx's default 1M would 413 most real commits.
|
||||||
#
|
#
|
||||||
|
|
@ -416,13 +422,9 @@ in
|
||||||
# `proxyWebsockets = true` keeps forgejo's live-update
|
# `proxyWebsockets = true` keeps forgejo's live-update
|
||||||
# endpoints (`/api/v1/events`) + any future websocket
|
# endpoints (`/api/v1/events`) + any future websocket
|
||||||
# endpoints working transparently. SSH stays direct on
|
# endpoints working transparently. SSH stays direct on
|
||||||
# `cfg.sshPort` (separate listener protocol, not HTTP).
|
# `forge.sshPort` (separate listener protocol, not HTTP).
|
||||||
lib.optionalAttrs (
|
lib.optionalAttrs (forgeCfg.enable or false && forgeCfg.behindGateway or false) {
|
||||||
forgeCfg.enable or false
|
"${forgeCfg.domain}" = {
|
||||||
&& (forgeCfg.subdomain or null) != null
|
|
||||||
&& hyperhiveDomain != null
|
|
||||||
) {
|
|
||||||
"${forgeCfg.subdomain}.${hyperhiveDomain}" = {
|
|
||||||
listen = [
|
listen = [
|
||||||
{
|
{
|
||||||
addr = "0.0.0.0";
|
addr = "0.0.0.0";
|
||||||
|
|
@ -455,12 +457,19 @@ in
|
||||||
# Operators with real DNS leave `localHostsEntry = false`; this
|
# Operators with real DNS leave `localHostsEntry = false`; this
|
||||||
# is the dev-loop shortcut for `http://<hive-domain>/` +
|
# is the dev-loop shortcut for `http://<hive-domain>/` +
|
||||||
# `http://forge.<hive-domain>/` resolving locally.
|
# `http://forge.<hive-domain>/` resolving locally.
|
||||||
|
#
|
||||||
|
# Forge's `cfg.domain` may equal `hyperhiveDomain` (e.g. operator
|
||||||
|
# set `forge.domain = "darkest.space"` matching the hive domain)
|
||||||
|
# — `lib.unique` collapses the duplicate so `/etc/hosts` doesn't
|
||||||
|
# carry the same entry twice.
|
||||||
networking.hosts = lib.mkIf (cfg.localHostsEntry && hyperhiveDomain != null) {
|
networking.hosts = lib.mkIf (cfg.localHostsEntry && hyperhiveDomain != null) {
|
||||||
"127.0.0.1" = [ hyperhiveDomain ]
|
"127.0.0.1" = lib.unique (
|
||||||
|
[ hyperhiveDomain ]
|
||||||
++ lib.optional (
|
++ lib.optional (
|
||||||
(config.services.hyperhive.forge.enable or false)
|
(config.services.hyperhive.forge.enable or false)
|
||||||
&& (config.services.hyperhive.forge.subdomain or null) != null
|
&& (config.services.hyperhive.forge.behindGateway or false)
|
||||||
) "${config.services.hyperhive.forge.subdomain}.${hyperhiveDomain}";
|
) config.services.hyperhive.forge.domain
|
||||||
|
);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue