Edit following integration of support of Moodle Tracker
authorFrederic Massart <fred@moodle.com>
Mon, 18 Feb 2013 03:59:23 +0000 (11:59 +0800)
committerFrederic Massart <fred@moodle.com>
Mon, 18 Feb 2013 04:33:58 +0000 (12:33 +0800)
config-dist.json
lib/git.py
lib/jira.py
moodle-backport.py
moodle-push.py

index 419080d..037f97b 100644 (file)
@@ -71,8 +71,8 @@
         "pgsql": "PostgreSQL"
     },
 
-    // The information for publishing diff urls to tracker
-    "jira": {
+    // The information for integrating MDK with Jira
+    "tracker": {
         "url": "https://tracker.moodle.org/",
         "username": "me",
         "fieldnames" : {
             "master" : {
                 "branch" : "Pull Master Branch",
                 "diffurl" : "Pull Master Diff URL"
-            },
+            }
         }
     },
-    // The base for github diff urls
-    "diffUrlTemplate": "https://github.com/me/moodle/compare/%headcommit%...%branch%",
+    // The base for diff URLs, you can use the following wildcards:
+    // - %branch%: The branch name;
+    // - %stablebranch%: The stable branch (MOODLE_23_STABLE, MOODLE_24_STABLE, master, ...);
+    // - %headcommit%: The head commit.
+    // This is used to populate the fields on the tracker issue.
+    "diffUrlTemplate": "https://github.com/YourGitHub/moodle/compare/%headcommit%...%branch%",
+
+    // The public acccess URL of your repository. It is used to populate the fields on the tracker issue.
+    "repositoryUrl": "git://github.com/YourGitHub/moodle.git",
 
     // The host name to set during an install
     "host": "localhost",
index 6b96a1c..bd26a99 100644 (file)
@@ -127,6 +127,12 @@ class Git(object):
         (returncode, stdout, stderr) = self.execute(cmd)
         return returncode == 0
 
+    def hashes(self, ref='', format='%H', limit=30):
+        """Returns the latest hashes from git log"""
+        cmd = 'log %s -n %d --format=%s' % (ref, limit, format)
+        hashlist = self.execute(cmd)
+        return hashlist[1].split('\n')[:-1]
+
     def isRepository(self, path = None):
         if path == None:
             path = self.getPath()
@@ -148,11 +154,6 @@ class Git(object):
         cmd = 'pull %s %s' % (remote, ref)
         return self.execute(cmd)
 
-    def hashes(self, ref = '', format = '%H', limit = 30):
-        cmd = 'log %s -n %d --format=%s' % (ref, limit, format)
-        hashlist = self.execute(cmd)
-        return hashlist[1].split('\n')[:-1]
-
     def push(self, remote = '', branch = '', force = None):
         if force:
             force = '--force '
