67b3fd23d1
gates / gates (push) Failing after 13m1s
Part A: claim table documentation/audits/website-refresh-2026-10-08/claims.md; "100% open source" corrected from the licence read; unbacked claims cut (firewall, RAID, snapshots, new-machine restore, self-managed mode, household VPN, "never lost"). Part B: dawarich, docmost, grimmory, homebox, karakeep, mealie, metube, radicale, recipe-importer, sparkyfitness cards; plant-it and wger cut (not offered). Part C: /en/ twins, nav language switch, hreflang both ways, sitemap with xhtml:link, og-image-en.png. Part D: site_gates.py twins/lang/same-apps/no-Hungarian/FAQ-JSON-LD/tail. Operator ruling 9 + choice B in 10-localisation.md §11, §10.7. Register: R-902 (contact mailer source in no repo); R-813, R-784, R-559 annotated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
121 lines
4.9 KiB
Python
121 lines
4.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Shared pieces of the website refresh 2026-10-08: the page map, the two navs, hreflang, file IO."""
|
|
import io, os, re, subprocess
|
|
|
|
REPO = "/mnt/5_hdd/felhom.eu/git/felhom.eu"
|
|
W = os.path.join(REPO, "website")
|
|
SITE = "https://felhom.eu"
|
|
|
|
# hu file, hu url, en file, en url, hu nav href that is "active", en nav href that is "active"
|
|
PAIRS = [
|
|
("index.html", "/", "en/index.html", "/en/", None, None),
|
|
("alkalmazasok.html", "/alkalmazasok", "en/apps.html", "/en/apps", "/alkalmazasok", "/en/apps"),
|
|
("technologiak.html", "/technologiak", "en/technology.html", "/en/technology", "/technologiak", "/en/technology"),
|
|
("biztonsagimentes.html", "/biztonsagimentes", "en/backups.html", "/en/backups", "/biztonsagimentes", "/en/backups"),
|
|
("gyik.html", "/gyik", "en/faq.html", "/en/faq", "/gyik", "/en/faq"),
|
|
("kapcsolat.html", "/kapcsolat", "en/contact.html", "/en/contact", "/kapcsolat", "/en/contact"),
|
|
("letoltes.html", "/letoltes", "en/download.html", "/en/download", None, None),
|
|
]
|
|
|
|
HU_ITEMS = [("/#szolgaltatasok", "Szolgáltatások"), ("/alkalmazasok", "Alkalmazások"), ("/technologiak", "Technológiák"),
|
|
("/biztonsagimentes", "Mentések"), ("/gyik", "GYIK"), ("/kapcsolat", "Kapcsolat")]
|
|
EN_ITEMS = [("/en/#services", "Services"), ("/en/apps", "Apps"), ("/en/technology", "Technology"),
|
|
("/en/backups", "Backups"), ("/en/faq", "FAQ"), ("/en/contact", "Contact")]
|
|
|
|
|
|
def nav(lang, active, twin_url):
|
|
items = HU_ITEMS if lang == "hu" else EN_ITEMS
|
|
lis = []
|
|
for href, label in items:
|
|
act = ' class="active"' if href == active else ""
|
|
lis.append(' <li><a href="%s"%s>%s</a></li>' % (href, act, label))
|
|
if lang == "hu":
|
|
lis.append(' <li><a href="%s" lang="en" hreflang="en" class="lang-switch">English</a></li>' % twin_url)
|
|
else:
|
|
lis.append(' <li><a href="%s" lang="hu" hreflang="hu" class="lang-switch">Magyar</a></li>' % twin_url)
|
|
home = "/" if lang == "hu" else "/en/"
|
|
open_l = "Menü megnyitása" if lang == "hu" else "Open menu"
|
|
return (""" <nav>
|
|
<div class="container">
|
|
<a href="%s" class="logo">
|
|
<img src="/assets/logo.png" alt="Felhom.eu logo">
|
|
<span class="logo-text">felhom.eu</span>
|
|
</a>
|
|
<ul class="nav-links">
|
|
%s
|
|
</ul>
|
|
<button class="hamburger" id="hamburger" aria-label="%s" aria-expanded="false">
|
|
<span class="hamburger-box">
|
|
<span class="hamburger-line"></span>
|
|
<span class="hamburger-line"></span>
|
|
<span class="hamburger-line"></span>
|
|
</span>
|
|
</button>
|
|
</div>
|
|
</nav>""" % (home, "\n".join(lis), open_l))
|
|
|
|
|
|
FOOTER_HU = """ <footer>
|
|
<div class="container">
|
|
<p>© 2026 felhom.eu — Saját felhőd, saját szabályaid</p>
|
|
</div>
|
|
</footer>"""
|
|
FOOTER_EN = """ <footer>
|
|
<div class="container">
|
|
<p>© 2026 felhom.eu — Your own cloud, your own rules. Felhom serves households in Hungary.</p>
|
|
</div>
|
|
</footer>"""
|
|
|
|
|
|
def alternates(hu_url, en_url):
|
|
return (' <link rel="alternate" hreflang="hu" href="%s%s">\n'
|
|
' <link rel="alternate" hreflang="en" href="%s%s">\n'
|
|
' <link rel="alternate" hreflang="x-default" href="%s%s">') % (SITE, hu_url, SITE, en_url, SITE, hu_url)
|
|
|
|
|
|
def head_text(path):
|
|
"""The file as it is at HEAD (the source every build starts from, so a re-run is idempotent)."""
|
|
rel = os.path.relpath(os.path.join(W, path), REPO)
|
|
out = subprocess.run(["git", "-C", REPO, "show", "HEAD:" + rel], capture_output=True)
|
|
if out.returncode != 0:
|
|
return None
|
|
return out.stdout.decode("utf-8-sig")
|
|
|
|
|
|
def read(path):
|
|
return io.open(os.path.join(W, path), encoding="utf-8-sig").read()
|
|
|
|
|
|
def write(path, text):
|
|
full = os.path.join(W, path)
|
|
os.makedirs(os.path.dirname(full), exist_ok=True)
|
|
with io.open(full, "w", encoding="utf-8-sig", newline="\n") as f:
|
|
f.write(text)
|
|
|
|
|
|
def sub1(s, old, new, count=1):
|
|
n = s.count(old)
|
|
if n != count:
|
|
raise SystemExit("expected %d x %r, found %d" % (count, old[:90], n))
|
|
return s.replace(old, new)
|
|
|
|
|
|
def set_nav(s, lang, active, twin_url):
|
|
s2, n = re.subn(r" <nav>.*?</nav>", lambda m: nav(lang, active, twin_url), s, count=1, flags=re.S)
|
|
assert n == 1
|
|
return s2
|
|
|
|
|
|
def set_footer(s, lang):
|
|
s2, n = re.subn(r" <footer>.*?</footer>", lambda m: FOOTER_HU if lang == "hu" else FOOTER_EN, s, count=1, flags=re.S)
|
|
assert n == 1
|
|
return s2
|
|
|
|
|
|
def set_alternates(s, hu_url, en_url):
|
|
"""Insert (or replace) the three hreflang links right after the canonical link."""
|
|
s = re.sub(r' <link rel="alternate" hreflang="[^"]+" href="[^"]+">\n', "", s)
|
|
m = re.search(r' <link rel="canonical" href="[^"]+">\n', s)
|
|
assert m, "no canonical"
|
|
return s[:m.end()] + alternates(hu_url, en_url) + "\n" + s[m.end():]
|