feat(publish): Python-basierter publish-apk Workflow
This commit is contained in:
parent
09e01dff00
commit
39956cc7d9
3 changed files with 365 additions and 22 deletions
17
.github/prompts/publish.prompt.md
vendored
17
.github/prompts/publish.prompt.md
vendored
|
|
@ -2,7 +2,15 @@
|
|||
description: "publish – Neue App-Version bauen und auf dem VPS veröffentlichen. Bumpt die Version automatisch, baut die APK, lädt sie auf den Server hoch, verifiziert QR-Code-Seite + Update-API und committet den Version-Bump."
|
||||
name: "publish"
|
||||
agent: agent
|
||||
tools: [read, search, execute/runInTerminal, execute/sendToTerminal, execute/getTerminalOutput, edit]
|
||||
tools:
|
||||
[
|
||||
read,
|
||||
search,
|
||||
execute/runInTerminal,
|
||||
execute/sendToTerminal,
|
||||
execute/getTerminalOutput,
|
||||
edit,
|
||||
]
|
||||
---
|
||||
|
||||
Lies **zuerst** die Publish-Skill-Datei `.github/skills/publish/SKILL.md` vollständig mit `read_file`.
|
||||
|
|
@ -22,12 +30,13 @@ Führe danach den Publish-Workflow durch:
|
|||
## Schritt 2 – Publish-Skript ausführen
|
||||
|
||||
```powershell
|
||||
& ".github/skills/publish/publish-apk.ps1"
|
||||
python ".github/skills/publish/publish-apk.py"
|
||||
# oder mit explizitem versionName:
|
||||
& ".github/skills/publish/publish-apk.ps1" -VersionName "2.0"
|
||||
python ".github/skills/publish/publish-apk.py" --version-name "2.0"
|
||||
```
|
||||
|
||||
Das Skript erledigt vollautomatisch:
|
||||
|
||||
1. versionCode +1 in `app/build.gradle.kts`
|
||||
2. `./gradlew assembleDebug`
|
||||
3. APK per SCP auf VPS hochladen
|
||||
|
|
@ -38,6 +47,7 @@ Das Skript erledigt vollautomatisch:
|
|||
Verwende `mode=sync` mit `timeout=300000`.
|
||||
|
||||
**Voraussetzungen prüfen** (falls Fehler auftreten):
|
||||
|
||||
- SSH-Agent: `ssh-add -l`
|
||||
- Token: `$env:BOLLWERK_ADMIN_TOKEN` muss gesetzt sein
|
||||
|
||||
|
|
@ -46,6 +56,7 @@ Verwende `mode=sync` mit `timeout=300000`.
|
|||
## Schritt 3 – Ergebnis berichten
|
||||
|
||||
Berichte kurz:
|
||||
|
||||
- Neue Version (versionCode + versionName)
|
||||
- Build-Status (✅ / ❌)
|
||||
- VPS-Deployment (✅ / ❌)
|
||||
|
|
|
|||
38
.github/skills/publish/SKILL.md
vendored
38
.github/skills/publish/SKILL.md
vendored
|
|
@ -109,17 +109,17 @@ Invoke-WebRequest -Uri "https://bollwerk.online/" -UseBasicParsing | Select-Obje
|
|||
|
||||
## Automatisiertes Skript
|
||||
|
||||
Das Skript `publish-apk.ps1` in diesem Skill-Ordner automatisiert den **gesamten** Release-Workflow (Version bumpen, bauen, hochladen, API-Call, verifizieren, committen):
|
||||
Das Skript `publish-apk.py` in diesem Skill-Ordner automatisiert den **gesamten** Release-Workflow (Version bumpen, bauen, hochladen, API-Call, verifizieren, committen):
|
||||
|
||||
```powershell
|
||||
# Alles automatisch (versionCode +1, versionName bleibt):
|
||||
& ".github/skills/publish/publish-apk.ps1"
|
||||
python ".github/skills/publish/publish-apk.py"
|
||||
|
||||
# Mit neuem versionName:
|
||||
& ".github/skills/publish/publish-apk.ps1" -VersionName "2.0"
|
||||
python ".github/skills/publish/publish-apk.py" --version-name "2.0"
|
||||
|
||||
# Nur hochladen (APK bereits gebaut, kein Push):
|
||||
& ".github/skills/publish/publish-apk.ps1" -SkipBuild -SkipPush
|
||||
python ".github/skills/publish/publish-apk.py" --skip-build --skip-push
|
||||
```
|
||||
|
||||
**Voraussetzungen:**
|
||||
|
|
@ -129,14 +129,14 @@ Das Skript `publish-apk.ps1` in diesem Skill-Ordner automatisiert den **gesamten
|
|||
|
||||
**Parameter:**
|
||||
|
||||
| Parameter | Pflicht | Default | Beschreibung |
|
||||
| -------------- | ------- | ------------- | --------------------------------------------------- |
|
||||
| `-VersionName` | nein | aktueller | Neuer versionName (z.B. "2.0"); sonst unverändert |
|
||||
| `-VersionCode` | nein | aktuell + 1 | Expliziter versionCode; sonst automatisch +1 |
|
||||
| `-ApkPath` | nein | debug-APK | Pfad zur APK-Datei |
|
||||
| `-SkipBuild` | nein | false | Gradle-Build überspringen |
|
||||
| `-SkipVerify` | nein | false | Verifizierung nach dem Deploy überspringen |
|
||||
| `-SkipPush` | nein | false | Git-Push überspringen (nur lokaler Commit) |
|
||||
| Parameter | Pflicht | Default | Beschreibung |
|
||||
| ---------------- | ------- | ----------- | ------------------------------------------------- |
|
||||
| `--version-name` | nein | aktueller | Neuer versionName (z.B. "2.0"); sonst unverändert |
|
||||
| `--version-code` | nein | aktuell + 1 | Expliziter versionCode; sonst automatisch +1 |
|
||||
| `--apk-path` | nein | debug-APK | Pfad zur APK-Datei |
|
||||
| `--skip-build` | nein | false | Gradle-Build überspringen |
|
||||
| `--skip-verify` | nein | false | Verifizierung nach dem Deploy überspringen |
|
||||
| `--skip-push` | nein | false | Git-Push überspringen (nur lokaler Commit) |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -145,8 +145,8 @@ Das Skript `publish-apk.ps1` in diesem Skill-Ordner automatisiert den **gesamten
|
|||
| Datei | Beschreibung |
|
||||
| -------------------------------------------------------- | ------------------------------------------ |
|
||||
| `app/build.gradle.kts` (L18-19) | `versionCode` / `versionName` |
|
||||
| `server/src/main/resources/application.conf` | Server-Version-Defaults + Env-Var-Override |
|
||||
| `server/src/main/kotlin/.../store/VersionStore.kt` | Persistente Version in `data/version.json` |
|
||||
| `server/src/main/resources/application.conf` | Server-Version-Defaults + Env-Var-Override |
|
||||
| `server/src/main/kotlin/.../store/VersionStore.kt` | Persistente Version in `data/version.json` |
|
||||
| `server/src/main/kotlin/.../routes/VersionRoutes.kt` | Homepage + `/api/version` + POST-Endpoint |
|
||||
| `server/src/main/kotlin/.../plugins/Routing.kt` | `staticFiles("/static", File("data"))` |
|
||||
| `app/src/main/java/.../usecase/CheckForUpdateUseCase.kt` | Update-Prüfung in der App |
|
||||
|
|
@ -156,11 +156,11 @@ Das Skript `publish-apk.ps1` in diesem Skill-Ordner automatisiert den **gesamten
|
|||
|
||||
## Environment-Variablen (VPS)
|
||||
|
||||
| Variable | Pflicht | Beschreibung |
|
||||
| ------------------------------- | ------- | --------------------------------------------------------- |
|
||||
| `BOLLWERK_ADMIN_TOKEN` | ja | Bearer-Token für `POST /api/admin/version` (min. 32 Zeichen) |
|
||||
| `BOLLWERK_APP_VERSION_CODE` | nein | Fallback-VersionCode (nur wenn `data/version.json` fehlt) |
|
||||
| `BOLLWERK_APP_VERSION_NAME` | nein | Fallback-VersionName (nur wenn `data/version.json` fehlt) |
|
||||
| Variable | Pflicht | Beschreibung |
|
||||
| --------------------------- | ------- | ------------------------------------------------------------ |
|
||||
| `BOLLWERK_ADMIN_TOKEN` | ja | Bearer-Token für `POST /api/admin/version` (min. 32 Zeichen) |
|
||||
| `BOLLWERK_APP_VERSION_CODE` | nein | Fallback-VersionCode (nur wenn `data/version.json` fehlt) |
|
||||
| `BOLLWERK_APP_VERSION_NAME` | nein | Fallback-VersionName (nur wenn `data/version.json` fehlt) |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
332
.github/skills/publish/publish-apk.py
vendored
Normal file
332
.github/skills/publish/publish-apk.py
vendored
Normal file
|
|
@ -0,0 +1,332 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Vollstaendiger Publish-Workflow: Version bumpen, bauen, auf VPS deployen, committen.
|
||||
|
||||
Schritte:
|
||||
1. versionCode automatisch erhoehen (aus build.gradle.kts)
|
||||
2. APK bauen (./gradlew assembleDebug) – ueberspringbar mit --skip-build
|
||||
3. APK per SCP auf VPS hochladen
|
||||
4. Server-Version per API aktualisieren (kein Container-Neustart noetig)
|
||||
5. Verifizieren – ueberspringbar mit --skip-verify
|
||||
6. git commit + push – ueberspringbar mit --skip-push
|
||||
|
||||
Rollback-Strategie:
|
||||
- Fehler vor/waehrend Upload -> build.gradle.kts wird automatisch zurueckgesetzt
|
||||
- Fehler nach Upload (API) -> APK liegt schon oben; kein Rollback, Recovery-Hinweis
|
||||
- Fehler bei Verify/Git -> Deployment war erfolgreich; nur Warnung, kein Abbruch
|
||||
|
||||
Verwendung:
|
||||
python publish-apk.py
|
||||
python publish-apk.py --version-name 2.0
|
||||
python publish-apk.py --skip-build --skip-push
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
||||
GRADLEW = "gradlew.bat" if platform.system() == "Windows" else "./gradlew"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Konfiguration
|
||||
# ---------------------------------------------------------------------------
|
||||
VPS = "root@195.246.231.210"
|
||||
REMOTE_DIR = "/opt/bollwerk"
|
||||
SERVER_URL = "https://bollwerk.online"
|
||||
BUILD_GRADLE = "app/build.gradle.kts"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Farb-Hilfsfunktionen (ANSI, funktioniert in Windows Terminal / PowerShell 7)
|
||||
# ---------------------------------------------------------------------------
|
||||
RESET = "\033[0m"
|
||||
GREEN = "\033[92m"
|
||||
YELLOW = "\033[93m"
|
||||
RED = "\033[91m"
|
||||
CYAN = "\033[96m"
|
||||
GRAY = "\033[90m"
|
||||
|
||||
def ok(msg): print(f"{GREEN}[OK] {msg}{RESET}", flush=True)
|
||||
def fail(msg): print(f"{RED}[!!] {msg}{RESET}", flush=True)
|
||||
def skip(msg): print(f"{GRAY}[--] {msg}{RESET}", flush=True)
|
||||
def step(msg): print(f"\n{YELLOW}{msg}{RESET}", flush=True)
|
||||
def warn(msg): print(f"{YELLOW}[??] {msg}{RESET}", flush=True)
|
||||
def info(msg): print(f"{GRAY} {msg}{RESET}", flush=True)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Hilfsfunktionen
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def run(cmd, check=True):
|
||||
"""Fuehrt einen Shell-Befehl aus und gibt den Return-Code zurueck."""
|
||||
result = subprocess.run(cmd, shell=True)
|
||||
if check and result.returncode != 0:
|
||||
raise RuntimeError(f"Befehl fehlgeschlagen (Exit {result.returncode}): {cmd}")
|
||||
return result.returncode
|
||||
|
||||
|
||||
def rollback_gradle(original_content: str):
|
||||
print(f"{YELLOW}[ROLLBACK] build.gradle.kts wird zurueckgesetzt...{RESET}", flush=True)
|
||||
try:
|
||||
with open(BUILD_GRADLE, "w", encoding="utf-8") as f:
|
||||
f.write(original_content)
|
||||
print(f"{YELLOW}[ROLLBACK] build.gradle.kts zurueckgesetzt.{RESET}", flush=True)
|
||||
except Exception as e:
|
||||
print(f"{RED}[ROLLBACK FEHLGESCHLAGEN] Bitte build.gradle.kts manuell korrigieren: {e}{RESET}", flush=True)
|
||||
|
||||
|
||||
def post_json(url: str, body: dict, token: str, timeout: int = 20):
|
||||
"""HTTP POST mit JSON-Body und Bearer-Token. Gibt status_code zurueck."""
|
||||
data = json.dumps(body).encode("utf-8")
|
||||
req = urllib.request.Request(
|
||||
url,
|
||||
data=data,
|
||||
headers={
|
||||
"Authorization": f"Bearer {token}",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method="POST",
|
||||
)
|
||||
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
||||
return resp.status
|
||||
|
||||
|
||||
def get_json(url: str, timeout: int = 15):
|
||||
req = urllib.request.Request(url)
|
||||
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
||||
return json.loads(resp.read().decode("utf-8"))
|
||||
|
||||
|
||||
def get_status(url: str, timeout: int = 10):
|
||||
req = urllib.request.Request(url)
|
||||
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
||||
return resp.status
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Argument-Parsing
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description="Bollwerk APK Publish-Workflow")
|
||||
parser.add_argument("--version-name", default="", help="Neue versionName (z.B. '1.8')")
|
||||
parser.add_argument("--version-code", type=int, default=0, help="Expliziter versionCode")
|
||||
parser.add_argument("--apk-path", default="app/build/outputs/apk/debug/app-debug.apk")
|
||||
parser.add_argument("--skip-build", action="store_true")
|
||||
parser.add_argument("--skip-verify", action="store_true")
|
||||
parser.add_argument("--skip-push", action="store_true")
|
||||
return parser.parse_args()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
|
||||
# --- Pre-Check: Admin-Token ---
|
||||
admin_token = os.environ.get("BOLLWERK_ADMIN_TOKEN", "")
|
||||
if not admin_token:
|
||||
fail("BOLLWERK_ADMIN_TOKEN ist nicht gesetzt.")
|
||||
info("Bitte setzen: $env:BOLLWERK_ADMIN_TOKEN = 'dein-token'")
|
||||
sys.exit(1)
|
||||
|
||||
# --- Pre-Check: build.gradle.kts lesen ---
|
||||
if not os.path.exists(BUILD_GRADLE):
|
||||
fail(f"Datei nicht gefunden: {BUILD_GRADLE}")
|
||||
sys.exit(1)
|
||||
|
||||
with open(BUILD_GRADLE, "r", encoding="utf-8") as f:
|
||||
original_content = f.read()
|
||||
|
||||
code_match = re.search(r'versionCode\s*=\s*(\d+)', original_content)
|
||||
name_match = re.search(r'versionName\s*=\s*"([^"]+)"', original_content)
|
||||
if not code_match:
|
||||
fail(f"versionCode nicht in {BUILD_GRADLE} gefunden.")
|
||||
sys.exit(1)
|
||||
if not name_match:
|
||||
fail(f"versionName nicht in {BUILD_GRADLE} gefunden.")
|
||||
sys.exit(1)
|
||||
|
||||
current_code = int(code_match.group(1))
|
||||
current_name = name_match.group(1)
|
||||
new_code = args.version_code if args.version_code > 0 else current_code + 1
|
||||
new_name = args.version_name if args.version_name else current_name
|
||||
|
||||
if new_code <= current_code:
|
||||
fail(f"Neuer versionCode ({new_code}) muss groesser als aktueller ({current_code}) sein.")
|
||||
sys.exit(1)
|
||||
|
||||
# --- Pre-Check: SSH-Agent ---
|
||||
rc = subprocess.run("ssh-add -l", shell=True, capture_output=True).returncode
|
||||
if rc != 0:
|
||||
fail("SSH-Agent hat keinen Key. Bitte ausfuehren: ssh-add C:\\Users\\JensR\\.ssh\\id_ed25519")
|
||||
sys.exit(1)
|
||||
|
||||
ok("Pre-Checks bestanden (Token, Gradle-Parsing, SSH-Agent)")
|
||||
|
||||
print(f"\n{CYAN}=== Publish APK v{new_name} (build {new_code}) ==={RESET}", flush=True)
|
||||
print(f"{GRAY} {current_name} ({current_code}) -> {new_name} ({new_code}){RESET}", flush=True)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Schritt 0: build.gradle.kts patchen
|
||||
# -------------------------------------------------------------------------
|
||||
patched = re.sub(
|
||||
rf'versionCode\s*=\s*{current_code}\b',
|
||||
f'versionCode = {new_code}',
|
||||
original_content,
|
||||
)
|
||||
if args.version_name:
|
||||
patched = re.sub(
|
||||
r'versionName\s*=\s*"[^"]+"',
|
||||
f'versionName = "{new_name}"',
|
||||
patched,
|
||||
)
|
||||
|
||||
try:
|
||||
with open(BUILD_GRADLE, "w", encoding="utf-8") as f:
|
||||
f.write(patched)
|
||||
ok(f"build.gradle.kts aktualisiert (versionCode={new_code}, versionName={new_name})")
|
||||
except Exception as e:
|
||||
fail(f"build.gradle.kts konnte nicht geschrieben werden: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Schritt 1: Build
|
||||
# -------------------------------------------------------------------------
|
||||
if args.skip_build:
|
||||
skip("Build uebersprungen (--skip-build)")
|
||||
else:
|
||||
step("[1/4] APK bauen...")
|
||||
rc = subprocess.run(f"{GRADLEW} assembleDebug", shell=True).returncode
|
||||
if rc != 0:
|
||||
fail(f"Build fehlgeschlagen (Exit {rc})")
|
||||
rollback_gradle(original_content)
|
||||
sys.exit(1)
|
||||
ok("APK gebaut")
|
||||
|
||||
if not os.path.exists(args.apk_path):
|
||||
fail(f"APK nicht gefunden nach Build: {args.apk_path}")
|
||||
rollback_gradle(original_content)
|
||||
sys.exit(1)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Schritt 2: APK hochladen
|
||||
# -------------------------------------------------------------------------
|
||||
step("[2/4] APK hochladen -> VPS...")
|
||||
try:
|
||||
rc = subprocess.run(
|
||||
f'ssh -o ConnectTimeout=15 {VPS} "mkdir -p {REMOTE_DIR}/data"',
|
||||
shell=True,
|
||||
).returncode
|
||||
if rc != 0:
|
||||
raise RuntimeError(f"ssh mkdir fehlgeschlagen (Exit {rc})")
|
||||
|
||||
rc = subprocess.run(
|
||||
f'scp -o ConnectTimeout=30 "{args.apk_path}" "{VPS}:{REMOTE_DIR}/data/app-latest.apk"',
|
||||
shell=True,
|
||||
).returncode
|
||||
if rc != 0:
|
||||
raise RuntimeError(f"scp fehlgeschlagen (Exit {rc})")
|
||||
|
||||
ok("APK hochgeladen")
|
||||
except Exception as e:
|
||||
fail(f"Upload fehlgeschlagen: {e}")
|
||||
rollback_gradle(original_content)
|
||||
info(f"TIPP: Pruefen ob VPS erreichbar ist: ssh {VPS} 'echo OK'")
|
||||
sys.exit(1)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Schritt 3: Server-Version per API setzen
|
||||
# Ab hier kein Rollback mehr (APK liegt schon oben)
|
||||
# -------------------------------------------------------------------------
|
||||
step("[3/4] Server-Version aktualisieren...")
|
||||
api_body = {"versionCode": new_code, "versionName": new_name}
|
||||
api_success = False
|
||||
max_retries = 2
|
||||
|
||||
for attempt in range(1, max_retries + 1):
|
||||
try:
|
||||
status = post_json(f"{SERVER_URL}/api/admin/version", api_body, admin_token)
|
||||
if status == 200:
|
||||
api_success = True
|
||||
break
|
||||
raise RuntimeError(f"HTTP {status}")
|
||||
except Exception as e:
|
||||
if attempt < max_retries:
|
||||
print(f" Versuch {attempt} fehlgeschlagen ({e}) - Retry in 3s...", flush=True)
|
||||
time.sleep(3)
|
||||
else:
|
||||
fail(f"API-Call nach {max_retries} Versuchen fehlgeschlagen: {e}")
|
||||
info(f"APK liegt bereits auf dem VPS. Recovery:")
|
||||
info(f" curl -X POST {SERVER_URL}/api/admin/version \\")
|
||||
info(f" -H 'Authorization: Bearer <TOKEN>' \\")
|
||||
info(f" -H 'Content-Type: application/json' \\")
|
||||
info(f" -d '{json.dumps(api_body)}'")
|
||||
|
||||
if api_success:
|
||||
ok(f"Version gesetzt: {new_name} (build {new_code})")
|
||||
else:
|
||||
warn("VPS-Version nicht aktualisiert. build.gradle.kts wird trotzdem committet.")
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Schritt 4: Verifizieren (non-fatal)
|
||||
# -------------------------------------------------------------------------
|
||||
if not api_success or args.skip_verify:
|
||||
skip("Verifizierung uebersprungen")
|
||||
else:
|
||||
step("[4/4] Verifizieren...")
|
||||
time.sleep(2)
|
||||
|
||||
try:
|
||||
ver = get_json(f"{SERVER_URL}/api/version")
|
||||
if ver.get("versionCode") == new_code and ver.get("versionName") == new_name:
|
||||
ok(f"/api/version: {ver.get('versionName')} ({ver.get('versionCode')})")
|
||||
else:
|
||||
warn(f"/api/version meldet {ver.get('versionName')} ({ver.get('versionCode')}) - erwartet {new_name} ({new_code})")
|
||||
except Exception as e:
|
||||
warn(f"/api/version nicht erreichbar: {e}")
|
||||
|
||||
try:
|
||||
status = get_status(f"{SERVER_URL}/")
|
||||
ok(f"Homepage erreichbar (HTTP {status})")
|
||||
except Exception as e:
|
||||
warn(f"Homepage nicht erreichbar: {e}")
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Git Commit + Push
|
||||
# -------------------------------------------------------------------------
|
||||
step("[Git] Version-Bump committen...")
|
||||
try:
|
||||
run(f'git add "{BUILD_GRADLE}"')
|
||||
run(f'git commit -m "chore: release v{new_name} ({new_code})"')
|
||||
except Exception as e:
|
||||
warn(f"git commit fehlgeschlagen: {e}")
|
||||
info(f'Manuell: git add "{BUILD_GRADLE}" ; git commit -m "chore: release v{new_name} ({new_code})"')
|
||||
|
||||
if not args.skip_push:
|
||||
try:
|
||||
run("git push")
|
||||
ok("Gepusht")
|
||||
except Exception as e:
|
||||
warn(f"git push fehlgeschlagen: {e}")
|
||||
info("Manuell: git push")
|
||||
else:
|
||||
skip("Push uebersprungen (--skip-push)")
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Zusammenfassung
|
||||
# -------------------------------------------------------------------------
|
||||
print(f"\n{CYAN}=== Publish abgeschlossen ==={RESET}", flush=True)
|
||||
print(f" Version : {new_name} (build {new_code})", flush=True)
|
||||
print(f" Homepage: {SERVER_URL}/", flush=True)
|
||||
print(f" API : {SERVER_URL}/api/version", flush=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in a new issue