mirror of
https://github.com/skoelle/calender_sync.git
synced 2026-09-18 10:40:25 +00:00
Compare commits
12
Commits
7bfd7c0ff3
...
7c07a8f866
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c07a8f866 | ||
|
|
9bda384559 | ||
|
|
6c6848bb0d | ||
|
|
0ca20bab27 | ||
|
|
87353aa362 | ||
|
|
a5c02cd6e0 | ||
|
|
0b1cc9ed45 | ||
|
|
9af9b66447 | ||
|
|
66b9018b16 | ||
|
|
52de06bbbb | ||
|
|
e10c6fc169 | ||
|
|
946c2832ea |
@@ -1,3 +1,5 @@
|
|||||||
.env
|
.env
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
|
.venv/
|
||||||
|
demo/demo.db
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Python-basierter Google Calendar → MariaDB Synchronizer. Läuft als Docker Con
|
|||||||
## Technologie-Stack
|
## Technologie-Stack
|
||||||
|
|
||||||
- **Sprache**: Python 3.12
|
- **Sprache**: Python 3.12
|
||||||
- **Datenbank**: MariaDB (mysql-connector-python)
|
- **Datenbank**: MariaDB (mysql-connector-python) oder SQLite (stdlib) via `DB_BACKEND` Env-Var
|
||||||
- **API**: FastAPI + Uvicorn + Jinja2
|
- **API**: FastAPI + Uvicorn + Jinja2
|
||||||
- **Docker**: Multi-Stage Build nicht verwendet, simples Slim-Image
|
- **Docker**: Multi-Stage Build nicht verwendet, simples Slim-Image
|
||||||
- **Dependencies**: requests, icalendar, recurring-ical-events, mysql-connector-python, fastapi, uvicorn, jinja2
|
- **Dependencies**: requests, icalendar, recurring-ical-events, mysql-connector-python, fastapi, uvicorn, jinja2
|
||||||
@@ -21,9 +21,12 @@ Python-basierter Google Calendar → MariaDB Synchronizer. Läuft als Docker Con
|
|||||||
├── sync.py # Hauptskript (alles in einer Datei)
|
├── sync.py # Hauptskript (alles in einer Datei)
|
||||||
├── api/
|
├── api/
|
||||||
│ ├── main.py # FastAPI App (REST API + HTML UI)
|
│ ├── main.py # FastAPI App (REST API + HTML UI)
|
||||||
│ ├── database.py # DB-Verbindung für die API
|
│ ├── database.py # DB-Verbindung (MySQL + SQLite)
|
||||||
│ └── templates/
|
│ └── templates/
|
||||||
│ └── index.html # Jinja2 Template für Web-UI
|
│ └── index.html # Jinja2 Template für Web-UI
|
||||||
|
├── demo/
|
||||||
|
│ └── seed.py # SQLite Demo-Datenbank erstellen
|
||||||
|
├── demo.sh # Lokaler Demo-Start (ohne Docker)
|
||||||
├── requirements.txt # Python Dependencies
|
├── requirements.txt # Python Dependencies
|
||||||
├── Dockerfile # Docker Image Definition
|
├── Dockerfile # Docker Image Definition
|
||||||
├── docker-compose.yml # Docker Compose Konfiguration
|
├── docker-compose.yml # Docker Compose Konfiguration
|
||||||
@@ -71,7 +74,7 @@ Sendet täglich um konfigurierte Uhrzeit eine HTML-Email mit anstehenden Termine
|
|||||||
|
|
||||||
## Entwicklung
|
## Entwicklung
|
||||||
|
|
||||||
### Lokaler Test
|
### Lokaler Test (Docker)
|
||||||
```bash
|
```bash
|
||||||
# Dependencies installieren
|
# Dependencies installieren
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
@@ -83,6 +86,20 @@ cp .env.example .env
|
|||||||
python sync.py
|
python sync.py
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Lokaler Test (Demo, ohne MariaDB)
|
||||||
|
```bash
|
||||||
|
# Startet venv, erstellt SQLite Demo-DB mit 4 Events, startet API
|
||||||
|
./demo.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Oder manuell:
|
||||||
|
```bash
|
||||||
|
python3 -m venv .venv && source .venv/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
python demo/seed.py
|
||||||
|
DB_BACKEND=sqlite DB_PATH=demo/demo.db python -m uvicorn api.main:app --port 8000
|
||||||
|
```
|
||||||
|
|
||||||
### Linting & Type Checking
|
### Linting & Type Checking
|
||||||
Keine Linting/Type-Checking Tools konfiguriert. Bei Bedarf hinzufügen:
|
Keine Linting/Type-Checking Tools konfiguriert. Bei Bedarf hinzufügen:
|
||||||
- `ruff` für Linting
|
- `ruff` für Linting
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ Läuft als Docker Container, pollt periodisch einen privaten Google Calendar ICS
|
|||||||
- REST API für programmsprachigen Zugriff auf Kalenderdaten
|
- REST API für programmsprachigen Zugriff auf Kalenderdaten
|
||||||
- Optionale tägliche E-Mail-Benachrichtigung um konfigurierte Uhrzeit
|
- Optionale tägliche E-Mail-Benachrichtigung um konfigurierte Uhrzeit
|
||||||
|
|
||||||
|
[](docs/screenshot.png)
|
||||||
|
|
||||||
## Voraussetzungen
|
## Voraussetzungen
|
||||||
|
|
||||||
- Docker & Docker Compose
|
- Docker & Docker Compose
|
||||||
@@ -53,6 +55,31 @@ Läuft als Docker Container, pollt periodisch einen privaten Google Calendar ICS
|
|||||||
docker compose up -d
|
docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Demo / Lokaler Test (ohne Docker)
|
||||||
|
|
||||||
|
Schneller Weg um Web-UI und API lokal zu testen, ohne MariaDB oder Docker:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./demo.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Das Script erstellt eine virtuelle Python-Umgebung, installiert Dependencies, legt eine SQLite Demo-Datenbank mit 4 Beispiel-Terminen an und startet die API auf Port 8000.
|
||||||
|
|
||||||
|
**Endpoints:**
|
||||||
|
- Web-UI: http://localhost:8000/
|
||||||
|
- API JSON: http://localhost:8000/api/events
|
||||||
|
- Health: http://localhost:8000/api/health
|
||||||
|
|
||||||
|
**Demo-Events (4 Stück):**
|
||||||
|
| Termin | Zeit | Status |
|
||||||
|
|--------|------|--------|
|
||||||
|
| Team Daily | heute 14:00–15:00 | CONFIRMED |
|
||||||
|
| Yoga Kurs | heute 18:30–20:00 | CONFIRMED |
|
||||||
|
| Projekt-Deadline | morgen Ganztag | TENTATIVE |
|
||||||
|
| Arzttermin | übermorgen 10:00–11:30 | CONFIRMED |
|
||||||
|
|
||||||
|
Die Demo verwendet SQLite (`DB_BACKEND=sqlite`) statt MariaDB. Die Events werden relativ zum heutigen Datum erstellt.
|
||||||
|
|
||||||
## Konfiguration
|
## Konfiguration
|
||||||
|
|
||||||
| Variable | Default | Beschreibung |
|
| Variable | Default | Beschreibung |
|
||||||
|
|||||||
+22
-7
@@ -2,16 +2,31 @@
|
|||||||
# Licensed under the MIT License. See LICENSE file in project root for details.
|
# Licensed under the MIT License. See LICENSE file in project root for details.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import mysql.connector
|
import sqlite3
|
||||||
|
|
||||||
DB_HOST = os.environ.get("DB_HOST", "mariadb.fritz.box")
|
DB_BACKEND = os.environ.get("DB_BACKEND", "mysql")
|
||||||
DB_PORT = int(os.environ.get("DB_PORT", "3306"))
|
|
||||||
DB_NAME = os.environ.get("DB_NAME", "calendar_sync")
|
|
||||||
DB_USER = os.environ["DB_USER"]
|
|
||||||
DB_PASSWORD = os.environ["DB_PASSWORD"]
|
|
||||||
|
|
||||||
|
if DB_BACKEND == "sqlite":
|
||||||
|
DB_PATH = os.environ.get("DB_PATH", "demo.db")
|
||||||
|
|
||||||
def get_connection(autocommit: bool = True):
|
def get_connection(autocommit: bool = True):
|
||||||
|
conn = sqlite3.connect(DB_PATH)
|
||||||
|
conn.execute("PRAGMA journal_mode=WAL")
|
||||||
|
conn.execute("PRAGMA foreign_keys=ON")
|
||||||
|
conn.row_factory = sqlite3.Row
|
||||||
|
if autocommit:
|
||||||
|
conn.isolation_level = None
|
||||||
|
return conn
|
||||||
|
else:
|
||||||
|
import mysql.connector
|
||||||
|
|
||||||
|
DB_HOST = os.environ.get("DB_HOST", "mariadb.fritz.box")
|
||||||
|
DB_PORT = int(os.environ.get("DB_PORT", "3306"))
|
||||||
|
DB_NAME = os.environ.get("DB_NAME", "calendar_sync")
|
||||||
|
DB_USER = os.environ["DB_USER"]
|
||||||
|
DB_PASSWORD = os.environ["DB_PASSWORD"]
|
||||||
|
|
||||||
|
def get_connection(autocommit: bool = True):
|
||||||
return mysql.connector.connect(
|
return mysql.connector.connect(
|
||||||
host=DB_HOST,
|
host=DB_HOST,
|
||||||
port=DB_PORT,
|
port=DB_PORT,
|
||||||
|
|||||||
+13
-7
@@ -12,10 +12,13 @@ from fastapi.responses import HTMLResponse
|
|||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from api.database import get_connection
|
from api.database import get_connection, DB_BACKEND
|
||||||
|
|
||||||
log = logging.getLogger("calendar-api")
|
log = logging.getLogger("calendar-api")
|
||||||
|
|
||||||
|
PH = "?" if DB_BACKEND == "sqlite" else "%s"
|
||||||
|
NOW_SQL = "datetime('now')" if DB_BACKEND == "sqlite" else "NOW()"
|
||||||
|
|
||||||
app = FastAPI(title="Calendar Sync API")
|
app = FastAPI(title="Calendar Sync API")
|
||||||
|
|
||||||
templates = Jinja2Templates(directory=Path(__file__).parent / "templates")
|
templates = Jinja2Templates(directory=Path(__file__).parent / "templates")
|
||||||
@@ -35,6 +38,8 @@ def to_local_timezone(dt: datetime) -> datetime:
|
|||||||
"""Konvertiere naive UTC datetime zu Benutzer-Zeitzone."""
|
"""Konvertiere naive UTC datetime zu Benutzer-Zeitzone."""
|
||||||
if dt is None:
|
if dt is None:
|
||||||
return None
|
return None
|
||||||
|
if isinstance(dt, str):
|
||||||
|
dt = datetime.fromisoformat(dt)
|
||||||
return dt.replace(tzinfo=timezone.utc).astimezone(TIMEZONE)
|
return dt.replace(tzinfo=timezone.utc).astimezone(TIMEZONE)
|
||||||
|
|
||||||
|
|
||||||
@@ -84,19 +89,19 @@ def fetch_events(limit: int = 10, search: str | None = None, calendar_label: str
|
|||||||
try:
|
try:
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
try:
|
try:
|
||||||
conditions = ["deleted = 0", "start_at >= NOW()"]
|
conditions = [f"deleted = 0", f"start_at >= {NOW_SQL}"]
|
||||||
params: list = []
|
params: list = []
|
||||||
if search:
|
if search:
|
||||||
conditions.append("summary LIKE %s")
|
conditions.append(f"summary LIKE {PH}")
|
||||||
params.append(f"%{search}%")
|
params.append(f"%{search}%")
|
||||||
if calendar_label:
|
if calendar_label:
|
||||||
conditions.append("calendar_label = %s")
|
conditions.append(f"calendar_label = {PH}")
|
||||||
params.append(calendar_label)
|
params.append(calendar_label)
|
||||||
params.append(limit)
|
params.append(limit)
|
||||||
where = " WHERE " + " AND ".join(conditions)
|
where = " WHERE " + " AND ".join(conditions)
|
||||||
cur.execute(
|
cur.execute(
|
||||||
f"SELECT {SELECT_COLUMNS} FROM calendar_events "
|
f"SELECT {SELECT_COLUMNS} FROM calendar_events "
|
||||||
f"{where} ORDER BY start_at ASC LIMIT %s",
|
f"{where} ORDER BY start_at ASC LIMIT {PH}",
|
||||||
tuple(params),
|
tuple(params),
|
||||||
)
|
)
|
||||||
return cur.fetchall()
|
return cur.fetchall()
|
||||||
@@ -141,7 +146,7 @@ def get_event(event_id: int):
|
|||||||
try:
|
try:
|
||||||
cur.execute(
|
cur.execute(
|
||||||
f"SELECT {SELECT_COLUMNS} FROM calendar_events "
|
f"SELECT {SELECT_COLUMNS} FROM calendar_events "
|
||||||
"WHERE id = %s AND deleted = 0",
|
f"WHERE id = {PH} AND deleted = 0",
|
||||||
(event_id,),
|
(event_id,),
|
||||||
)
|
)
|
||||||
row = cur.fetchone()
|
row = cur.fetchone()
|
||||||
@@ -186,6 +191,7 @@ def index(
|
|||||||
})
|
})
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
|
request,
|
||||||
"index.html",
|
"index.html",
|
||||||
{"request": request, "events": events, "search": search or ""},
|
{"events": events, "search": search or ""},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -116,7 +116,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.event-location::before {
|
.event-location::before {
|
||||||
content: "\\1F4CD ";
|
content: "\1F4CD ";
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
VENV_DIR="$SCRIPT_DIR/.venv"
|
||||||
|
DB_PATH="$SCRIPT_DIR/demo/demo.db"
|
||||||
|
|
||||||
|
echo "=== Calendar Sync Demo ==="
|
||||||
|
|
||||||
|
# Venv erstellen
|
||||||
|
if [ ! -d "$VENV_DIR" ]; then
|
||||||
|
echo "Erstelle virtuelles Umfeld..."
|
||||||
|
python3 -m venv "$VENV_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
source "$VENV_DIR/bin/activate"
|
||||||
|
|
||||||
|
# Dependencies installieren (ohne mysql-connector)
|
||||||
|
pip install -q --upgrade pip
|
||||||
|
pip install -q fastapi uvicorn[standard] jinja2 icalendar recurring-ical-events
|
||||||
|
|
||||||
|
# Demo-Datenbank erstellen
|
||||||
|
echo "Erstelle Demo-Datenbank mit 4 Terminen..."
|
||||||
|
python "$SCRIPT_DIR/demo/seed.py"
|
||||||
|
|
||||||
|
# API starten
|
||||||
|
echo ""
|
||||||
|
echo "Starte API auf http://localhost:8000"
|
||||||
|
echo " Web-UI: http://localhost:8000/"
|
||||||
|
echo " API: http://localhost:8000/api/events"
|
||||||
|
echo " Health: http://localhost:8000/api/health"
|
||||||
|
echo ""
|
||||||
|
echo "Druecke Ctrl+C zum Beenden."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
DB_BACKEND=sqlite DB_PATH="$DB_PATH" \
|
||||||
|
exec python -m uvicorn api.main:app --host 0.0.0.0 --port 8000
|
||||||
+117
@@ -0,0 +1,117 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Erstellt eine lokale SQLite Demo-Datenbank mit 4 Beispiel-Terminen."""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sqlite3
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
DB_PATH = os.environ.get("DB_PATH", os.path.join(os.path.dirname(__file__), "demo.db"))
|
||||||
|
|
||||||
|
SCHEMA = """
|
||||||
|
CREATE TABLE IF NOT EXISTS calendar_events (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
calendar_label TEXT NOT NULL,
|
||||||
|
instance_key TEXT NOT NULL,
|
||||||
|
uid TEXT NOT NULL,
|
||||||
|
recurrence_id TEXT,
|
||||||
|
summary TEXT,
|
||||||
|
description TEXT,
|
||||||
|
location TEXT,
|
||||||
|
start_at TEXT NOT NULL,
|
||||||
|
end_at TEXT,
|
||||||
|
all_day INTEGER NOT NULL DEFAULT 0,
|
||||||
|
status TEXT DEFAULT 'CONFIRMED',
|
||||||
|
deleted INTEGER NOT NULL DEFAULT 0,
|
||||||
|
last_seen_at TEXT NOT NULL,
|
||||||
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||||
|
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||||
|
UNIQUE (calendar_label, instance_key)
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_start ON calendar_events(start_at);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_deleted ON calendar_events(deleted);
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def seed():
|
||||||
|
if os.path.exists(DB_PATH):
|
||||||
|
os.remove(DB_PATH)
|
||||||
|
|
||||||
|
conn = sqlite3.connect(DB_PATH)
|
||||||
|
conn.executescript(SCHEMA)
|
||||||
|
|
||||||
|
now = datetime.now().replace(second=0, microsecond=0, tzinfo=None)
|
||||||
|
today = now.replace(hour=0, minute=0, second=0)
|
||||||
|
|
||||||
|
# Wenn es nach 14 Uhr ist, alle Termine einen Tag verschieben
|
||||||
|
# damit sie in der Demo immer sichtbar sind
|
||||||
|
day_offset = 1 if now.hour >= 14 else 0
|
||||||
|
base = today + timedelta(days=day_offset)
|
||||||
|
|
||||||
|
events = [
|
||||||
|
{
|
||||||
|
"calendar_label": "demo",
|
||||||
|
"instance_key": f"team-daily-{base.strftime('%Y%m%d')}",
|
||||||
|
"uid": "team-daily-001@demo",
|
||||||
|
"summary": "Team Daily",
|
||||||
|
"description": "Tägliches Standup-Meeting mit dem gesamten Team.",
|
||||||
|
"location": "Besprechungsraum A / Zoom",
|
||||||
|
"start_at": (base + timedelta(hours=14)).strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
"end_at": (base + timedelta(hours=15)).strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
"all_day": 0,
|
||||||
|
"status": "CONFIRMED",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"calendar_label": "demo",
|
||||||
|
"instance_key": f"yoga-{base.strftime('%Y%m%d')}",
|
||||||
|
"uid": "yoga-002@demo",
|
||||||
|
"summary": "Yoga Kurs",
|
||||||
|
"description": "Wöchentlicher Yoga-Kurs in der Volkshochschule.",
|
||||||
|
"location": "Volkshochschule, Raum 204",
|
||||||
|
"start_at": (base + timedelta(hours=18, minutes=30)).strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
"end_at": (base + timedelta(hours=20)).strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
"all_day": 0,
|
||||||
|
"status": "CONFIRMED",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"calendar_label": "demo",
|
||||||
|
"instance_key": f"deadline-{(base + timedelta(days=1)).strftime('%Y%m%d')}",
|
||||||
|
"uid": "deadline-003@demo",
|
||||||
|
"summary": "Projekt-Deadline",
|
||||||
|
"description": "Abgabe des Projektberichts an die Geschäftsleitung.",
|
||||||
|
"location": "",
|
||||||
|
"start_at": (base + timedelta(days=1)).strftime("%Y-%m-%d 00:00:00"),
|
||||||
|
"end_at": (base + timedelta(days=1)).strftime("%Y-%m-%d 23:59:59"),
|
||||||
|
"all_day": 1,
|
||||||
|
"status": "TENTATIVE",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"calendar_label": "demo",
|
||||||
|
"instance_key": f"arzt-{(base + timedelta(days=2)).strftime('%Y%m%d')}",
|
||||||
|
"uid": "arzt-004@demo",
|
||||||
|
"summary": "Arzttermin",
|
||||||
|
"description": "Routineuntersuchung beim Hausarzt. Bitte Impfpass mitbringen.",
|
||||||
|
"location": "Dr. Müller, Gesundheitsstraße 12",
|
||||||
|
"start_at": (base + timedelta(days=2, hours=10)).strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
"end_at": (base + timedelta(days=2, hours=11, minutes=30)).strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
"all_day": 0,
|
||||||
|
"status": "CONFIRMED",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
conn.executemany(
|
||||||
|
"""INSERT INTO calendar_events
|
||||||
|
(calendar_label, instance_key, uid, summary, description, location,
|
||||||
|
start_at, end_at, all_day, status, last_seen_at)
|
||||||
|
VALUES
|
||||||
|
(:calendar_label, :instance_key, :uid, :summary, :description, :location,
|
||||||
|
:start_at, :end_at, :all_day, :status, datetime('now'))""",
|
||||||
|
events,
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
print(f"Demo-Datenbank erstellt: {DB_PATH}")
|
||||||
|
print(f"4 Demo-Events eingefuegt.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
seed()
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"extends": [
|
||||||
|
"config:recommended"
|
||||||
|
],
|
||||||
|
"packageRules": [
|
||||||
|
{
|
||||||
|
"matchUpdateTypes": ["minor", "patch", "pin", "digest"],
|
||||||
|
"automerge": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matchManagers": ["github-actions"],
|
||||||
|
"groupName": "GitHub Actions",
|
||||||
|
"automerge": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matchManagers": ["dockerfile", "docker-compose"],
|
||||||
|
"groupName": "Docker",
|
||||||
|
"automerge": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matchPackageNames": ["icalendar"],
|
||||||
|
"matchUpdateTypes": ["major"],
|
||||||
|
"automerge": false,
|
||||||
|
"labels": ["major-update"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matchPackageNames": ["mysql-connector-python"],
|
||||||
|
"matchUpdateTypes": ["major"],
|
||||||
|
"automerge": false,
|
||||||
|
"labels": ["major-update"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"schedule": ["before 6am on Monday"]
|
||||||
|
}
|
||||||
+4
-4
@@ -1,7 +1,7 @@
|
|||||||
requests==2.32.3
|
requests==2.32.3
|
||||||
icalendar==6.1.0
|
icalendar==6.3.2
|
||||||
recurring-ical-events==3.4.1
|
recurring-ical-events==3.8.2
|
||||||
mysql-connector-python==9.1.0
|
mysql-connector-python==9.7.0
|
||||||
fastapi==0.115.0
|
fastapi==0.115.0
|
||||||
uvicorn[standard]==0.30.0
|
uvicorn[standard]==0.30.0
|
||||||
jinja2==3.1.4
|
jinja2==3.1.6
|
||||||
|
|||||||
Reference in New Issue
Block a user