/[soft]/mga-gnome/trunk/mga-gnome
ViewVC logotype

Contents of /mga-gnome/trunk/mga-gnome

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3039 - (show annotations) (download)
Thu Feb 23 20:10:41 2012 UTC (12 years, 1 month ago) by ovitters
File size: 15609 byte(s)
don't change packages which have uncommitted changes
1 #!/usr/bin/python
2
3 import os
4 import os.path
5 import sys
6 import re
7 import subprocess
8 import urllib2
9 import urlparse
10 import argparse
11 import errno
12 import tempfile
13 import shutil
14 from sgmllib import SGMLParser
15
16 MEDIA="Core Release Source"
17 URL="http://download.gnome.org/sources/"
18 PKGROOT='~/pkgs'
19
20 re_version = re.compile(r'([-.]|\d+|[^-.\d]+)')
21
22 def version_cmp(a, b):
23 """Compares two versions
24
25 Returns
26 -1 if a < b
27 0 if a == b
28 1 if a > b
29
30 Logic from Bugzilla::Install::Util::vers_cmp"""
31 A = re_version.findall(a.lstrip('0'))
32 B = re_version.findall(b.lstrip('0'))
33
34 while A and B:
35 a = A.pop(0)
36 b = B.pop(0)
37
38 if a == b:
39 continue
40 elif a == '-':
41 return -1
42 elif b == '-':
43 return 1
44 elif a == '.':
45 return -1
46 elif b == '.':
47 return 1
48 elif a.isdigit() and b.isdigit():
49 c = cmp(a, b) if (a.startswith('0') or b.startswith('0')) else cmp(int(a, 10), int(b, 10))
50 if c:
51 return c
52 else:
53 c = cmp(a.upper(), b.upper())
54 if c:
55 return c
56
57 return cmp(len(A), len(B))
58
59 def get_latest_version(versions, max_version=None):
60 """Gets the latest version number
61
62 if max_version is specified, gets the latest version number before
63 max_version"""
64 latest = None
65 for version in versions:
66 if ( latest is None or version_cmp(version, latest) > 0 ) \
67 and ( max_version is None or version_cmp(version, max_version) < 0 ):
68 latest = version
69 return latest
70
71 def line_input (file):
72 for line in file:
73 if line[-1] == '\n':
74 yield line[:-1]
75 else:
76 yield line
77
78 def call_editor(filename):
79 """Return a sequence of possible editor binaries for the current platform"""
80
81 editors = []
82
83 for varname in 'VISUAL', 'EDITOR':
84 if varname in os.environ:
85 editors.append(os.environ[varname])
86
87 editors.extend(('/usr/bin/editor', 'vi', 'pico', 'nano', 'joe'))
88
89 for editor in editors:
90 try:
91 ret = subprocess.call([editor, filename])
92 except OSError, e:
93 if e.errno == 2:
94 continue
95 raise
96
97 if ret == 127:
98 continue
99
100 return True
101
102 class urllister(SGMLParser):
103 def reset(self):
104 SGMLParser.reset(self)
105 self.urls = []
106
107 def start_a(self, attrs):
108 href = [v for k, v in attrs if k=='href']
109 if href:
110 self.urls.extend(href)
111
112 class SpecFile(object):
113 re_update_version = re.compile(r'^(?P<pre>Version:\s*)(?P<version>.+)(?P<post>\s*)$', re.MULTILINE + re.IGNORECASE)
114 re_update_release = re.compile(r'^(?P<pre>Release:\s*)(?P<release>%mkrel \d+)(?P<post>\s*)$', re.MULTILINE + re.IGNORECASE)
115
116 def __init__(self, path):
117 self.path = path
118 self.cwd = os.path.dirname(path)
119
120 @property
121 def version(self):
122 return subprocess.check_output(["rpm", "--specfile", self.path, "--queryformat", "%{VERSION}\n"]).splitlines()[0]
123
124 def update(self, version):
125 """Update specfile (increase version)"""
126 cur_version = self.version
127
128 compare = version_cmp(version, cur_version)
129
130 if compare == 0:
131 print >>sys.stderr, "ERROR: Already at version %s!" % (cur_version)
132 return False
133
134 if compare != 1:
135 print >>sys.stderr, "ERROR: Version %s is older than current version %s!" % (version, cur_version)
136 return False
137
138 # XXX - os.path.join is hackish
139 if subprocess.check_output(["svn", "diff", os.path.join(self.path, '..')]) != '':
140 print >>sys.stderr, "ERROR: Package has uncommitted changes!"
141 return False
142
143 with open(self.path, "rw") as f:
144 data = f.read()
145
146 if data.count("%mkrel") != 1:
147 print >>sys.stderr, "ERROR: Multiple %mkrel found; don't know what to do!"
148 return False
149
150 data, nr = self.re_update_version.subn(r'\g<pre>%s\g<post>' % version, data, 1)
151 if nr != 1:
152 print >>sys.stderr, "ERROR: Could not increase version!"
153 return False
154
155 data, nr = self.re_update_release.subn(r'\g<pre>%mkrel 1\g<post>', data, 1)
156 if nr != 1:
157 print >>sys.stderr, "ERROR: Could not reset release!"
158 return False
159
160 # Overwrite file with new version number
161 write_file(self.path, data)
162
163
164 # Check RPM also agrees that version number has increased
165 if self.version != version:
166 print "ERROR: Increased version to %s, but RPM doesn't agree!?!" % version
167 return False
168
169 try:
170 # Download new tarball
171 subprocess.check_call(['mgarepo', 'sync', '-d'], cwd=self.cwd)
172 # Check patches still apply
173 subprocess.check_call(['bm', '-p', '--nodeps'], cwd=self.cwd)
174 except subprocess.CalledProcessError:
175 return False
176
177 return True
178
179 class Patch(object):
180 """Do things with patches"""
181
182 re_dep3 = re.compile(r'^(?:#\s*)?(?P<header>[-A-Za-z0-9]+?):\s*(?P<data>.*)$')
183 re_dep3_cont = re.compile(r'^#?\s+(?P<data>.*)$')
184
185 def __init__(self, path, show_path=False):
186 """Path: path to patch (might not exist)"""
187 self.path = path
188 self.show_path = show_path
189
190 def __str__(self):
191 return self.path if self.show_path else os.path.basename(self.path)
192
193 def add_dep3(self):
194 """Add DEP-3 headers to a patch file"""
195 if self.dep3['valid']:
196 return False
197
198 new_headers = (
199 ('Author', self.svn_author),
200 ('Subject', ''),
201 ('Applied-Upstream', ''),
202 ('Forwarded', ''),
203 ('Bug', ''),
204 )
205
206 with tempfile.NamedTemporaryFile(dir=os.path.dirname(self.path), delete=False) as fdst:
207 with open(self.path, "r") as fsrc:
208 # Start with any existing DEP3 headers
209 for i in range(self.dep3['last_nr']):
210 fdst.write(fsrc.read())
211
212 # After that add the DEP3 headers
213 add_line = False
214 for header, data in new_headers:
215 if header in self.dep3['headers']:
216 continue
217
218 # XXX - wrap this at 80 chars
219 add_line = True
220 print >>fdst, "%s: %s" % (header, "" if data is None else data)
221
222 if add_line: print >>fdst, ""
223 # Now copy any other data and the patch
224 shutil.copyfileobj(fsrc, fdst)
225
226 fdst.flush()
227 os.rename(fdst.name, self.path)
228
229 call_editor(self.path)
230
231 #Author: fwang
232 #Subject: Build fix: Fix glib header inclusion
233 #Applied-Upstream: commit:30602
234 #Forwarded: yes
235 #Bug: http://bugzilla.abisource.com/show_bug.cgi?id=13247
236
237 def _read_dep3(self):
238 """Read DEP-3 headers from an existing patch file
239
240 This will also parse git headers"""
241 dep3 = {}
242 headers = {}
243
244 last_header = None
245 last_nr = 0
246 nr = 0
247 try:
248 with open(self.path, "r") as f:
249 for line in line_input(f):
250 nr += 1
251 # stop trying to parse when real patch begins
252 if line == '---':
253 break
254
255 r = self.re_dep3.match(line)
256 if r:
257 info = r.groupdict()
258
259 # Avoid matching URLS
260 if info['data'].startswith('//') and info['header'].lower () == info['header']:
261 continue
262
263 headers[info['header']] = info['data']
264 last_header = info['header']
265 last_nr = nr
266 continue
267
268 r = self.re_dep3_cont.match(line)
269 if r:
270 info = r.groupdict()
271 if last_header:
272 headers[last_header] = " ".join((headers[last_header], info['data']))
273 last_nr = nr
274 continue
275
276 last_header = None
277 except IOError:
278 pass
279
280 dep3['valid'] = \
281 (('Description' in headers and headers['Description'].strip() != '')
282 or ('Subject' in headers and headers['Subject'].strip() != '')) \
283 and (('Origin' in headers and headers['Origin'].strip() != '') \
284 or ('Author' in headers and headers['Author'].strip() != '') \
285 or ('From' in headers and headers['From'].strip() != ''))
286 dep3['last_nr'] = last_nr
287 dep3['headers'] = headers
288
289 self._dep3 = dep3
290
291 @property
292 def dep3(self):
293 if not hasattr(self, '_dep3'):
294 self._read_dep3()
295
296 return self._dep3
297
298 @property
299 def svn_author(self):
300 if not hasattr(self, '_svn_author'):
301 p = subprocess.Popen(['svn', 'log', '-q', "--", self.path], stdout=subprocess.PIPE, close_fds=True)
302 contents = p.stdout.read().strip("\n").splitlines()
303 ecode = p.wait()
304 if ecode == 0:
305 for line in contents:
306 if ' | ' not in line:
307 continue
308
309 fields = line.split(' | ')
310 if len(fields) >= 3:
311 self._svn_author = fields[1]
312
313 if not hasattr(self, '_svn_author'):
314 return None
315
316 return self._svn_author
317
318 def get_upstream_names():
319 urlopen = urllib2.build_opener()
320
321 good_dir = re.compile('^[-A-Za-z0-9_+.]+/$')
322
323 # Get the files
324 usock = urlopen.open(URL)
325 parser = urllister()
326 parser.feed(usock.read())
327 usock.close()
328 parser.close()
329 files = parser.urls
330
331 tarballs = set([filename.replace('/', '') for filename in files if good_dir.search(filename)])
332
333 return tarballs
334
335 def get_downstream_names():
336 re_file = re.compile(r'^(?P<module>.*?)[_-](?:(?P<oldversion>([0-9]+[\.])*[0-9]+)-)?(?P<version>([0-9]+[\.\-])*[0-9]+)\.(?P<format>(?:tar\.|diff\.)?[a-z][a-z0-9]*)$')
337
338 p = subprocess.Popen(['urpmf', '--files', '.', "--media", MEDIA], stdout=subprocess.PIPE, close_fds=True)
339 contents = p.stdout.read().strip("\n").splitlines()
340 ecode = p.wait()
341 if ecode != 0:
342 sys.exit(1)
343
344 FILES = {}
345 TARBALLS = {}
346
347 for line in contents:
348 try:
349 srpm, filename = line.split(":")
350 except ValueError:
351 print >>sys.stderr, line
352 continue
353
354 if '.tar' in filename:
355 r = re_file.match(filename)
356 if r:
357 fileinfo = r.groupdict()
358 module = fileinfo['module']
359
360 if module not in TARBALLS:
361 TARBALLS[module] = set()
362 TARBALLS[module].add(srpm)
363
364 if srpm not in FILES:
365 FILES[srpm] = set()
366 FILES[srpm].add(filename)
367
368 return TARBALLS, FILES
369
370
371 def write_file(path, data):
372 with tempfile.NamedTemporaryFile(dir=os.path.dirname(path), delete=False) as fdst:
373 fdst.write(data)
374 fdst.flush()
375 os.rename(fdst.name, path)
376
377 def cmd_co(options, parser):
378 upstream = get_upstream_names()
379 downstream, downstream_files = get_downstream_names()
380
381 cwd = os.path.expanduser(PKGROOT)
382
383 matches = upstream & set(downstream.keys())
384 for module in matches:
385 print module, "\t".join(downstream[module])
386 for package in downstream[module]:
387 subprocess.call(['mgarepo', 'co', package], cwd=cwd)
388
389 def cmd_ls(options, parser):
390 upstream = get_upstream_names()
391 downstream, downstream_files = get_downstream_names()
392
393 matches = upstream & set(downstream.keys())
394 for module in matches:
395 print "\n".join(downstream[module])
396
397 def cmd_patches(options, parser):
398 upstream = get_upstream_names()
399 downstream, downstream_files = get_downstream_names()
400
401 path = os.path.expanduser(PKGROOT)
402
403 import pprint
404
405 matches = upstream & set(downstream.keys())
406 for module in sorted(matches):
407 for srpm in downstream[module]:
408 for filename in downstream_files[srpm]:
409 if '.patch' in filename or '.diff' in filename:
410
411 p = Patch(os.path.join(path, srpm, "SOURCES", filename), show_path=options.path)
412 valid = ""
413 forwarded = ""
414 if p.dep3['headers']:
415 forwarded = p.dep3['headers'].get('Forwarded', "no")
416 if p.dep3['valid']:
417 valid="VALID"
418 print "\t".join((module, srpm, str(p), forwarded, valid))
419
420 def cmd_dep3(options, parser):
421 p = Patch(options.patch)
422 p.add_dep3()
423
424 def cmd_package_new_version(options, parser):
425 package = options.package
426
427 cwd = os.path.join(os.path.expanduser(PKGROOT), package)
428
429 subprocess.call(['mgarepo', 'co', package], cwd=cwd)
430 s = SpecFile(os.path.join(cwd, "SPECS", "%s.spec" % package))
431 print "%s => %s" % (s.version, options.version)
432 if not s.update(options.version):
433 sys.exit(1)
434
435 if options.submit:
436 try:
437 # checkin changes
438 subprocess.check_call(['mgarepo', 'ci', '-m', 'new version'], cwd=cwd)
439 # and submit
440 subprocess.check_call(['mgarepo', 'submit'], cwd=cwd)
441 except subprocess.CalledProcessError:
442 sys.exit(1)
443
444
445 def main():
446 description = """Mageia GNOME commands."""
447 epilog="""Report bugs to Olav Vitters"""
448 parser = argparse.ArgumentParser(description=description,epilog=epilog)
449
450 # SUBPARSERS
451 subparsers = parser.add_subparsers(title='subcommands')
452 # install
453 subparser = subparsers.add_parser('co', help='checkout all GNOME modules')
454 subparser.set_defaults(
455 func=cmd_co
456 )
457
458 subparser = subparsers.add_parser('packages', help='list all GNOME packages')
459 subparser.set_defaults(
460 func=cmd_ls
461 )
462
463 subparser = subparsers.add_parser('patches', help='list all GNOME patches')
464 subparser.add_argument("-p", "--path", action="store_true", dest="path",
465 help="Show full path to patch")
466 subparser.set_defaults(
467 func=cmd_patches, path=False
468 )
469
470 subparser = subparsers.add_parser('dep3', help='Add dep3 headers')
471 subparser.add_argument("patch", help="Patch")
472 subparser.set_defaults(
473 func=cmd_dep3, path=False
474 )
475
476 subparser = subparsers.add_parser('increase', help='Increase version number')
477 subparser.add_argument("package", help="Package name")
478 subparser.add_argument("version", help="Version number")
479 subparser.add_argument("-s", "--submit", action="store_true", dest="submit",
480 help="Commit changes and submit")
481 subparser.set_defaults(
482 func=cmd_package_new_version, submit=False
483 )
484
485 if len(sys.argv) == 1:
486 parser.print_help()
487 sys.exit(2)
488
489 options = parser.parse_args()
490
491 try:
492 options.func(options, parser)
493 except KeyboardInterrupt:
494 print('Interrupted')
495 sys.exit(1)
496 except EOFError:
497 print('EOF')
498 sys.exit(1)
499 except IOError, e:
500 if e.errno != errno.EPIPE:
501 raise
502 sys.exit(0)
503
504 if __name__ == "__main__":
505 main()

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.30