Add packaging-move-dev-to-attic.py, which now supports aptly. This is a
[arvados-dev.git] / jenkins / packaging-move-dev-to-attic.py
1 #!/usr/bin/env python3
2
3 # Copyright (C) The Arvados Authors. All rights reserved.
4 #
5 # SPDX-License-Identifier: AGPL-3.0
6
7 import argparse
8 import datetime
9 import os
10 import subprocess
11 import pprint
12 import re
13
14 class DebugExecutor:
15   def __init__(self, package_list):
16     self.package_list = package_list
17
18   def do_it(self):
19     for a in self.package_list:
20       print (a[2])
21
22 class MoveExecutor:
23   def __init__(self, distro, package_list):
24     self.package_list = package_list
25     self.distro = distro
26
27   def move_it(self):
28     for a in self.package_list:
29       if a[2]:
30         source = a[0]
31         destination = source.replace('dev','attic')
32         os.makedirs(os.path.dirname(destination), exist_ok=True)
33         print ("Moving " + a[0] + " to " + destination)
34         f = os.path.basename(os.path.splitext(a[0])[0])
35         output = subprocess.getoutput("aptly repo move " + source + " " + destination)
36         print(output)
37
38   def update_it(self):
39     distroBase = re.sub('-.*$', '', self.distro)
40     output = subprocess.getoutput("aptly publish update " + distroBase + "-dev filesystem:" + distroBase + ":")
41     print(output)
42     output = subprocess.getoutput("aptly publish update " + distroBase + "-attic filesystem:" + distroBase + ":")
43     print(output)
44
45 class CollectPackageName:
46   def __init__(self, cache_dir, distro, min_packages,  cutoff_date):
47     self.cache_dir = cache_dir
48     self.distro = distro
49     self.min_packages = min_packages
50     self.cutoff_date_unixepoch = int(cutoff_date.strftime('%s'))
51
52   def collect_packages(self):
53     distroBase = re.sub('-.*$', '', self.distro)
54     directory=os.path.join(self.cache_dir,distroBase,'pool/main')
55
56     ## rtn will have 4 element tuple: package_name, the path, the creation time for sorting, and if it's a candidate for deletion
57     rtn = []
58
59     # Get the list of packages in the repo
60     output = subprocess.getoutput("aptly repo search " + self.distro)
61     for f in output.splitlines():
62       pkg = f.split('_')[0]
63       # This is nasty and slow, but aptly doesn't seem to have a way to provide
64       # the on-disk path for a package in its repository. We also can't query
65       # for the list of packages that fit the cutoff date constraint with a
66       # 'package-query' parameter: the 'Date' field would be appropriate for
67       # that, but it's not populated for our packages because we don't ship a
68       # changelog with them (that's where the 'Date' field comes from, as per
69       # the Debian policy manual, cf.
70       # https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-date).
71       the_file = subprocess.getoutput("find " + directory + " -name " + f + ".deb")
72       if the_file == "":
73           print("WARNING: skipping package, could not find file for package " + f + " under directory " + directory)
74           continue
75       rtn.append ( (pkg, the_file,
76                     os.path.getmtime(the_file),
77                     os.path.getmtime(the_file) < self.cutoff_date_unixepoch) )
78     return self.collect_candidates_excluding_N_last(rtn)
79
80   def collect_candidates_excluding_N_last(self, tuples_with_packages):
81     return_value = []
82
83     ## separate all file into packages. (use the first element in the tuple for this)
84     dictionary_per_package  = {}
85     for x in tuples_with_packages:
86       dictionary_per_package.setdefault(x[0], []).append(x[1:])
87
88     for pkg_name, metadata in dictionary_per_package.items():
89       candidates_local_copy = metadata[:]
90
91       ## order them by date
92       candidates_local_copy.sort(key=lambda tup: tup[1])
93
94       return_value.extend(candidates_local_copy[:-self.min_packages])
95
96     return return_value
97
98 parser = argparse.ArgumentParser(description='List the packages to delete.')
99 parser.add_argument('distro', choices=['bionic-dev','jessie-dev','precise-dev','stretch-dev','trusty-dev','wheezy-dev','xenial-dev','buster-dev'],
100                     help='distro to do the clean up')
101 parser.add_argument('--repo_dir',
102                     default='/var/www/aptly_public/',
103                     help='parent directory of the aptly repositories (default:  %(default)s)')
104 parser.add_argument('--min_packages', type=int,
105                     default=5,
106                     help='minimum amount of packages to leave in the repo (default:  %(default)s)')
107 parser.add_argument('--cutoff_date', type=lambda s: datetime.datetime.strptime(s, '%Y-%m-%d'),
108                     default='2017-06-31',
109                     help='date to cut-off in format YYYY-MM-DD (default:  %(default)s)')
110
111 args = parser.parse_args()
112
113
114 p = CollectPackageName(args.repo_dir, args.distro, args.min_packages,  args.cutoff_date)
115
116 #executor = DebugExecutor(p.collect_packages())
117 executor = MoveExecutor(args.distro, p.collect_packages())
118
119 executor.move_it()
120 executor.update_it()
121