index 311cf7e..6106b18 100644 (file)
@@ -22,16 +22,28 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 http://github.com/FMCorz/mdk
 """
 
+import sys
 import json
-
-from tools import debug, process
+from tools import debug
 from config import Conf
 import getpass
-import keyring
-from restkit import request, BasicAuth
+try:
+    from restkit import request, BasicAuth
+except:
+    debug('Could not load module restkit for Jira.')
+    debug('Try `apt-get install python-restkit`, or visit http://pypi.python.org/pypi/restkit')
+    debug('Exiting...')
+    sys.exit(1)
+try:
+    import keyring
+except:
+    debug('Could not load module keyring. You might want to install it.')
+    debug('Try `apt-get install python-keyring`, or visit http://pypi.python.org/pypi/keyring')
+    pass
 
 C = Conf()
 
+
 class Jira(object):
 
     serverurl = ''
@@ -46,23 +58,15 @@ class Jira(object):
         self.version = {}
         self._load()
 
-    def get(self, param, default = None):
+    def get(self, param, default=None):
         """Returns a property of this instance"""
         return self.info().get(param, default)
 
-    def info(self):
-        """Returns a dictionary of information about this instance"""
-        info = {}
-        self._load()
-        for (k, v) in self.version.items():
-            info[k] = v
-        return info
-
-    def get_issue(self, key):
+    def getIssue(self, key):
         """Load the issue info from the jira server using a rest api call"""
 
         requesturl = self.url + 'rest/api/' + self.apiversion + '/issue/' + key + '?expand=names'
-        response = request(requesturl, filters=[self.auth]);
+        response = request(requesturl, filters=[self.auth])
 
         if response.status_int == 404:
             raise JiraException('Issue could not be found.')
@@ -73,11 +77,11 @@ class Jira(object):
         issue = json.loads(response.body_string())
         return issue
 
-    def get_server_info(self):
+    def getServerInfo(self):
         """Load the version info from the jira server using a rest api call"""
 
         requesturl = self.url + 'rest/api/' + self.apiversion + '/serverInfo'
-        response = request(requesturl, filters=[self.auth]);
+        response = request(requesturl, filters=[self.auth])
 
         if not response.status_int == 200:
             raise JiraException('Jira is not available: ' + response.status)
@@ -85,6 +89,14 @@ class Jira(object):
         serverinfo = json.loads(response.body_string())
         self.version = serverinfo
 
+    def info(self):
+        """Returns a dictionary of information about this instance"""
+        info = {}
+        self._load()
+        for (k, v) in self.version.items():
+            info[k] = v
+        return info
+
     def _load(self):
         """Loads the information"""
 
@@ -92,10 +104,16 @@ class Jira(object):
             return True
 
         # First get the jira details from the config file.
-        self.url = C.get('jira.url')
-        self.username = C.get('jira.username')
-        # str() is needed because keyring does not handle unicode.
-        self.password = keyring.get_password('mdk-jira-password', str(self.username))
+        self.url = C.get('tracker.url')
+        self.username = C.get('tracker.username')
+
+        try:
+            # str() is needed because keyring does not handle unicode.
+            self.password = keyring.get_password('mdk-jira-password', str(self.username))
+        except:
+            # Do not die if keyring package is not available.
+            self.password = None
+            pass
 
         if not self.url or not self.username:
             raise JiraException('Jira has not been configured in the config file.')
@@ -107,28 +125,39 @@ class Jira(object):
                 self.auth = BasicAuth(self.username, self.password)
 
                 try:
-                    self.get_server_info()
+                    self.getServerInfo()
                     self._loaded = True
                 except JiraException:
                     print 'Either the password is incorrect or you may need to enter a Captcha to continue.'
             if not self._loaded:
-                self.password = getpass.getpass('Jira password for user %s:' % self.username)
-        keyring.set_password('mdk-jira-password', str(self.username), str(self.password))
+                self.password = getpass.getpass('Jira password for user %s: ' % self.username)
+
+        try:
+            keyring.set_password('mdk-jira-password', str(self.username), str(self.password))
+        except:
+            # Do not die if keyring package is not available.
+            pass
 
         return True
 
-    def set_custom_fields(self, key, updates):
-        """ Set a list of fields for this issue in Jira
+    def reload(self):
+        """Reloads the information"""
+        self._loaded = False
+        return self._load()
+
+    def setCustomFields(self, key, updates):
+        """Set a list of fields for this issue in Jira
 
         The updates parameter is a dictionary of key values where the key is the custom field name
-        and the value is the new value to set. This only works for fields of type text.
+        and the value is the new value to set.
+
+        /!\ This only works for fields of type text.
         """
-        issue = self.get_issue(key)
+        issue = self.getIssue(key)
 
-        customfieldkey = ''
         namelist = {}
 
-        update = { 'fields' : {} }
+        update = {'fields': {}}
         namelist = issue['names']
 
         for fieldkey in issue['fields']:
@@ -146,17 +175,13 @@ class Jira(object):
             return True
 
         requesturl = self.url + 'rest/api/' + self.apiversion + '/issue/' + key
-        response = request(requesturl, filters=[self.auth], method='PUT', body=json.dumps(update), headers={'Content-Type' : 'application/json'});
+        response = request(requesturl, filters=[self.auth], method='PUT', body=json.dumps(update), headers={'Content-Type': 'application/json'})
 
         if response.status_int != 204:
             raise JiraException('Issue was not updated:' + response.status)
 
         return True
 
-    def reload(self):
-        """Reloads the information"""
-        self._loaded = False
-        return self._load()
 
 class JiraException(Exception):
     pass
index 1ee12c8..1e88985 100755 (executable)
@@ -161,34 +161,6 @@ for v in versions:
         else:
             debug('Popped the stash')
 
-    # Update jira
-    if args.updatejira:
-        repositoryurl = C.get('repositoryUrl')
-        diffurltemplate = C.get('diffUrlTemplate')
-        stablebranch = M2.get('stablebranch')
-        upstreamremote = C.get('upstreamRemote')
-        # Get the hash of the last upstream commit
-        ref = '%s/%s' % (upstreamremote, stablebranch)
-        headcommit = M.git().hashes(ref=ref, limit=1)[0]
-
-        J = jira.Jira()
-        diffurl = diffurltemplate.replace('%branch%', newbranch).replace('%stablebranch%', stablebranch).replace('%headcommit%', headcommit)
-
-        fieldrepositoryurl = C.get('jira.fieldnames.repositoryurl')
-        fieldbranch = C.get('jira.fieldnames.%s.branch' % v)
-        fielddiffurl = C.get('jira.fieldnames.%s.diffurl' % v)
-
-        if not (fieldrepositoryurl or fieldbranch or fielddiffurl):
-            debug('Cannot set Jira fields for this version(%s) as the field names are not configured in the config file.', v)
-
-        else:
-            debug('Setting jira fields: \n\t%s: %s \n\t%s: %s \n\t%s: %s\n' % (fieldrepositoryurl, repositoryurl,
-                                                                               fieldbranch, newbranch,
-                                                                               fielddiffurl, diffurl))
-            J.set_custom_fields(issue, { fieldrepositoryurl : repositoryurl,
-                                         fieldbranch : newbranch,
-                                         fielddiffurl : diffurl })
-
     debug('Instance %s successfully patched!' % name)
     debug('')
 
