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

Diff of /mga-gnome/trunk/mga-gnome

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 2944 by ovitters, Tue Feb 14 10:38:56 2012 UTC revision 3086 by ovitters, Mon Feb 27 09:32:36 2012 UTC
# Line 1  Line 1 
1  #!/usr/bin/python  #!/usr/bin/python
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  import os
9  import os.path  import os.path
10  import sys  import sys
11  import re  import re
12  import subprocess  import subprocess
13  import urllib2  
14  import urlparse  # command line parsing, error handling:
15  import argparse  import argparse
16  import errno  import errno
17    
18    # overwriting files by moving them (safer):
19  import tempfile  import tempfile
20  import shutil  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  from sgmllib import SGMLParser
33    import urllib2
34    import urlparse
35    
36  MEDIA="Core Release Source"  MEDIA="Core Release Source"
37  URL="http://download.gnome.org/sources/"  URL="http://download.gnome.org/sources/"
38  PKGROOT='~/pkgs'  PKGROOT='~/pkgs'
39    
40    re_version = re.compile(r'([-.]|\d+|[^-.\d]+)')
41    
42    def version_cmp(a, b):
43        """Compares two versions
44    
45        Returns
46          -1 if a < b
47          0  if a == b
48          1  if a > b
49        """
50    
51        return rpm.labelCompare(('1', a, '1'), ('1', b, '1'))
52    
53    def get_latest_version(versions, max_version=None):
54        """Gets the latest version number
55    
56        if max_version is specified, gets the latest version number before
57        max_version"""
58        latest = None
59        for version in versions:
60            if ( latest is None or version_cmp(version, latest) > 0 ) \
61               and ( max_version is None or version_cmp(version, max_version) < 0 ):
62                latest = version
63        return latest
64    
65  def line_input (file):  def line_input (file):
66      for line in file:      for line in file:
67          if line[-1] == '\n':          if line[-1] == '\n':
# Line 24  def line_input (file): Line 69  def line_input (file):
69          else:          else:
70              yield line              yield line
71    
72    def call_editor(filename):
73        """Return a sequence of possible editor binaries for the current platform"""
74    
75        editors = []
76    
77        for varname in 'VISUAL', 'EDITOR':
78            if varname in os.environ:
79                editors.append(os.environ[varname])
80    
81        editors.extend(('/usr/bin/editor', 'vi', 'pico', 'nano', 'joe'))
82    
83        for editor in editors:
84            try:
85                ret = subprocess.call([editor, filename])
86            except OSError, e:
87                if e.errno == 2:
88                    continue
89                raise
90    
91            if ret == 127:
92                continue
93    
94            return True
95    
96  class urllister(SGMLParser):  class urllister(SGMLParser):
97      def reset(self):      def reset(self):
98          SGMLParser.reset(self)          SGMLParser.reset(self)
# Line 34  class urllister(SGMLParser): Line 103  class urllister(SGMLParser):
103          if href:          if href:
104              self.urls.extend(href)              self.urls.extend(href)
105    
106    class XzTarFile(tarfile.TarFile):
107    
108        OPEN_METH = tarfile.TarFile.OPEN_METH.copy()
109        OPEN_METH["xz"] = "xzopen"
110    
111        @classmethod
112        def xzopen(cls, name, mode="r", fileobj=None, **kwargs):
113            """Open gzip compressed tar archive name for reading or writing.
114               Appending is not allowed.
115            """
116            if len(mode) > 1 or mode not in "rw":
117                raise ValueError("mode must be 'r' or 'w'")
118    
119            if fileobj is not None:
120                fileobj = _LMZAProxy(fileobj, mode)
121            else:
122                fileobj = lzma.LZMAFile(name, mode)
123    
124            try:
125                # lzma doesn't immediately return an error
126                # try and read a bit of data to determine if it is a valid xz file
127                fileobj.read(_LZMAProxy.blocksize)
128                fileobj.seek(0)
129                t = cls.taropen(name, mode, fileobj, **kwargs)
130            except IOError:
131                raise tarfile.ReadError("not a xz file")
132            except lzma.error:
133                raise tarfile.ReadError("not a xz file")
134            t._extfileobj = False
135            return t
136    
137    if not hasattr(tarfile.TarFile, 'xzopen'):
138        tarfile.open = XzTarFile.open
139    
140    class SpecFile(object):
141        re_update_version = re.compile(r'^(?P<pre>Version:\s*)(?P<version>.+)(?P<post>\s*)$', re.MULTILINE + re.IGNORECASE)
142        re_update_release = re.compile(r'^(?P<pre>Release:\s*)(?P<release>%mkrel \d+)(?P<post>\s*)$', re.MULTILINE + re.IGNORECASE)
143    
144        def __init__(self, path):
145            self.path = path
146            self.cwd = os.path.dirname(path)
147    
148        @property
149        def version(self):
150            return subprocess.check_output(["rpm", "--specfile", self.path, "--queryformat", "%{VERSION}\n"]).splitlines()[0]
151    
152        def update(self, version):
153            """Update specfile (increase version)"""
154            cur_version = self.version
155    
156            compare = version_cmp(version, cur_version)
157    
158            if compare == 0:
159                print >>sys.stderr, "ERROR: Already at version %s!" % (cur_version)
160                return False
161    
162            if compare != 1:
163                print >>sys.stderr, "ERROR: Version %s is older than current version %s!" % (version, cur_version)
164                return False
165    
166            # XXX - os.path.join is hackish
167            if subprocess.check_output(["svn", "diff", os.path.join(self.path, '..')]) != '':
168                print >>sys.stderr, "ERROR: Package has uncommitted changes!"
169                return False
170    
171            with open(self.path, "rw") as f:
172                data = f.read()
173    
174                if data.count("%mkrel") != 1:
175                    print >>sys.stderr, "ERROR: Multiple %mkrel found; don't know what to do!"
176                    return False
177    
178                data, nr = self.re_update_version.subn(r'\g<pre>%s\g<post>' % version, data, 1)
179                if nr != 1:
180                    print >>sys.stderr, "ERROR: Could not increase version!"
181                    return False
182    
183                data, nr = self.re_update_release.subn(r'\g<pre>%mkrel 1\g<post>', data, 1)
184                if nr != 1:
185                    print >>sys.stderr, "ERROR: Could not reset release!"
186                    return False
187    
188                # Overwrite file with new version number
189                write_file(self.path, data)
190    
191    
192            # Verify that RPM also agrees that version number has changed
193            if self.version != version:
194                print "ERROR: Increased version to %s, but RPM doesn't agree!?!" % version
195                return False
196    
197            try:
198                # Download new tarball
199                subprocess.check_call(['mgarepo', 'sync', '-d'], cwd=self.cwd)
200                # Check patches still apply
201                subprocess.check_call(['bm', '-p', '--nodeps'], cwd=self.cwd)
202            except subprocess.CalledProcessError:
203                return False
204    
205            return True
206    
207  class Patch(object):  class Patch(object):
208      """Do things with patches"""      """Do things with patches"""
209    
# Line 49  class Patch(object): Line 219  class Patch(object):
219          return self.path if self.show_path else os.path.basename(self.path)          return self.path if self.show_path else os.path.basename(self.path)
220    
221      def add_dep3(self):      def add_dep3(self):
222            """Add DEP-3 headers to a patch file"""
223          if self.dep3['valid']:          if self.dep3['valid']:
224              return False              return False
225    
# Line 74  class Patch(object): Line 245  class Patch(object):
245    
246                      # XXX - wrap this at 80 chars                      # XXX - wrap this at 80 chars
247                      add_line = True                      add_line = True
248                      print >>fdst, "%s: %s" % (header, data)                      print >>fdst, "%s: %s" % (header, "" if data is None else data)
249    
250                  if add_line: print >>fdst, ""                  if add_line: print >>fdst, ""
251                  # Now copy any other data and the patch                  # Now copy any other data and the patch
# Line 83  class Patch(object): Line 254  class Patch(object):
254              fdst.flush()              fdst.flush()
255              os.rename(fdst.name, self.path)              os.rename(fdst.name, self.path)
256    
257            call_editor(self.path)
258    
259      #Author: fwang      #Author: fwang
260      #Subject: Build fix: Fix glib header inclusion      #Subject: Build fix: Fix glib header inclusion
261      #Applied-Upstream: commit:30602      #Applied-Upstream: commit:30602
# Line 90  class Patch(object): Line 263  class Patch(object):
263      #Bug: http://bugzilla.abisource.com/show_bug.cgi?id=13247      #Bug: http://bugzilla.abisource.com/show_bug.cgi?id=13247
264    
265      def _read_dep3(self):      def _read_dep3(self):
266          """This will also parse git headers"""          """Read DEP-3 headers from an existing patch file
267    
268            This will also parse git headers"""
269          dep3 = {}          dep3 = {}
270          headers = {}          headers = {}
271    
# Line 108  class Patch(object): Line 283  class Patch(object):
283                      r = self.re_dep3.match(line)                      r = self.re_dep3.match(line)
284                      if r:                      if r:
285                          info = r.groupdict()                          info = r.groupdict()
286    
287                            # Avoid matching URLS
288                            if info['data'].startswith('//') and info['header'].lower () == info['header']:
289                                continue
290    
291                          headers[info['header']] = info['data']                          headers[info['header']] = info['data']
292                          last_header = info['header']                          last_header = info['header']
293                          last_nr = nr                          last_nr = nr
# Line 146  class Patch(object): Line 326  class Patch(object):
326      @property      @property
327      def svn_author(self):      def svn_author(self):
328          if not hasattr(self, '_svn_author'):          if not hasattr(self, '_svn_author'):
329              p = subprocess.Popen(['svn', 'log', '-q', "--", self.path], stdout=subprocess.PIPE, close_fds=True)              try:
330              contents = p.stdout.read().strip("\n").splitlines()                  contents = subprocess.check_output(['svn', 'log', '-q', "--", self.path], close_fds=True).strip("\n").splitlines()
331              ecode = p.wait()  
             if ecode == 0:  
