Split the public banner URL from the runner-internal clone URL
tests / pytest (push) Successful in 7s
tests / pytest (push) Successful in 7s
Every auto-published page, in all three pipelines, carried a source link to
`localhost:3005`. The template was never at fault — it links `{{{source}}}`
verbatim; the bad URL was baked into each page's wikitext by the publisher.
Root cause was one variable serving two incompatible purposes. `URL_TO_GITEA`
has to be an address the runner container can clone from, and since the runner
uses host networking that is `http://localhost:3005`. All three workflows then
composed `GITEA_REPO_URL` — the base of the public banner link, written into
the wiki for anyone to read — out of that same loopback value.
Adds a second org variable, `PUBLIC_URL_TO_GITEA`, for the public base
(`https://git.novoyuuparosk.org`), and points the banner at it. Cloning keeps
using `URL_TO_GITEA`. The two are now documented as deliberately separate,
because collapsing them looks like tidying up.
`lib/wiki.public_source_base()` validates the banner base rather than trusting
it: unset, relative, loopback, mDNS (`*.local`) or private-range values raise
with the variable to set. A misconfiguration that would publish an unreachable
link now stops the run instead. Note the failure mode this creates on purpose:
until `PUBLIC_URL_TO_GITEA` exists, publishing aborts rather than proceeding
with a wrong URL.
Also builds the source path with `rel.as_posix()`, so the separator in a URL
does not depend on the OS the publisher runs on — it produced backslashes when
the renderer was exercised on Windows.
Prerequisites before this can publish:
1. Add the org variable `PUBLIC_URL_TO_GITEA` = https://git.novoyuuparosk.org
2. Flush the act_runner callee cache — `docker compose up -d --force-recreate
act_runner`. Per the cross-cutting decisions log, edits to `publish-*.yml`
keep executing the stale cached copy, and this change is in those files.
Pages already published still carry the old URL in their stored wikitext. They
are only corrected by re-publishing each pipeline in full.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+58
@@ -1,6 +1,7 @@
|
||||
"""Shared utilities for novoyuuparosk-auto-wiki pipelines."""
|
||||
|
||||
import html
|
||||
import ipaddress
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
@@ -199,6 +200,63 @@ def markdown_to_wikitext(body: str) -> str:
|
||||
return expand_columns(result.stdout)
|
||||
|
||||
|
||||
_LOOPBACK_HOSTNAMES = frozenset({"localhost", "localhost.localdomain", "ip6-localhost"})
|
||||
|
||||
|
||||
def public_source_base(env_var: str = "GITEA_REPO_URL") -> str:
|
||||
"""The public base URL for the source links baked into published pages.
|
||||
|
||||
This URL ends up in the `{{Auto-generated}}` banner of every page, so it is
|
||||
read by anyone on the internet — it is *not* the address the runner clones
|
||||
from. Those two are different hosts here: the runner container uses host
|
||||
networking and reaches Gitea on `localhost:3005`, while a reader needs the
|
||||
public name. Composing the banner from the clone address published pages
|
||||
citing `localhost:3005`, which is why this is validated rather than trusted.
|
||||
|
||||
Rejects an address that cannot possibly work for a reader — unset, missing a
|
||||
scheme or host, loopback, mDNS (`*.local`), or a private IP range — so the
|
||||
misconfiguration fails the run instead of being written to the wiki.
|
||||
"""
|
||||
raw = os.environ.get(env_var, "").strip().rstrip("/")
|
||||
if not raw:
|
||||
raise RuntimeError(
|
||||
f"{env_var} is unset. It must be the *public* Gitea base URL plus the "
|
||||
f"repository path (e.g. https://git.example.org/org/repo); the workflow "
|
||||
f"composes it from the PUBLIC_URL_TO_GITEA Gitea variable."
|
||||
)
|
||||
|
||||
parsed = urlparse(raw)
|
||||
if not parsed.scheme or not parsed.netloc:
|
||||
raise RuntimeError(
|
||||
f"{env_var} is not an absolute URL: {raw!r}. Expected scheme and host, "
|
||||
f"e.g. https://git.example.org/org/repo."
|
||||
)
|
||||
|
||||
host = (parsed.hostname or "").lower()
|
||||
reason = None
|
||||
if host in _LOOPBACK_HOSTNAMES:
|
||||
reason = "a loopback hostname"
|
||||
elif host.endswith(".local"):
|
||||
reason = "an mDNS name, resolvable only on the local network"
|
||||
else:
|
||||
try:
|
||||
address = ipaddress.ip_address(host)
|
||||
except ValueError:
|
||||
address = None
|
||||
if address is not None and (address.is_loopback or address.is_private):
|
||||
reason = "a loopback or private-range IP address"
|
||||
|
||||
if reason:
|
||||
raise RuntimeError(
|
||||
f"{env_var} points at {reason}: {raw!r}. This URL is published in the "
|
||||
f"page banner and must resolve for a reader off the network. Set the "
|
||||
f"PUBLIC_URL_TO_GITEA Gitea variable to the public Gitea base URL — "
|
||||
f"URL_TO_GITEA is the runner-internal clone address and is not usable here."
|
||||
)
|
||||
|
||||
return raw
|
||||
|
||||
|
||||
def connect_wiki() -> mwclient.Site:
|
||||
api_url = os.environ["WIKI_API_URL"]
|
||||
parsed = urlparse(api_url)
|
||||
|
||||
Reference in New Issue
Block a user