54 lines
1.7 KiB
Python
Executable File
54 lines
1.7 KiB
Python
Executable File
#!/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()
|