Make Gitea canonical; derive the changelog URL from the remote instead of hard-coding GitHub

This commit is contained in:
2026-07-13 17:21:30 +00:00
parent 8a82bde531
commit 4ced730d65
12 changed files with 1489 additions and 19 deletions
+37 -9
View File
@@ -43,7 +43,16 @@ DISABLED_MARKER = os.path.join(PATCH_DIR, "update_alerts_disabled")
_TAG_RE = re.compile(r"^v\d+\.\d+\.\d+$")
_VERSION_RE = re.compile(r'^VERSION="([^"]+)"', re.M)
_GITHUB_RE = re.compile(r"github\.com[:/]([^/]+)/([^/.]+)")
#: owner/repo out of any of:
#: git@github.com:sudolulo/repo.git
#: https://github.com/sudolulo/repo.git
#: ssh://git@git.onetick.ninja:55214/flan/repo.git
#: https://git.onetick.ninja/flan/repo.git
#: The SSH port is deliberately not captured: it is not the web port.
_REMOTE_RE = re.compile(
r"^(?:\w+://)?(?:[^@/]+@)?([^:/]+)(?::\d+)?[:/]([^/]+)/([^/]+?)(?:\.git)?/?$"
)
_TIMEOUT = 20
@@ -206,21 +215,40 @@ class TrueCloudPatchUpdateAlertSource(ThreadedAlertSource):
detail = ", ".join(ordered)
return level, versions, f" Changes: {detail}." if detail else ""
def _remote_changelog(self, tag):
"""CHANGELOG.md at `tag`, over HTTPS. None if it cannot be read."""
def _changelog_url(self, tag):
"""Where to read CHANGELOG.md at `tag`, derived from the origin remote.
Forge-agnostic on purpose. This project is canonically hosted on Gitea and
mirrored to GitHub, and hard-coding either one has a nastier failure than it
looks: when the changelog cannot be read, _classify() falls back to
"notable" and alerts ANYWAY, because the alternative is silently hiding a
security fix. So a stale URL does not disable the alert -- it makes the
alert fire on every release including documentation-only ones, which is
precisely the nagging this whole mechanism exists to prevent.
"""
try:
remote = self._git("remote", "get-url", "origin").strip()
except Exception:
return None
m = _GITHUB_RE.search(remote)
m = _REMOTE_RE.match(remote)
if not m:
return None # not a GitHub remote; skip classification
return None
url = (
f"https://raw.githubusercontent.com/{m.group(1)}/{m.group(2)}/"
f"{tag}/CHANGELOG.md"
)
host, owner, repo = m.group(1), m.group(2), m.group(3)
if host.endswith("github.com"):
return f"https://raw.githubusercontent.com/{owner}/{repo}/{tag}/CHANGELOG.md"
# Gitea and Forgejo both serve /{owner}/{repo}/raw/tag/{tag}/{path} over the
# web port, which is not the SSH port the remote may name.
return f"https://{host}/{owner}/{repo}/raw/tag/{tag}/CHANGELOG.md"
def _remote_changelog(self, tag):
"""CHANGELOG.md at `tag`, over HTTPS. None if it cannot be read."""
url = self._changelog_url(tag)
if not url:
return None
try:
with urllib.request.urlopen(url, timeout=_TIMEOUT) as resp: # noqa: S310
if resp.status != 200:
+77 -1
View File
@@ -204,7 +204,67 @@ else
_NESTED_ENABLED=0
fi
# Is either module still doing something useful?
# ── compatibility preflight ──────────────────────────────────────────────────
#
# The native probes above ask "has iX made this module unnecessary?". This asks the
# other question, the dangerous one: "has iX changed middleware so that this module
# no longer WORKS?"
#
# middlewared is internal API with no stability contract, and TrueNAS 26 rewrites
# the entire cloud_backup path from async to synchronous. Every block the nested
# module injects is an `async def` wrapping an `await`ed original; on 26 that hands
# sync.py a coroutine where it unpacks a tuple. The backup does not fail cleanly --
# it fails at the point where you needed it.
#
# tools/compat.py records what each module assumes and checks it against the
# middlewared ACTUALLY INSTALLED HERE. A module whose assumptions no longer hold is
# not applied. Stock TrueNAS without a feature beats TrueNAS with a broken one.
#
# Fail direction, deliberately asymmetric:
# * a definite "assumption violated" -> disable that module. Strong evidence.
# * the checker cannot run at all -> change nothing. That is a tooling glitch,
# not evidence, and turning it into a disabled module would break working boxes.
_TC_COMPAT_JSON="$PATCH_DIR/incompatible.json"
rm -f "$_TC_COMPAT_JSON"
_tc_compat=$("$PYTHON" - "$PATCH_DIR" "$_MW_DIR" "$_TC_COMPAT_JSON" <<'PYEOF' 2>/dev/null || printf 'unknown\nunknown\n')
import json, os, sys
patch_dir, mw_dir, out_path = sys.argv[1], sys.argv[2], sys.argv[3]
# APPEND, never insert(0) -- see the note by the mw_patch import below. Shadowing
# the stdlib for this interpreter is a much worse failure than not finding compat.
sys.path.append(os.path.join(patch_dir, 'tools'))
try:
import compat
result = compat.check_tree(mw_dir)
except Exception:
print('unknown')
print('unknown')
raise SystemExit(0)
def verdict(r):
return 'broken' if (not r['ok'] and not r['native']) else 'ok'
broken = {m: r for m, r in result.items() if verdict(r) == 'broken'}
if broken:
# The alert source reads this. Written before we print, so a box that is
# incompatible always has the evidence on disk even if apply.sh dies later.
try:
with open(out_path, 'w', encoding='utf-8') as fh:
json.dump(broken, fh, indent=2)
except OSError:
pass
print(verdict(result['providers']))
print(verdict(result['nested']))
PYEOF
)
_tc_compat_providers=$(printf '%s' "$_tc_compat" | sed -n '1p')
_tc_compat_nested=$(printf '%s' "$_tc_compat" | sed -n '2p')
# Is either module still doing something useful -- and can it still be applied?
_providers_needed=1
[ "$_tc_native_b2" = "yes" ] && _providers_needed=0
@@ -213,6 +273,22 @@ if [ "$_NESTED_ENABLED" = "1" ] && [ "$_tc_native_nested" != "yes" ]; then
_nested_needed=1
fi
if [ "$_tc_compat_providers" = "broken" ]; then
echo "WARNING: truecloud-patch is NOT COMPATIBLE with this TrueNAS version."
echo "WARNING: The B2/S3 providers module will NOT be applied. TrueCloud is"
echo "WARNING: left stock, so B2/S3 tasks will not run until this is fixed."
echo "WARNING: Details: $_TC_COMPAT_JSON"
_providers_needed=0
fi
if [ "$_tc_compat_nested" = "broken" ] && [ "$_NESTED_ENABLED" = "1" ]; then
echo "WARNING: truecloud-patch's nested-snapshot module is NOT COMPATIBLE with"
echo "WARNING: this TrueNAS version and will NOT be applied. Backups still"
echo "WARNING: run; datasets nested under the target are not included."
echo "WARNING: Details: $_TC_COMPAT_JSON"
_nested_needed=0
fi
if [ "$_providers_needed" = "0" ] && [ "$_nested_needed" = "0" ]; then
echo "NOTICE: Nothing left for truecloud-patch to do:"
[ "$_tc_native_b2" = "yes" ] && echo "NOTICE: - TrueNAS now provides native B2 restic support."
+3 -3
View File
@@ -111,7 +111,7 @@ def main():
print(
"[truecloud-patch] WARNING: filterByProviders pattern not found in any JS bundle.\n"
"[truecloud-patch] The TrueNAS webui may have been restructured in this version.\n"
"[truecloud-patch] File an issue at https://github.com/sudolulo/truenas-truecloud-patch\n"
"[truecloud-patch] File an issue at https://git.onetick.ninja/flan/truenas-truecloud-patch\n"
f"[truecloud-patch] TrueNAS version info: {_tnversion()}"
)
return
@@ -136,7 +136,7 @@ def main():
f"[truecloud-patch] WARNING: {count} replacement(s) in {path}; "
f"expected exactly 1 — skipping write to avoid corrupting the bundle.\n"
f"[truecloud-patch] File an issue at "
f"https://github.com/sudolulo/truenas-truecloud-patch"
f"https://git.onetick.ninja/flan/truenas-truecloud-patch"
)
return
@@ -154,7 +154,7 @@ def main():
"[truecloud-patch] The UI is UNCHANGED and still works. This means the "
"pattern no longer fits this TrueNAS build.\n"
"[truecloud-patch] File an issue at "
"https://github.com/sudolulo/truenas-truecloud-patch"
"https://git.onetick.ninja/flan/truenas-truecloud-patch"
)
return