Files
mop-skills/skills/create-website/scripts/publish_website.py
T

111 lines
3.9 KiB
Python
Executable File

#!/usr/bin/env python3
# /// script
# dependencies = [
# "boto3>=1.34.0",
# ]
# ///
"""
Publish single-page websites to hosting.haumdaucher.de with validation and clean URL reporting.
Integrates with the existing 'hosting' skill publisher.
"""
import argparse
import os
import re
import subprocess
import sys
from pathlib import Path
def validate_html(content: str) -> list[str]:
"""Basic pre-flight checks to ensure the generated website isn't malformed."""
warnings = []
content_lower = content.lower()
if "<!doctype html>" not in content_lower:
warnings.append("Missing <!DOCTYPE html> declaration.")
if "viewport" not in content_lower:
warnings.append("Missing responsive viewport meta tag.")
if "cdn.tailwindcss.com" not in content_lower and "tailwindcss" not in content_lower:
warnings.append("Tailwind CSS CDN not detected in HTML.")
if "alpine" not in content_lower:
warnings.append("Alpine.js script not detected (interactivity might be missing).")
if "lucide" in content_lower and "lucide.createicons" not in content_lower:
warnings.append("Lucide icons included, but 'lucide.createIcons()' initialization is missing.")
return warnings
def resolve_hosting_script() -> Path:
"""Finds the publish_file.py script relative to this script or in standard locations."""
current_dir = Path(__file__).resolve().parent
# Standard relative location in mop-skills
sibling_hosting = current_dir.parent.parent / "hosting" / "scripts" / "publish_file.py"
if sibling_hosting.exists():
return sibling_hosting
# Fallback in container
pod_hosting = Path("/home/node/mop-skills/mop-skills/skills/hosting/scripts/publish_file.py")
if pod_hosting.exists():
return pod_hosting
raise FileNotFoundError(f"Cannot find publish_file.py in {sibling_hosting} or {pod_hosting}")
def main():
parser = argparse.ArgumentParser(description="Publish websites to hosting.haumdaucher.de")
parser.add_argument("--file", "-f", help="Local HTML file to upload")
parser.add_argument("--content", "-c", help="Inline HTML content string")
parser.add_argument("--slug", "-s", required=True, help="Slug identifier for URL (e.g. creami-calculator)")
parser.add_argument("--category", default="showcase", help="Category folder (default: showcase)")
parser.add_argument("--skip-validation", action="store_true", help="Skip HTML quality checks")
parser.add_argument("--url-only", action="store_true", help="Print only public URL")
args = parser.parse_args()
if not args.file and args.content is None:
parser.error("You must provide either --file or --content.")
# Read content for validation
html_content = ""
if args.file:
file_path = Path(args.file)
if not file_path.exists():
sys.exit(f"Error: File not found: {args.file}")
html_content = file_path.read_text(encoding="utf-8", errors="replace")
else:
html_content = args.content
# Quality validation
if not args.skip_validation:
warnings = validate_html(html_content)
if warnings:
print("⚠️ Pre-flight HTML Warnings:", file=sys.stderr)
for w in warnings:
print(f" - {w}", file=sys.stderr)
# Delegate to hosting/scripts/publish_file.py
publisher_script = resolve_hosting_script()
cmd = [
sys.executable,
str(publisher_script),
"--category", args.category,
"--slug", args.slug,
"--ext", "html",
]
if args.file:
cmd.extend(["--file", str(args.file)])
else:
cmd.extend(["--content", args.content])
if args.url_only:
cmd.append("--url-only")
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
sys.stderr.write(result.stderr)
sys.exit(result.returncode)
print(result.stdout.strip())
if __name__ == "__main__":
main()