Command pull to fetch a patch from a tracker issue
authorFrederic Massart <fred@moodle.com>
Mon, 18 Feb 2013 07:24:38 +0000 (15:24 +0800)
committerFrederic Massart <fred@moodle.com>
Mon, 18 Feb 2013 07:24:38 +0000 (15:24 +0800)
README.md
extra/bash_completion
lib/jira.py
moodle-pull.py [new file with mode: 0755]
moodle-rebase.py

index 96e9271..4869f50 100644 (file)
--- a/README.md
+++ b/README.md
@@ -214,6 +214,18 @@ To purge the cache of all the instances
     mdk purge --all
 
 
+pull
+----
+
+Pulls a patch using the information from a tracker issue.
+
+**Example**
+
+Assuming we type that command on a 2.3 instance, pulls the corresponding patch from the issue MDL-12345 in a testing branch
+
+    mdk pull --testing 12345
+
+
 push
 ----
 
index a20e5aa..27fd890 100644 (file)
@@ -44,7 +44,7 @@ function _moodle() {
     if [[ "${COMP_CWORD}" == 1 ]]; then
         # List the commands and aliases.
         # Ignoring these commands on purpose: init
-        OPTS="alias backport backup check config create fix info install phpunit purge push rebase remove run update upgrade"
+        OPTS="alias backport backup check config create fix info install phpunit purge pull push rebase remove run update upgrade"
         OPTS="$OPTS $($BIN alias list | cut -d ':' -f 1)"
     else
         # List of options according to the command.
@@ -132,6 +132,9 @@ function _moodle() {
                     OPTS="$OPTS $($BIN info -ln)"
                 fi
                 ;;
+            pull)
+                OPTS="--integration --testing"
+                ;;
             push)
                 if [[ "$PREV" != "--branch" && "$PREV" != "-b" && "$PREV" != "--remote" && "$PREV" != "-r" ]]; then
                     OPTS="--branch --remote --force --include-stable --force-stable --update-tracker"
index 6106b18..d73865f 100644 (file)
@@ -26,6 +26,7 @@ import sys
 import json
 from tools import debug
 from config import Conf
+from urllib import urlencode
 import getpass
 try:
     from restkit import request, BasicAuth
@@ -62,10 +63,14 @@ class Jira(object):
         """Returns a property of this instance"""
         return self.info().get(param, default)
 
-    def getIssue(self, key):
-        """Load the issue info from the jira server using a rest api call"""
+    def getIssue(self, key, fields='*all'):
+        """Load the issue info from the jira server using a rest api call.
 
-        requesturl = self.url + 'rest/api/' + self.apiversion + '/issue/' + key + '?expand=names'
+        The returned key 'named' of the returned dict is organised by name of the fields, not id.
+        """
+
+        querystring = {'fields': fields, 'expand': 'names'}
+        requesturl = self.url + 'rest/api/' + self.apiversion + '/issue/' + key + '?%s' % (urlencode(querystring))
         response = request(requesturl, filters=[self.auth])
 
         if response.status_int == 404:
@@ -75,6 +80,14 @@ class Jira(object):
             raise JiraException('Jira is not available.')
 
         issue = json.loads(response.body_string())
+        issue['named'] = {}
+
+        # Populate the named fields in a separate key. Allows us to easily find them without knowing the field ID.
+        namelist = issue.get('names', {})
+        for fieldkey, fieldvalue in issue.get('fields', {}).items():
+            if namelist.get(fieldkey, None) != None:
+                issue['named'][namelist.get(fieldkey)] = fieldvalue
+
         return issue
 
     def getServerInfo(self):
diff --git a/moodle-pull.py b/moodle-pull.py
new file mode 100755 (executable)
index 0000000..dc8a876
--- /dev/null
@@ -0,0 +1,109 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Moodle Development Kit
+
+Copyright (c) 2013 Frédéric Massart - FMCorz.net
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+http://github.com/FMCorz/mdk
+"""
+
+import sys
+import argparse
+import re
+from lib import workplace, tools, jira
+from lib.tools import debug
+from lib.config import Conf
+
+Wp = workplace.Workplace()
+C = Conf()
+
+# Arguments
+parser = argparse.ArgumentParser(description="Pull a branch from a tracker issue.")
+parser.add_argument('-i', '--integration', action='store_true', help='checkout the stable branch before proceeding to the pull (Integration mode).')
+parser.add_argument('-t', '--testing', action='store_true', help='checkout a testing branch before proceeding to the pull (Testing mode).')
+parser.add_argument('issue', metavar='issue', default=None, nargs='?', help='tracker issue to pull from (MDL-12345, 12345). If not specified, read from current branch.')
+args = parser.parse_args()
+
+M = Wp.resolve()
+if not M:
+    debug('This is not a Moodle instance')
+    sys.exit(1)
+
+if args.testing and args.integration:
+    debug('You cannot combine --integration and --testing')
+    sys.exit(1)
+
+# Tracker issue number.
+issue = re.sub(r'(MDL|mdl)(-|_)?', '', args.issue)
+mdl = 'MDL-' + issue
+
+# Reading the information about the current instance.
+branch = M.get('branch')
+
+# Get information from Tracker
+debug('Retrieving information about %s from Moodle Tracker' % (mdl))
+J = jira.Jira()
+issueInfo = J.getIssue(mdl)
+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')
+
+# Stash
+stash = M.git().stash(untracked=True)
+if stash[0] != 0:
+    debug('Error while trying to stash your changes. Exiting...')
+    sys.exit(1)
+elif not stash[1].startswith('No local changes'):
+    debug('Stashed your local changes')
+
+# Create a testing branch
+if args.testing:
+    i = 0
+    while True:
+        i += 1
+        suffix = 'test' if i <= 1 else 'test' + str(i)
+        newBranch = M.generateBranchName(issue, suffix=suffix, version=branch)
+        if not M.git().hasBranch(newBranch):
+            break
+    track = '%s/%s' % (C.get('upstreamRemote'), M.get('stablebranch'))
+    M.git().createBranch(newBranch, track=track)
+    if not M.git().checkout(newBranch):
+        debug('Could not checkout branch %s' % (newBranch))
+        sys.exit(1)
+    debug('Checked out branch %s' % (newBranch))
+
+# Checkout the stable branch
+elif args.integration:
+    if not M.git().checkout(M.get('stablebranch')):
+        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)
+
+# Stash pop
+if not stash[1].startswith('No local changes'):
+    pop = M.git().stash(command='pop')
+    if pop[0] != 0:
+        debug('An error ocured while unstashing your changes')
+    else:
+        debug('Popped the stash')
+
+debug('Done.')
index 511b82f..86140d1 100755 (executable)
@@ -121,7 +121,7 @@ for M in Mlist:
                pop = M.git().stash(command='pop')
                if pop[0] != 0:
                        debug('An error ocured while unstashing your changes')
-                       debug(result[2])
+                       debug(pop[2])
                else:
                        debug('Popped the stash')