summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorDawid Rycerz <dawid@rycerz.xyz>2026-01-22 22:07:32 +0100
committerDawid Rycerz <dawid@rycerz.xyz>2026-02-10 18:44:26 +0100
commit064a1d01c5c14f5ecc032fa9b8346a4a88b893f6 (patch)
treea2023f9ccd297ed8a41a3a0cc5699c2add09244d /examples
witryna 0.1.0 — initial releasev0.1.0
Minimalist Git-based static site deployment orchestrator. Webhook-triggered builds in Podman/Docker containers with atomic symlink publishing, SIGHUP hot-reload, and zero-downtime deploys. See README.md for usage, CHANGELOG.md for details.
Diffstat (limited to 'examples')
-rw-r--r--examples/caddy/Caddyfile25
-rwxr-xr-xexamples/hooks/caddy-deploy.sh118
-rw-r--r--examples/nginx/witryna.conf48
-rw-r--r--examples/systemd/docker.conf3
-rw-r--r--examples/systemd/podman.conf3
-rw-r--r--examples/witryna.toml63
-rw-r--r--examples/witryna.yaml14
7 files changed, 274 insertions, 0 deletions
diff --git a/examples/caddy/Caddyfile b/examples/caddy/Caddyfile
new file mode 100644
index 0000000..b2285f6
--- /dev/null
+++ b/examples/caddy/Caddyfile
@@ -0,0 +1,25 @@
+# Caddyfile — Witryna with auto-managed site configs
+#
+# Site configs are generated by the caddy-deploy.sh hook script
+# and imported from /etc/caddy/sites.d/. See examples/hooks/caddy-deploy.sh.
+#
+# Caddy obtains and renews TLS certificates automatically via ACME.
+# See https://caddyserver.com/docs/ for full documentation.
+
+# Import auto-managed site configs
+import /etc/caddy/sites.d/*.caddy
+
+# Webhook endpoint — reverse proxy to Witryna
+witryna.example.com {
+ reverse_proxy 127.0.0.1:8080
+
+ # Restrict access to POST requests only
+ @not_post not method POST
+ respond @not_post 405
+
+ # Security headers
+ header {
+ X-Content-Type-Options "nosniff"
+ -Server
+ }
+}
diff --git a/examples/hooks/caddy-deploy.sh b/examples/hooks/caddy-deploy.sh
new file mode 100755
index 0000000..7f2173b
--- /dev/null
+++ b/examples/hooks/caddy-deploy.sh
@@ -0,0 +1,118 @@
+#!/bin/sh
+# caddy-deploy.sh — Post-deploy hook for Witryna + Caddy integration
+#
+# Generates a Caddyfile snippet for the deployed site and reloads Caddy.
+# Supports wildcard hosting domains and custom primary domains with redirects.
+#
+# Env vars from Witryna (automatic):
+# WITRYNA_SITE — site name
+# WITRYNA_PUBLIC_DIR — stable "current" symlink path (document root)
+#
+# Env vars from [sites.env] in witryna.toml:
+# BASE_DOMAIN — wildcard hosting domain (e.g. mywitrynahost.com)
+# PRIMARY_DOMAIN — (optional) custom primary domain
+# REDIRECT_DOMAINS — (optional) comma-separated additional redirect domains
+# CADDY_SITES_DIR — (optional) where to write configs (default: /etc/caddy/sites.d)
+#
+# Behavior matrix:
+# BASE_DOMAIN set, PRIMARY_DOMAIN not set:
+# Serving: {site}.{base}
+# Redirects: (none)
+#
+# BASE_DOMAIN set, PRIMARY_DOMAIN set:
+# Serving: PRIMARY_DOMAIN
+# Redirects: {site}.{base} + REDIRECT_DOMAINS → PRIMARY_DOMAIN
+#
+# BASE_DOMAIN not set, PRIMARY_DOMAIN set:
+# Serving: PRIMARY_DOMAIN
+# Redirects: REDIRECT_DOMAINS → PRIMARY_DOMAIN
+#
+# Neither set: error
+#
+# Usage in witryna.toml:
+# post_deploy = ["/etc/witryna/hooks/caddy-deploy.sh"]
+# [sites.env]
+# BASE_DOMAIN = "mywitrynahost.com"
+# PRIMARY_DOMAIN = "blog.example.com"
+
+set -eu
+
+SITES_DIR="${CADDY_SITES_DIR:-/etc/caddy/sites.d}"
+CADDY_CONFIG="${CADDY_CONFIG:-/etc/caddy/Caddyfile}"
+
+# Validate required env vars
+if [ -z "${WITRYNA_SITE:-}" ]; then
+ echo "ERROR: WITRYNA_SITE is not set" >&2
+ exit 1
+fi
+if [ -z "${WITRYNA_PUBLIC_DIR:-}" ]; then
+ echo "ERROR: WITRYNA_PUBLIC_DIR is not set" >&2
+ exit 1
+fi
+
+# Determine serving domain and redirect domains
+auto_domain=""
+if [ -n "${BASE_DOMAIN:-}" ]; then
+ auto_domain="${WITRYNA_SITE}.${BASE_DOMAIN}"
+fi
+
+serving_domain=""
+redirect_domains=""
+
+if [ -n "${PRIMARY_DOMAIN:-}" ]; then
+ serving_domain="$PRIMARY_DOMAIN"
+ # Auto-domain redirects to primary (if base is set)
+ if [ -n "$auto_domain" ]; then
+ redirect_domains="$auto_domain"
+ fi
+ # Append user-specified redirect domains
+ if [ -n "${REDIRECT_DOMAINS:-}" ]; then
+ if [ -n "$redirect_domains" ]; then
+ redirect_domains="${redirect_domains}, ${REDIRECT_DOMAINS}"
+ else
+ redirect_domains="$REDIRECT_DOMAINS"
+ fi
+ fi
+elif [ -n "$auto_domain" ]; then
+ serving_domain="$auto_domain"
+ # No primary → REDIRECT_DOMAINS still apply as redirects to auto_domain
+ if [ -n "${REDIRECT_DOMAINS:-}" ]; then
+ redirect_domains="$REDIRECT_DOMAINS"
+ fi
+else
+ echo "ERROR: at least one of BASE_DOMAIN or PRIMARY_DOMAIN must be set" >&2
+ exit 1
+fi
+
+# Ensure sites directory exists
+mkdir -p "$SITES_DIR"
+
+# Generate Caddyfile snippet
+config_file="${SITES_DIR}/${WITRYNA_SITE}.caddy"
+{
+ echo "# Managed by witryna caddy-deploy.sh — do not edit"
+ echo "${serving_domain} {"
+ echo " root * ${WITRYNA_PUBLIC_DIR}"
+ echo " file_server"
+ echo " encode gzip"
+ echo " header {"
+ echo " X-Frame-Options \"DENY\""
+ echo " X-Content-Type-Options \"nosniff\""
+ echo " Referrer-Policy \"strict-origin-when-cross-origin\""
+ echo " -Server"
+ echo " }"
+ echo "}"
+
+ if [ -n "$redirect_domains" ]; then
+ echo ""
+ echo "${redirect_domains} {"
+ echo " redir https://${serving_domain}{uri} permanent"
+ echo "}"
+ fi
+} > "$config_file"
+
+echo "Wrote Caddy config: $config_file"
+
+# Reload Caddy
+caddy reload --config "$CADDY_CONFIG"
+echo "Caddy reloaded"
diff --git a/examples/nginx/witryna.conf b/examples/nginx/witryna.conf
new file mode 100644
index 0000000..5f56ef2
--- /dev/null
+++ b/examples/nginx/witryna.conf
@@ -0,0 +1,48 @@
+# witryna.conf — Nginx reverse proxy configuration for Witryna
+#
+# Two server blocks:
+# 1. Public site — serves the built static assets
+# 2. Webhook endpoint — proxies deploy triggers to Witryna
+#
+# TLS is not configured here — use certbot or similar to add certificates:
+# sudo certbot --nginx -d my-site.example.com -d witryna.example.com
+
+# Public site — serves your built static files
+server {
+ listen 80;
+ server_name my-site.example.com;
+
+ root /var/lib/witryna/builds/my-site/current;
+ index index.html;
+
+ location / {
+ try_files $uri $uri/ =404;
+ }
+
+ # Security headers
+ add_header X-Frame-Options "DENY" always;
+ add_header X-Content-Type-Options "nosniff" always;
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
+}
+
+# Webhook endpoint — reverse proxy to Witryna
+server {
+ listen 80;
+ server_name witryna.example.com;
+
+ # Only allow POST requests
+ location / {
+ limit_except POST {
+ deny all;
+ }
+
+ proxy_pass http://127.0.0.1:8080;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ }
+
+ # Security headers
+ add_header X-Content-Type-Options "nosniff" always;
+}
diff --git a/examples/systemd/docker.conf b/examples/systemd/docker.conf
new file mode 100644
index 0000000..9ee2b2d
--- /dev/null
+++ b/examples/systemd/docker.conf
@@ -0,0 +1,3 @@
+[Service]
+SupplementaryGroups=docker
+ReadWritePaths=/var/run/docker.sock
diff --git a/examples/systemd/podman.conf b/examples/systemd/podman.conf
new file mode 100644
index 0000000..98502f8
--- /dev/null
+++ b/examples/systemd/podman.conf
@@ -0,0 +1,3 @@
+[Service]
+RestrictNamespaces=no
+Environment="XDG_RUNTIME_DIR=/run/user/%U"
diff --git a/examples/witryna.toml b/examples/witryna.toml
new file mode 100644
index 0000000..6256d63
--- /dev/null
+++ b/examples/witryna.toml
@@ -0,0 +1,63 @@
+# /etc/witryna/witryna.toml — Witryna configuration
+# See witryna.toml(5) for full documentation.
+
+listen_address = "127.0.0.1:8080"
+container_runtime = "podman"
+base_dir = "/var/lib/witryna"
+log_dir = "/var/log/witryna"
+log_level = "info"
+rate_limit_per_minute = 10
+max_builds_to_keep = 5
+# git_timeout = "2m" # default: 60s, range: 5s..1h
+
+# [[sites]]
+# name = "my-site"
+# repo_url = "https://github.com/user/my-site.git"
+# branch = "main"
+# webhook_token = "CHANGE-ME"
+# # Or use environment variable: webhook_token = "${WITRYNA_TOKEN}"
+# # Or use file (Docker/K8s secrets): webhook_token_file = "/run/secrets/token"
+# # Omit webhook_token to disable authentication (e.g., behind VPN)
+#
+# # Polling (default: disabled, webhook-only)
+# # poll_interval = "30m" # min: 60s
+#
+# # Build timeout (default: 10m, range: 10s..24h)
+# # build_timeout = "15m"
+#
+# # Git clone depth (default: 1 for shallow, 0 for full history)
+# # git_depth = 0
+#
+# # Container resource limits
+# # container_memory = "512m"
+# # container_cpus = 1.0
+# # container_pids_limit = 256
+# # container_network = "bridge" # bridge (default) | none | host | slirp4netns
+#
+# # Container working directory (for monorepos)
+# # container_workdir = "packages/frontend"
+#
+# # Custom repo config file (default: .witryna.yaml → .witryna.yml → witryna.yaml → witryna.yml)
+# # config_file = ".witryna.yaml"
+#
+# # Cache directories (container paths, persisted across builds)
+# # cache_dirs = ["/root/.npm"]
+#
+# # Build config overrides (all three → witryna.yaml optional)
+# # image = "node:20-alpine"
+# # command = "npm ci && npm run build"
+# # public = "dist"
+#
+# # Post-deploy hook (30s timeout, non-fatal)
+# # post_deploy = ["systemctl", "reload", "nginx"]
+#
+# # Caddy auto-configuration (see examples/hooks/caddy-deploy.sh)
+# # post_deploy = ["/etc/witryna/hooks/caddy-deploy.sh"]
+#
+# # Environment variables for builds and hooks
+# # [sites.env]
+# # NODE_ENV = "production"
+# # BASE_DOMAIN = "mywitrynahost.com" # for Caddy hook
+# # PRIMARY_DOMAIN = "my-site.example.com" # for Caddy hook
+# # REDIRECT_DOMAINS = "www.my-site.example.com" # for Caddy hook
+
diff --git a/examples/witryna.yaml b/examples/witryna.yaml
new file mode 100644
index 0000000..3d6a09f
--- /dev/null
+++ b/examples/witryna.yaml
@@ -0,0 +1,14 @@
+# witryna.yaml — per-repository build configuration
+# Place this file in the root of your Git repository.
+# Supported filenames: .witryna.yaml, .witryna.yml, witryna.yaml, witryna.yml
+# Or set config_file in witryna.toml for a custom path.
+# See witryna.toml(5) for overriding these values in the server config.
+
+# Container image for the build environment
+image: node:20-alpine
+
+# Build command (executed via sh -c inside the container)
+command: "npm ci && npm run build"
+
+# Directory containing built static assets (relative to repo root)
+public: dist