From 698049c0defb602f3bc04d14250db16acb89f7e2 Mon Sep 17 00:00:00 2001 From: niklascfw Date: Thu, 9 Apr 2026 23:23:26 +0200 Subject: [PATCH] ci: daily refresh of badge-data.json and README badge cleanup - Add Gitea Actions workflow and update-badge-data.py to refresh download counts and version from the releases API on a schedule. - Simplify README shields (fewer dynamic badges) and move social badges to the footer; document the badge-data maintenance path. - Drop release_published_date from badge-data.json; ignore __pycache__. Made-with: Cursor --- .github/workflows/update-badge-data.yml | 42 +++++++++ .gitignore | 3 + README.md | 20 ++-- badge-data.json | 11 +-- scripts/update-badge-data.py | 118 ++++++++++++++++++++++++ 5 files changed, 176 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/update-badge-data.yml create mode 100644 scripts/update-badge-data.py diff --git a/.github/workflows/update-badge-data.yml b/.github/workflows/update-badge-data.yml new file mode 100644 index 0000000..3a2b5b8 --- /dev/null +++ b/.github/workflows/update-badge-data.yml @@ -0,0 +1,42 @@ +# Aktualisiert täglich badge-data.json (Download-Summe, Version aus letztem Release, Switch-FW aus Release-Body). +# Läuft auf Gitea Actions wie die übrigen Workflows (.github/workflows); github.api_url = Gitea-API. + +name: Update badge-data + +on: + schedule: + - cron: "0 5 * * *" + workflow_dispatch: + +permissions: + contents: write + +jobs: + refresh: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 0 + + - name: Regenerate badge-data.json + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_REPOSITORY: ${{ github.repository }} + run: python3 scripts/update-badge-data.py + + - name: Commit and push if changed + run: | + set -euo pipefail + git config user.name "OmniNX CI" + git config user.email "omninx-ci@users.noreply.git.niklascfw.de" + git add badge-data.json + if git diff --staged --quiet; then + echo "badge-data.json unverändert." + exit 0 + fi + git commit -m "chore: refresh badge-data.json [skip ci]" + git push diff --git a/.gitignore b/.gitignore index 73fffbf..9e2df90 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Python +__pycache__/ + # Build artifacts build/ output/ diff --git a/README.md b/README.md index 5ab13ae..76cbfee 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,8 @@ # OmniNX CFW Pack -[![Aktuelle Version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fgit.niklascfw.de%2FOmniNX%2FOmniNX%2Fraw%2Fbranch%2Fmain%2Fbadge-data.json&query=%24.omninx_pack_version&label=OmniNX%20Pack%20Version&color=blue)](https://git.niklascfw.de/OmniNX/OmniNX/releases/latest) -[![Firmware](https://img.shields.io/github/v/release/THZoria/NX_Firmware?display_name=release&filter=*22.1.0&label=Kompatibel%20mit%20FW&color=898c8c&logo=nintendoswitch)](https://github.com/THZoria/NX_Firmware/releases/tag/22.1.0) -[![Atmosphere Version](https://img.shields.io/github/v/release/Atmosphere-NX/Atmosphere?include_prereleases&filter=1.11.1&label=Atmosphere%20Version&color=189c11)](https://github.com/Atmosphere-NX/Atmosphere/releases/tag/1.11.1) -[![Release Datum](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fgit.niklascfw.de%2FOmniNX%2FOmniNX%2Fraw%2Fbranch%2Fmain%2Fbadge-data.json&query=%24.release_published_date&label=Released&color=222222)](https://git.niklascfw.de/OmniNX/OmniNX/releases) -[![OmniNX Pack Downloads](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fgit.niklascfw.de%2FOmniNX%2FOmniNX%2Fraw%2Fbranch%2Fmain%2Fbadge-data.json&query=%24.release_zip_downloads_total&label=OmniNX%20Pack%20Downloads&color=6f42c1)](https://git.niklascfw.de/OmniNX/OmniNX/releases) -[![Discord](https://img.shields.io/discord/733728731432091648?logo=discord&logoColor=white&label=NiklasCFW%20Modding%20Community&color=5865F2)](https://discord.gg/5rMJ4fWQT3) -[![YouTube Channel](https://img.shields.io/youtube/channel/subscribers/UCdEkFmAShnlE15CCimAwnYg?logo=youtube&label=NiklasCFW&color=ff0000)](https://www.youtube.com/@NiklasCFW) +[![Aktuelle Version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fgit.niklascfw.de%2FOmniNX%2FOmniNX%2Fraw%2Fbranch%2Fmain%2Fbadge-data.json&query=%24.omninx_pack_version&label=OmniNX%20Version&color=blue)](https://git.niklascfw.de/OmniNX/OmniNX/releases/latest) +[![Firmware](https://img.shields.io/github/v/release/THZoria/NX_Firmware?display_name=release&filter=*22.1.0&label=Kompatibel%20mit%20FW&color=189c11&logo=nintendoswitch)](https://github.com/THZoria/NX_Firmware/releases/tag/22.1.0) +[![OmniNX Pack Downloads](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fgit.niklascfw.de%2FOmniNX%2FOmniNX%2Fraw%2Fbranch%2Fmain%2Fbadge-data.json&query=%24.release_zip_downloads_total&label=OmniNX%20Downloads&color=6f42c1)](https://git.niklascfw.de/OmniNX/OmniNX/releases) OmniNX ist ein vollständiges Custom-Firmware-Setup für die Nintendo Switch, verfügbar in drei Varianten mit Fokus auf Flexibilität und Modularität. @@ -114,7 +110,7 @@ Zusätzlich zum **Standard**-Inhalt liefert die **OC**-Variante u. a.: ``` OmniNX/ -├── badge-data.json # README-Shields: Pack-Version, Released-Datum, Download-Summe (Gitea-ZIPs), switch_firmware +├── badge-data.json # README-Shields (Version, Downloads, FW); täglich per CI scripts/update-badge-data.py ├── staging/ # Gemeinsame Install-Stage-Dateien (Bootloader, Payloads, Nyx, etc.) ├── variants/ # Varianten-Inhalt │ ├── light/ # Light-Variante (minimal) @@ -189,11 +185,11 @@ Die verwendeten Projekte unterliegen ihren jeweiligen Lizenzen. Das OmniNX-Pack * **Discord:** [NiklasCFW Modding Community](https://discord.gg/5rMJ4fWQT3) ---- -
-[![OmniNX](https://img.shields.io/badge/OmniNX-CFW%20Pack-7d7d7d?style=for-the-badge)](https://git.niklascfw.de/OmniNX/OmniNX/releases) -[![Variants](https://img.shields.io/badge/Varianten-Standard%20%7C%20Light%20%7C%20OC-7d7d7d?style=for-the-badge)](https://git.niklascfw.de/OmniNX/OmniNX) +### Socials + +[![Discord](https://img.shields.io/discord/733728731432091648?logo=discord&logoColor=white&label=NiklasCFW%20Modding%20Community&color=5865F2)](https://discord.gg/5rMJ4fWQT3) +[![YouTube Channel](https://img.shields.io/youtube/channel/subscribers/UCdEkFmAShnlE15CCimAwnYg?logo=youtube&label=NiklasCFW&color=ff0000)](https://www.youtube.com/@NiklasCFW)
diff --git a/badge-data.json b/badge-data.json index 94b3c73..abd9841 100644 --- a/badge-data.json +++ b/badge-data.json @@ -1,6 +1,5 @@ -{ - "switch_firmware": "22.1.0", - "release_zip_downloads_total": 1016, - "omninx_pack_version": "1.0.0-b9", - "release_published_date": "2026-04-07" -} +{ + "switch_firmware": "22.1.0", + "release_zip_downloads_total": 1016, + "omninx_pack_version": "1.0.0-b9" +} diff --git a/scripts/update-badge-data.py b/scripts/update-badge-data.py new file mode 100644 index 0000000..beb4476 --- /dev/null +++ b/scripts/update-badge-data.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 +"""Schreibt badge-data.json aus der Releases-API (Gitea/GitHub-kompatibel).""" + +from __future__ import annotations + +import json +import os +import re +import sys +import urllib.error +import urllib.request + +BADGE_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), "badge-data.json") + + +def api_get(url: str, token: str) -> bytes: + req = urllib.request.Request(url) + if token: + req.add_header("Authorization", f"Bearer {token}") + req.add_header("Accept", "application/json") + with urllib.request.urlopen(req, timeout=120) as resp: + return resp.read() + + +def fetch_releases(api_base: str, repo: str, token: str) -> list: + out: list = [] + page = 1 + while True: + url = f"{api_base}/repos/{repo}/releases?limit=100&page={page}" + batch = json.loads(api_get(url, token)) + if not batch: + break + out.extend(batch) + if len(batch) < 100: + break + page += 1 + return out + + +def sum_zip_downloads(releases: list) -> int: + total = 0 + for rel in releases: + for asset in rel.get("assets") or []: + name = str(asset.get("name", "")) + if name.endswith(".zip"): + total += int(asset.get("download_count") or 0) + return total + + +def parse_firmware_from_body(body: str) -> str | None: + if not body: + return None + patterns = ( + r"Firmware\s+Version\s*\*\*(\d+\.\d+\.\d+)\*\*", + r"mit\s+Firmware\s+Version\s*\*\*(\d+\.\d+\.\d+)\*\*", + r"\*\*(\d+\.\d+\.\d+)\*\*\s+in\s+der\s+CFW", + ) + for p in patterns: + m = re.search(p, body, re.I | re.MULTILINE) + if m: + return m.group(1) + return None + + +def load_existing_firmware() -> str | None: + if not os.path.isfile(BADGE_PATH): + return None + try: + with open(BADGE_PATH, encoding="utf-8") as f: + return json.load(f).get("switch_firmware") + except (OSError, json.JSONDecodeError, TypeError): + return None + + +def main() -> int: + api_base = os.environ.get("GITHUB_API_URL", "").rstrip("/") + repo = os.environ.get("GITHUB_REPOSITORY", "") + token = os.environ.get("GITHUB_TOKEN", "") + + if not api_base or not repo: + print("GITHUB_API_URL und GITHUB_REPOSITORY werden benötigt.", file=sys.stderr) + return 1 + + try: + releases = fetch_releases(api_base, repo, token) + except urllib.error.HTTPError as e: + print(f"API: HTTP {e.code} — {e.reason}", file=sys.stderr) + return 1 + + if not releases: + print("Keine Releases gefunden.", file=sys.stderr) + return 1 + + latest = next((r for r in releases if not r.get("draft")), None) + if not latest: + print("Kein nicht-Draft-Release.", file=sys.stderr) + return 1 + + tag = str(latest.get("tag_name") or "").strip() + body = str(latest.get("body") or "") + fw = parse_firmware_from_body(body) or load_existing_firmware() or "unknown" + + data = { + "switch_firmware": fw, + "release_zip_downloads_total": sum_zip_downloads(releases), + "omninx_pack_version": tag, + } + + with open(BADGE_PATH, "w", encoding="utf-8") as f: + json.dump(data, f, indent=2, ensure_ascii=False) + f.write("\n") + + print(json.dumps(data, indent=2)) + return 0 + + +if __name__ == "__main__": + sys.exit(main())