build: Docker-Images, Compose-Setup und Gitea-Workflows
- Mehrstufige Dockerfiles je Dienst mit Stufen für Entwicklung und Betrieb, beide Images laufen als unprivilegierter Benutzer - Backend-Entrypoint wartet auf die Datenbank, migriert und seedt nur bei leerem Kategoriebaum (neues Flag --if-empty) - docker-compose.yml mit Netz-Trennung, Healthchecks und benannten Volumes; die Datenbank ist ausschließlich im internen Netz erreichbar - nginx liefert das Bundle aus und reicht /api weiter; index.html ungecacht, gehashte Assets ein Jahr - Gitea-Workflows: ci.yml für Lint, Tests und Bundle-Bau, build.yml für die Images nach linux/amd64 ohne Deploy-Schritt - docs/runner.md und docs/deployment.md, .dockerignore je Dienst Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014e7t8UpmoVNMtWivY5LiSH
This commit is contained in:
@@ -1,14 +1,28 @@
|
||||
"""CLI: Stammdaten seeden – `python -m app.scripts.seed`."""
|
||||
"""CLI: Stammdaten seeden – `python -m app.scripts.seed [--if-empty]`."""
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
|
||||
from sqlalchemy import func, select
|
||||
|
||||
from app.db.session import SessionLocal, engine
|
||||
from app.models import Category
|
||||
from app.services.seed import seed_all
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
async def main(only_if_empty: bool) -> None:
|
||||
async with SessionLocal() as session:
|
||||
if only_if_empty:
|
||||
vorhanden = (
|
||||
await session.execute(select(func.count()).select_from(Category))
|
||||
).scalar_one()
|
||||
if vorhanden:
|
||||
print(f"Seed übersprungen: es gibt bereits {vorhanden} Kategorien.")
|
||||
await engine.dispose()
|
||||
return
|
||||
|
||||
result = await seed_all(session)
|
||||
|
||||
await engine.dispose()
|
||||
print(
|
||||
f"Seed fertig: {result['categories']} Kategorien, "
|
||||
@@ -17,4 +31,12 @@ async def main() -> None:
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument(
|
||||
"--if-empty",
|
||||
action="store_true",
|
||||
help="Nur seeden, wenn noch keine Kategorie existiert. Für den Containerstart gedacht, "
|
||||
"damit bewusst gelöschte Kategorien nicht wiederkehren.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
asyncio.run(main(args.if_empty))
|
||||
|
||||
Reference in New Issue
Block a user