fix: use non-paginated Gitea release attachment endpoint
This commit is contained in:
@@ -103,6 +103,13 @@ class API:
|
||||
raise SyncError('Attachment download failed after retries') from None
|
||||
time.sleep(2 ** attempt)
|
||||
|
||||
def list_assets(self, release_id):
|
||||
# Gitea's assets endpoint is NOT paginated; page/limit are ignored.
|
||||
data = self.request('GET', f'/releases/{release_id}/assets')
|
||||
if not isinstance(data, list):
|
||||
raise SyncError('Expected attachment list')
|
||||
return data
|
||||
|
||||
|
||||
def digest(path):
|
||||
h = hashlib.sha256()
|
||||
@@ -151,7 +158,7 @@ def release_body(source, source_url, hashes, source_sha):
|
||||
def sync_one(src, dst, source, anchor, dry_run, directory):
|
||||
tag = source['tag_name']
|
||||
key = urllib.parse.quote(tag, safe='')
|
||||
source_assets = src.pages(f'/releases/{source["id"]}/assets')
|
||||
source_assets = src.list_assets(source['id'])
|
||||
validate_names(source_assets)
|
||||
source_tag = src.request('GET', '/tags/' + key)
|
||||
if not source_tag or not source_tag.get('commit', {}).get('sha'):
|
||||
@@ -162,7 +169,7 @@ def sync_one(src, dst, source, anchor, dry_run, directory):
|
||||
p = Path(directory) / ('source-' + str(a['id']))
|
||||
hashes[a['name']] = src.download(a, p)
|
||||
files[a['name']] = p
|
||||
fresh = src.pages(f'/releases/{source["id"]}/assets')
|
||||
fresh = src.list_assets(source['id'])
|
||||
identity = lambda rows: sorted((a['id'], a['name'], a['size']) for a in rows)
|
||||
if identity(fresh) != identity(source_assets):
|
||||
raise SyncError('Source attachment list changed during download; retry later')
|
||||
@@ -175,7 +182,7 @@ def sync_one(src, dst, source, anchor, dry_run, directory):
|
||||
result = {'tag': tag, 'source_sha': source_sha, 'created': target is None, 'assets': [], 'metadata_changed': False}
|
||||
if target is None and not dry_run:
|
||||
target = dst.request('POST', '/releases', dict(desired, tag_name=tag, target_commitish=anchor, draft=True))
|
||||
assets = dst.pages(f'/releases/{target["id"]}/assets') if target else []
|
||||
assets = dst.list_assets(target['id']) if target else []
|
||||
for source_asset in source_assets:
|
||||
name, sha = source_asset['name'], hashes[source_asset['name']]
|
||||
existing = next((a for a in assets if a['name'] == name), None)
|
||||
|
||||
@@ -33,6 +33,8 @@ class Fake:
|
||||
return copy.deepcopy(a)
|
||||
def pages(self, path):
|
||||
return copy.deepcopy(self.assets)
|
||||
def list_assets(self, release_id):
|
||||
return copy.deepcopy(self.assets)
|
||||
def download(self, a, p):
|
||||
Path(p).write_bytes(self.data[a['id']])
|
||||
validate_file(p, a)
|
||||
@@ -118,6 +120,15 @@ class Tests(unittest.TestCase):
|
||||
api = API('https://example.invalid', 'a/b', 'secret')
|
||||
with self.assertRaises(SyncError):
|
||||
api.download({'browser_download_url': 'https://other.invalid/file'}, '/tmp/unused')
|
||||
def test_assets_endpoint_is_not_paginated(self):
|
||||
api = API('https://example.invalid', 'a/b', '')
|
||||
calls = []
|
||||
def request(method, path):
|
||||
calls.append(path)
|
||||
return [{'id': 1}]
|
||||
api.request = request
|
||||
self.assertEqual([{'id': 1}], api.list_assets(2))
|
||||
self.assertEqual(['/releases/2/assets'], calls)
|
||||
|
||||
|
||||
if __name__ == '__main__': unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user