- Install Docker 27.5.1 static binary + compose v2 plugin instead of ancient docker.io from apt (API 1.41 vs required 1.44) - Read current GITEA_TOKEN from .env before rotation so the API call to change_admin_password uses a valid token, not the derived placeholder hash - Set DOCKER_HOST for the static binary
305 lines
10 KiB
YAML
305 lines
10 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:
|
|
# gitea.homelab.fhirworx.io/homelab/<service>:sha-<8chars>
|
|
#
|
|
# Buildkit pushes via HTTP through Traefik (TLS terminated at edge).
|
|
# DNS resolved by CoreDNS via Traefik UDP on the CI network.
|
|
|
|
when:
|
|
- event: push
|
|
branch: main
|
|
|
|
variables:
|
|
- &buildx_image woodpeckerci/plugin-docker-buildx:5-insecure
|
|
- &buildx_base
|
|
buildkit_config: |
|
|
[registry."gitea.homelab.fhirworx.io"]
|
|
http = true
|
|
registry: gitea.homelab.fhirworx.io
|
|
username:
|
|
from_secret: registry_user
|
|
password:
|
|
from_secret: registry_pass
|
|
- &trivy_scan
|
|
image: aquasec/trivy:latest
|
|
- &diag_env
|
|
UV_PYTHON_PREFERENCE: only-system
|
|
UV_LINK_MODE: copy
|
|
UV_PROJECT_ENVIRONMENT: .venv
|
|
GITEA_TOKEN:
|
|
from_secret: gitea_token
|
|
|
|
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: *buildx_image
|
|
settings:
|
|
<<: *buildx_base
|
|
repo: gitea.homelab.fhirworx.io/homelab/notebooks
|
|
dockerfile: notebooks/Dockerfile
|
|
context: notebooks/
|
|
tags:
|
|
- "sha-${CI_COMMIT_SHA:0:8}"
|
|
- latest
|
|
|
|
- name: scan-notebooks
|
|
<<: *trivy_scan
|
|
commands:
|
|
- trivy image --image-src remote --insecure --severity HIGH,CRITICAL --exit-code 0 --format json
|
|
-o notebooks-scan.json
|
|
"gitea.homelab.fhirworx.io/homelab/notebooks:sha-${CI_COMMIT_SHA:0:8}"
|
|
depends_on:
|
|
- build-push-notebooks
|
|
|
|
# ── Zotero image ────────────────────────────────────────────
|
|
- name: build-push-zotero
|
|
image: *buildx_image
|
|
settings:
|
|
<<: *buildx_base
|
|
repo: gitea.homelab.fhirworx.io/homelab/zotero
|
|
dockerfile: zotero/Dockerfile
|
|
context: zotero/
|
|
tags:
|
|
- "sha-${CI_COMMIT_SHA:0:8}"
|
|
- latest
|
|
|
|
- name: scan-zotero
|
|
<<: *trivy_scan
|
|
commands:
|
|
- trivy image --image-src remote --insecure --severity HIGH,CRITICAL --exit-code 0 --format json
|
|
-o zotero-scan.json
|
|
"gitea.homelab.fhirworx.io/homelab/zotero:sha-${CI_COMMIT_SHA:0:8}"
|
|
depends_on:
|
|
- build-push-zotero
|
|
|
|
# ── Docs image ──────────────────────────────────────────────
|
|
- 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: *buildx_image
|
|
settings:
|
|
<<: *buildx_base
|
|
repo: gitea.homelab.fhirworx.io/homelab/docs
|
|
dockerfile: docs/Dockerfile
|
|
context: .
|
|
tags:
|
|
- "sha-${CI_COMMIT_SHA:0:8}"
|
|
- latest
|
|
depends_on:
|
|
- prep-docs-context
|
|
|
|
- name: scan-docs
|
|
<<: *trivy_scan
|
|
commands:
|
|
- trivy image --image-src remote --insecure --severity HIGH,CRITICAL --exit-code 0 --format json
|
|
-o docs-scan.json
|
|
"gitea.homelab.fhirworx.io/homelab/docs:sha-${CI_COMMIT_SHA:0:8}"
|
|
depends_on:
|
|
- build-push-docs
|
|
|
|
# ── API image ───────────────────────────────────────────────
|
|
- name: build-push-api
|
|
image: *buildx_image
|
|
settings:
|
|
<<: *buildx_base
|
|
repo: gitea.homelab.fhirworx.io/homelab/api
|
|
dockerfile: api/Dockerfile
|
|
context: .
|
|
tags:
|
|
- "sha-${CI_COMMIT_SHA:0:8}"
|
|
- latest
|
|
|
|
- name: scan-api
|
|
<<: *trivy_scan
|
|
commands:
|
|
- trivy image --image-src remote --insecure --severity HIGH,CRITICAL --exit-code 0 --format json
|
|
-o api-scan.json
|
|
"gitea.homelab.fhirworx.io/homelab/api:sha-${CI_COMMIT_SHA:0:8}"
|
|
depends_on:
|
|
- build-push-api
|
|
|
|
# ── MC (MinIO Client) sidecar ────────────────────────────────
|
|
- name: build-push-mc
|
|
image: *buildx_image
|
|
settings:
|
|
<<: *buildx_base
|
|
repo: gitea.homelab.fhirworx.io/homelab/mc
|
|
dockerfile: rustfs/Dockerfile.mc
|
|
context: rustfs/
|
|
tags:
|
|
- "sha-${CI_COMMIT_SHA:0:8}"
|
|
- latest
|
|
|
|
# ── Upload scan results ─────────────────────────────────────
|
|
- name: upload-notebooks-scan
|
|
image: woodpeckerci/plugin-s3
|
|
failure: ignore
|
|
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
|
|
failure: ignore
|
|
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
|
|
failure: ignore
|
|
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
|
|
failure: ignore
|
|
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
|
|
|
|
# ── Report vulnerabilities ──────────────────────────────────
|
|
- name: report-vulns
|
|
image: ghcr.io/astral-sh/uv:python3.13-bookworm-slim
|
|
failure: ignore
|
|
environment:
|
|
<<: *diag_env
|
|
commands:
|
|
- uv sync --no-dev
|
|
- |
|
|
for f in notebooks-scan.json zotero-scan.json docs-scan.json api-scan.json; do
|
|
[ -f "$f" ] && uv run python -m api.diag.vuln "$f" || true
|
|
done
|
|
depends_on:
|
|
- scan-notebooks
|
|
- scan-zotero
|
|
- scan-docs
|
|
- scan-api
|
|
|
|
# ── Deploy + Provision (two-phase) ─────────────────────────────
|
|
# Phase 1: rotate backend passwords (ALTER ROLE, Gitea API)
|
|
# Phase 2: write .env + docker compose up (services read matching passwords)
|
|
# Phase 3: sync Woodpecker secrets + health check + rollback on failure
|
|
- name: deploy
|
|
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:
|
|
UV_PYTHON_PREFERENCE: only-system
|
|
UV_LINK_MODE: copy
|
|
UV_PROJECT_ENVIRONMENT: .venv
|
|
ROOT_KEY:
|
|
from_secret: root_key
|
|
commands:
|
|
# Install docker CLI (official static binary, matches host daemon)
|
|
- apt-get update -qq && apt-get install -y -qq curl git >/dev/null 2>&1
|
|
- curl -fsSL https://download.docker.com/linux/static/stable/x86_64/docker-27.5.1.tgz | tar xz -C /usr/local/bin --strip-components=1 docker/docker
|
|
- curl -fsSL -o /usr/local/bin/docker-compose https://github.com/docker/compose/releases/download/v2.35.1/docker-compose-linux-x86_64 && chmod +x /usr/local/bin/docker-compose
|
|
- mkdir -p /usr/local/lib/docker/cli-plugins && ln -sf /usr/local/bin/docker-compose /usr/local/lib/docker/cli-plugins/docker-compose
|
|
- export DOCKER_HOST=unix:///var/run/docker.sock
|
|
- cd /home/kert/stack
|
|
# Pull latest code, preserve .env
|
|
- 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
|
|
# Retag registry images for compose
|
|
- TAG=sha-${CI_COMMIT_SHA:0:8}
|
|
- FQDN=gitea.homelab.fhirworx.io
|
|
- for SVC in notebooks zotero docs api mc; do
|
|
docker pull $FQDN/homelab/$SVC:$TAG &&
|
|
docker tag $FQDN/homelab/$SVC:$TAG fhirworx/$SVC:$TAG;
|
|
done
|
|
# Two-phase provision + restart + health check
|
|
- uv sync --no-dev
|
|
- uv run python -m api.auth deploy ${CI_COMMIT_SHA}
|
|
depends_on:
|
|
- publish-package
|
|
- scan-notebooks
|
|
- scan-zotero
|
|
- scan-docs
|
|
- scan-api
|
|
- build-push-mc
|
|
|
|
# ── Failure reporter ─────────────────────────────────────────
|
|
- name: report-failure
|
|
image: ghcr.io/astral-sh/uv:python3.13-bookworm-slim
|
|
failure: ignore
|
|
environment:
|
|
<<: *diag_env
|
|
commands:
|
|
- uv sync --no-dev
|
|
- uv run python -m api.diag
|
|
when:
|
|
- status: [failure]
|