Skip to content

Instantly share code, notes, and snippets.

@kevinjqiu
Last active November 27, 2017 20:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kevinjqiu/eb7f3bc91c8ff1d2f3b019610442614e to your computer and use it in GitHub Desktop.
Save kevinjqiu/eb7f3bc91c8ff1d2f3b019610442614e to your computer and use it in GitHub Desktop.
cleanup.py
import argparse
import bisect
import os
import datetime
import pprint
import logging
import shutil
from operator import attrgetter
from collections import namedtuple
Repository = namedtuple('Repository', ['path', 'delta_mtime', 'delta_ctime', 'total_size'])
def sizeof_fmt(num, suffix='B'):
"""
>>> sizeof_fmt(1000)
1000B
>>> sizeof_fmt(1000, suffix='Bytes')
1000Bytes
>>> sizeof_fmt(1024*1024)
1Mi
"""
for unit in ['','Ki','Mi','Gi']:
if abs(num) < 1024.0:
return "%3.1f%s%s" % (num, unit, suffix)
num /= 1024.0
return "%.1f%s%s" % (num, 'Yi', suffix)
def get_total_size(root):
total_size = 0
for root, dirs, files in os.walk(root):
for f in files:
total_size += os.path.getsize(os.path.join(root, f))
return total_size
def get_all_repositories(root):
now = datetime.datetime.now()
repos = []
for root, dirs, files in os.walk(root):
for dir_ in dirs:
path = os.path.join(root, dir_)
try:
if '.git' in os.listdir(path):
stats = os.stat(path)
delta_mtime = now - datetime.datetime.fromtimestamp(stats.st_mtime)
delta_ctime = now - datetime.datetime.fromtimestamp(stats.st_ctime)
repos.append(Repository(path, delta_mtime, delta_ctime, get_total_size(path)))
except OSError as e:
if e.errno in (2, 13):
continue
raise
return repos
def cleanup(olderthan, root, force):
logging.debug(locals())
repositories = sorted(get_all_repositories(root), key=attrgetter('delta_mtime'))
logging.debug('Total git repositories discovered: {}'.format(len(repositories)))
logging.info('Delete repositories that have not been touched for more than {} day(s)'.format(olderthan))
n = bisect.bisect_left(list(map(attrgetter('delta_mtime'), repositories)),
datetime.timedelta(days=olderthan))
to_delete = repositories[n:]
logging.debug('Total number candidates for deletion: {}'.format(len(to_delete)))
total_size_saved = sum(list(map(attrgetter('total_size'), to_delete)))
logging.debug('Expected to save: {}'.format(sizeof_fmt(total_size_saved)))
for r in to_delete:
logging.info('{}\t\tCreated: {}\tModified: {}\tSize: {}'.format(r.path, -1 * r.delta_ctime, -1 * r.delta_mtime, r.total_size))
if not force:
answer = raw_input('Are you sure you want to delete these repositories? [Y/n]')
if answer != 'Y':
logging.info('Aborted')
return
for r in to_delete:
logging.debug('Deleting {}'.format(r.path))
try:
shutil.rmtree(r.path)
except OSError as e:
logging.warn(str(e))
continue
logging.info('Done')
if __name__ == '__main__':
parser = argparse.ArgumentParser('build_cleanup')
parser.add_argument('olderthan', help="Delete the folder that hasn't been modified for this many days", type=int)
parser.add_argument('-b', '--builds_dir', help='The root of the builds folder.', default='/builds')
parser.add_argument('-f', '--force',
action='store_true',
help='This will not prompt user for input before deleting the candidate directories',
default=False)
parser.add_argument('-v', '--verbose', help='Verbose', action='store_true', default=False)
args = parser.parse_args()
format = '%(message)s'
if args.verbose:
logging.basicConfig(level='DEBUG', format=format)
else:
logging.basicConfig(level='INFO', format=format)
cleanup(args.olderthan, args.builds_dir, args.force)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment