Last active
November 27, 2017 20:21
-
-
Save kevinjqiu/eb7f3bc91c8ff1d2f3b019610442614e to your computer and use it in GitHub Desktop.
cleanup.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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