Can pull patches from a tracker issue
authorFrederic Massart <fred@moodle.com>
Thu, 28 Feb 2013 09:06:41 +0000 (17:06 +0800)
committerFrederic Massart <fred@moodle.com>
Thu, 28 Feb 2013 09:06:41 +0000 (17:06 +0800)
lib/git.py
lib/jira.py
moodle-pull.py

index 217ef7c..2c139ad 100644 (file)
@@ -42,6 +42,17 @@ class Git(object):
         result = self.execute(cmd)
         return result[0] == 0
 
+    def apply(self, files):
+        if type(files) == list:
+            files = ' '.join(files)
+        cmd = 'apply --check %s' % (files)
+        result = self.execute(cmd)
+        if result[0] != 0:
+            return False
+        cmd = 'am %s' % (files)
+        result = self.execute(cmd)
+        return result[0] != 0
+
     def checkout(self, branch):
         if self.currentBranch == branch:
             return True
index 4d0e520..92cbb4c 100644 (file)
@@ -25,9 +25,11 @@ http://github.com/FMCorz/mdk
 import json
 from tools import debug, question
 from config import Conf
-from urllib import urlencode
+from urllib import urlencode, urlretrieve
 from urlparse import urlparse
 from base64 import b64encode
+from datetime import datetime
+import os
 import httplib
 import getpass
 try:
@@ -58,11 +60,34 @@ class Jira(object):
         self.version = {}
         self._load()
 
+    def download(self, url, dest):
+        """Download a URL to the destination while authenticating the user"""
+        headers = {}
+        headers['Authorization'] = 'Basic %s' % (b64encode('%s:%s' % (self.username, self.password)))
+
+        if self.ssl:
+            r = httplib.HTTPSConnection(self.host)
+        else:
+            r = httplib.HTTPConnection(self.host)
+        r.request('GET', url, None, headers)
+
+        resp = r.getresponse()
+        if resp.status == 403:
+            raise JiraException('403 Request not authorized. %s %s' % ('GET', url))
+
+        data = resp.read()
+        if len(data) > 0:
+            f = open(dest, 'w')
+            f.write(data)
+            f.close()
+
+        return os.path.isfile(dest)
+
     def get(self, param, default=None):
         """Returns a property of this instance"""
         return self.info().get(param, default)
 
-    def getIssue(self, key, fields='*all'):
+    def getIssue(self, key, fields='*all,-comment'):
         """Load the issue info from the jira server using a rest api call.
 
         The returned key 'named' of the returned dict is organised by name of the fields, not id.
@@ -154,7 +179,6 @@ class Jira(object):
             # Do not die if keyring package is not available.
             pass
 
-
         return True
 
     def reload(self):
@@ -194,6 +218,11 @@ class Jira(object):
 
         return {'status': resp.status, 'data': data}
 
+    @staticmethod
+    def parseDate(value):
+        """Parse a date returned by Jira API"""
+        return datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.000+0000')
+
     def setCustomFields(self, key, updates):
         """Set a list of fields for this issue in Jira
 
index 4668100..4c29c16 100755 (executable)
@@ -25,8 +25,10 @@ http://github.com/FMCorz/mdk
 import sys
 import argparse
 import re
+import os
+from datetime import datetime
 from lib import workplace, tools, jira
-from lib.tools import debug
+from lib.tools import debug, question
 from lib.config import Conf
 
 Wp = workplace.Workplace()
@@ -67,10 +69,50 @@ branch = M.get('branch')
 debug('Retrieving information about %s from Moodle Tracker' % (mdl))
 J = jira.Jira()
 issueInfo = J.getIssue(mdl)
+
+mode = 'pull'
 remoteUrl = issueInfo.get('named').get(C.get('tracker.fieldnames.repositoryurl'))
 remoteBranch = issueInfo.get('named').get(C.get('tracker.fieldnames.%s.branch' % (branch)))
-if not remoteUrl or not remoteBranch:
-    debug('Not enough information to pull the branch')
+patchesToApply = []
+
+if True or not remoteUrl or not remoteBranch:
+    attachments = issueInfo.get('fields').get('attachment')
+    patches = {}
+    for attachment in attachments:
+        if attachment['filename'].endswith('.patch'):
+            patches[attachment['filename']] = attachment
+
+    if len(patches) > 0:
+        mapping = {}
+        i = 1
+        for key in sorted(patches.keys()):
+            patch = patches[key]
+            mapping[i] = patch
+            date = jira.Jira.parseDate(patch['created'])
+            print '{0:<2}: {1:<60} {2}'.format(i, patch['filename'][:60], datetime.strftime(date, '%Y-%m-%d %H:%M'))
+            i += 1
+
+        ids = question('What patches would you like to apply?')
+        if ids:
+            ids = re.split(r'\s*[, ]\s*', ids)
+            for i in ids:
+                i = int(i)
+                if not i in mapping.keys():
+                    continue
+                j = 0
+                while True:
+                    mapping[i]['mdk-filename'] = mapping[i]['filename'] + (('.' + str(j)) if j > 0 else '')
+                    j += 1
+                    if not os.path.isfile(mapping[i]['mdk-filename']):
+                        break
+                patchesToApply.append(mapping[i])
+            mode = 'patch'
+    else:
+        mode = False
+
+if not mode:
+    debug('Did not find enough information to pull a patch.')
+    sys.exit()
 
 # Stash
 stash = M.git().stash(untracked=True)
@@ -102,9 +144,29 @@ elif args.integration:
         debug('Could not checkout branch %s' % (M.get('stablebranch')))
     debug('Checked out branch %s' % (M.get('stablebranch')))
 
-# Pull branch from tracker
-debug('Pulling branch %s from %s into %s' % (remoteBranch, remoteUrl, M.currentBranch()))
-M.git().pull(remote=remoteUrl, ref=remoteBranch)
+if mode == 'pull':
+    # Pull branch from tracker
+    debug('Pulling branch %s from %s into %s' % (remoteBranch, remoteUrl, M.currentBranch()))
+    M.git().pull(remote=remoteUrl, ref=remoteBranch)
+elif mode == 'patch':
+    # Apply a patch from tracker
+    files = []
+    for patch in patchesToApply:
+        dest = patch['mdk-filename']
+        debug('Downloading %s' % (patch['filename']))
+        if not J.download(patch['content'], dest):
+            debug('Failed to download. Aborting...')
+            files = []
+            break
+        files.append(dest)
+
+    if len(files) > 0:
+        debug('Applying patch(es)...')
+        if not M.git().apply(files):
+            debug('Could not apply the patch(es), please apply manually')
+        else:
+            for f in files:
+                os.remove(f)
 
 # Stash pop
 if not stash[1].startswith('No local changes'):