Files
stack/.woodpecker/deploy.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

312 lines
9.7 KiB
YAML

# ── Deploy ───────────────────────────────────────────────────────
# Builds the Python package, pushes it to Gitea's PyPI registry,
# builds, scans, and pushes container images, then updates the
# host working copy and restarts all changed services.
# Only runs on pushes to main (i.e. after PR merge).
#
# Image naming:
# localhost:3000/homelab/<service>:sha-<8chars> (registry)
# localhost:3000/homelab/<service>:latest (registry)
#
# The deploy step writes COMMIT_SHA=sha-<short> into .env so
# compose.yml resolves the exact image just built.
when:
- event: push
branch: main
steps:
# ── Python package ──────────────────────────────────────────
- name: build-package
image: ghcr.io/astral-sh/uv:python3.13-bookworm-slim
commands:
- BASE=$(grep '^version' pyproject.toml | head -1 | sed 's/.*"\(.*\)"/\1/')
- uv version "$BASE.dev${CI_PIPELINE_NUMBER}" --no-sync
- uv build --out-dir dist/
- ls -lh dist/
- name: publish-package
image: ghcr.io/astral-sh/uv:python3.13-bookworm-slim
environment:
REGISTRY_USER:
from_secret: registry_user
REGISTRY_PASS:
from_secret: registry_pass
commands:
- uv publish --publish-url http://gitea:3000/api/packages/homelab/pypi --username "$REGISTRY_USER" --password "$REGISTRY_PASS" dist/*
depends_on:
- build-package
# ── Notebooks image ─────────────────────────────────────────
- name: build-push-notebooks
image: woodpeckerci/plugin-docker-buildx
settings:
repo: localhost:3000/homelab/notebooks
dockerfile: notebooks/Dockerfile
context: notebooks/
tags:
- "sha-${CI_COMMIT_SHA:0:8}"
- latest
daemon_off: true
insecure: true
username:
from_secret: registry_user
password:
from_secret: registry_pass
volumes:
- /run/user/1000/docker.sock:/var/run/docker.sock
- name: scan-notebooks
image: woodpeckerci/plugin-trivy
settings:
image_ref: "localhost:3000/homelab/notebooks:sha-${CI_COMMIT_SHA:0:8}"
severity: HIGH,CRITICAL
exit_code: 0
output: notebooks-scan.json
format: json
volumes:
- /run/user/1000/docker.sock:/var/run/docker.sock
depends_on:
- build-push-notebooks
# ── Zotero image ────────────────────────────────────────────
- name: build-push-zotero
image: woodpeckerci/plugin-docker-buildx
settings:
repo: localhost:3000/homelab/zotero
dockerfile: zotero/Dockerfile
context: zotero/
tags:
- "sha-${CI_COMMIT_SHA:0:8}"
- latest
daemon_off: true
insecure: true
username:
from_secret: registry_user
password:
from_secret: registry_pass
volumes:
- /run/user/1000/docker.sock:/var/run/docker.sock
- name: scan-zotero
image: woodpeckerci/plugin-trivy
settings:
image_ref: "localhost:3000/homelab/zotero:sha-${CI_COMMIT_SHA:0:8}"
severity: HIGH,CRITICAL
exit_code: 0
output: zotero-scan.json
format: json
volumes:
- /run/user/1000/docker.sock:/var/run/docker.sock
depends_on:
- build-push-zotero
# ── Docs image ──────────────────────────────────────────────
# Needs bib.sqlite from host data dir in the build context.
- name: prep-docs-context
image: alpine:3
volumes:
- /home/kert/stack/data:/host-data:ro
commands:
- mkdir -p data
- cp /host-data/bib.sqlite data/ 2>/dev/null || true
- name: build-push-docs
image: woodpeckerci/plugin-docker-buildx
settings:
repo: localhost:3000/homelab/docs
dockerfile: docs/Dockerfile
context: .
tags:
- "sha-${CI_COMMIT_SHA:0:8}"
- latest
daemon_off: true
insecure: true
username:
from_secret: registry_user
password:
from_secret: registry_pass
volumes:
- /run/user/1000/docker.sock:/var/run/docker.sock
depends_on:
- prep-docs-context
- name: scan-docs
image: woodpeckerci/plugin-trivy
settings:
image_ref: "localhost:3000/homelab/docs:sha-${CI_COMMIT_SHA:0:8}"
severity: HIGH,CRITICAL
exit_code: 0
output: docs-scan.json
format: json
volumes:
- /run/user/1000/docker.sock:/var/run/docker.sock
depends_on:
- build-push-docs
# ── API image ───────────────────────────────────────────────
- name: build-push-api
image: woodpeckerci/plugin-docker-buildx
settings:
repo: localhost:3000/homelab/api
dockerfile: api/Dockerfile
context: .
tags:
- "sha-${CI_COMMIT_SHA:0:8}"
- latest
daemon_off: true
insecure: true
username:
from_secret: registry_user
password:
from_secret: registry_pass
volumes:
- /run/user/1000/docker.sock:/var/run/docker.sock
- name: scan-api
image: woodpeckerci/plugin-trivy
settings:
image_ref: "localhost:3000/homelab/api:sha-${CI_COMMIT_SHA:0:8}"
severity: HIGH,CRITICAL
exit_code: 0
output: api-scan.json
format: json
volumes:
- /run/user/1000/docker.sock:/var/run/docker.sock
depends_on:
- build-push-api
# ── MC (MinIO Client) sidecar ────────────────────────────────
- name: build-push-mc
image: woodpeckerci/plugin-docker-buildx
settings:
repo: localhost:3000/homelab/mc
dockerfile: rustfs/Dockerfile.mc
context: rustfs/
tags:
- "sha-${CI_COMMIT_SHA:0:8}"
- latest
daemon_off: true
insecure: true
username:
from_secret: registry_user
password:
from_secret: registry_pass
volumes:
- /run/user/1000/docker.sock:/var/run/docker.sock
# ── Upload scan results ─────────────────────────────────────
- name: upload-notebooks-scan
image: woodpeckerci/plugin-s3
settings:
endpoint: http://rustfs:9000
bucket: gitea
access_key:
from_secret: s3_access_key
secret_key:
from_secret: s3_secret_key
source: "notebooks-scan.json"
target: /ci/${CI_REPO}/${CI_COMMIT_SHA:0:8}/
path_style: true
depends_on:
- scan-notebooks
- name: upload-zotero-scan
image: woodpeckerci/plugin-s3
settings:
endpoint: http://rustfs:9000
bucket: gitea
access_key:
from_secret: s3_access_key
secret_key:
from_secret: s3_secret_key
source: "zotero-scan.json"
target: /ci/${CI_REPO}/${CI_COMMIT_SHA:0:8}/
path_style: true
depends_on:
- scan-zotero
- name: upload-docs-scan
image: woodpeckerci/plugin-s3
settings:
endpoint: http://rustfs:9000
bucket: gitea
access_key:
from_secret: s3_access_key
secret_key:
from_secret: s3_secret_key
source: "docs-scan.json"
target: /ci/${CI_REPO}/${CI_COMMIT_SHA:0:8}/
path_style: true
depends_on:
- scan-docs
- name: upload-api-scan
image: woodpeckerci/plugin-s3
settings:
endpoint: http://rustfs:9000
bucket: gitea
access_key:
from_secret: s3_access_key
secret_key:
from_secret: s3_secret_key
source: "api-scan.json"
target: /ci/${CI_REPO}/${CI_COMMIT_SHA:0:8}/
path_style: true
depends_on:
- scan-api
# ── Deploy: pull latest code + restart services ────────────────
- name: deploy
image: docker:cli
volumes:
- /run/user/1000/docker.sock:/var/run/docker.sock
- /home/kert/stack:/home/kert/stack
commands:
- apk add --no-cache git
- cd /home/kert/stack
- cp .env .env.bak 2>/dev/null || true
- git fetch http://gitea:3000/homelab/stack.git main
- git reset --hard FETCH_HEAD
- cp .env.bak .env 2>/dev/null || true
# Pin compose to the exact images just built
- TAG=sha-${CI_COMMIT_SHA:0:8}
- sed -i "s/^COMMIT_SHA=.*/COMMIT_SHA=$TAG/" .env 2>/dev/null || echo "COMMIT_SHA=$TAG" >> .env
- docker compose up -d --remove-orphans
depends_on:
- publish-package
- upload-notebooks-scan
- upload-zotero-scan
- upload-api-scan
- upload-docs-scan
# ── Provision: derive credentials and rotate backends ────────
- name: provision
image: ghcr.io/astral-sh/uv:python3.13-bookworm-slim
volumes:
- /run/user/1000/docker.sock:/var/run/docker.sock
- /home/kert/stack:/home/kert/stack
environment:
ROOT_KEY:
from_secret: root_key
commands:
- cd /home/kert/stack
- uv run python -m api.auth provision ${CI_COMMIT_SHA}
depends_on:
- deploy
# ── 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]