]> git.decadent.org.uk Git - dak.git/blob - heidi
usage() + options cleanup
[dak.git] / heidi
1 #!/usr/bin/env python
2
3 # Manipulate suite tags
4 # Copyright (C) 2000, 2001  James Troup <james@nocrew.org>
5 # $Id: heidi,v 1.9 2001-09-27 01:22:51 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,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU 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  USA
20
21 #######################################################################################
22
23 # 8to6Guy: "Wow, Bob, You look rough!"
24 # BTAF: "Mbblpmn..."
25 # BTAF <.oO>: "You moron! This is what you get for staying up all night drinking vodka and salad dressing!"
26 # BTAF <.oO>: "This coffee I.V. drip is barely even keeping me awake! I need something with more kick! But what?"
27 # BTAF: "OMIGOD! I OVERDOSED ON HEROIN"
28 # CoWorker#n: "Give him air!!"
29 # CoWorker#n+1: "We need a syringe full of adrenaline!"
30 # CoWorker#n+2: "Stab him in the heart!"
31 # BTAF: "*YES!*"
32 # CoWorker#n+3: "Bob's been overdosing quite a bit lately..."
33 # CoWorker#n+4: "Third time this week."
34
35 # -- http://www.angryflower.com/8to6.gif
36
37 #######################################################################################
38
39 # Adds or removes packages from a suite.  Takes the list of files
40 # either from stdin or as a command line argument.  Special action
41 # "set", will reset the suite (!) and add all packages from scratch.
42
43 #######################################################################################
44
45 import os, pg, string, sys;
46 import apt_pkg;
47 import utils, db_access, logging;
48
49 #######################################################################################
50
51 Cnf = None;
52 projectB = None;
53 Logger = None;
54
55 ################################################################################
56
57 def usage (exit_code=0):
58     print """Usage: heidi [OPTIONS] [FILE]
59 Display or alter the contents of a suite using FILE(s), or stdin.
60
61   -a, --add=SUITE            add to SUITE
62   -l, --list=SUITE           list the contents of SUITE
63   -r, --remove=SUITE         remove from SUITE
64   -s, --set=SUITE            set SUITE
65   -h, --help                 show this help and exit"""
66
67     sys.exit(exit_code)
68
69 #######################################################################################
70
71 def get_id (package, version, architecture):
72     if architecture == "source":
73         q = projectB.query("SELECT id FROM source WHERE source = '%s' AND version = '%s'" % (package, version))
74     else:
75         q = projectB.query("SELECT b.id FROM binaries b, architecture a WHERE b.package = '%s' AND b.version = '%s' AND (a.arch_string = '%s' OR a.arch_string = 'all') AND b.architecture = a.id" % (package, version, architecture))
76
77     ql = q.getresult();
78     if ql == []:
79         utils.warn("Couldn't find '%s~%s~%s'." % (package, version, architecture));
80         return None;
81     if len(ql) > 1:
82         utils.warn("Found more than one match for '%s~%s~%s'." % (package, version, architecture));
83         return None;
84     id = ql[0][0];
85     return id;
86
87 #######################################################################################
88
89 def set_suite (file, suite_id):
90     lines = file.readlines();
91
92     projectB.query("BEGIN WORK");
93
94     # Build up a dictionary of what is currently in the suite
95     current = {};
96     q = projectB.query("SELECT b.package, b.version, a.arch_string, ba.id FROM binaries b, bin_associations ba, architecture a WHERE ba.suite = %s AND ba.bin = b.id AND b.architecture = a.id" % (suite_id));
97     ql = q.getresult();
98     for i in ql:
99         key = string.join(i[:3]);
100         current[key] = i[3];
101     q = projectB.query("SELECT s.source, s.version, sa.id FROM source s, src_associations sa WHERE sa.suite = %s AND sa.source = s.id" % (suite_id));
102     ql = q.getresult();
103     for i in ql:
104         key = string.join(i[:2]) + " source";
105         current[key] = i[2];
106
107     # Build up a dictionary of what should be in the suite
108     desired = {};
109     for line in lines:
110         split_line = string.split(string.strip(line[:-1]));
111         if len(split_line) != 3:
112             utils.warn("'%s' does not break into 'package version architecture'." % (line[:-1]));
113             continue;
114         key = string.join(split_line);
115         desired[key] = "";
116
117     # Check to see which packages need removed and remove them
118     for key in current.keys():
119         if not desired.has_key(key):
120             (package, version, architecture) = string.split(key);
121             id = current[key];
122             if architecture == "source":
123                 q = projectB.query("DELETE FROM src_associations WHERE id = %s" % (id));
124             else:
125                 q = projectB.query("DELETE FROM bin_associations WHERE id = %s" % (id));
126             Logger.log(["removed",key,id]);
127
128     # Check to see which packages need added and add them
129     for key in desired.keys():
130         if not current.has_key(key):
131             (package, version, architecture) = string.split(key);
132             id = get_id (package, version, architecture);
133             if not id:
134                 continue;
135             if architecture == "source":
136                 q = projectB.query("INSERT INTO src_associations (suite, source) VALUES (%s, %s)" % (suite_id, id));
137             else:
138                 q = projectB.query("INSERT INTO bin_associations (suite, bin) VALUES (%s, %s)" % (suite_id, id));
139             Logger.log(["added",key,id]);
140
141     projectB.query("COMMIT WORK");
142
143 #######################################################################################
144
145 def process_file (file, suite_id, action):
146
147     if action == "set":
148         set_suite (file, suite_id);
149         return;
150
151     lines = file.readlines();
152
153     projectB.query("BEGIN WORK");
154
155     for line in lines:
156         split_line = string.split(string.strip(line[:-1]));
157         if len(split_line) != 3:
158             utils.warn("'%s' does not break into 'package version architecture'." % (line[:-1]));
159             continue;
160
161         (package, version, architecture) = split_line;
162
163         id = get_id(package, version, architecture);
164         if not id:
165             continue;
166
167         if architecture == "source":
168             # Find the existing assoications ID, if any
169             q = projectB.query("SELECT id FROM src_associations WHERE suite = %s and source = %s" % (suite_id, id));
170             ql = q.getresult();
171             if ql == []:
172                 assoication_id = None;
173             else:
174                 assoication_id = ql[0][0];
175             # Take action
176             if action == "add":
177                 if assoication_id != None:
178                     utils.warn("'%s~%s~%s' already exists in suite %s." % (package, version, architecture, suite_id));
179                     continue;
180                 else:
181                     q = projectB.query("INSERT INTO src_associations (suite, source) VALUES (%s, %s)" % (suite_id, id));
182             elif action == "remove":
183                 if assoication_id == None:
184                     utils.warn("'%s~%s~%s' doesn't exist in suite %s." % (package, version, architecture, suite_id));
185                     continue;
186                 else:
187                     q = projectB.query("DELETE FROM src_associations WHERE id = %s" % (assoication_id));
188         else:
189             # Find the existing assoications ID, if any
190             q = projectB.query("SELECT id FROM bin_associations WHERE suite = %s and bin = %s" % (suite_id, id));
191             ql = q.getresult();
192             if ql == []:
193                 assoication_id = None;
194             else:
195                 assoication_id = ql[0][0];
196             # Take action
197             if action == "add":
198                 if assoication_id != None:
199                     utils.warn("'%s~%s~%s' already exists in suite %s." % (package, version, architecture, suite_id));
200                     continue;
201                 else:
202                     q = projectB.query("INSERT INTO bin_associations (suite, bin) VALUES (%s, %s)" % (suite_id, id));
203             elif action == "remove":
204                 if assoication_id == None:
205                     utils.warn("'%s~%s~%s' doesn't exist in suite %s." % (package, version, architecture, suite_id));
206                     continue;
207                 else:
208                     q = projectB.query("DELETE FROM bin_associations WHERE id = %s" % (assoication_id));
209
210     projectB.query("COMMIT WORK");
211
212 #######################################################################################
213
214 def get_list (suite_id):
215     # List binaries
216     q = projectB.query("SELECT b.package, b.version, a.arch_string FROM binaries b, bin_associations ba, architecture a WHERE ba.suite = %s AND ba.bin = b.id AND b.architecture = a.id" % (suite_id));
217     ql = q.getresult();
218     for i in ql:
219         print string.join(i);
220
221     # List source
222     q = projectB.query("SELECT s.source, s.version FROM source s, src_associations sa WHERE sa.suite = %s AND sa.source = s.id" % (suite_id));
223     ql = q.getresult();
224     for i in ql:
225         print string.join(i) + " source";
226
227 #######################################################################################
228
229 def main ():
230     global Cnf, projectB, Logger;
231
232     apt_pkg.init();
233
234     Cnf = apt_pkg.newConfiguration();
235     apt_pkg.ReadConfigFileISC(Cnf,utils.which_conf_file());
236
237     Arguments = [('a',"add","Heidi::Options::Add", "HasArg"),
238                  ('h',"help","Heidi::Options::Help"),
239                  ('l',"list","Heidi::Options::List","HasArg"),
240                  ('r',"remove", "Heidi::Options::Remove", "HasArg"),
241                  ('s',"set", "Heidi::Options::Set", "HasArg")];
242
243     for i in ["add", "help", "list", "remove", "set", "version" ]:
244         Cnf["Heidi::Options::%s" % (i)] = "";
245
246     file_list = apt_pkg.ParseCommandLine(Cnf,Arguments,sys.argv);
247     Options = Cnf.SubTree("Heidi::Options")
248
249     if Options["Help"]:
250         usage();
251
252     projectB = pg.connect(Cnf["DB::Name"], Cnf["DB::Host"],int(Cnf["DB::Port"]));
253
254     db_access.init(Cnf, projectB);
255
256     action = None;
257
258     for i in ("add", "list", "remove", "set"):
259         suite = Cnf["Heidi::Options::%s" % (i)];
260         if suite !="":
261             if not Cnf.has_key("Suite::%s" % (suite)):
262                 utils.fubar("Unknown suite '%s'." %(suite));
263             else:
264                 suite_id = db_access.get_suite_id(suite);
265                 if action != None:
266                     utils.fubar("Can only perform one action at a time.");
267                 action = i;
268
269     # Need an action...
270     if action == None:
271         utils.fubar("No action specified.");
272
273     # Safety/Sanity check
274     if action == "set" and suite != "testing":
275         utils.fubar("Will not reset a suite other than testing.");
276
277     if action == "list":
278         get_list(suite_id);
279     else:
280         Logger = logging.Logger(Cnf, "heidi");
281         if file_list != []:
282             for file in file_list:
283                 process_file(utils.open_file(file,'r'), suite_id, action);
284         else:
285             process_file(sys.stdin, suite_id, action);
286         Logger.close();
287
288 #######################################################################################
289
290 if __name__ == '__main__':
291     main()
292