Files
site-publish/README.md
T
2026-05-06 08:07:28 -05:00

4.0 KiB

action/site-publish

Composite Gitea Action that publishes a website to the fritzlab k8s cluster. Supports static, hugo, mkdocs (content → Garage S3 + ExternalName Service) and docker (Dockerfile → Deployment + headless Service). Handles manifest rendering, TLS via cert-manager, and Garage bucket aliases.

Renamed from fritzlab/publish-siteaction/site-publish as part of the 2026 action-org consolidation.

Convention

Bucket name = repo name = canonical domain. Sibling hostnames (e.g. www., ipv6.) are declared as aliases: in site.yaml — the action registers each as a Garage globalAlias on the bucket and adds it to the Ingress + Certificate on every deploy. Manual edits to manifests in the apps repo are clobbered; edit site.yaml instead.

Usage

Scaffold a new site (handles repo creation + Garage bucket):

./new-site.sh --name my-site.vino.network --domain my-site.vino.network --type static

Or do it manually. site.yaml:

domain: my-site.vino.network
type: static          # static | hugo | mkdocs | docker
# content_dir: html   # subdirectory containing content (default: repo root)
# aliases:            # additional hostnames (each gets a globalAlias on the bucket)
#   - www.my-site.vino.network
# tidy: true          # set false to skip HTML tidy
# enabled: true       # set false to decommission

.gitea/workflows/publish.yaml:

name: Publish
on:
  push:
    branches: [main]
jobs:
  publish:
    runs-on: fritzlab
    steps:
      - uses: actions/checkout@v4
      - uses: https://code.fritzlab.net/action/site-publish@v1
        with:
          token: ${{ secrets.CI_BOT_TOKEN }}
          s3-access-key: ${{ secrets.GARAGE_S3_ACCESS_KEY }}
          s3-secret-key: ${{ secrets.GARAGE_S3_SECRET_KEY }}
          garage-admin-token: ${{ secrets.GARAGE_ADMIN_TOKEN }}

DNS: subdomains of vino.network are covered by the wildcard CNAME to traefik.edge.svc…. For other zones, add an explicit CNAME:

my-site.fritzlab.net  300  IN  CNAME  traefik.edge.svc.k8s.sjc001.fritzlab.net.

Inputs

Input Required Default Description
token yes Gitea token for apps repo push
s3-access-key static/hugo/mkdocs Garage ci-deploy-key access key id
s3-secret-key static/hugo/mkdocs Garage ci-deploy-key secret key
s3-endpoint no http://garage.storage.svc:3900 Garage S3 endpoint
garage-admin-token static/hugo/mkdocs (only if site has aliases) Garage admin API token (admin-token from garage-rpc-secret in storage ns)
garage-admin-endpoint no http://garage.storage.svc:3903 Garage admin API endpoint
registry-password docker inputs.token Container registry password
username no ci-bot Gitea username

Org secrets in websites: CI_BOT_TOKEN, GARAGE_S3_ACCESS_KEY, GARAGE_S3_SECRET_KEY, GARAGE_ADMIN_TOKEN.

Tools

  • new-site.sh — create a new site: Gitea repo, Garage bucket, web hosting enabled.
  • scripts/publish.py decommission <site> — remove a site's manifests from apps repo. Bucket purge is manual.

Architecture

push to websites/<repo>
  → CI runs site-publish action
  → reads site.yaml, builds content (static copy / hugo / mkdocs), runs tidy
  → aws s3 sync → Garage bucket named after the repo
  → admin API: ensures every alias from site.yaml is a globalAlias on the bucket
  → renders manifests in fritzlab/apps from templates: ExternalName Service →
    garage.storage.svc, Traefik Ingress (canonical + aliases), cert-manager
    Certificate (canonical + aliases as SANs), kustomization
  → commits + pushes apps repo only if diff is non-empty
  → ArgoCD syncs → site live with TLS

The Ingress + Certificate are re-rendered on every deploy from site.yaml. There is no "first-deploy vs. update" branching — every deploy is idempotent.

No nginx pods, no per-site Docker images. Garage matches Host: header to bucket name (or any of its globalAliases), so every site shares a single ExternalName target.