332                  for line in contents:                  for line in contents:
333                      if ' | ' not in line:                      if ' | ' not in line:
334                          continue                          continue
# Line 157  class Patch(object): Line 336  class Patch(object):
336                      fields = line.split(' | ')                      fields = line.split(' | ')
337                      if len(fields) >= 3:                      if len(fields) >= 3:
338                          self._svn_author = fields[1]                          self._svn_author = fields[1]
339                except subprocess.CalledProcessError:
340                    pass
341    
342            if not hasattr(self, '_svn_author'):
343                return None
344    
345          return self._svn_author          return self._svn_author
346    
# Line 180  def get_upstream_names(): Line 364  def get_upstream_names():
364  def get_downstream_names():  def get_downstream_names():
365      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]*)$')      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]*)$')
366    
367      p = subprocess.Popen(['urpmf', '--files', '.', "--media", MEDIA], stdout=subprocess.PIPE, close_fds=True)      contents = subprocess.check_output(['urpmf', '--files', '.', "--media", MEDIA], close_fds=True).strip("\n").splitlines()
     contents = p.stdout.read().strip("\n").splitlines()  
     ecode = p.wait()  
     if ecode != 0:  
         sys.exit(1)  
368    
369      FILES = {}      FILES = {}
370      TARBALLS = {}      TARBALLS = {}
# Line 212  def get_downstream_names(): Line 392  def get_downstream_names():
392    
393      return TARBALLS, FILES      return TARBALLS, FILES
394    
395    
396    def write_file(path, data):
397        with tempfile.NamedTemporaryFile(dir=os.path.dirname(path), delete=False) as fdst:
398            fdst.write(data)
399            fdst.flush()
400            os.rename(fdst.name, path)
401    
402  def cmd_co(options, parser):  def cmd_co(options, parser):
403      upstream = get_upstream_names()      upstream = get_upstream_names()
404      downstream, downstream_files = get_downstream_names()      downstream, downstream_files = get_downstream_names()
# Line 230  def cmd_ls(options, parser): Line 417  def cmd_ls(options, parser):
417    
418      matches = upstream & set(downstream.keys())      matches = upstream & set(downstream.keys())
419      for module in matches:      for module in matches:
420          print "\n".join(downstream[module])          print "\n".join(sorted(downstream[module]))
421    
422  def cmd_patches(options, parser):  def cmd_patches(options, parser):
423      upstream = get_upstream_names()      upstream = get_upstream_names()
# Line 245  def cmd_patches(options, parser): Line 432  def cmd_patches(options, parser):
432          for srpm in downstream[module]:          for srpm in downstream[module]:
433              for filename in downstream_files[srpm]:              for filename in downstream_files[srpm]:
434                  if '.patch' in filename or '.diff' in filename:                  if '.patch' in filename or '.diff' in filename:
435    
436                      p = Patch(os.path.join(path, srpm, "SOURCES", filename), show_path=options.path)                      p = Patch(os.path.join(path, srpm, "SOURCES", filename), show_path=options.path)
437                      print "\t".join((module, srpm, str(p)))                      valid = ""
438                        forwarded = ""
439                      if p.dep3['headers']:                      if p.dep3['headers']:
440                          pprint.pprint(p.dep3['headers'])                          forwarded = p.dep3['headers'].get('Forwarded', "no")
441                          if p.dep3['valid']:                          if p.dep3['valid']:
442                              print "VALID"                              valid="VALID"
443                        print "\t".join((module, srpm, str(p), forwarded, valid))
444    
445  def cmd_dep3(options, parser):  def cmd_dep3(options, parser):
446      p = Patch(options.patch)      p = Patch(options.patch)
447      p.add_dep3()      p.add_dep3()
448    
449    def cmd_package_new_version(options, parser):
450        if options.upstream:
451            downstream, downstream_files = get_downstream_names()
452    
453            if options.package not in downstream:
454                print >>sys.stderr, "ERROR: No packages for upstream name: %s" % options.package
455                sys.exit(1)
456    
457            if len(downstream[options.package]) != 1:
458                # XXX - Make it more intelligent
459                print >>sys.stderr, "ERROR: Multiple packages for upstream name: %s" % options.package
460                sys.exit(1)
461    
462            package = list(downstream[options.package])[0]
463        else:
464            package = options.package
465    
466    
467    
468        root = os.path.expanduser(PKGROOT)
469        cwd = os.path.join(root, package)
470    
471        try:
472            subprocess.check_call(['mgarepo', 'co', package], cwd=root)
473        except subprocess.CalledProcessError:
474            sys.exit(1)
475        s = SpecFile(os.path.join(cwd, "SPECS", "%s.spec" % package))
476        print "%s => %s" % (s.version, options.version)
477        if not s.update(options.version):
478            sys.exit(1)
479    
480        if options.submit:
481            try:
482                # checkin changes
483                subprocess.check_call(['mgarepo', 'ci', '-m', 'new version'], cwd=cwd)
484                # and submit
485                subprocess.check_call(['mgarepo', 'submit'], cwd=cwd)
486            except subprocess.CalledProcessError:
487                sys.exit(1)
488    
489    
490  def main():  def main():
491      description = """Mageia GNOME commands."""      description = """Mageia GNOME commands."""
492      epilog="""Report bugs to Olav Vitters"""      epilog="""Report bugs to Olav Vitters"""
# Line 287  def main(): Line 518  def main():
518          func=cmd_dep3, path=False          func=cmd_dep3, path=False
519      )      )
520    
521        subparser = subparsers.add_parser('increase', help='Increase version number')
522        subparser.add_argument("package", help="Package name")
523        subparser.add_argument("version", help="Version number")
524        subparser.add_argument("-u", "--upstream", action="store_true", dest="upstream",
525                                           help="Package name reflects the upstream name")
526        subparser.add_argument("-s", "--submit", action="store_true", dest="submit",
527                                           help="Commit changes and submit")
528        subparser.set_defaults(
529            func=cmd_package_new_version, submit=False, upstream=False
530        )
531    
532      if len(sys.argv) == 1:      if len(sys.argv) == 1:
533          parser.print_help()          parser.print_help()
534          sys.exit(2)          sys.exit(2)

Legend:
Removed from v.2944  
changed lines
  Added in v.3086

  ViewVC Help
Powered by ViewVC 1.1.30