Pull command tracks the right branch
[mdk.git] / mdk / commands / pull.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 """
5 Moodle Development Kit
6
7 Copyright (c) 2013 Frédéric Massart - FMCorz.net
8
9 This program is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
21
22 http://github.com/FMCorz/mdk
23 """
24
25 import re
26 import os
27 import logging
28 from datetime import datetime
29 from .. import tools, jira, fetch
30 from ..command import Command
31 from ..tools import question
32
33
34 class PullCommand(Command):
35
36 _arguments = [
37 (
38 ['-i', '--integration'],
39 {
40 'action': 'store_true',
41 'help': 'checkout the stable branch before proceeding to the pull. Short for --mode integration.'
42 }
43 ),
44 (
45 ['-n', '--no-merge'],
46 {
47 'action': 'store_true',
48 'dest': 'nomerge',
49 'help': 'checkout the remote branch without merging. Short for --mode checkout.'
50 }
51 ),
52 (
53 ['--fetch-only'],
54 {
55 'action': 'store_true',
56 'dest': 'fetchonly',
57 'help': 'only fetches the remote branch, you can then use FETCH_HEAD. Short for --mode fetch.'
58 }
59 ),
60 (
61 ['-t', '--testing'],
62 {
63 'action': 'store_true',
64 'help': 'checkout a testing branch before proceeding to the pull. Short for --mode testing.'
65 }
66 ),
67 (
68 ['-m', '--mode'],
69 {
70 'action': 'store',
71 'choices': ['checkout', 'fetch', 'integration', 'pull', 'testing'],
72 'default': 'pull',
73 'help': 'define the mode to use'
74 }
75 ),
76 (
77 ['-p', '--prompt'],
78 {
79 'action': 'store_true',
80 'help': 'prompts the user to choose the patch to download.'
81 }
82 ),
83 (
84 ['issue'],
85 {
86 'default': None,
87 'help': 'tracker issue to pull from (MDL-12345, 12345). If not specified, read from current branch.',
88 'metavar': 'issue',
89 'nargs': '?'
90 }
91 )
92 ]
93 _description = 'Pull a branch from a tracker issue'
94
95 def run(self, args):
96
97 M = self.Wp.resolve()
98 if not M:
99 raise Exception('This is not a Moodle instance')
100
101 # Get the mode.
102 mode = args.mode
103 if args.fetchonly:
104 mode = 'fetch'
105 elif args.nomerge:
106 mode = 'checkout'
107 elif args.testing:
108 mode = 'testing'
109 elif args.integration:
110 mode = 'integration'
111
112 # Prompt?
113 prompt = args.prompt
114
115 # Tracker issue number.
116 issuenb = args.issue
117 if not issuenb:
118 parsedbranch = tools.parseBranch(M.currentBranch())
119 if not parsedbranch:
120 raise Exception('Could not extract issue number from %s' % M.currentBranch())
121 issuenb = parsedbranch['issue']
122
123 issue = re.sub(r'(MDL|mdl)(-|_)?', '', issuenb)
124 mdl = 'MDL-' + issue
125
126 # Reading the information about the current instance.
127 branch = M.get('branch')
128
129 # Get information from Tracker
130 logging.info('Retrieving information about %s from Moodle Tracker' % (mdl))
131 fetcher = fetch.FetchTracker(M)
132
133 try:
134 if not prompt:
135 fetcher.setFromTracker(mdl, branch)
136 except (fetch.FetchTrackerRepoException, fetch.FetchTrackerBranchException) as e:
137 prompt = True
138
139 if prompt:
140 patches = self.pickPatches(mdl)
141 if not patches:
142 raise Exception('Could not find any relevant information for a successful pull')
143 fetcher.usePatches(patches)
144
145 if mode == 'pull':
146 fetcher.pull()
147 elif mode == 'checkout':
148 fetcher.checkout()
149 elif mode == 'fetch':
150 fetcher.fetch()
151 elif mode == 'integration':
152 fetcher.pull(into=M.get('stablebranch'))
153 elif mode == 'testing':
154 i = 0
155 while True:
156 i += 1
157 suffix = 'test' if i <= 1 else 'test' + str(i)
158 newBranch = M.generateBranchName(issue, suffix=suffix, version=branch)
159 if not M.git().hasBranch(newBranch):
160 break
161 fetcher.pull(into=newBranch, track=M.get('stablebranch'))
162
163 def pickPatches(self, mdl):
164 """Prompts the user to pick a patch"""
165
166 J = jira.Jira()
167 patches = J.getAttachments(mdl)
168 patches = {k: v for k, v in patches.items() if v.get('filename').endswith('.patch')}
169 toApply = []
170
171 if len(patches) < 1:
172 return False
173
174 mapping = {}
175 i = 1
176 for key in sorted(patches.keys()):
177 patch = patches[key]
178 mapping[i] = patch
179 print '{0:<2}: {1:<60} {2}'.format(i, key[:60], datetime.strftime(patch.get('date'), '%Y-%m-%d %H:%M'))
180 i += 1
181
182 while True:
183 try:
184 ids = question('What patches would you like to apply?')
185 if ids.lower() == 'ankit':
186 logging.warning('Sorry, I am unable to punch a child at the moment...')
187 continue
188 elif ids:
189 ids = re.split(r'\s*[, ]\s*', ids)
190 toApply = [mapping[int(i)] for i in ids if int(i) in mapping.keys()]
191 except ValueError:
192 logging.warning('Error while parsing the list of patches, try a little harder.')
193 continue
194 break
195
196 return toApply
197