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).
4.7 KiB
action/site-publish
Composite Gitea Action that publishes a static-content website to the
fritzlab k8s cluster. Supports static, hugo, and mkdocs. Content goes
to a Garage S3 bucket; Traefik fronts the bucket via an ExternalName
Service with cert-manager TLS.
Containerized web apps (Dockerfile-based) are NOT handled here. Use the standard image-producer chain instead:
action/image-build+action/image-push+action/image-deploy. Hand-author the apps-repo manifests once (Deployment, Service, Ingress, Certificate, kustomization withimages:block) and letimage-deploypin the tag on every push. Seesjc001/websites/rainsounds.vino.network/for the canonical example. site-publish errors out explicitly ifsite.yamlhastype: docker.
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
# 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 |
yes | Garage ci-deploy-key access key id |
|
s3-secret-key |
yes | Garage ci-deploy-key secret key |
|
s3-endpoint |
no | http://garage.storage.svc:3900 |
Garage S3 endpoint |
garage-admin-token |
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 |
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.
History
- 2026-05-06: removed
type: dockersupport. The single docker site (rainsounds.vino.network) migrated to theimage-*chain. site-publish is now scoped strictly to static-content sites. - 2026-05-06: renamed from
fritzlab/publish-site→action/site-publish.