]> git.decadent.org.uk Git - dak.git/blob - amber
initial import
[dak.git] / amber
1 #!/usr/bin/env python
2
3 # Wrapper for Debian Security team
4 # Copyright (C) 2002  James Troup <james@nocrew.org>
5 # $Id: amber,v 1.1 2002-05-14 15:24:45 troup Exp $
6
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
11
12 # This program is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 # General Public License for more details.
16
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 # USA
21
22 ################################################################################
23
24 # <aj> neuro: <usual question>?
25 # <neuro> aj: PPG: the movie!  july 3!
26 # <aj> _PHWOAR_!!!!!
27 # <aj> (you think you can distract me, and you're right)
28 # <aj> urls?!
29 # <aj> promo videos?!
30 # <aj> where, where!?
31
32 ################################################################################
33
34 import commands, pwd, os, string, sys, time;
35 import apt_pkg;
36 import katie, utils;
37
38 ################################################################################
39
40 Cnf = None;
41 Katie = None;
42
43 ################################################################################
44
45 def usage (exit_code=0):
46     print """Usage: amber ADV_NUMBER CHANGES_FILE[...]
47 Install CHANGES_FILE(s) as security advisory ADV_NUMBER
48
49   -h, --help                 show this help and exit
50
51 """
52     sys.exit(exit_code)
53
54 ################################################################################
55
56 def get_file_list(arguments):
57     file_list = "";
58     for arg in arguments:
59         arg = utils.validate_changes_file_arg(arg);
60         Katie.pkg.changes_file = arg;
61         Katie.init_vars();
62         Katie.update_vars();
63         files = Katie.pkg.files;
64         changes = Katie.pkg.changes;
65         for file in files.keys():
66             poolname = os.path.join(Cnf["Dir::Root"], Cnf["Dir::PoolRoot"],
67                                     utils.poolify(changes["source"], files[file]["component"]),
68                                     file);
69             file_list = "%s %s" % (file_list, poolname);
70     file_list = "%s %s" % (file_list, string.join(map(os.path.abspath, arguments)));
71     return file_list;
72
73 ################################################################################
74
75 def join_with_commas_and(list):
76         if len(list) == 0: return "nothing";
77         if len(list) == 1: return list[0];
78         return string.join(list[:-1], ", ") + " and " + list[-1];
79
80 ######################################################################
81
82 # Originally written by aj, nih-ishly merged into amber by me.
83
84 def make_advisory(advisory_nr, changes_files):
85     adv_packages = [];
86     updated_pkgs = {};  # updated_pkgs[distro][arch][file] = {path,md5,size}
87
88     for arg in changes_files:
89         arg = utils.validate_changes_file_arg(arg);
90         Katie.pkg.changes_file = arg;
91         Katie.init_vars();
92         Katie.update_vars();
93
94         src = Katie.pkg.changes["source"];
95         if src not in adv_packages:
96             adv_packages = adv_packages + [src];
97
98         suites = Katie.pkg.changes["distribution"].keys();
99         for suite in suites:
100             if not updated_pkgs.has_key(suite):
101                 updated_pkgs[suite] = {};
102
103         files = Katie.pkg.files;
104         for file in files.keys():
105             arch = files[file]["architecture"];
106             md5 = files[file]["md5sum"];
107             size = files[file]["size"];
108             poolname = Cnf["Dir::PoolRoot"] + \
109                 utils.poolify(src, files[file]["component"]);
110             if arch == "source" and file[-4:] == ".dsc":
111                 dscpoolname = poolname;
112             for suite in suites:
113                 if not updated_pkgs[suite].has_key(arch):
114                     updated_pkgs[suite][arch] = {}
115                 updated_pkgs[suite][arch][file] = {
116                     "md5": md5, "size": size,
117                     "poolname": poolname };
118
119         dsc_files = Katie.pkg.dsc_files;
120         for file in dsc_files.keys():
121             arch = "source"
122             if not dsc_files[file].has_key("files id"):
123                 continue;
124
125             # otherwise, it's already in the pool and needs to be
126             # listed specially
127             md5 = dsc_files[file]["md5sum"];
128             size = dsc_files[file]["size"];
129             for suite in suites:
130                 if not updated_pkgs[suite].has_key(arch):
131                     updated_pkgs[suite][arch] = {};
132                 updated_pkgs[suite][arch][file] = {
133                     "md5": md5, "size": size,
134                     "poolname": dscpoolname };
135
136     if os.environ.has_key("SUDO_UID"):
137         whoami = string.atol(os.environ["SUDO_UID"]);
138     else:
139         whoami = os.getuid();
140     whoamifull = pwd.getpwuid(whoami);
141     username = string.split(whoamifull[4], ",")[0];
142
143     Subst = {
144         "__ADVISORY__": advisory_nr,
145         "__WHOAMI__": username,
146         "__DATE__": time.strftime("%B %d, %Y", time.gmtime(time.time())),
147         "__PACKAGE__": string.join(adv_packages,", ")
148     };
149
150     adv = "";
151     archive = Cnf["Archive::%s::PrimaryMirror" % (utils.where_am_i())];
152     for suite in updated_pkgs.keys():
153         suite_header = "%s %s (%s)" % (Cnf["Dinstall::MyDistribution"],
154                                        Cnf["Suite::%s::Version" % suite], suite);
155         adv = adv + "%s\n%s\n\n" % (suite_header, "-"*len(suite_header));
156
157         arches = Cnf.ValueList("Suite::%s::Architectures" % suite);
158         if "source" in arches:
159             arches.remove("source");
160         if "all" in arches:
161             arches.remove("all");
162         arches.sort();
163
164         adv = adv + "  %s was released for %s.\n\n" % (
165                 string.capitalize(suite), join_with_commas_and(arches));
166
167         for a in ["source", "all"] + arches:
168             if not updated_pkgs[suite].has_key(a):
169                 continue;
170
171             if a == "source":
172                 adv = adv + "  Source archives:\n\n";
173             elif a == "all":
174                 adv = adv + "  Architecture independent packages:\n\n";
175             else:
176                 adv = adv + "  %s architecture (%s)\n\n" % (a,
177                         Cnf["Architectures::%s" % a]);
178
179             for file in updated_pkgs[suite][a].keys():
180                 adv = adv + "    http://%s/%s%s\n" % (
181                                 archive, updated_pkgs[suite][a][file]["poolname"], file);
182                 adv = adv + "      Size/MD5 checksum: %8s %s\n" % (
183                         updated_pkgs[suite][a][file]["size"],
184                         updated_pkgs[suite][a][file]["md5"]);
185             adv = adv + "\n";
186     adv = string.rstrip(adv);
187
188     Subst["__ADVISORY_TEXT__"] = adv;
189
190     adv = utils.TemplateSubst(Subst, Cnf["Dir::Templates"]+"/amber.advisory");
191     utils.send_mail (adv, "");
192
193 ######################################################################
194
195 def init():
196     global Cnf, Katie;
197
198     apt_pkg.init();
199     Cnf = utils.get_conf();
200
201     Arguments = [('h',"help","Amber::Options::Help")];
202
203     for i in [ "help" ]:
204         Cnf["Amber::Options::%s" % (i)] = "";
205
206     arguments = apt_pkg.ParseCommandLine(Cnf,Arguments,sys.argv);
207     Katie = katie.Katie(Cnf);
208
209     if Cnf["Amber::Options::Help"]:
210         usage(0);
211
212     if not arguments:
213         usage(1);
214
215     advisory_number = arguments[0];
216     changes_files = sys.argv[2:];
217     if advisory_number[-8:] == ".changes":
218         utils.warn("first argument must be the advisory number.");
219         usage(1);
220     for file in changes_files:
221         file = utils.validate_changes_file_arg(file);
222     return (advisory_number, changes_files);
223
224 ######################################################################
225
226 def yes_no(prompt):
227     while 1:
228         answer = string.lower(utils.our_raw_input(prompt+" "));
229         if answer == "y" or answer == "n":
230             break;
231         else:
232             print "Invalid answer; please try again.";
233     return answer;
234
235 ######################################################################
236
237 def spawn(command):
238     (result, output) = commands.getstatusoutput(command);
239     if (result != 0):
240         utils.fubar("Invocation of '%s' failed:\n%s\n" % (command, output), result);
241
242 ######################################################################
243
244
245 def main():
246     (advisory_number, changes_files) = init();
247
248     print "About to install the following files: "
249     for file in changes_files:
250         print "  %s" % (file);
251     answer = yes_no("Continue (Y/n)?");
252     if answer == "n":
253         sys.exit(0);
254
255     os.chdir(Cnf["Dir::Queue::Accepted"]);
256     print "Installing packages into the archive...";
257     spawn("%s/katie -pa %s" % (Cnf["Dir::Katie"], string.join(changes_files)));
258     os.chdir(Cnf["Dir::Katie"]);
259     print "Updating file lists for apt-ftparchive...";
260     spawn("./jenna");
261     print "Updating Packages and Sources files...";
262     spawn("apt-ftparchive generate %s" % (utils.which_apt_conf_file()));
263     print "Updating Release files...";
264     spawn("./ziyi");
265
266     os.chdir(Cnf["Dir::Queue::Done"]);
267     print "Generating template advisory...";
268     make_advisory(advisory_number, changes_files);
269
270     answer = yes_no("Upload to ftp-master (Y/n)? ");
271     if answer == "y":
272         upload_files = get_file_list(changes_files);
273         print "Uploading files...";
274         spawn("lftp -c 'open %s; cd %s; put %s'" % (Cnf["Amber::UploadHost"],
275                                                     Cnf["Amber::UploadDir"],
276                                                     upload_files));
277
278 ################################################################################
279
280 if __name__ == '__main__':
281     main();
282
283 ################################################################################