8cc34552c6
Removes type: docker handling from action.yaml, scripts (build/deploy/utils/setup), and templates (deployment.yaml.j2, service-docker.yaml.j2). Renamed service-static.yaml.j2 -> service.yaml.j2. If site.yaml has type: docker, parse_site_yaml() now dies with a clear message pointing to action/image-build + action/image-push + action/image-deploy with hand-authored apps-repo manifests. rainsounds.vino.network was the only docker consumer and has already migrated. Drops registry-password input from action.yaml (no longer needed).
145 lines
4.2 KiB
Python
145 lines
4.2 KiB
Python
"""Shared utilities for the site-publish 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_DEPRECATION_MSG = """\
|
|
type: docker is no longer supported by action/site-publish.
|
|
|
|
site-publish handles only static-content sites (static, hugo, mkdocs)
|
|
that ship to Garage S3. For containerized web apps, use the standard
|
|
image-producer chain:
|
|
|
|
- uses: action/image-build@v1 # build + smoke-test
|
|
- uses: action/image-push@v1 # push + prune
|
|
- uses: action/image-deploy@v1 # apps repo image-pin
|
|
|
|
Hand-author your apps-repo manifests once (Deployment, Service, Ingress,
|
|
Certificate, kustomization with images: block) under
|
|
sjc001/websites/<repo>/manifests/. image-deploy will pin the tag on
|
|
every CI run. See action/image-deploy README and
|
|
sjc001/websites/rainsounds.vino.network/manifests/ for the canonical
|
|
example.\
|
|
"""
|
|
|
|
|
|
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 == "docker":
|
|
die(DOCKER_DEPRECATION_MSG)
|
|
|
|
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 [],
|
|
"content_dir": cfg.get("content_dir", ""),
|
|
"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):
|
|
"""Render Jinja2 templates for a static-content site."""
|
|
templates_dir = Path(action_dir) / "templates"
|
|
jinja_env = Environment(
|
|
loader=FileSystemLoader(str(templates_dir)),
|
|
keep_trailing_newline=True,
|
|
)
|
|
|
|
tmpl_names = ["app.yaml.j2", "certificate.yaml.j2", "ingress.yaml.j2",
|
|
"kustomization.yaml.j2", "service.yaml.j2"]
|
|
|
|
for tmpl_name in tmpl_names:
|
|
tmpl = jinja_env.get_template(tmpl_name)
|
|
rendered = tmpl.render(**template_vars)
|
|
out_name = tmpl_name.replace(".j2", "")
|
|
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
|