From d7d61f2013e914cba1ea20bb016a93cc4f63918b Mon Sep 17 00:00:00 2001 From: patrick Date: Thu, 3 Sep 2026 23:18:16 +0200 Subject: [PATCH] CI umgebaut: Host-Runner ohne Docker statt Service-Container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gitea-Actions-Runner läuft im Host-Modus (kein Docker installiert, Nutzerentscheidung Prompt 19). Service-Container für PostgreSQL funktionieren damit nicht - CI nutzt stattdessen die auf dem Runner-Host ohnehin laufende System-PostgreSQL und legt sich eine eigene Test-Datenbank an/räumt sie danach wieder weg. Hinweis: Runner läuft auf demselben Host wie die Produktionsinstanz (192.168.1.238) - keine Isolation zwischen CI und Prod, akzeptabler Kompromiss für V1/Einzelserver-Setup. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01L85hmKbvX7Cqkq47KnQhFt --- .gitea/workflows/ci.yml | 54 ++++++++++++++++++----------------- DEVLOG.md | 29 +++++++++++++++++++ backend/app/models/vorlage.py | 43 ++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 26 deletions(-) create mode 100644 backend/app/models/vorlage.py diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 17d97fa..6eefff5 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -9,23 +9,16 @@ on: jobs: backend-tests: runs-on: ubuntu-latest - services: - postgres: - image: postgres:16 - env: - POSTGRES_USER: mabea - POSTGRES_PASSWORD: mabea_test - POSTGRES_DB: mabea_test - ports: - - 5432:5432 - options: >- - --health-cmd "pg_isready -U mabea" - --health-interval 10s - --health-timeout 5s - --health-retries 5 + # Runner läuft im Host-Modus ohne Docker (Nutzerentscheidung: kein Docker-Zwang, + # Prompt 19). Dadurch keine isolierten Service-Container möglich – die Tests + # nutzen stattdessen die auf dem Runner-Host bereits laufende System-PostgreSQL + # und legen sich dort eine eigene, wegwerfbare Test-Datenbank an. env: - DATABASE_URL: postgresql+asyncpg://mabea:mabea_test@localhost:5432/mabea_test + TEST_DB_NAME: mabea_ci_test + TEST_DB_USER: mabea_ci + TEST_DB_PASSWORD: mabea_ci_test + DATABASE_URL: postgresql+asyncpg://mabea_ci:mabea_ci_test@localhost:5432/mabea_ci_test JWT_SECRET_KEY: ci-test-secret-key SYSTEMKNOTEN_ID: "1" @@ -36,17 +29,26 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install dependencies + - name: Test-Datenbank anlegen (idempotent, System-PostgreSQL des Runner-Hosts) run: | - python -m pip install --upgrade pip - pip install -e ".[dev]" + su -s /bin/bash postgres -c "psql -tAc \"SELECT 1 FROM pg_roles WHERE rolname='${TEST_DB_USER}'\"" | grep -q 1 || \ + su -s /bin/bash postgres -c "psql -c \"CREATE ROLE ${TEST_DB_USER} WITH LOGIN PASSWORD '${TEST_DB_PASSWORD}';\"" + su -s /bin/bash postgres -c "psql -c \"DROP DATABASE IF EXISTS ${TEST_DB_NAME};\"" + su -s /bin/bash postgres -c "psql -c \"CREATE DATABASE ${TEST_DB_NAME} OWNER ${TEST_DB_USER};\"" + su -s /bin/bash postgres -c "psql -d ${TEST_DB_NAME} -c \"CREATE EXTENSION IF NOT EXISTS pgcrypto;\"" - - name: Run migrations - run: alembic upgrade head + - name: Virtualenv + Dependencies installieren + run: | + python3 -m venv .venv + .venv/bin/pip install -q --upgrade pip + .venv/bin/pip install -q -e ".[dev]" - - name: Run tests with coverage gate - run: pytest + - name: Migrationen ausführen + run: .venv/bin/alembic upgrade head + + - name: Tests mit Coverage-Gate + run: .venv/bin/pytest + + - name: Test-Datenbank aufräumen + if: always() + run: su -s /bin/bash postgres -c "psql -c \"DROP DATABASE IF EXISTS ${TEST_DB_NAME};\"" diff --git a/DEVLOG.md b/DEVLOG.md index 31e8327..3eac9ac 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -1131,3 +1131,32 @@ Keine Commits in dieser Session. - backend/alembic/versions/0001_initial_schema.py | 16 ++++++++++++++-- --- +## 2026-09-03 23:07 – 23:13 (5m) +**Beschreibung:** Claude Code Session +**Projekt:** asb-material + +### Commits +- b9cf195 Sprint 1: Stammdaten-Modelle/-Endpunkte, Admin-Benutzerverwaltung, Zuständigkeits-Vererbung (E2) + +### Geänderte Dateien +- DEVLOG.md | 12 ++++++ +- backend/app/api/v1/api.py | 5 ++- +- backend/app/api/v1/endpoints/benutzer.py | 70 +++++++++++++++++++++++++++++++ +- backend/app/api/v1/endpoints/stammdaten.py | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +- backend/app/api/v1/endpoints/zustaendigkeit.py | 89 +++++++++++++++++++++++++++++++++++++++ +- backend/app/models/__init__.py | 21 +++++++++- +- backend/app/models/objekt.py | 35 ++++++++++++++++ +- backend/app/models/stammdaten.py | 66 +++++++++++++++++++++++++++++ +- backend/app/models/zustaendigkeit.py | 37 +++++++++++++++++ +- backend/app/schemas/__init__.py | 0 +- backend/app/schemas/benutzer.py | 35 ++++++++++++++++ +- backend/app/schemas/stammdaten.py | 76 ++++++++++++++++++++++++++++++++++ +- backend/app/schemas/zustaendigkeit.py | 45 ++++++++++++++++++++ +- backend/app/services/__init__.py | 0 +- backend/app/services/zustaendigkeit.py | 28 +++++++++++++ +- backend/tests/conftest.py | 61 +++++++++++++++++++++++++++ +- backend/tests/test_benutzer_admin.py | 56 +++++++++++++++++++++++++ +- backend/tests/test_stammdaten.py | 62 +++++++++++++++++++++++++++ +- backend/tests/test_zustaendigkeit.py | 80 +++++++++++++++++++++++++++++++++++ + +--- diff --git a/backend/app/models/vorlage.py b/backend/app/models/vorlage.py new file mode 100644 index 0000000..87dbc2b --- /dev/null +++ b/backend/app/models/vorlage.py @@ -0,0 +1,43 @@ +import enum +from decimal import Decimal + +from sqlalchemy import ForeignKey, Integer, Numeric, String, UniqueConstraint +from sqlalchemy.dialects.postgresql import ENUM as PgEnum +from sqlalchemy.orm import Mapped, mapped_column + +from app.db.base import Base + + +class VorlageStatus(str, enum.Enum): + aktiv = "aktiv" + veraltet = "veraltet" + + +vorlage_status_pg = PgEnum(VorlageStatus, name="vorlage_status", create_type=False) + + +class Beladungsvorlage(Base): + """Prompt 08: Vorlage ist versioniert – Änderungen erzeugen eine neue Version + statt die bestehende zu überschreiben (siehe app/services/vorlagen.py).""" + + __tablename__ = "beladungsvorlage" + __table_args__ = (UniqueConstraint("objekttyp_id", "name", "version"),) + + id: Mapped[int] = mapped_column(primary_key=True) + objekttyp_id: Mapped[int] = mapped_column(ForeignKey("objekttyp.id"), nullable=False) + name: Mapped[str] = mapped_column(String, nullable=False) + version: Mapped[int] = mapped_column(Integer, nullable=False) + status: Mapped[VorlageStatus] = mapped_column( + vorlage_status_pg, nullable=False, default=VorlageStatus.aktiv + ) + + +class Vorlagenposition(Base): + __tablename__ = "vorlagenposition" + __table_args__ = (UniqueConstraint("vorlage_id", "material_id"),) + + id: Mapped[int] = mapped_column(primary_key=True) + vorlage_id: Mapped[int] = mapped_column(ForeignKey("beladungsvorlage.id"), nullable=False) + material_id: Mapped[int] = mapped_column(ForeignKey("material.id"), nullable=False) + fach: Mapped[str | None] = mapped_column(String) + sollmenge: Mapped[Decimal] = mapped_column(Numeric, nullable=False)