Added command to backport an issue
authorFrederic Massart <fred@moodle.com>
Tue, 7 Aug 2012 07:51:02 +0000 (15:51 +0800)
committerFrederic Massart <fred@moodle.com>
Tue, 7 Aug 2012 07:51:02 +0000 (15:51 +0800)
lib/git.py
lib/moodle.py
lib/workplace.py
moodle-backport.py [new file with mode: 0755]

index 8d2e847..0c6a4e8 100644 (file)
@@ -66,8 +66,11 @@ class Git():
                cmd = 'fetch %s %s' % (remote, ref)
                return self.execute(cmd)
 
-       def hasBranch(self, branch):
-               cmd = 'show-ref --verify --quiet "refs/heads/%s"' % branch
+       def hasBranch(self, branch, remote = ''):
+               if remote != '':
+                       cmd = 'show-ref --verify --quiet "refs/remotes/%s/%s"' % (remote, branch)
+               else:
+                       cmd = 'show-ref --verify --quiet "refs/heads/%s"' % branch
                (returncode, stdout, stderr) = self.execute(cmd)
                return returncode == 0
 
@@ -84,6 +87,10 @@ class Git():
                proc.wait()
                return proc.returncode == 0
 
+       def pick(self, refs):
+               cmd = 'cherry-pick %s' % refs
+               return self.execute(cmd)
+
        def pull(self, remote = '', ref = ''):
                cmd = 'pull %s %s' % (remote, ref)
                return self.execute(cmd)
@@ -96,6 +103,14 @@ class Git():
                cmd = 'push %s%s %s' % (force, toremote, tobranch)
                return self.execute(cmd)
 
+       def reset(self, to, hard = False):
+               mode = ''
+               if hard:
+                       mode == '--hard'
+               cmd = 'reset --hard %s' % (to)
+               result = self.execute(cmd)
+               return result[0] == 0
+
        def status(self):
                return self.execute('status')
 
index 0cf1f7e..31572a0 100644 (file)
@@ -16,8 +16,8 @@ class Moodle():
     identifier = None
     path = None
     installed = False
-    version = {}
-    config = {}
+    version = None
+    config = None
 
     _dbo = None
     _git = None
@@ -26,6 +26,8 @@ class Moodle():
     def __init__(self, path, identifier = None):
         self.path = path
         self.identifier = identifier
+        self.version = {}
+        self.config = {}
         self._load()
 
     def addConfig(self, name, value):
@@ -71,6 +73,16 @@ class Moodle():
                     pass
         return self._dbo
 
+    def generateBranchName(self, issue, suffix='', version=''):
+        """Generates a branch name"""
+        mdl = re.sub(r'MDL(-|_)?', '', issue, flags=re.I)
+        if version == '':
+            version = self.get('branch')
+        branch = C('wording.branchFormat') % (mdl, version)
+        if suffix != None and suffix != '':
+            branch += C('wording.branchSuffixSeparator') + suffix
+        return branch
+
     def get(self, param, default = None):
         """Returns a property of this instance"""
         info = self.info()
index 0a1db74..ed1efab 100644 (file)
@@ -132,6 +132,27 @@ class Workplace():
         if DB and dbname:
             DB.dropdb(dbname)
 
+    def generateInstanceName(self, version, integration=False, suffix=''):
+        """Creates a name (identifier) from arguments"""
+
+        # Wording version
+        if version == 'master':
+            prefixVersion = C('wording.prefixMaster')
+        else:
+            prefixVersion = version
+
+        # Generating name
+        if integration:
+            name = C('wording.prefixIntegration') + prefixVersion
+        else:
+            name = C('wording.prefixStable') + prefixVersion
+
+        # Append the suffix
+        if suffix != None and suffix != '':
+            name += C('wording.suffixSeparator') + suffix
+
+        return name
+
     def get(self, name):
         """Returns an instance defined by its name, or by path"""
         # Extracts name from path
diff --git a/moodle-backport.py b/moodle-backport.py
new file mode 100755 (executable)
index 0000000..11d8d48
--- /dev/null
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys
+import argparse
+import re
+from lib import config, workplace, moodle, tools
+from lib.tools import debug
+
+C = config.Conf().get
+Wp = workplace.Workplace()
+
+# Arguments
+parser = argparse.ArgumentParser(description="Backports a branch")
+parser.add_argument('-i', '--issue', metavar='issue', required=True, help='the issue to backport')
+parser.add_argument('-s', '--suffix', metavar='suffix', help='the suffix of the branch of this issue')
+parser.add_argument('-r', '--remote', metavar='remote', help='the remote to fetch from. Default is %s.' % C('mineRepo'))
+parser.add_argument('-v', '--versions', metavar='version', required=True, nargs='+', choices=[ str(x) for x in range(13, C('masterBranch')) ] + ['master'], help='versions to backport to')
+parser.add_argument('name', metavar='name', default=None, nargs='?', help='name of the instance to work on')
+args = parser.parse_args()
+
+M = Wp.resolve(args.name)
+if not M:
+    debug('This is not a Moodle instance')
+    sys.exit(1)
+
+remote = args.remote
+if remote == None:
+       remote = C('mineRepo')
+
+
+branch = M.generateBranchName(args.issue, suffix=args.suffix)
+originaltrack = M.get('stablebranch')
+if not M.git().hasBranch(branch):
+       debug('Could not find original branch %s.' % (branch, remote))
+       sys.exit(1)
+if not M.git().hasBranch(branch):
+       debug('Could not find original branch %s.' % (branch, remote))
+       sys.exit(1)
+
+# Begin backport
+for v in args.versions:
+
+       # Gets the instance to cherry-pick to
+       name = Wp.generateInstanceName(v, integration=M.get('integration'))
+       if not Wp.isMoodle(name):
+               debug('Could not find instance %s for version %s' % (name, v))
+               continue
+       M2 = Wp.get(name)
+
+       debug("Preparing cherry-pick of %s/%s in %s" % (remote, branch, name))
+
+       # Fetch the remote to get reference to the branch to backport
+       debug("Fetching remote %s..." % remote)
+       M2.git().fetch(remote)
+
+       # Creates a new branch if necessary
+       newbranch = M2.generateBranchName(args.issue, suffix=args.suffix)
+       track = 'origin/%s' % M2.get('stablebranch')
+       if not M2.git().hasBranch(newbranch):
+               debug('Creating branch %s' % newbranch)
+               if not M2.git().createBranch(newbranch, track=track):
+                       debug('Could not create branch %s tracking %s in %s' % (newbranch, track, name))
+                       continue
+               M2.git().checkout(newbranch)
+       else:
+               M2.git().checkout(newbranch)
+               debug('Hard reset to %s' % (track))
+               M2.git().reset(to=track, hard=True)
+
+       # Picking the diff origin/MOODLE_23_STABLE..github/MDL-12345-master
+       cherry = 'origin/%s..%s/%s' % (originaltrack, remote, branch)
+       debug('Cherry-picking %s' % (cherry))
+       result = M2.git().pick(cherry)
+       if result[0] != 0:
+               debug('Error while cherry-picking %s/%s in %s.' % (remote, branch, name))
+               debug(result[2])
+               continue
+
+       debug('Instance %s successfully patched!' % name)
+       debug('')
+
+debug('Done.')