#!/usr/bin/env python3 from __future__ import print_function import argparse import os import shutil from pathlib import Path def main(): parser = argparse.ArgumentParser(description='Keep a bounded number of immutable platform releases.') parser.add_argument('root') parser.add_argument('limit', type=int) parser.add_argument('protected', nargs='*') args = parser.parse_args() root = Path(args.root).resolve() releases = (root / 'releases').resolve() if root == Path('/') or releases.parent != root or not releases.is_dir(): raise SystemExit('unsafe or missing release root: {}'.format(releases)) if args.limit < 2: raise SystemExit('release history limit must be at least 2') protected = set() for value in args.protected: path = Path(value).resolve() if path.parent != releases or not path.is_dir(): raise SystemExit('protected release is outside the release root: {}'.format(path)) protected.add(path) candidates = [path.resolve() for path in releases.iterdir() if path.is_dir() and not path.is_symlink()] candidates.sort(key=lambda path: path.stat().st_mtime, reverse=True) keep = set(protected) for path in candidates: if len(keep) >= args.limit: break keep.add(path) removed = 0 for path in candidates: if path in keep: continue if path.parent != releases: raise SystemExit('refusing to remove a nested or escaped path: {}'.format(path)) shutil.rmtree(str(path)) removed += 1 print('release_prune=ok before={} kept={} removed={}'.format(len(candidates), len(keep), removed)) if __name__ == '__main__': main()