Mercurial > bitten > bitten-test
comparison contrib/deletebuild.py @ 862:9d65ffa31414
Add script for deleting duplicate builds encountered during upgrades.
author | hodgestar |
---|---|
date | Mon, 18 Oct 2010 00:21:17 +0000 |
parents | |
children | a24744710a65 |
comparison
equal
deleted
inserted
replaced
860:ac28bc3feb2a | 862:9d65ffa31414 |
---|---|
1 # -*- coding: utf-8 -*- | |
2 # | |
3 # Maintained by Simon Cross <hodegstar+bittencontrib@gmail.com> | |
4 # | |
5 # Copyright (C) 2010 Edgewall Software | |
6 # All rights reserved. | |
7 # | |
8 # This software is licensed as described in the file COPYING, which | |
9 # you should have received as part of this distribution. The terms | |
10 # are also available at http://bitten.edgewall.org/wiki/License. | |
11 | |
12 """Utility for deleting duplicate builds encounted while upgrading | |
13 to schema version 10.""" | |
14 | |
15 import os | |
16 | |
17 from trac.env import Environment | |
18 from trac.attachment import Attachment | |
19 from trac.resource import Resource | |
20 | |
21 __version__ = "1.0a1" | |
22 | |
23 class BuildDeleter(object): | |
24 """Class for deleting a build.""" | |
25 | |
26 def __init__(self, env_path): | |
27 self.env = Environment(env_path) | |
28 self.logs_dir = self.env.config.get('bitten', 'logs_dir', 'log/bitten') | |
29 | |
30 def _log_files(self, cursor, build): | |
31 """Return a list of log files.""" | |
32 cursor.execute("SELECT filename FROM bitten_log WHERE build=%s", | |
33 (build,)) | |
34 rows = cursor.fetchall() | |
35 | |
36 all_files = [] | |
37 for row in rows: | |
38 filename = row[0] | |
39 file_prefix = os.path.join(self.logs_dir, filename) | |
40 for suffix in ['', '.level', '.levels']: | |
41 log_file = file_prefix + suffix | |
42 if os.path.isfile(log_file): | |
43 all_files.append(log_file) | |
44 | |
45 return all_files | |
46 | |
47 def discover(self, build): | |
48 """Print a summary of what is linked to the build.""" | |
49 print "Items to delete for build %r" % (build,) | |
50 | |
51 db = self.env.get_db_cnx() | |
52 cursor = db.cursor() | |
53 | |
54 print "Attachments for build resource:" | |
55 cursor.execute("SELECT config FROM bitten_build WHERE id=%s", (build,)) | |
56 config = cursor.fetchone()[0] | |
57 print "\n %s/%s" % (config, build) | |
58 | |
59 print "Log files:" | |
60 print "\n ".join(self._log_files(cursor, build)) | |
61 | |
62 print "Rows from bitten_log with ids:" | |
63 cursor.execute("SELECT id FROM bitten_log WHERE build=%s", (build,)) | |
64 print "\n ".join(row[0] for row in cursor.fetchall()) | |
65 | |
66 print "Rows from bitten_report with ids:" | |
67 cursor.execute("SELECT id FROM bitten_report WHERE build=%s", (build,)) | |
68 print "\n ".join(row[0] for row in cursor.fetchall()) | |
69 print "Rows from bitten_report_item with report set to these ids will" | |
70 print "also be deleted." | |
71 | |
72 print "Rows from bitten_step for this build with names:" | |
73 cursor.execute("SELECT name FROM bitten_step WHERE build=%s", (build,)) | |
74 print "\n ".join(row[0] for row in cursor.fetchall()) | |
75 | |
76 print "Row from bitten_build with id:" | |
77 cursor.execute("SELECT name FROM bitten_step WHERE build=%s", (build,)) | |
78 print "\n ".join(row[0] for row in cursor.fetchall()) | |
79 | |
80 def remove(self, build): | |
81 """Delete what is linked to the build.""" | |
82 print "Deleting items for build %r" % (build,) | |
83 | |
84 db = self.env.get_db_cnx() | |
85 cursor = db.cursor() | |
86 | |
87 print "Determining associated config." | |
88 cursor.execute("SELECT config FROM bitten_build WHERE id=%s", (build,)) | |
89 config = cursor.fetchone()[0] | |
90 | |
91 print "Collecting log files." | |
92 filenames = self._log_files(cursor, build) | |
93 | |
94 try: | |
95 print "Deleting bitten_log entries." | |
96 cursor.execute("DELETE FROM bitten_log WHERE build=%s", (build,)) | |
97 | |
98 print "Deleting bitten_report_item_entries." | |
99 cursor.execute("DELETE FROM bitten_report_item WHERE report IN (" | |
100 "SELECT bitten_report.id FROM bitten_report " | |
101 "WHERE bitten_report.build=%s" | |
102 ")", (build,)) | |
103 | |
104 print "Deleting bitten_report entires." | |
105 cursor.execute("DELETE FROM bitten_report WHERE build=%s", | |
106 (build,)) | |
107 | |
108 print "Deleting bitten_step entries." | |
109 cursor.execute("DELETE FROM bitten_step WHERE build=%s", (build,)) | |
110 | |
111 print "Delete bitten_build entry." | |
112 cursor.execute("DELETE FROM bitten_build WHERE id=%s", (build,)) | |
113 except: | |
114 db.rollback() | |
115 print "Build deletion failed. Database rolled back." | |
116 raise | |
117 | |
118 print "Bitten database changes committed." | |
119 db.commit() | |
120 | |
121 print "Removing log files." | |
122 for filename in filenames: | |
123 os.remove(filename) | |
124 | |
125 print "Removing attachments." | |
126 resource = Resource('build', '%s/%s' % (config, build)) | |
127 Attachment.delete_all(self.env, 'build', resource.id, db) | |
128 | |
129 | |
130 def main(): | |
131 from optparse import OptionParser | |
132 | |
133 parser = OptionParser(usage='usage: %prog env_path build_id', | |
134 version='%%prog %s' % __version__) | |
135 | |
136 options, args = parser.parse_args() | |
137 | |
138 if len(args) != 2: | |
139 parser.error('incorrect number of arguments') | |
140 | |
141 env_path, build_id = args | |
142 | |
143 deleter = BuildDeleter(env_path) | |
144 deleter.discover(build_id) | |
145 proceed = raw_input('Continue [y/n]? ') | |
146 if proceed == 'y': | |
147 deleter.remove(build_id) | |
148 | |
149 | |
150 if __name__ == "__main__": | |
151 import sys | |
152 sys.exit(main()) |