aboutsummaryrefslogtreecommitdiff
path: root/util/cleanup-github-caches.py
diff options
context:
space:
mode:
Diffstat (limited to 'util/cleanup-github-caches.py')
-rwxr-xr-xutil/cleanup-github-caches.py142
1 files changed, 142 insertions, 0 deletions
diff --git a/util/cleanup-github-caches.py b/util/cleanup-github-caches.py
new file mode 100755
index 000000000..41e4ede55
--- /dev/null
+++ b/util/cleanup-github-caches.py
@@ -0,0 +1,142 @@
+"""Cleans out the GitHub Actions cache by deleting obsolete caches.
+
+ Usage:
+ python cleanup-github-caches.py
+"""
+
+import collections
+import datetime
+import json
+import os
+import re
+import subprocess
+import sys
+
+
+def main(argv):
+ if len(argv) > 1:
+ raise ValueError('Expected no arguments: {}'.format(argv))
+
+ # Group caches by their Git reference, e.g "refs/pull/3968/merge"
+ caches_by_ref = collections.defaultdict(list)
+ for cache in get_caches():
+ caches_by_ref[cache['ref']].append(cache)
+
+ # Caclulate caches that should be deleted.
+ caches_to_delete = []
+ for ref, caches in caches_by_ref.items():
+ # If the pull request is already "closed", then delete all caches.
+ if (ref != 'refs/heads/master' and ref != 'master'):
+ match = re.findall(r'refs/pull/(\d+)/merge', ref)
+ if match:
+ pull_request_number = match[0]
+ pull_request = get_pull_request(pull_request_number)
+ if pull_request['state'] == 'closed':
+ caches_to_delete += caches
+ continue
+ else:
+ raise ValueError('Could not find pull request number:', ref)
+
+ # Check for caches with the same key prefix and delete the older caches.
+ caches_by_key = {}
+ for cache in caches:
+ key_prefix = re.findall('(.*)-.*', cache['key'])[0]
+ if key_prefix in caches_by_key:
+ prev_cache = caches_by_key[key_prefix]
+ if (get_created_at(cache) > get_created_at(prev_cache)):
+ caches_to_delete.append(prev_cache)
+ caches_by_key[key_prefix] = cache
+ else:
+ caches_to_delete.append(cache)
+ else:
+ caches_by_key[key_prefix] = cache
+
+ for cache in caches_to_delete:
+ print('Deleting cache ({}): {}'.format(cache['ref'], cache['key']))
+ print(delete_cache(cache))
+
+
+def get_created_at(cache):
+ created_at = cache['created_at'].split('.')[0]
+ # GitHub changed its date format so support both the old and new format for
+ # now.
+ for date_format in ('%Y-%m-%dT%H:%M:%SZ', '%Y-%m-%dT%H:%M:%S'):
+ try:
+ return datetime.datetime.strptime(created_at, date_format)
+ except ValueError:
+ pass
+ raise ValueError('no valid date format found: "%s"' % created_at)
+
+
+def delete_cache(cache):
+ # pylint: disable=line-too-long
+ """Deletes the given cache from GitHub Actions.
+
+ See https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#delete-a-github-actions-cache-for-a-repository-using-a-cache-id
+
+ Args:
+ cache: The cache to delete.
+
+ Returns:
+ The response of the api call.
+ """
+ return call_github_api(
+ """-X DELETE \
+ https://api.github.com/repos/google/dagger/actions/caches/{0}
+ """.format(cache['id'])
+ )
+
+
+def get_caches():
+ # pylint: disable=line-too-long
+ """Gets the list of existing caches from GitHub Actions.
+
+ See https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#list-github-actions-caches-for-a-repository
+
+ Returns:
+ The list of existing caches.
+ """
+ result = call_github_api(
+ 'https://api.github.com/repos/google/dagger/actions/caches'
+ )
+ return json.loads(result)['actions_caches']
+
+
+def get_pull_request(pr_number):
+ # pylint: disable=line-too-long
+ """Gets the pull request with given number from GitHub Actions.
+
+ See https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#get-a-pull-request
+
+ Args:
+ pr_number: The pull request number used to get the pull request.
+
+ Returns:
+ The pull request.
+ """
+ result = call_github_api(
+ 'https://api.github.com/repos/google/dagger/pulls/{0}'.format(pr_number)
+ )
+ return json.loads(result)
+
+
+def call_github_api(endpoint):
+ auth_cmd = ''
+ if 'GITHUB_TOKEN' in os.environ:
+ token = os.environ.get('GITHUB_TOKEN')
+ auth_cmd = '-H "Authorization: Bearer {0}"'.format(token)
+ cmd = """curl -L \
+ {auth_cmd} \
+ -H \"Accept: application/vnd.github+json\" \
+ -H \"X-GitHub-Api-Version: 2022-11-28\" \
+ {endpoint}""".format(auth_cmd=auth_cmd, endpoint=endpoint)
+ return subprocess.run(
+ [cmd],
+ check=True,
+ shell=True,
+ capture_output=True
+ ).stdout.decode('utf-8')
+
+
+if __name__ == '__main__':
+ main(sys.argv)