feat(v2/seo): richer per-page descriptions from top resources + creation dates

- Enrich each page's meta description with its top-ranked resource names
  (long-tail keywords) instead of a pure template — e.g. 'Top Kubernetes
  resources for 2026, AI-ranked: Helm, kube-prometheus and more — curated Cloud
  Native tools, guides and references.' URL/path-like and emoji-laden titles are
  filtered out; pages whose top links are all URL-like fall back to the clean
  template. Capped at ~160 chars on a word boundary.
- Enable git-revision creation dates (enable_creation_date: true) so every page
  footer shows both 'Created' and 'Last update' — an age/freshness signal.

Verified locally: descriptions are clean, tool-named, 126-151 chars; the footer
renders both dates. Pages pick up the new front-matter on the next Publisher run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Nubenetes Bot
2026-06-20 13:39:44 +02:00
parent bcbb3a2466
commit c377b4e4d9
2 changed files with 42 additions and 6 deletions

View File

@@ -1295,13 +1295,49 @@ class V2VisionEngine:
v1_link = f"/v1/{f_name.replace('.md', '/')}"
# Unique per-page SEO meta description (front-matter) so each page no
# longer falls back to the identical global site_description. Material
# emits it as <meta name="description"> + og:description.
# emits it as <meta name="description"> + og:description. Enrich it with
# the page's top resource names (long-tail keywords), filtering out
# URL/path-like titles for readability; fall back to a clean template.
def _collect_links(node, acc):
if "__links__" in node:
acc.extend(node["__links__"])
for _k, _v in node.items():
if _k != "__links__" and isinstance(_v, dict):
_collect_links(_v, acc)
return acc
_page_links = _collect_links(info["content"], [])
_page_links.sort(key=lambda x: (-(x.get("stars") or 0), -(int(x["year"]) if str(x.get("year", "")).isdigit() else 0)))
_names, _seen_names = [], set()
for _l in _page_links:
_nm = nuclear_strip(str(_l.get("title", "")))
# Drop emojis/stars and edge punctuation that leak into titles.
_nm = re.sub(r'[\U0001F000-\U0001FAFF☀-➿⭐⭕️]', '', _nm)
_nm = re.sub(r'\s+', ' ', _nm).strip(" -–—·:|")
_low = _nm.lower()
if not (3 <= len(_nm) <= 26):
continue
if "/" in _nm or "http" in _low or any(d in _low for d in (".com", ".io", ".org", ".net", ".dev", ".sh", ".ai", ".xyz")):
continue
if _low in _seen_names:
continue
_seen_names.add(_low)
_names.append(_nm)
if len(_names) >= 2:
break
_seo_title = str(info.get('title') or info['long_title'])[:60].strip()
_seo_desc = (
f"Curated, AI-ranked {_seo_title} resources for the 2026 Cloud Native "
f"architect: top-tier tools, guides and references ({info['dim']})."
)
if len(_names) >= 2:
_seo_desc = (
f"Top {_seo_title} resources for 2026, AI-ranked: {', '.join(_names)} and more "
f"— curated Cloud Native tools, guides and references."
)
else:
_seo_desc = (
f"Curated, AI-ranked {_seo_title} resources for the 2026 Cloud Native "
f"architect: top-tier tools, guides and references ({info['dim']})."
)
_seo_desc = _seo_desc.replace("\\", " ").replace('"', "'").replace("\n", " ").strip()
if len(_seo_desc) > 165:
_seo_desc = _seo_desc[:162].rsplit(" ", 1)[0].rstrip(" ,;:—-") + "."
md = (
f"---\n"
f"description: \"{_seo_desc}\"\n"