From 77f9e4ae2f7558f566f83acaa60301e5873221da Mon Sep 17 00:00:00 2001 From: Hermes Date: Sun, 30 Aug 2026 18:19:50 -0500 Subject: [PATCH] fix: publish raw OTA files to rolling release with backup artifacts --- .gitea/workflows/ota-build.yml | 87 +++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/ota-build.yml b/.gitea/workflows/ota-build.yml index 8c19b05..28f5b8e 100644 --- a/.gitea/workflows/ota-build.yml +++ b/.gitea/workflows/ota-build.yml @@ -17,7 +17,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: "3.11" - name: Install PlatformIO run: | @@ -49,9 +49,92 @@ jobs: fi find dist/ota -maxdepth 1 -type f | sort - - name: Upload direct OTA files as artifacts + - name: Upload backup OTA artifact ZIP uses: actions/upload-artifact@v3 with: name: ota-files path: dist/ota/* if-no-files-found: error + + - name: Publish raw OTA files to rolling release + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + run: | + python - <<'PY' + import json + import mimetypes + import os + import sys + from pathlib import Path + from urllib.request import Request, urlopen + from urllib.error import HTTPError + + server = os.environ['GITHUB_SERVER_URL'] + repo = os.environ['GITHUB_REPOSITORY'] + token = os.environ.get('GITEA_TOKEN') + if not token: + print('GITEA_TOKEN missing', file=sys.stderr) + sys.exit(1) + api = f"{server}/api/v1/repos/{repo}" + headers = { + 'Authorization': f'token {token}', + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'User-Agent': 'Hermes-Agent' + } + tag = 'ota-builds' + release_name = 'OTA Builds' + body = 'Rolling OTA files from the latest successful push build.' + + def req(url, method='GET', data=None, extra_headers=None): + h = dict(headers) + if extra_headers: + h.update(extra_headers) + payload = None if data is None else json.dumps(data).encode() + r = Request(url, data=payload, headers=h, method=method) + with urlopen(r, timeout=120) as resp: + body = resp.read() + if not body: + return None + text = body.decode() + try: + return json.loads(text) + except json.JSONDecodeError: + return text + + try: + release = req(f"{api}/releases/tags/{tag}") + except HTTPError as e: + if e.code != 404: + raise + release = req(f"{api}/releases", method='POST', data={ + 'tag_name': tag, + 'target': os.environ.get('GITHUB_SHA', ''), + 'name': release_name, + 'body': body, + 'draft': False, + 'prerelease': True, + }) + + release_id = release['id'] + existing = req(f"{api}/releases/{release_id}/assets") or [] + for asset in existing: + rid = asset['id'] + req(f"{api}/releases/{release_id}/assets/{rid}", method='DELETE') + + for path in sorted(Path('dist/ota').glob('*')): + if not path.is_file(): + continue + mime = mimetypes.guess_type(path.name)[0] or 'application/octet-stream' + upload_headers = { + 'Authorization': f'token {token}', + 'Content-Type': mime, + 'Accept': 'application/json', + 'User-Agent': 'Hermes-Agent' + } + url = f"{api}/releases/{release_id}/assets?name={path.name}" + data = path.read_bytes() + r = Request(url, data=data, headers=upload_headers, method='POST') + with urlopen(r, timeout=240) as resp: + print(resp.read().decode()) + PY