index 63b90e5..525270b 100755 (executable)
@@ -24,7 +24,7 @@ http://github.com/FMCorz/mdk
 
 import sys
 import argparse
-from lib import workplace, moodle, tools, jira
+from lib import workplace, tools, jira
 from lib.tools import debug
 from lib.config import Conf
 
@@ -35,8 +35,8 @@ C = Conf()
 parser = argparse.ArgumentParser(description="Push a branch to a remote.")
 parser.add_argument('-b', '--branch', metavar='branch', help='the branch to push. Default is current branch.')
 parser.add_argument('-r', '--remote', metavar='remote', help='remote to push to. Default is your remote.')
-parser.add_argument('-f', '--force', action='store_true', help='force the push (does not apply on the stable branch)')
-parser.add_argument('-j', '--update-jira', action='store_true', help='also add the github links to the jira issue.', dest='updatejira')
+parser.add_argument('-f', '--force', action='store_true', help='force the push (does not apply on the stable branch).')
+parser.add_argument('-t', '--update-tracker', action='store_true', help='also add the diff information to the tracker issue.', dest='updatetracker')
 parser.add_argument('-s', '--include-stable', action='store_true', help='also push the stable branch (MOODLE_xx_STABLE, master)', dest='includestable')
 parser.add_argument('-k', '--force-stable', action='store_true', help='force the push on the stable branch', dest='forcestable')
 parser.add_argument('name', metavar='name', default=None, nargs='?', help='name of the instance to work on')
@@ -49,28 +49,27 @@ if not M:
 
 # Setting remote
 if args.remote == None:
-       remote = C.get('myRemote')
+    remote = C.get('myRemote')
 else:
-       remote = args.remote
+    remote = args.remote
 
 # Setting branch
 if args.branch == None:
-       branch = M.currentBranch()
-       if branch == 'HEAD':
-               debug('Cannot push HEAD branch')
-               sys.exit(1)
+    branch = M.currentBranch()
+    if branch == 'HEAD':
+        debug('Cannot push HEAD branch')
+        sys.exit(1)
 else:
-       branch = args.branch
-
+    branch = args.branch
 
 # Pushing current branch
 debug('Pushing branch %s to remote %s...' % (branch, remote))
 result = M.git().push(remote, branch, force=args.force)
 if result[0] != 0:
-       debug(result[2])
-       sys.exit(1)
+    debug(result[2])
+    sys.exit(1)
 
-if args.updatejira:
+if args.updatetracker:
     # Getting issue number
     # Parsing the branch
     parsedbranch = tools.parseBranch(branch, C.get('wording.branchRegex'))
@@ -93,28 +92,28 @@ if args.updatejira:
     J = jira.Jira()
     diffurl = diffurltemplate.replace('%branch%', branch).replace('%stablebranch%', stablebranch).replace('%headcommit%', headcommit)
 
-    fieldrepositoryurl = C.get('jira.fieldnames.repositoryurl')
-    fieldbranch = C.get('jira.fieldnames.%s.branch' % version)
-    fielddiffurl = C.get('jira.fieldnames.%s.diffurl' % version)
+    fieldrepositoryurl = C.get('tracker.fieldnames.repositoryurl')
+    fieldbranch = C.get('tracker.fieldnames.%s.branch' % version)
+    fielddiffurl = C.get('tracker.fieldnames.%s.diffurl' % version)
 
     if not (fieldrepositoryurl or fieldbranch or fielddiffurl):
-        debug('Cannot set Jira fields for this version(%s) as the field names are not configured in the config file.', version)
+        debug('Cannot set tracker fields for this version (%s) as the field names are not configured in the config file.', version)
 
     else:
-        debug('Setting jira fields: \n\t%s: %s \n\t%s: %s \n\t%s: %s\n' % (fieldrepositoryurl, repositoryurl,
+        debug('Setting tracker fields: \n\t%s: %s \n\t%s: %s \n\t%s: %s\n' % (fieldrepositoryurl, repositoryurl,
                                                                            fieldbranch, branch,
                                                                            fielddiffurl, diffurl))
-        J.set_custom_fields(issue, { fieldrepositoryurl : repositoryurl,
-                                     fieldbranch : branch,
-                                     fielddiffurl : diffurl })
+        J.setCustomFields(issue, {fieldrepositoryurl: repositoryurl,
+                                     fieldbranch: branch,
+                                     fielddiffurl: diffurl})
 
 # Pushing stable branch
 if args.includestable:
-       branch = M.get('stablebranch')
-       debug('Pushing branch %s to remote %s...' % (branch, remote))
-       result = M.git().push(remote, branch, force=args.forcestable)
-       if result[0] != 0:
-               debug(result[2])
-               sys.exit(1)
+    branch = M.get('stablebranch')
+    debug('Pushing branch %s to remote %s...' % (branch, remote))
+    result = M.git().push(remote, branch, force=args.forcestable)
+    if result[0] != 0:
+        debug(result[2])
+        sys.exit(1)
 
 debug('Done.')