diff --git a/nix/modules/hive-forge.nix b/nix/modules/hive-forge.nix index cc99f7bc..80974157 100644 --- a/nix/modules/hive-forge.nix +++ b/nix/modules/hive-forge.nix @@ -34,6 +34,18 @@ let else "http://${cfg.domain}:${toString cfg.httpPort}/"; effectiveRootUrl = if cfg.rootUrl != null then cfg.rootUrl else defaultRootUrl; + + # When CI is enabled, the runner needs `actions/checkout` resolvable + # without external DNS (hive-ci shares the host netns, so a host-resolver + # blip otherwise reds every `actions/checkout@vN` fetch from + # data.forgejo.org — see #2072). Auto-append a pull-mirror of it and point + # forgejo's DEFAULT_ACTIONS_URL at this instance so `uses:` resolves local. + ciEnabled = config.services.hyperhive.forge.ci.enable; + actionCheckoutMirror = { + upstream = "https://github.com/actions/checkout"; + dest = "actions/checkout"; + }; + effectiveMirrors = cfg.mirrors ++ lib.optional ciEnabled actionCheckoutMirror; in { # Private Forgejo in a `hive-forge` nixos-container, shared host @@ -177,6 +189,49 @@ in config before rebuilding. ''; }; + + mirrors = lib.mkOption { + type = lib.types.listOf ( + lib.types.submodule { + options = { + upstream = lib.mkOption { + type = lib.types.str; + example = "https://github.com/actions/checkout"; + description = "Upstream clone URL to mirror from."; + }; + dest = lib.mkOption { + type = lib.types.str; + example = "actions/checkout"; + description = '' + Local `/` the pull-mirror is created at. The + `` org is auto-created if missing. Keep mirror dests + in their own orgs (e.g. `actions/*`) — separate from the + hive-c0re-managed namespaces (config/shared/agents/core) so + the seed never collides with core's own provisioning. + ''; + }; + }; + } + ); + default = [ ]; + example = lib.literalExpression '' + [ { upstream = "https://github.com/actions/checkout"; dest = "actions/checkout"; } ] + ''; + description = '' + General-purpose Forgejo **pull-mirrors** to auto-seed on the local + forge. Each entry is created as a real Forgejo pull-mirror (it + re-syncs from `upstream` out-of-band), not a one-off pushed clone — + so a host-resolver blip leaves a *stale* mirror, never a hard + failure on whatever reads it. + + When `services.hyperhive.forge.ci.enable` is set, an + `actions/checkout` mirror is auto-appended to this list and + forgejo's `DEFAULT_ACTIONS_URL` is pointed at this instance, so CI + `uses: actions/checkout@vN` steps resolve entirely on loopback with + no external DNS on the critical path (the seed/re-sync needs + external DNS, but that's off the CI path). See `#2072`. + ''; + }; }; config = lib.mkIf config.services.hyperhive.enable { @@ -302,6 +357,13 @@ in # of token scopes. Required by `hive-ci-register.service` # in the hive-ci container. actions.ENABLED = true; + # When CI is enabled, resolve `uses: /@vN` from + # THIS instance (the seeded `actions/checkout` pull-mirror) + # instead of the upstream default `data.forgejo.org` — keeps + # the checkout step on loopback, immune to a host-resolver + # blip (#2072). `self` = forgejo expands actions against its + # own ROOT_URL. + actions.DEFAULT_ACTIONS_URL = lib.mkIf ciEnabled "self"; # F3 (federation) computes its data dir relative to the # forgejo binary, which lands in the read-only nix # store and crashes anything that touches the F3