EXTENDED: Deabonination, Berechtigungen, Sortierung der Projekte
docker-build-and-push / build (push) Successful in 18s

This commit is contained in:
KI-Sparringpartner
2026-08-03 15:51:44 +02:00
parent 419ee9dfb0
commit 8827234afc
3 changed files with 102 additions and 15 deletions
+32
View File
@@ -1,4 +1,5 @@
import subprocess import subprocess
import os
from pathlib import Path from pathlib import Path
@@ -17,9 +18,40 @@ def clone_repo(repo_url: str, target_dir: Path, token: str = None):
text=True text=True
) )
# ✅ BERECHTIGUNGEN SETZEN NACH DEM KLOONEN!
if result.returncode == 0:
try:
os.chmod(str(target_dir), 0o755)
for root, dirs, files in os.walk(str(target_dir)):
for d in dirs:
os.chmod(os.path.join(root, d), 0o755)
for f in files:
os.chmod(os.path.join(root, f), 0o644)
except Exception as e:
print(f"Warnung bei Berechtigungen: {e}")
return {"success": result.returncode == 0, "output": result.stdout, "error": result.stderr} return {"success": result.returncode == 0, "output": result.stdout, "error": result.stderr}
def unsubscribe_repo(target_dir: Path):
"""Loescht ein abonniertes Projekt."""
import shutil
if not target_dir.exists():
return {"success": False, "error": "Projekt nicht gefunden"}
try:
# ✅ ALLE CHANGES MARKIERUNG LÖSCHEN
changes_marker = target_dir / ".gitea-changes.marker"
if changes_marker.exists():
changes_marker.unlink()
shutil.rmtree(str(target_dir))
return {"success": True, "message": f"{target_dir.name} erfolgreich geloescht"}
except Exception as e:
return {"success": False, "error": str(e)}
def push_changes(repo_path: Path, message: str = "Update 🧠"): def push_changes(repo_path: Path, message: str = "Update 🧠"):
"""Pusht lokale Änderungen ins Repository.""" """Pusht lokale Änderungen ins Repository."""
result = subprocess.run( result = subprocess.run(
+1 -1
View File
@@ -5,7 +5,7 @@ from fastapi.responses import HTMLResponse
# Umgebungsvariablen mit Fallback # Umgebungsvariablen mit Fallback
GITEE_URL = os.getenv("GITEE_URL", "https://git.carabella.ch/") GITEE_URL = os.getenv("GITEE_URL", "https://git.carabella.ch/")
ACCESS_TOKEN = os.getenv("ACCESS_TOKEN", "") # Leerlassen, falls nicht gesetzt ACCESS_TOKEN = os.getenv("ACCESS_TOKEN", "") # Leerlassen falls nicht gesetzt
COMMIT_NAME = os.getenv("COMMIT_NAME", "KI-Sparringpartner") COMMIT_NAME = os.getenv("COMMIT_NAME", "KI-Sparringpartner")
# Projekt-Ordner (gemountet im Docker) # Projekt-Ordner (gemountet im Docker)
+69 -14
View File
@@ -1,4 +1,5 @@
import httpx import httpx
import os
from pathlib import Path from pathlib import Path
from fastapi import APIRouter, HTTPException from fastapi import APIRouter, HTTPException
from pydantic import BaseModel from pydantic import BaseModel
@@ -13,39 +14,90 @@ class SubscribeRequest(BaseModel):
token_provided: str | None = None token_provided: str | None = None
@router.get("/projects") #✅ NEU: Listet abonierte Projekte! class UnsubscribeRequest(BaseModel):
"""Schema fuer Deabonination"""
project_name: str
@router.get("/projects") #✅ Listet abonierte Projekte!
async def list_projects(): async def list_projects():
"""Listet lokale (abonierte) Projekte an.""" """Listet lokale (abonierte) Projekte an."""
from .main import PROJECTS_DIR, get_token, ACCESS_TOKEN from .main import PROJECTS_DIR, ACCESS_TOKEN
projects = [] projects = []
unsubscribed_markers = set() #Fuer Deabonination Marker
if PROJECTS_DIR.exists() and str(PROJECTS_DIR).strip(): if PROJECTS_DIR.exists() and str(PROJECTS_DIR).strip():
# Suche nach .gitea-unsubscribe marker Dateien (fuer Deabbonation)
for marker_file in PROJECTS_DIR.glob(".*.unsubscribe"):
project_name = marker_file.stem.replace(".gitea-unsubscribe", "")
unsubscribed_markers.add(project_name)
if PROJECTS_DIR.exists() and str(PROJECTS_DIR).strip():
# Sortiere: Abonnierte zuerst, dann alphabtsortiert
all_projects = []
for project_dir in sorted(PROJECTS_DIR.iterdir()): for project_dir in sorted(PROJECTS_DIR.iterdir()):
if project_dir.is_dir() and not project_dir.name.startswith('.'): if project_dir.is_dir() and not project_dir.name.startswith('.'):
# Prüfe ob es ein Git-Repo ist
has_git = (project_dir / '.git').exists() has_git = (project_dir / '.git').exists()
changes_marker = Path(project_dir / ".gitea-changes.marker")
# Prüfe auf unentdeckte Changes
changes_marker = project_dir /'.gitea-changes.marker'
has_changes = changes_marker.exists() has_changes = changes_marker.exists()
projects.append({ all_projects.append({
"name": project_dir.name, "name": project_dir.name,
"path": str(project_dir), "path": str(project_dir),
"subscribed": True, "subscribed": True,
"has_git": bool(has_git), "has_git": bool(has_git),
"has_undiscovered_changes": has_changes "has_undiscovered_changes": has_changes,
"_sort_priority": 0 if not has_changes else 1 # ohne changes = priority höher (kommt zuerst)
}) })
# Sortiere nach Changes-Status zuerst, dann alphabetisch
projects = sorted(all_projects, key=lambda x: (-x["_sort_priority"], x["name"]))
# Entferne Hilfsfeld für API-Ausgabe
for p in projects:
del p["_sort_priority"]
return {"projects": projects} return {"projects": projects}
@router.get("/repos") #✅ VORHANDEN: Listet Gitea-Repos! @router.post("/unsubscribe") #❌ NEU: Deabonination Endpoint!
async def unsubscribe_repo(req: UnsubscribeRequest):
"""Loeschen eines abonnierten Projekts."""
from .main import PROJECTS_DIR
target_dir = PROJECTS_DIR / req.project_name
if not target_dir.exists():
return {"success": False, "error": f"Projekt '{req.project_name}' nicht gefunden"}
result = unsubscribe_repo(target_dir)
return result
def unsubscribe_repo(target_dir: Path):
"""Loeschen eines abonnierten Projekts."""
import shutil
if not target_dir.exists():
return {"success": False, "error": "Projekt nicht gefunden"}
try:
shutil.rmtree(str(target_dir))
marker_file = Path(str(target_dir) + ".unsubscribe")
marker_file.touch()
return {"success": True, "message": f"{target_dir.name} erfolgreich geloescht"}
except Exception as e:
return {"success": False, "error": str(e)}
@router.get("/repos") # Listet Gitea-Repos!
async def list_repos(): async def list_repos():
"""Listet Repositories von Gitea auf.""" """Listet Repositories von Gitea auf."""
from .main import GITEE_URL, ACCESS_TOKEN from .main import GITEE_URL, ACCESS_TOKEN
token = ACCESS_TOKEN or "" #✅ Direkter Zugriff! token = ACCESS_TOKEN or ""
if not token: if not token:
return {"error": "Kein Token vorhanden!", "hint": "Setze ACCESS_TOKEN Umgebungsvariable"} return {"error": "Kein Token vorhanden!", "hint": "Setze ACCESS_TOKEN Umgebungsvariable"}
@@ -57,7 +109,7 @@ async def list_repos():
return resp.json() return resp.json()
@router.post("/subscribe") #✅ VORHANDEN: Clont Repo! @router.post("/subscribe") # Klont Repo!
async def subscribe_repo(req: SubscribeRequest): async def subscribe_repo(req: SubscribeRequest):
"""Abonniert und klont ein Repository.""" """Abonniert und klont ein Repository."""
from .main import ACCESS_TOKEN from .main import ACCESS_TOKEN
@@ -74,11 +126,14 @@ async def subscribe_repo(req: SubscribeRequest):
if not result["success"]: if not result["success"]:
return {"error": result.get("error"), "success": False} return {"error": result.get("error"), "success": False}
# Marker für Changes erstellen
(target_dir / ".gitea-changes.marker").touch() (target_dir / ".gitea-changes.marker").touch()
generate_changelog(target_dir) generate_changelog(target_dir)
# Loesche Deabonination-Marker wenn Projekt wieder abonniert wurde
marker_file = Path(str(target_dir) + ".unsubscribe")
if marker_file.exists():
marker_file.unlink()
return { return {
"message": f"{req.target_name} geklont!", "message": f"{req.target_name} geklont!",
"path": str(target_dir), "path": str(target_dir),
@@ -87,6 +142,6 @@ async def subscribe_repo(req: SubscribeRequest):
} }
# Importiere hier damit die Funktionen verfügbar sind! # Importiere die Funktionen am Ende damit sie verfügbar sind!
from .git_manager import clone_repo from .git_manager import clone_repo
from .changelog_gen import generate_changelog from .changelog_gen import generate_changelog