From 56bea135f93581fa4d20ff2326a01d7b07fc8c39 Mon Sep 17 00:00:00 2001 From: Gerald Fruhmann Date: Sat, 4 Jul 2026 20:17:18 +0200 Subject: [PATCH] feat(docker): add self-hosted stack compose file and complete setup guide - docker/docker-compose.yml: XWiki 16 + Redmine 5 each with dedicated Postgres 16 backend, healthchecks, named volumes, restart policies - docker/.env.example: template for all required secrets (passwords, Redmine secret key base, adapter credentials) - .gitignore: exclude docker/.env from version control - docs/setup.md: full 6-step walkthrough from clone to first deploy Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 3 + docker/.env.example | 15 +++++ docker/docker-compose.yml | 80 ++++++++++++++++++++++++++ docs/setup.md | 117 +++++++++++++++++++++++++++++++------- 4 files changed, 196 insertions(+), 19 deletions(-) create mode 100644 docker/.env.example create mode 100644 docker/docker-compose.yml diff --git a/.gitignore b/.gitignore index 3f7d27b..d0b9389 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,9 @@ config/clients/* !config/clients/.gitkeep !config/clients/example.yaml +# Docker env file with real credentials +docker/.env + # OS .DS_Store Thumbs.db diff --git a/docker/.env.example b/docker/.env.example new file mode 100644 index 0000000..0a7d2b9 --- /dev/null +++ b/docker/.env.example @@ -0,0 +1,15 @@ +# Copy to docker/.env and fill in before running docker compose. +# Never commit docker/.env to version control. + +# XWiki database password (choose a strong password) +XWIKI_DB_PASSWORD=change-me-xwiki + +# Redmine database password (choose a strong password) +REDMINE_DB_PASSWORD=change-me-redmine + +# Redmine secret key base — generate with: openssl rand -hex 64 +REDMINE_SECRET_KEY_BASE=change-me-generate-with-openssl-rand-hex-64 + +# qms-kit runtime credentials (used by the adapters, not by docker compose) +XWIKI_PASSWORD=Admin +REDMINE_API_KEY=your-redmine-api-key-from-my-account diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..ffbb591 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,80 @@ +services: + + # --------------------------------------------------------------------------- + # XWiki — document management layer + # --------------------------------------------------------------------------- + xwiki: + image: xwiki:16-postgres-tomcat + container_name: qms-xwiki + depends_on: + xwiki-db: + condition: service_healthy + environment: + DB_HOST: xwiki-db + DB_DATABASE: xwiki + DB_USER: xwiki + DB_PASSWORD: ${XWIKI_DB_PASSWORD} + ports: + - "8080:8080" + volumes: + - xwiki-data:/usr/local/xwiki + restart: unless-stopped + + xwiki-db: + image: postgres:16-alpine + container_name: qms-xwiki-db + environment: + POSTGRES_DB: xwiki + POSTGRES_USER: xwiki + POSTGRES_PASSWORD: ${XWIKI_DB_PASSWORD} + volumes: + - xwiki-db-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U xwiki"] + interval: 10s + timeout: 5s + retries: 5 + restart: unless-stopped + + # --------------------------------------------------------------------------- + # Redmine — records and tracking layer + # --------------------------------------------------------------------------- + redmine: + image: redmine:5-alpine + container_name: qms-redmine + depends_on: + redmine-db: + condition: service_healthy + environment: + REDMINE_DB_POSTGRES: redmine-db + REDMINE_DB_DATABASE: redmine + REDMINE_DB_USERNAME: redmine + REDMINE_DB_PASSWORD: ${REDMINE_DB_PASSWORD} + REDMINE_SECRET_KEY_BASE: ${REDMINE_SECRET_KEY_BASE} + ports: + - "3000:3000" + volumes: + - redmine-data:/usr/src/redmine/files + restart: unless-stopped + + redmine-db: + image: postgres:16-alpine + container_name: qms-redmine-db + environment: + POSTGRES_DB: redmine + POSTGRES_USER: redmine + POSTGRES_PASSWORD: ${REDMINE_DB_PASSWORD} + volumes: + - redmine-db-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U redmine"] + interval: 10s + timeout: 5s + retries: 5 + restart: unless-stopped + +volumes: + xwiki-data: + xwiki-db-data: + redmine-data: + redmine-db-data: diff --git a/docs/setup.md b/docs/setup.md index 36f502d..311c1ea 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -1,37 +1,116 @@ # Setup Guide -> TODO: Full installation walkthrough will be written once the self-hosted adapter is complete. +Complete walkthrough from zero to first QMS deployment on the self-hosted stack (XWiki + Redmine). ## Prerequisites -- Docker and Docker Compose installed -- Python 3.12+ -- `uv` package manager (`pip install uv`) +- **Docker + Docker Compose** ≥ 24 / v2 — [docs.docker.com](https://docs.docker.com/get-docker/) +- **Python** ≥ 3.12 — [python.org](https://www.python.org/downloads/) +- **uv** (latest) — `pip install uv` -## Quick Start (Self-hosted) +## Step 1 — Clone and install ```bash -# 1. Clone the repository git clone https://github.com/gerfru/qms-kit.git cd qms-kit - -# 2. Install dependencies uv sync +``` + +## Step 2 — Start the self-hosted stack + +```bash +# Copy the env template and fill in passwords +cp docker/.env.example docker/.env +# edit docker/.env — set real passwords and generate REDMINE_SECRET_KEY_BASE: +# openssl rand -hex 64 + +docker compose -f docker/docker-compose.yml up -d +``` + +First startup takes 2–5 minutes while XWiki and Redmine initialize their databases. + +**Verify both services are up:** -# 3. Copy and fill in the client overlay -cp config/selfhosted.yaml config/clients/acme.yaml -# edit config/clients/acme.yaml with your org details, XWiki URL, Redmine URL +| Service | URL | Default credentials | +|---------|----------------------------------------|---------------------| +| XWiki | | Admin / Admin | +| Redmine | | admin / admin | + +> Change default passwords immediately after first login. + +## Step 3 — One-time Redmine setup (manual) + +Custom field *definitions* cannot be created via the Redmine API — they must be set up once through the web UI. + +Follow [docs/redmine-setup.md](redmine-setup.md) to create the required trackers and custom fields. + +After setup: go to **My account → API access key** in Redmine and copy the key into `docker/.env` as `REDMINE_API_KEY`. + +## Step 4 — Create a client overlay + +```bash +cp config/clients/example.yaml config/clients/acme.yaml +``` -# 4. Start the self-hosted stack -docker compose -f adapters/selfhosted/docker-compose.yml up -d +Edit `config/clients/acme.yaml`: -# 5. Run the one-time Redmine manual setup (custom field definitions) -# See docs/redmine-setup.md +```yaml +organisation: + name: "Acme GmbH" + short: "ACM" + quality_officer: "Jane Smith" + management: "John Doe" + +selfhosted: + xwiki: + base_url: "http://localhost:8080" + space_key: "QMS" + username: "Admin" + redmine: + base_url: "http://localhost:3000" + project_key: "qms-acme" + tracker_mapping: + nc: "Nonconformity" + capa: "CAPA" + audit: "Internal Audit" + kpi: "KPI Measurement" +``` + +## Step 5 — Dry-run (recommended first) + +```bash +export XWIKI_PASSWORD=Admin +export REDMINE_API_KEY= + +qms-kit deploy --target selfhosted --config config/clients/acme.yaml --dry-run +``` + +This renders all templates and validates the config without touching XWiki or Redmine. +Expected output: `Dry run complete — N template(s) rendered, nothing deployed.` + +## Step 6 — Deploy + +```bash +qms-kit deploy --target selfhosted --config config/clients/acme.yaml +``` + +The command will: + +1. Create the QMS space in XWiki and upsert all document pages +2. Create the QMS project in Redmine and seed scaffold issues per tracker + +## Stopping and cleaning up + +```bash +# Stop containers (data volumes preserved) +docker compose -f docker/docker-compose.yml down -# 6. Deploy the QMS scaffold -python -m cli deploy --target selfhosted --config config/clients/acme.yaml +# Full reset including all data volumes +docker compose -f docker/docker-compose.yml down -v ``` -## Next Steps +## Next steps -See [Configuration Reference](configuration.md) for all available options. +- [Configuration Reference](configuration.md) — all YAML keys explained +- [Redmine Setup](redmine-setup.md) — manual tracker and custom field setup +- [Architecture](architecture.md) — how the layers fit together