From d8dec52a310a50e7dfdd98ed407ef7620ac4a4f7 Mon Sep 17 00:00:00 2001 From: niklascfw Date: Sat, 28 Mar 2026 20:53:52 +0100 Subject: [PATCH] Workflow: refresh latest release zip assets on push to main Replace release .zips after each main push using curl/Python (no gh). Support Gitea DELETE path for release attachments. Made-with: Cursor --- .github/workflows/update-latest-release.yml | 136 ++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 .github/workflows/update-latest-release.yml diff --git a/.github/workflows/update-latest-release.yml b/.github/workflows/update-latest-release.yml new file mode 100644 index 0000000..b11709e --- /dev/null +++ b/.github/workflows/update-latest-release.yml @@ -0,0 +1,136 @@ +name: Update Latest Release Assets + +# Baut OmniNX neu und ersetzt die .zip-Anhänge am neuesten (nicht-Draft-)Release. +# Nutzt curl + Python (kein gh CLI) — läuft z. B. unter nektos/act. +# +# Lokal mit act: 404/401 = Repo/Credentials passen nicht zu api.github.com (z. B. nur Gitea-Remote). +# Mit act: Event-JSON mit passendem repository.full_name zum GitHub-Repo + GITHUB_TOKEN. +# Bei nicht erreichbarer Releases-API (404/401 …) wird nur dieser Schritt übersprungen (grüner Job). + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: write + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_REPOSITORY: ${{ github.repository }} + +jobs: + rebuild-latest-assets: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Get latest non-draft release + id: release + run: | + set -euo pipefail + API_URL="${GITHUB_API_URL%/}" + REQ_URL="${API_URL}/repos/${GITHUB_REPOSITORY}/releases?per_page=30" + HTTP_CODE=$(curl -sS -o /tmp/gh-releases.json -w "%{http_code}" \ + -H "Authorization: Bearer ${GITHUB_TOKEN}" \ + -H "Accept: application/vnd.github+json" \ + "${REQ_URL}") || HTTP_CODE="000" + + if [ "${HTTP_CODE}" != "200" ]; then + echo "Releases-API: HTTP ${HTTP_CODE} — URL: ${REQ_URL}" + echo "Überspringe Asset-Update (z. B. act: falsches Repo, kein Token, oder Repo nur auf Gitea)." + echo "skip=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + + TAG=$(python3 -c " + import json,sys + data=json.load(open('/tmp/gh-releases.json')) + for r in data: + if not r.get('draft'): + print(r.get('tag_name','')) + break + ") + if [ -z "$TAG" ]; then + echo "Kein Release gefunden — kein Asset-Update." + echo "skip=true" >> "$GITHUB_OUTPUT" + else + echo "Neuestes Release: $TAG" + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + + - name: Delete existing .zip assets from that release + if: steps.release.outputs.skip == 'false' + run: | + set -euo pipefail + TAG="${{ steps.release.outputs.tag }}" + ENC=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1], safe=''))" "$TAG") + API_URL="${GITHUB_API_URL%/}" + TAG_URL="${API_URL}/repos/${GITHUB_REPOSITORY}/releases/tags/${ENC}" + HTTP_CODE=$(curl -sS -o /tmp/gh-release-tag.json -w "%{http_code}" \ + -H "Authorization: Bearer ${GITHUB_TOKEN}" \ + -H "Accept: application/vnd.github+json" \ + "${TAG_URL}") || HTTP_CODE="000" + if [ "${HTTP_CODE}" != "200" ]; then + echo "Release-by-tag API: HTTP ${HTTP_CODE} — ${TAG_URL}" + exit 1 + fi + REL_JSON=$(cat /tmp/gh-release-tag.json) + export REL_JSON + python3 << 'PY' + import json + import os + import urllib.error + import urllib.request + + r = json.loads(os.environ["REL_JSON"]) + token = os.environ["GITHUB_TOKEN"] + api = os.environ["GITHUB_API_URL"].rstrip("/") + repo = os.environ["GITHUB_REPOSITORY"] + release_id = r.get("id") + + for a in r.get("assets", []): + name = str(a.get("name", "")) + if not name.endswith(".zip"): + continue + aid = a["id"] + # GitHub: DELETE .../releases/assets/{id} + # Gitea: DELETE .../releases/{release_id}/assets/{attachment_id} + urls = [f"{api}/repos/{repo}/releases/assets/{aid}"] + if release_id is not None: + urls.append(f"{api}/repos/{repo}/releases/{release_id}/assets/{aid}") + + for url in urls: + req = urllib.request.Request(url, method="DELETE") + req.add_header("Authorization", f"Bearer {token}") + req.add_header("Accept", "application/vnd.github+json") + try: + urllib.request.urlopen(req) + print("Gelöscht:", aid, name) + break + except urllib.error.HTTPError as e: + if e.code == 404 and url != urls[-1]: + continue + print(f"DELETE {aid} failed: {e.code} {e.reason} — {url}") + raise + PY + + - name: Build all variants + if: steps.release.outputs.skip == 'false' + run: bash scripts/build-all.sh + + - name: Attach new zips to release + if: steps.release.outputs.skip == 'false' + uses: softprops/action-gh-release@v1 + with: + tag_name: ${{ steps.release.outputs.tag }} + files: | + output/OmniNX-Light-*.zip + output/OmniNX-Standard-*.zip + output/OmniNX-OC-*.zip + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}