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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3653 - (show annotations) (download)
Wed Mar 21 19:53:09 2012 UTC (12 years, 1 month ago) by ovitters
File size: 31116 byte(s)
packages command: allow sorting according to the order of a file
1 #!/usr/bin/python -u
2
3 # A lot of the code comes from ftpadmin, see
4 # http://git.gnome.org/browse/sysadmin-bin/tree/ftpadmin
5 # Written by Olav Vitters
6
7 # basic modules:
8 import os
9 import os.path
10 import sys
11 import re
12 import subprocess
13
14 # command line parsing, error handling:
15 import argparse
16 import errno
17
18 # overwriting files by moving them (safer):
19 import tempfile
20 import shutil
21
22 # version comparison:
23 import rpm
24
25 # opening tarballs:
26 import tarfile
27 import gzip
28 import bz2
29 import lzma # pyliblzma
30
31 # getting links from HTML document:
32 from sgmllib import SGMLParser
33 import urllib2
34 import urlparse
35
36 # for checking hashes
37 import hashlib
38
39 # for parsing ftp-release-list emails
40 import email
41 from email.mime.text import MIMEText
42
43 # to be able to sleep for a while
44 import time
45
46 # version freeze
47 import datetime
48
49 # packages --sort
50 import itertools
51
52 MEDIA="Core Release Source"
53 URL="http://download.gnome.org/sources/"
54 PKGROOT='~/pkgs'
55 SLEEP_INITIAL=180
56 SLEEP_REPEAT=30
57 SLEEP_TIMES=20
58
59 re_majmin = re.compile(r'^([0-9]+\.[0-9]+).*')
60 re_version = re.compile(r'([-.]|\d+|[^-.\d]+)')
61
62 def version_cmp(a, b):
63 """Compares two versions
64
65 Returns
66 -1 if a < b
67 0 if a == b
68 1 if a > b
69 """
70
71 return rpm.labelCompare(('1', a, '1'), ('1', b, '1'))
72
73 def get_latest_version(versions, max_version=None):
74 """Gets the latest version number
75
76 if max_version is specified, gets the latest version number before
77 max_version"""
78 latest = None
79 for version in versions:
80 if ( latest is None or version_cmp(version, latest) > 0 ) \
81 and ( max_version is None or version_cmp(version, max_version) < 0 ):
82 latest = version
83 return latest
84
85 def judge_version_increase(version_old, version_new):
86 """Judge quality of version increase:
87
88 Returns a tuple containing judgement and message
89
90 Judgement:
91 Less than 0: Error
92 0 to 4: Better not
93 5+: Ok"""
94 versions = (version_old, version_new)
95
96 # First do a basic version comparison to ensure version_new is actually newer
97 compare = version_cmp(version_new, version_old)
98
99 if compare == 0:
100 # 1.0.0 -> 1.0.1
101 return (-2, "Already at version %s!" % (version_old))
102
103 if compare != 1:
104 # 1.0.1 -> 1.0.0
105 return (-3, "Version %s is older than current version %s!" % (version_new, version_old))
106
107 # Version is newer, but we don't want to see if it follows the GNOME versioning scheme
108 majmins = [re_majmin.sub(r'\1', ver) for ver in versions if re_majmin.match(ver) is not None]
109
110 if len(majmins) == 1:
111 return (-1, "Version number scheme changes: %s" % (", ".join(versions)))
112
113 if len(majmins) == 0:
114 return (0, "Unsupported version numbers: %s" % (", ".join(versions)))
115
116 # Follows GNOME versioning scheme
117 # Meaning: x.y.z
118 # x = major
119 # y = minor : even if stable
120 # z = micro
121
122 # Major+minor the same? Then go ahead and upgrade!
123 if majmins[0] == majmins[1]:
124 # Majmin of both versions are the same, looks good!
125 # 1.1.x -> 1.1.x or 1.0.x -> 1.0.x
126 return (10, None)
127
128 # More detailed analysis needed, so figure out the numbers
129 majmin_nrs = [map(long, ver.split('.')) for ver in majmins]
130
131 # Check/ensure major version number is the same
132 if majmin_nrs[0][0] != majmin_nrs[1][0]:
133 # 1.0.x -> 2.0.x
134 return (1, "Major version number increase")
135
136 # Minor indicates stable/unstable
137 devstate = (majmin_nrs[0][1] % 2 == 0, majmin_nrs[1][1] % 2 == 0)
138
139 # Upgrading to unstable is weird
140 if not devstate[1]:
141 if devstate[0]:
142 # 1.2.x -> 1.3.x
143 return (1, "Stable to unstable increase")
144
145 # 1.3.x -> 1.5.x
146 return (4, "Unstable to unstable version increase")
147
148 # Unstable => stable is always ok
149 if not devstate[0]:
150 # 1.1.x -> 1.2.x
151 return (5, "Unstable to stable")
152
153 # Can only be increase of minors from one stable to the next
154 # 1.0.x -> 1.2.x
155 return (6, "Stable version increase")
156
157 def line_input (file):
158 for line in file:
159 if line[-1] == '\n':
160 yield line[:-1]
161 else:
162 yield line
163
164 def call_editor(filename):
165 """Return a sequence of possible editor binaries for the current platform"""
166
167 editors = []
168
169 for varname in 'VISUAL', 'EDITOR':
170 if varname in os.environ:
171 editors.append(os.environ[varname])
172
173 editors.extend(('/usr/bin/editor', 'vi', 'pico', 'nano', 'joe'))
174
175 for editor in editors:
176 try:
177 ret = subprocess.call([editor, filename])
178 except OSError, e:
179 if e.errno == 2:
180 continue
181 raise
182
183 if ret == 127:
184 continue
185
186 return True
187
188 class urllister(SGMLParser):
189 def reset(self):
190 SGMLParser.reset(self)
191 self.urls = []
192
193 def start_a(self, attrs):
194 href = [v for k, v in attrs if k=='href']
195 if href:
196 self.urls.extend(href)
197
198 class XzTarFile(tarfile.TarFile):
199
200 OPEN_METH = tarfile.TarFile.OPEN_METH.copy()
201 OPEN_METH["xz"] = "xzopen"
202
203 @classmethod
204 def xzopen(cls, name, mode="r", fileobj=None, **kwargs):
205 """Open gzip compressed tar archive name for reading or writing.
206 Appending is not allowed.
207 """
208 if len(mode) > 1 or mode not in "rw":
209 raise ValueError("mode must be 'r' or 'w'")
210
211 if fileobj is not None:
212 fileobj = _LMZAProxy(fileobj, mode)
213 else:
214 fileobj = lzma.LZMAFile(name, mode)
215
216 try:
217 # lzma doesn't immediately return an error
218 # try and read a bit of data to determine if it is a valid xz file
219 fileobj.read(_LZMAProxy.blocksize)
220 fileobj.seek(0)
221 t = cls.taropen(name, mode, fileobj, **kwargs)
222 except IOError:
223 raise tarfile.ReadError("not a xz file")
224 except lzma.error:
225 raise tarfile.ReadError("not a xz file")
226 t._extfileobj = False
227 return t
228
229 if not hasattr(tarfile.TarFile, 'xzopen'):
230 tarfile.open = XzTarFile.open
231
232 def is_valid_hash(path, algo, hexdigest):
233 if algo not in hashlib.algorithms:
234 raise ValueError("Unknown hash algorithm: %s" % algo)
235
236 local_hash = getattr(hashlib, algo)()
237
238 with open(path, 'rb') as fp:
239 data = fp.read(32768)
240 while data:
241 local_hash.update(data)
242 data = fp.read(32768)
243
244 return local_hash.hexdigest() == hexdigest
245
246 class SpecFile(object):
247 re_update_version = re.compile(r'^(?P<pre>Version:\s*)(?P<version>.+)(?P<post>\s*)$', re.MULTILINE + re.IGNORECASE)
248 re_update_release = re.compile(r'^(?P<pre>Release:\s*)(?P<release>%mkrel \d+)(?P<post>\s*)$', re.MULTILINE + re.IGNORECASE)
249
250 def __init__(self, path):
251 self.path = path
252 self.cwd = os.path.dirname(path)
253
254 @property
255 def version(self):
256 return subprocess.check_output(["rpm", "--specfile", self.path, "--queryformat", "%{VERSION}\n"]).splitlines()[0]
257 @property
258 def sources(self):
259 ts = rpm.ts()
260 spec = ts.parseSpec(self.path)
261 srclist = spec.sources if isinstance(spec.sources, (list, tuple)) \
262 else spec.sources()
263 return dict((os.path.basename(name), name) for name, no, flags in srclist)
264
265 def update(self, version, force=False):
266 """Update specfile (increase version)"""
267 cur_version = self.version
268
269 (judgement, msg) = judge_version_increase(cur_version, version)
270
271 if judgement < 0:
272 print >>sys.stderr, "ERROR: %s!" % (msg)
273 return False
274
275 if judgement < 5:
276 print "WARNING: %s!" % (msg)
277 if not force: return False
278
279 # XXX - os.path.join is hackish
280 svn_diff_output = subprocess.check_output(["svn", "diff", os.path.join(self.path, '..')])
281 if svn_diff_output != '':
282 print svn_diff_output
283 print >>sys.stderr, "ERROR: Package has uncommitted changes!"
284 if not force:
285 return False
286
287 # Forcing package submission: revert changes
288 try:
289 print >>sys.stderr, "WARNING: Force used; reverting svn changes"
290 subprocess.check_call(["svn", "revert", "-R", os.path.join(self.path, '..')])
291 except subprocess.CalledProcessError:
292 return False
293
294 with open(self.path, "rw") as f:
295 data = f.read()
296
297 if data.count("%mkrel") != 1:
298 print >>sys.stderr, "ERROR: Multiple %mkrel found; don't know what to do!"
299 return False
300
301 data, nr = self.re_update_version.subn(r'\g<pre>%s\g<post>' % version, data, 1)
302 if nr != 1:
303 print >>sys.stderr, "ERROR: Could not increase version!"
304 return False
305
306 data, nr = self.re_update_release.subn(r'\g<pre>%mkrel 1\g<post>', data, 1)
307 if nr != 1:
308 print >>sys.stderr, "ERROR: Could not reset release!"
309 return False
310
311 # Overwrite file with new version number
312 write_file(self.path, data)
313
314
315 # Verify that RPM also agrees that version number has changed
316 if self.version != version:
317 print "ERROR: Increased version to %s, but RPM doesn't agree!?!" % version
318 return False
319
320
321 # Try to download the new tarball various times and wait between attempts
322 tries = 0
323 while tries < SLEEP_TIMES:
324 tries += 1
325 if tries > 1: time.sleep(SLEEP_REPEAT)
326 try:
327 # Download new tarball
328 subprocess.check_call(['mgarepo', 'sync', '-d'], cwd=self.cwd)
329 # success, so exit loop
330 break
331 except subprocess.CalledProcessError, e:
332 # mgarepo sync returns 1 if the tarball cannot be downloaded
333 if e.returncode != 1:
334 return False
335 else:
336 return False
337
338
339 try:
340 # Check patches still apply
341 subprocess.check_call(['bm', '-p', '--nodeps'], cwd=self.cwd)
342 except subprocess.CalledProcessError:
343 logfile = os.path.join(os.path.dirname(self.path), 'log.%s' % os.path.splitext(os.path.basename(self.path))[0])
344 if os.path.exists(logfile):
345 subprocess.call(['tail', '-n', '15', logfile])
346 return False
347
348 return True
349
350 class Patch(object):
351 """Do things with patches"""
352
353 re_dep3 = re.compile(r'^(?:#\s*)?(?P<header>[-A-Za-z0-9]+?):\s*(?P<data>.*)$')
354 re_dep3_cont = re.compile(r'^#?\s+(?P<data>.*)$')
355
356 def __init__(self, path, show_path=False):
357 """Path: path to patch (might not exist)"""
358 self.path = path
359 self.show_path = show_path
360
361 def __str__(self):
362 return self.path if self.show_path else os.path.basename(self.path)
363
364 def add_dep3(self):
365 """Add DEP-3 headers to a patch file"""
366 if self.dep3['valid']:
367 return False
368
369 new_headers = (
370 ('Author', self.svn_author),
371 ('Subject', ''),
372 ('Applied-Upstream', ''),
373 ('Forwarded', ''),
374 ('Bug', ''),
375 )
376
377 with tempfile.NamedTemporaryFile(dir=os.path.dirname(self.path), delete=False) as fdst:
378 with open(self.path, "r") as fsrc:
379 # Start with any existing DEP3 headers
380 for i in range(self.dep3['last_nr']):
381 fdst.write(fsrc.read())
382
383 # After that add the DEP3 headers
384 add_line = False
385 for header, data in new_headers:
386 if header in self.dep3['headers']:
387 continue
388
389 # XXX - wrap this at 80 chars
390 add_line = True
391 print >>fdst, "%s: %s" % (header, "" if data is None else data)
392
393 if add_line: print >>fdst, ""
394 # Now copy any other data and the patch
395 shutil.copyfileobj(fsrc, fdst)
396
397 fdst.flush()
398 os.rename(fdst.name, self.path)
399
400 call_editor(self.path)
401
402 #Author: fwang
403 #Subject: Build fix: Fix glib header inclusion
404 #Applied-Upstream: commit:30602
405 #Forwarded: yes
406 #Bug: http://bugzilla.abisource.com/show_bug.cgi?id=13247
407
408 def _read_dep3(self):
409 """Read DEP-3 headers from an existing patch file
410
411 This will also parse git headers"""
412 dep3 = {}
413 headers = {}
414
415 last_header = None
416 last_nr = 0
417 nr = 0
418 try:
419 with open(self.path, "r") as f:
420 for line in line_input(f):
421 nr += 1
422 # stop trying to parse when real patch begins
423 if line == '---':
424 break
425
426 r = self.re_dep3.match(line)
427 if r:
428 info = r.groupdict()
429
430 # Avoid matching URLS
431 if info['data'].startswith('//') and info['header'].lower () == info['header']:
432 continue
433
434 headers[info['header']] = info['data']
435 last_header = info['header']
436 last_nr = nr
437 continue
438
439 r = self.re_dep3_cont.match(line)
440 if r:
441 info = r.groupdict()
442 if last_header:
443 headers[last_header] = " ".join((headers[last_header], info['data']))
444 last_nr = nr
445 continue
446
447 last_header = None
448 except IOError:
449 pass
450
451 dep3['valid'] = \
452 (('Description' in headers and headers['Description'].strip() != '')
453 or ('Subject' in headers and headers['Subject'].strip() != '')) \
454 and (('Origin' in headers and headers['Origin'].strip() != '') \
455 or ('Author' in headers and headers['Author'].strip() != '') \
456 or ('From' in headers and headers['From'].strip() != ''))
457 dep3['last_nr'] = last_nr
458 dep3['headers'] = headers
459
460 self._dep3 = dep3
461
462 @property
463 def dep3(self):
464 if not hasattr(self, '_dep3'):
465 self._read_dep3()
466
467 return self._dep3
468
469 @property
470 def svn_author(self):
471 if not hasattr(self, '_svn_author'):
472 try:
473 contents = subprocess.check_output(['svn', 'log', '-q', "--", self.path], close_fds=True).strip("\n").splitlines()
474
475 for line in contents:
476 if ' | ' not in line:
477 continue
478
479 fields = line.split(' | ')
480 if len(fields) >= 3:
481 self._svn_author = fields[1]
482 except subprocess.CalledProcessError:
483 pass
484
485 if not hasattr(self, '_svn_author'):
486 return None
487
488 return self._svn_author
489
490
491 class Upstream(object):
492
493 limit = None
494
495 def __init__(self):
496 urlopen = urllib2.build_opener()
497
498 good_dir = re.compile('^[-A-Za-z0-9_+.]+/$')
499
500 # Get the files
501 usock = urlopen.open(URL)
502 parser = urllister()
503 parser.feed(usock.read())
504 usock.close()
505 parser.close()
506 files = parser.urls
507
508 tarballs = set([filename.replace('/', '') for filename in files if good_dir.search(filename)])
509 if self.limit is not None:
510 tarballs.intersection_update(self.limit)
511
512 self.names = tarballs
513
514 class Downstream(object):
515 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]*)$')
516
517 def __init__(self):
518 contents = subprocess.check_output(['urpmf', '--qf', '%name|%version|%files', '.', "--media", MEDIA], close_fds=True).strip("\n").splitlines()
519
520 FILES = {}
521 TARBALLS = {}
522
523 for line in contents:
524 try:
525 srpm, version, filename = line.split("|")
526 except ValueError:
527 print >>sys.stderr, line
528 continue
529
530 if '.tar' in filename:
531 r = self.re_file.match(filename)
532 if r:
533 fileinfo = r.groupdict()
534 module = fileinfo['module']
535
536 if module not in TARBALLS:
537 TARBALLS[module] = {}
538 TARBALLS[module][srpm] = version
539
540 if srpm not in FILES:
541 FILES[srpm] = set()
542 FILES[srpm].add(filename)
543
544 self.tarballs = TARBALLS
545 self.files = FILES
546
547 def get_downstream_from_upstream(self, upstream, version):
548 if upstream not in self.tarballs:
549 raise ValueError("No packages for upstream name: %s" % upstream)
550
551 if len(self.tarballs[upstream]) == 1:
552 return self.tarballs[upstream].keys()
553
554 # Directories packages are located in
555 root = os.path.expanduser(PKGROOT)
556
557 packages = {}
558 for package in self.tarballs[upstream].keys():
559 cwd = os.path.join(root, package)
560
561 # Checkout package to ensure the checkout reflects the latest changes
562 try:
563 subprocess.check_call(['mgarepo', 'co', package], cwd=root)
564 except subprocess.CalledProcessError:
565 raise ValueError("Multiple packages found and cannot checkout %s" % package)
566
567 # Determine version from spec file
568 try:
569 packages[package] = SpecFile(os.path.join(cwd, "SPECS", "%s.spec" % package)).version
570 except subprocess.CalledProcessError:
571 raise ValueError("Multiple packages found and cannot determine version of %s" % package)
572
573 # Return all packages reflecting the current version
574 matches = [package for package in packages if packages[package] == version]
575 if len(matches):
576 return matches
577
578 # Return all packages reflecting the version before the current version
579 latest_version = get_latest_version(packages.values(), max_version=version)
580 matches = [package for package in packages if packages[package] == latest_version]
581 if len(matches):
582 return matches
583
584 # Give up
585 raise ValueError("Multiple packages found and cannot determine package for version %s" % version)
586
587 def write_file(path, data):
588 with tempfile.NamedTemporaryFile(dir=os.path.dirname(path), delete=False) as fdst:
589 fdst.write(data)
590 fdst.flush()
591 os.rename(fdst.name, path)
592
593 def cmd_co(options, parser):
594 root = os.path.expanduser(PKGROOT)
595
596 for package, module, package_version, spec_version, downstream_files in sorted(join_streams()):
597 print "%s => %s" % (module, package)
598 subprocess.call(['mgarepo', 'co', package], cwd=root)
599
600 def join_streams(show_version=False, only_diff_version=False):
601 root = os.path.expanduser(PKGROOT)
602
603 upstream = Upstream().names
604 downstream = Downstream()
605
606 matches = upstream & set(downstream.tarballs.keys())
607 for module in matches:
608 for package in downstream.tarballs[module].keys():
609 package_version = downstream.tarballs[module][package]
610 spec_version = None
611 if show_version or only_diff_version:
612 cwd = os.path.join(root, package)
613 try:
614 spec_version = SpecFile(os.path.join(cwd, "SPECS", "%s.spec" % package)).version
615 except subprocess.CalledProcessError:
616 spec_version = 'N/A'
617
618 if only_diff_version and package_version == spec_version:
619 continue
620
621 yield (package, module, package_version, spec_version, downstream.files[package])
622
623 def cmd_ls(options, parser):
624 streams = join_streams(show_version=options.show_version, only_diff_version=options.diff)
625 if options.sort:
626 SORT=dict(zip(options.sort.read().splitlines(), itertools.count()))
627
628 streams = sorted(streams, key=lambda a: (SORT.get(a[1], 9999), a[0]))
629 else:
630 streams = sorted(streams)
631
632 for package, module, package_version, spec_version, downstream_files in streams:
633 sys.stdout.write(package)
634 if options.upstream: sys.stdout.write("\t%s" % module)
635 if options.show_version: sys.stdout.write("\t%s\t%s" % (spec_version, package_version))
636 print
637
638 def cmd_patches(options, parser):
639 root = os.path.expanduser(PKGROOT)
640
641 for package, module, package_version, spec_version, downstream_files in sorted(join_streams()):
642 for filename in downstream_files:
643 if '.patch' in filename or '.diff' in filename:
644
645 p = Patch(os.path.join(root, package, "SOURCES", filename), show_path=options.path)
646 valid = ""
647 forwarded = ""
648 if p.dep3['headers']:
649 forwarded = p.dep3['headers'].get('Forwarded', "no")
650 if p.dep3['valid']:
651 valid="VALID"
652 print "\t".join((module, package, str(p), forwarded, valid))
653
654 def cmd_dep3(options, parser):
655 p = Patch(options.patch)
656 p.add_dep3()
657
658 def cmd_package_new_version(options, parser):
659 # Determine the package name
660 if options.upstream:
661 try:
662 package = Downstream().get_downstream_from_upstream(options.package, options.version)[0]
663 except ValueError, e:
664 print >>sys.stderr, "ERROR: %s" % e
665 sys.exit(1)
666 else:
667 package = options.package
668
669 # Directories packages are located in
670 root = os.path.expanduser(PKGROOT)
671 cwd = os.path.join(root, package)
672
673 # Checkout package to ensure the checkout reflects the latest changes
674 try:
675 subprocess.check_call(['mgarepo', 'co', package], cwd=root)
676 except subprocess.CalledProcessError:
677 sys.exit(1)
678
679 # SpecFile class handles the actual version+release change
680 s = SpecFile(os.path.join(cwd, "SPECS", "%s.spec" % package))
681 print "%s => %s" % (s.version, options.version)
682 if not s.update(options.version, force=options.force):
683 sys.exit(1)
684
685 # Check hash, if given
686 if options.hexdigest is not None:
687 sources = [name for name, origname in s.sources.iteritems() if '://' in origname]
688 if not len(sources):
689 print >>sys.stderr, "ERROR: Cannot determine source file (for hash check)!"
690 sys.stderr(1)
691
692 for filename in sources:
693 path = os.path.join(cwd, "SOURCES", filename)
694 if not is_valid_hash(path, options.algo, options.hexdigest):
695 print >>sys.stderr, "ERROR: Hash file failed check for %s!" % path
696 print >>sys.stderr, "ERROR: Reverting changes!"
697 subprocess.call(['svn', 'revert', '-R', cwd], cwd=cwd)
698 sys.exit(1)
699
700 # We can even checkin and submit :-)
701 if options.submit:
702 try:
703 # checkin changes
704 subprocess.check_call(['mgarepo', 'ci', '-m', 'new version %s' % options.version], cwd=cwd)
705 # and submit
706 # XXX HACK NOT TO AUTO SUBMIT ATM
707 if options.hexdigest is None:
708 subprocess.check_call(['mgarepo', 'submit'], cwd=cwd)
709 except subprocess.CalledProcessError:
710 sys.exit(1)
711
712 def cmd_parse_ftp_release_list(options, parser):
713 def _send_reply_mail(contents, orig_msg, to, packages=[], error=False):
714 """Send an reply email"""
715 contents.seek(0)
716 msg = MIMEText(contents.read(), _charset='utf-8')
717
718 if error:
719 # XXX - ugly
720 contents.seek(0)
721 lastline = contents.read().rstrip().splitlines()[-1]
722 # Remove things like "ERROR: " and so on from the last line
723 lastline = re.sub(r'^(?:[^ :]+:\s+)+', '', lastline)
724 # Remove things like " - " (youri output from mgarepo submit)
725 lastline = re.sub(r'^\s+-\s+', '', lastline)
726 subjecterror = " (ERROR: %s)" % lastline if lastline else " (ERROR)"
727 else:
728 subjecterror = ""
729
730 if packages:
731 subject = "%s %s%s" % (", ".join(packages), orig_msg['X-Module-Version'], subjecterror)
732 else:
733 subject = "Re: %s%s" % (orig_msg['Subject'], subjecterror)
734
735 msg['Subject'] = subject
736 msg['To'] = to
737 msg["In-Reply-To"] = orig_msg["Message-ID"]
738 msg["References"] = orig_msg["Message-ID"]
739
740 # Call sendmail program directly so it doesn't matter if the service is running
741 cmd = ['/usr/sbin/sendmail', '-oi', '--']
742 cmd.extend([to])
743 p = subprocess.Popen(cmd, stdin=subprocess.PIPE)
744 p.stdin.write(msg.as_string())
745 p.stdin.flush()
746 p.stdin.close()
747 p.wait()
748
749
750 msg = email.email.message_from_file(sys.stdin)
751
752 if options.mail:
753 stdout = tempfile.TemporaryFile()
754 stderr = stdout
755 else:
756 stdout = sys.stdout
757 stderr = sys.stderr
758
759 try:
760 module = msg['X-Module-Name']
761 version = msg['X-Module-Version']
762 hexdigest = msg['X-Module-SHA256-tar.xz']
763 except KeyError, e:
764 print >>stderr, "ERROR: %s" % e
765 if options.mail: _send_reply_mail(stdout, msg, options.mail, error=True)
766 sys.exit(1)
767
768 try:
769 packages = Downstream().get_downstream_from_upstream(module, version)
770 except ValueError, e:
771 print >>stderr, "ERROR: %s" % e
772 if options.mail: _send_reply_mail(stdout, msg, options.mail, error=True)
773 sys.exit(1)
774
775 if options.wait:
776 # maildrop aborts and will try to deliver after 5min
777 # fork to avoid this
778 if os.fork() != 0: sys.exit(0)
779 # wait SLEEP_INITIAL after the message was sent
780 secs = SLEEP_INITIAL
781 t = email.utils.parsedate_tz(msg['Date'])
782 if t is not None:
783 msg_time = email.utils.mktime_tz(t)
784 secs = SLEEP_INITIAL - (time.time() - msg_time)
785
786 if secs > 0: time.sleep(secs)
787
788 error = False
789 for package in packages:
790 cmd = ['mga-gnome', 'increase', '--submit', '--hash', hexdigest]
791 if options.force:
792 cmd.append('--force')
793 cmd.extend((package, version))
794 if subprocess.call(cmd, stdout=stdout, stderr=stderr):
795 error = True
796
797 if options.mail: _send_reply_mail(stdout, msg, options.mail, packages=packages, error=error)
798
799 def main():
800 description = """Mageia GNOME commands."""
801 epilog="""Report bugs to Olav Vitters"""
802 parser = argparse.ArgumentParser(description=description,epilog=epilog)
803 parser.add_argument("-l", "--limit", type=argparse.FileType('r', 0),
804 dest="limit_upstream", metavar="FILE",
805 help="File containing upstream names")
806
807 # SUBPARSERS
808 subparsers = parser.add_subparsers(title='subcommands')
809 # install
810 subparser = subparsers.add_parser('co', help='checkout all GNOME modules')
811 subparser.set_defaults(
812 func=cmd_co
813 )
814
815 subparser = subparsers.add_parser('packages', help='list all GNOME packages')
816 subparser.add_argument("-m", "--m", action="store_true", dest="upstream",
817 help="Show upstream module")
818 subparser.add_argument( "--version", action="store_true", dest="show_version",
819 help="Show version numbers")
820 subparser.add_argument( "--diff", action="store_true", dest="diff",
821 help="Only show packages with different version")
822 subparser.add_argument( "--sort", type=argparse.FileType('r', 0),
823 dest="sort", metavar="FILE",
824 help="Sort packages according to order in given FILE")
825
826 subparser.set_defaults(
827 func=cmd_ls, upstream=False, show_version=False, diff=False
828 )
829
830 subparser = subparsers.add_parser('patches', help='list all GNOME patches')
831 subparser.add_argument("-p", "--path", action="store_true", dest="path",
832 help="Show full path to patch")
833 subparser.set_defaults(
834 func=cmd_patches, path=False
835 )
836
837 subparser = subparsers.add_parser('dep3', help='Add dep3 headers')
838 subparser.add_argument("patch", help="Patch")
839 subparser.set_defaults(
840 func=cmd_dep3, path=False
841 )
842
843 subparser = subparsers.add_parser('increase', help='Increase version number')
844 subparser.add_argument("package", help="Package name")
845 subparser.add_argument("version", help="Version number")
846 subparser.add_argument("-f", "--force", action="store_true", dest="force",
847 help="Override warnings, just do it")
848 subparser.add_argument("-u", "--upstream", action="store_true", dest="upstream",
849 help="Package name reflects the upstream name")
850 subparser.add_argument("-s", "--submit", action="store_true", dest="submit",
851 help="Commit changes and submit")
852 subparser.add_argument("-a", "--algorithm", choices=hashlib.algorithms, dest="algo",
853 help="Hash algorithm")
854 subparser.add_argument("--hash", dest="hexdigest",
855 help="Hexdigest of the hash")
856 subparser.set_defaults(
857 func=cmd_package_new_version, submit=False, upstream=False, hexdigest=None, algo="sha256",
858 force=False
859 )
860
861 subparser = subparsers.add_parser('gnome-release-email', help='Submit packages based on GNOME ftp-release-list email')
862 subparser.add_argument("-m", "--mail", help="Email address to send the progress to")
863 subparser.add_argument("-w", "--wait", action="store_true",
864 help="Wait before trying to retrieve the new version")
865 subparser.add_argument("-f", "--force", action="store_true",
866 help="Force submission")
867 subparser.set_defaults(
868 func=cmd_parse_ftp_release_list, force=False, wait=False
869 )
870
871 if len(sys.argv) == 1:
872 parser.print_help()
873 sys.exit(2)
874
875 options = parser.parse_args()
876 if options.limit_upstream:
877 Upstream.limit = set(options.limit_upstream.read().strip("\n").splitlines())
878
879 try:
880 options.func(options, parser)
881 except KeyboardInterrupt:
882 print('Interrupted')
883 sys.exit(1)
884 except EOFError:
885 print('EOF')
886 sys.exit(1)
887 except IOError, e:
888 if e.errno != errno.EPIPE:
889 raise
890 sys.exit(0)
891
892 if __name__ == "__main__":
893 os.environ['PYTHONUNBUFFERED'] = '1'
894 main()

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.30