Files
site-publish/scripts/utils.py
T
2026-05-06 08:07:28 -05:00

141 lines
4.3 KiB
Python

"""Shared utilities for the publish-site action."""
import os
import shutil
import subprocess
import sys
from pathlib import Path
import yaml
from jinja2 import Environment, FileSystemLoader
APPS_REPO = "fritzlab/apps"
GITEA_HOST = "code.fritzlab.net"
NAMESPACE = "websites"
DEFAULT_S3_ENDPOINT = "http://garage.storage.svc:3900"
EXCLUDE_FILES = {
".git", ".gitea", ".gitignore", "site.yaml",
"build", "Makefile", "README.md", "CLAUDE.md",
"Dockerfile", ".dockerignore", "go.mod", "go.sum",
}
VALID_TYPES = {"static", "hugo", "mkdocs", "docker"}
def k8s_name(name):
"""Sanitize for DNS-1035 label (dots → dashes)."""
return name.replace(".", "-")
def env(key, default=None):
val = os.environ.get(key, default)
if val is None:
die(f"Missing required env var: {key}")
return val
def die(msg):
print(f"ERROR: {msg}", file=sys.stderr)
sys.exit(1)
def run(cmd, **kwargs):
print(f" $ {cmd}")
return subprocess.run(cmd, shell=True, check=True, **kwargs)
def parse_site_yaml(site_dir):
path = Path(site_dir) / "site.yaml"
if not path.exists():
die("site.yaml not found in repo root")
with open(path) as f:
cfg = yaml.safe_load(f)
if not cfg.get("domain"):
die("domain is required in site.yaml")
site_type = cfg.get("type", "static")
if site_type not in VALID_TYPES:
die(f"Unknown site type: {site_type} (valid: {', '.join(sorted(VALID_TYPES))})")
site = {
"domain": cfg["domain"],
"type": site_type,
"enabled": cfg.get("enabled", True),
"aliases": cfg.get("aliases") or [],
}
if site_type == "docker":
if not cfg.get("image"):
die("image is required in site.yaml for type: docker")
site["image"] = cfg["image"]
site["port"] = cfg.get("port", 8080)
site["build_args"] = cfg.get("build_args") or {}
site["health_path"] = cfg.get("health_path", "/healthz")
site["replicas"] = cfg.get("replicas", 1)
else:
site["content_dir"] = cfg.get("content_dir", "")
site["tidy"] = cfg.get("tidy", True)
print("Site config:")
for k, v in site.items():
print(f" {k}: {v}")
return site
def clone_apps(token):
user = env("CI_BOT_USER", "ci-bot")
apps_dir = Path("/tmp/apps-deploy")
if apps_dir.exists():
shutil.rmtree(apps_dir)
run(f"git clone --depth 1 https://{user}:{token}@{GITEA_HOST}/{APPS_REPO}.git {apps_dir}")
run(f"git -C {apps_dir} config user.name {user}")
run(f"git -C {apps_dir} config user.email {user}@fritzlab.net")
return apps_dir
def render_templates(action_dir, template_vars, app_dir, manifests_dir, site_type):
"""Render Jinja2 templates, selecting the right set for the site type."""
templates_dir = Path(action_dir) / "templates"
jinja_env = Environment(
loader=FileSystemLoader(str(templates_dir)),
keep_trailing_newline=True,
)
if site_type == "docker":
service_tmpl = "service-docker.yaml.j2"
tmpl_names = ["app.yaml.j2", "certificate.yaml.j2", "ingress.yaml.j2",
"kustomization.yaml.j2", service_tmpl, "deployment.yaml.j2"]
else:
service_tmpl = "service-static.yaml.j2"
tmpl_names = ["app.yaml.j2", "certificate.yaml.j2", "ingress.yaml.j2",
"kustomization.yaml.j2", service_tmpl]
for tmpl_name in tmpl_names:
tmpl = jinja_env.get_template(tmpl_name)
rendered = tmpl.render(**template_vars)
out_name = tmpl_name.replace(".j2", "")
# service-docker.yaml / service-static.yaml → service.yaml
if out_name.startswith("service-"):
out_name = "service.yaml"
dest = app_dir / out_name if tmpl_name == "app.yaml.j2" else manifests_dir / out_name
dest.write_text(rendered)
print(f" Rendered {tmpl_name} -> {dest}")
def commit_and_push(apps_dir, message):
run(f"git -C {apps_dir} add -A")
result = subprocess.run(
f"git -C {apps_dir} diff --cached --quiet",
shell=True, check=False,
)
if result.returncode == 0:
print("No manifest changes to commit")
return False
run(f"git -C {apps_dir} commit -m '{message}'")
run(f"git -C {apps_dir} push")
print("Manifests pushed — ArgoCD will sync")
return True