Files
stack/.woodpecker/infra-ci.yml
kert dd721e8497 add mc sidecar and WebDAV service backed by RustFS S3 for Zotero
refs #94 #95 #96 #97

- Add rustfs/Dockerfile.mc: least-privilege mc sidecar (non-root,
  read-only fs, all caps dropped, tmpfs for config)
- Add mc service to compose.yml with storage network access only
- Add rclone WebDAV service using env-based S3 config, serving
  the zotero bucket with HTTP basic auth
- Add Traefik route at webdav.homelab.fhirworx.io
- Add zotero.json IAM policy for bucket access
- Add mc image to deploy, infra-ci, and rebuild-all pipelines
- Add WEBDAV_* env vars to .env.example
2026-03-22 00:57:48 -04:00

225 lines
7.4 KiB
YAML

# ── Infrastructure quality gate ──────────────────────────────────
# Validates Dockerfiles, config files, and container builds for
# every service in the stack. Runs in parallel with ci.yml;
# path-filtered so only relevant steps execute.
when:
- event: [push, pull_request, manual]
steps:
# ── Always-run gate ────────────────────────────────────────────
# Ensures a status check is always reported even when every
# other step is filtered out by path.
- name: infra-gate
image: alpine:3
commands:
- echo "infra-ci gate passed"
# ── Notebooks image ────────────────────────────────────────────
- name: hadolint-notebooks
image: hadolint/hadolint:latest-debian
commands:
- hadolint notebooks/Dockerfile
when:
- path: "notebooks/**"
- name: build-notebooks
image: woodpeckerci/plugin-docker-buildx
settings:
dockerfile: notebooks/Dockerfile
context: notebooks/
tags: ["ci-test"]
daemon_off: true
dry_run: true
volumes:
- /run/user/1000/docker.sock:/var/run/docker.sock
depends_on:
- hadolint-notebooks
when:
- path: "notebooks/**"
# ── Zotero image ───────────────────────────────────────────────
- name: hadolint-zotero
image: hadolint/hadolint:latest-debian
commands:
- hadolint zotero/Dockerfile
when:
- path: "zotero/**"
- name: build-zotero
image: woodpeckerci/plugin-docker-buildx
settings:
dockerfile: zotero/Dockerfile
context: zotero/
tags: ["ci-test"]
daemon_off: true
dry_run: true
volumes:
- /run/user/1000/docker.sock:/var/run/docker.sock
depends_on:
- hadolint-zotero
when:
- path: "zotero/**"
# ── Docs image ────────────────────────────────────────────────
- name: hadolint-docs
image: hadolint/hadolint:latest-debian
commands:
- hadolint docs/Dockerfile
when:
- path: "docs/**"
- name: build-docs
image: woodpeckerci/plugin-docker-buildx
settings:
dockerfile: docs/Dockerfile
context: .
tags: ["ci-test"]
daemon_off: true
dry_run: true
volumes:
- /run/user/1000/docker.sock:/var/run/docker.sock
depends_on:
- hadolint-docs
when:
- path: "docs/**"
# ── MC (MinIO Client) sidecar ─────────────────────────────────
- name: hadolint-mc
image: hadolint/hadolint:latest-debian
commands:
- hadolint rustfs/Dockerfile.mc
when:
- path: "rustfs/**"
- name: build-mc
image: woodpeckerci/plugin-docker-buildx
settings:
dockerfile: rustfs/Dockerfile.mc
context: rustfs/
tags: ["ci-test"]
daemon_off: true
dry_run: true
volumes:
- /run/user/1000/docker.sock:/var/run/docker.sock
depends_on:
- hadolint-mc
when:
- path: "rustfs/**"
# ── API image ─────────────────────────────────────────────────
- name: hadolint-api
image: hadolint/hadolint:latest-debian
commands:
- hadolint api/Dockerfile
when:
- path:
- "api/**"
- "src/**"
- "pyproject.toml"
- name: build-api
image: woodpeckerci/plugin-docker-buildx
settings:
dockerfile: api/Dockerfile
context: .
tags: ["ci-test"]
daemon_off: true
dry_run: true
volumes:
- /run/user/1000/docker.sock:/var/run/docker.sock
depends_on:
- hadolint-api
when:
- path:
- "api/**"
- "src/**"
- "pyproject.toml"
# ── Nginx / Dashboard ─────────────────────────────────────────
- name: validate-nginx
image: nginx:alpine
commands:
- cp nginx/nginx.conf /etc/nginx/nginx.conf
- nginx -t
when:
- path: "nginx/**"
# ── Prometheus ─────────────────────────────────────────────────
- name: validate-prometheus
image: prom/prometheus:latest
commands:
- promtool check config prometheus/prometheus.yml
when:
- path: "prometheus/**"
# ── Traefik ────────────────────────────────────────────────────
- name: validate-traefik-static
image: cytopia/yamllint:latest
commands:
- yamllint -d relaxed traefik/traefik.yml
when:
- path: "traefik/traefik.yml"
- name: validate-traefik-template
image: traefik:v3.3
environment:
DOMAIN: ci-test.fhirworx.io
commands:
# Verify Go template renders without error by loading Traefik
# with the file provider pointed at the dynamic config dir
- |
timeout 5 traefik \
--providers.file.directory=traefik/dynamic \
--api.dashboard=false \
--log.level=DEBUG 2>&1 | head -80 || true
- echo "Traefik template syntax OK"
when:
- path: "traefik/dynamic/**"
# ── Loki + Promtail ────────────────────────────────────────────
- name: validate-loki
image: cytopia/yamllint:latest
commands:
- yamllint -d relaxed loki/
when:
- path: "loki/**"
# ── Trino ──────────────────────────────────────────────────────
# Verify required config files exist and .properties have valid
# key=value syntax. jvm.config uses -flag format (not checked).
- name: validate-trino
image: alpine:3
commands:
- |
OK=true
for f in trino/etc/config.properties trino/etc/node.properties trino/etc/jvm.config; do
if [ ! -f "$f" ]; then echo "MISSING: $f"; OK=false; fi
done
ls trino/etc/catalog/*.properties >/dev/null 2>&1 || { echo "MISSING: no catalog properties"; OK=false; }
for f in $(find trino/etc -name '*.properties'); do
while IFS= read -r line; do
case "$line" in ''|'#'*|'!'*) continue ;; esac
echo "$line" | grep -q '=' || { echo "BAD LINE in $f: $line"; OK=false; }
done < "$f"
done
$OK && echo "Trino config validation passed"
$OK
when:
- path: "trino/etc/**"
# ── Failure reporter ─────────────────────────────────────────
- name: report-failure
image: ghcr.io/astral-sh/uv:python3.13-bookworm-slim
environment:
UV_PYTHON_PREFERENCE: only-system
UV_LINK_MODE: copy
UV_PROJECT_ENVIRONMENT: .venv
GITEA_TOKEN:
from_secret: gitea_token
commands:
- uv sync --no-dev
- uv run python -m api.diag
when:
- status: [failure]