mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-11 02:51:10 +00:00
ci: cache the pinned Turnip archive and retry its download (#2276)
* ci: cache the pinned Turnip archive and retry its download "Prepare graphics driver variants" fetched the same checksum-pinned Banners-Turnip zip from GitHub on every run with a single 120 s attempt, so a 502 or a read timeout failed whole PR builds three times today. The archive is now cached between runs (it is still checked against its SHA-256 after a restore), and a cold download is retried on timeouts, 429 and 5xx with a growing pause. * ci: keep PowerVR's verified downloads between runs tools/powervr/fetch.py already keeps checksum-verified downloads in ~/.cache/droiddeck-build, but CI never saved that folder, so every run fetched the PowerVR components again and a read timeout failed "Build the add-on packs". The folder joins the existing pack-inputs cache, keyed on release.json too.
This commit is contained in:
1 parent
aa883830ca
commit
c755d05473
3 files changed
+82
-4
No files matched your search
@@ -313,6 +313,11 @@ jobs:
|
||||
- name: Build the DiRT 3 / DiRT Showdown ws2_32 stand-in
|
||||
run: python3 tools/components/prepare.py dirt-ws2
|
||||
|
||||
- name: Cache graphics driver inputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: app/build/quest-inputs
|
||||
key: quest-inputs-${{ hashFiles('tools/quest/build-driver.py') }}
|
||||
- name: Prepare graphics driver variants
|
||||
run: python3 tools/quest/build-driver.py "$ANDROID_HOME/ndk/$NDK_VERSION"
|
||||
- name: Record and pack prepared components
|
||||
@@ -558,9 +563,11 @@ jobs:
|
||||
- name: Cache checksum-pinned pack inputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/droiddeck-pack-inputs
|
||||
key: pack-inputs-v1-${{ hashFiles('tools/packs/build.sh', 'tools/packs/fetch-verified.sh', 'tools/packs/tracking/release.env') }}
|
||||
restore-keys: pack-inputs-v1-
|
||||
path: |
|
||||
~/.cache/droiddeck-pack-inputs
|
||||
~/.cache/droiddeck-build
|
||||
key: pack-inputs-v2-${{ hashFiles('tools/packs/build.sh', 'tools/packs/fetch-verified.sh', 'tools/packs/tracking/release.env', 'tools/powervr/release.json') }}
|
||||
restore-keys: pack-inputs-v2-
|
||||
- name: Build the add-on packs
|
||||
run: |
|
||||
python3 tools/powervr/fetch.py
|
||||
|
||||
@@ -7,6 +7,9 @@ import sys
|
||||
import tarfile
|
||||
import tempfile
|
||||
import hashlib
|
||||
import http.client
|
||||
import time
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
import zipfile
|
||||
|
||||
@@ -32,11 +35,23 @@ def pack(entries, output):
|
||||
print(output)
|
||||
|
||||
|
||||
def download(url, attempts=4):
|
||||
for attempt in range(1, attempts + 1):
|
||||
try:
|
||||
with urllib.request.urlopen(url, timeout=120) as response:
|
||||
return response.read()
|
||||
except (OSError, http.client.HTTPException) as error:
|
||||
if attempt == attempts or isinstance(error, urllib.error.HTTPError) and error.code < 500 and error.code != 429:
|
||||
raise
|
||||
print(f'Download failed ({error}); retrying in {attempt * 15} s')
|
||||
time.sleep(attempt * 15)
|
||||
|
||||
|
||||
def build_a6xx(ndk, output_dir=OUTPUT.parent, archive_path=None):
|
||||
cached = archive_path or ROOT / 'app/build/quest-inputs' / f'Turnip-{A6XX_TAG}.zip'
|
||||
if not cached.exists():
|
||||
cached.parent.mkdir(parents=True, exist_ok=True)
|
||||
data = urllib.request.urlopen(A6XX_URL, timeout=120).read()
|
||||
data = download(A6XX_URL)
|
||||
if hashlib.sha256(data).hexdigest() != A6XX_SHA256:
|
||||
raise ValueError('A6xx archive checksum mismatch')
|
||||
cached.write_bytes(data)
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import importlib.util
|
||||
import io
|
||||
import unittest
|
||||
import urllib.error
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
SCRIPT = Path(__file__).resolve().parents[1] / "quest/build-driver.py"
|
||||
spec = importlib.util.spec_from_file_location("build_driver", SCRIPT)
|
||||
driver = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(driver)
|
||||
|
||||
|
||||
def http_error(code):
|
||||
return urllib.error.HTTPError("https://example.invalid/a.zip", code, "error", {}, io.BytesIO())
|
||||
|
||||
|
||||
class Response(io.BytesIO):
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
self.close()
|
||||
|
||||
|
||||
class DriverDownloadTest(unittest.TestCase):
|
||||
def fetch(self, *outcomes):
|
||||
calls = []
|
||||
|
||||
def urlopen(url, timeout):
|
||||
calls.append(url)
|
||||
outcome = outcomes[len(calls) - 1]
|
||||
if isinstance(outcome, Exception):
|
||||
raise outcome
|
||||
return Response(outcome)
|
||||
|
||||
with mock.patch.object(driver.urllib.request, "urlopen", urlopen), mock.patch.object(driver.time, "sleep") as sleep:
|
||||
try:
|
||||
return driver.download("https://example.invalid/a.zip"), len(calls), sleep.call_count
|
||||
except Exception as error:
|
||||
return error, len(calls), sleep.call_count
|
||||
|
||||
def test_a_timeout_or_server_error_is_retried(self):
|
||||
self.assertEqual((b"zip", 3, 2), self.fetch(TimeoutError("read timed out"), http_error(502), b"zip"))
|
||||
|
||||
def test_a_missing_file_fails_at_once(self):
|
||||
error, calls, sleeps = self.fetch(http_error(404))
|
||||
self.assertEqual((404, 1, 0), (error.code, calls, sleeps))
|
||||
|
||||
def test_it_gives_up_after_the_last_attempt(self):
|
||||
error, calls, sleeps = self.fetch(*[http_error(504)] * 4)
|
||||
self.assertEqual((504, 4, 3), (error.code, calls, sleeps))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user