clean-assets: delete release in case all assets need to be deleted

In case a release has hundreds of files that need to be deleted this
requires quite a bit of time and also works against the API rate limiting.

In case we want to delete all assets of an release just delete and
re-create the whole release instead.

Fixes #77
This commit is contained in:
Christoph Reiter 2023-07-30 14:33:56 +02:00
parent 5b61a937a1
commit 4db4e22d09

View File

@ -1,8 +1,9 @@
import re
import fnmatch
from concurrent.futures import ThreadPoolExecutor
from typing import Any, Dict, List
from typing import Any, List, Tuple
from github.GitReleaseAsset import GitReleaseAsset
from github.GitRelease import GitRelease
from .config import get_all_build_types
from .gh import (get_asset_filename, get_current_repo, get_release,
@ -10,51 +11,73 @@ from .gh import (get_asset_filename, get_current_repo, get_release,
from .queue import get_buildqueue
def get_assets_to_delete() -> List[GitReleaseAsset]:
repo = get_current_repo()
def get_assets_to_delete() -> Tuple[List[GitRelease], List[GitReleaseAsset]]:
print("Fetching packages to build...")
patterns = []
keep_patterns = []
for pkg in get_buildqueue():
for build_type in pkg.get_build_types():
patterns.append(pkg.get_failed_name(build_type))
patterns.extend(pkg.get_build_patterns(build_type))
keep_patterns.append(pkg.get_failed_name(build_type))
keep_patterns.extend(pkg.get_build_patterns(build_type))
keep_pattern_regex = re.compile('|'.join(fnmatch.translate(p) for p in keep_patterns))
def should_be_deleted(asset: GitReleaseAsset) -> bool:
filename = get_asset_filename(asset)
return not keep_pattern_regex.match(filename)
def get_to_delete(release: GitRelease) -> Tuple[List[GitRelease], List[GitReleaseAsset]]:
assets = get_release_assets(release, include_incomplete=True)
to_delete = []
for asset in assets:
if should_be_deleted(asset):
to_delete.append(asset)
# Deleting and re-creating a release requires two write calls, so delete
# the release if all assets should be deleted and there are more than 2.
if len(to_delete) > 2 and len(assets) == len(to_delete):
return [release], []
else:
return [], to_delete
def get_all_releases() -> List[GitRelease]:
repo = get_current_repo()
releases = []
for build_type in get_all_build_types():
releases.append(get_release(repo, "staging-" + build_type))
releases.append(get_release(repo, "staging-failed"))
return releases
print("Fetching assets...")
assets: Dict[str, List[GitReleaseAsset]] = {}
for build_type in get_all_build_types():
release = get_release(repo, "staging-" + build_type)
for asset in get_release_assets(release, include_incomplete=True):
assets.setdefault(get_asset_filename(asset), []).append(asset)
releases = []
assets = []
for release in get_all_releases():
r, a = get_to_delete(release)
releases.extend(r)
assets.extend(a)
release = get_release(repo, "staging-failed")
for asset in get_release_assets(release, include_incomplete=True):
assets.setdefault(get_asset_filename(asset), []).append(asset)
for pattern in patterns:
for key in fnmatch.filter(assets.keys(), pattern):
del assets[key]
result = []
for items in assets.values():
for asset in items:
result.append(asset)
return result
return releases, assets
def clean_gha_assets(args: Any) -> None:
assets = get_assets_to_delete()
repo = get_current_repo()
releases, assets = get_assets_to_delete()
def delete_asset(asset: GitReleaseAsset) -> None:
print("Resetting releases...")
for release in releases:
print(f"Resetting {release.tag_name}...")
if not args.dry_run:
with make_writable(release):
release.delete_release()
get_release(repo, release.tag_name)
print("Deleting assets...")
for asset in assets:
print(f"Deleting {get_asset_filename(asset)}...")
if not args.dry_run:
with make_writable(asset):
asset.delete_asset()
with ThreadPoolExecutor(2) as executor:
for item in executor.map(delete_asset, assets):
pass
def add_parser(subparsers: Any) -> None:
sub = subparsers.add_parser("clean-assets", help="Clean up GHA assets", allow_abbrev=False)