markemacht/_adametz.media/scripts/build-site.py
Kevin Adametz 9c6b7ed4f3
Some checks are pending
linter / quality (push) Waiting to run
tests / ci (8.3) (push) Waiting to run
tests / ci (8.4) (push) Waiting to run
tests / ci (8.5) (push) Waiting to run
Optimierung markemacht.de und adametz.media für Live-Deploy.
Beide Sites mit lokalem Font-Hosting, WebP, Build-Pipeline, SEO-Basis,
HSTS, Performance-Tuning und aktualisierten Impressum/Datenschutz-Texten.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-09 09:55:25 +00:00

135 lines
4.2 KiB
Python
Executable file

#!/usr/bin/env python3
"""Synchronisiert head-common, Header und Footer in alle HTML-Seiten."""
from __future__ import annotations
import re
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
VERSION = (ROOT / 'assets' / 'version.txt').read_text().strip()
HEAD_COMMON = ROOT / 'assets' / 'components' / 'head-common.html'
HEADER_INDEX = ROOT / 'assets' / 'components' / 'header-index.html'
HEADER_SUB = ROOT / 'assets' / 'components' / 'header-sub.html'
FOOTER = ROOT / 'assets' / 'components' / 'footer.html'
HEAD_MOUNT = re.compile(r' <!-- data-site-head -->\n')
HEAD_BLOCK = re.compile(
r' <meta property="og:locale" content="de_DE" />.*?"name": "ADAMETZ\.MEDIA"\n \}\n \}\n </script>\n',
re.DOTALL,
)
HEADER_MOUNT = re.compile(r' <div data-site-header></div>\n')
FOOTER_MOUNT = re.compile(r' <div data-site-footer></div>\n')
STYLESHEET = re.compile(
r'<link rel="stylesheet" href="assets/styles\.css(?:\?v=[^"]*)?" />'
)
APP_JS = re.compile(
r'<script src="assets/js/app\.js(?:\?v=[^"]*)?" defer></script>'
)
HERO_IMG = re.compile(
r'<img\s+src="assets/img/hero-bg2\.webp"[^>]*>',
re.DOTALL,
)
HERO_IMG_REPLACEMENT = '''<img
src="assets/img/hero-bg2.webp"
srcset="assets/img/hero-bg2-640.webp 640w, assets/img/hero-bg2-960.webp 960w, assets/img/hero-bg2.webp 1920w"
sizes="100vw"
alt="Abstrakte architektonische Struktur"
width="1920"
height="1080"
loading="eager"
fetchpriority="high"
decoding="async"
/>'''
INDEX_PAGES = {'index.html'}
def indent_block(text: str, prefix: str = ' ') -> str:
lines = text.strip('\n').splitlines()
return '\n'.join(prefix + line if line.strip() else '' for line in lines) + '\n'
def build_head() -> str:
content = HEAD_COMMON.read_text()
content = content.replace('{{VERSION}}', VERSION)
return indent_block(content)
def build_header(path: Path) -> str:
if path.name in INDEX_PAGES:
return indent_block(HEADER_INDEX.read_text())
return indent_block(HEADER_SUB.read_text())
def process_html(path: Path) -> bool:
text = path.read_text()
if '<!-- data-site-head -->' not in text and 'og:locale' not in text:
return False
original = text
if HEAD_MOUNT.search(text):
text = HEAD_MOUNT.sub(build_head(), text, count=1)
elif 'og:locale' in text:
text = HEAD_BLOCK.sub(build_head(), text, count=1)
if HEADER_MOUNT.search(text):
text = HEADER_MOUNT.sub(build_header(path), text, count=1)
if FOOTER_MOUNT.search(text):
footer = indent_block(FOOTER.read_text())
text = FOOTER_MOUNT.sub(footer, text, count=1)
text = STYLESHEET.sub(
f'<link rel="stylesheet" href="assets/styles.css?v={VERSION}" />',
text,
)
text = APP_JS.sub(
f'<script src="assets/js/app.js?v={VERSION}" defer></script>',
text,
)
if path.name == 'index.html':
text = HERO_IMG.sub(HERO_IMG_REPLACEMENT, text, count=1)
preload = (
' <link rel="preload" as="image" href="assets/img/hero-bg2-960.webp" '
'type="image/webp" fetchpriority="high" '
'imagesrcset="assets/img/hero-bg2-640.webp 640w, '
'assets/img/hero-bg2-960.webp 960w, assets/img/hero-bg2.webp 1920w" '
'imagesizes="100vw" />\n'
)
for _ in range(3):
text = re.sub(
r' <link rel="preload" as="image" href="assets/img/hero-bg2[^"]*"[^/]*/>\n',
'',
text,
)
manifest_line = ' <link rel="manifest" href="manifest.json" />\n'
if preload.strip() not in text and manifest_line in text:
text = text.replace(manifest_line, manifest_line + preload, 1)
if text != original:
path.write_text(text)
return True
return False
def main() -> None:
updated = []
for path in sorted(ROOT.glob('*.html')):
if process_html(path):
updated.append(path.name)
print(f'Version: {VERSION}')
for name in updated:
print(f' aktualisiert: {name}')
if not updated:
print(' keine Änderungen')
if __name__ == '__main__':
main()