0f75cd40b4ce537335e3302a72b944ec2cabaecf
2 # -*- coding: utf-8 -*-
7 Copyright (c) 2013 Frédéric Massart - FMCorz.net
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.
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.
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/>.
22 http://github.com/FMCorz/mdk
30 from ..command
import Command
31 from ..tools
import mkdir
, resolveEditor
34 class DoctorCommand(Command
):
40 'action': 'store_true',
41 'help': 'Automatically fix all the identified problems'
47 'action': 'store_true',
48 'help': 'Enable all the checks'
54 'action': 'store_true',
55 'help': 'Check the branch checked out on your integration instances'
61 'action': 'store_true',
62 'help': 'Check the cached repositories'
68 'action': 'store_true',
69 'help': 'Check various dependencies'
75 'action': 'store_true',
76 'help': 'Check the directories set in the config file'
82 'action': 'store_true',
83 'help': 'What you see it totally unrelated to what you get',
90 'action': 'store_true',
91 'help': 'Check the status of the master branch'
97 'action': 'store_true',
98 'help': 'Check the remotes of your instances'
104 'action': 'store_true',
105 'help': 'Check the symlinks of the instances'
111 'action': 'store_true',
112 'help': 'Check the $CFG->wwwroot of your instances'
116 _description
= 'Perform several checks on your current installation'
120 optionsCount
= sum([1 for k
, v
in vars(args
).items() if v
!= False])
121 if optionsCount
== 0 or (optionsCount
== 1 and args
.fix
):
122 self
.argumentError('You should probably tell me what symptoms you are experiencing')
129 if args
.directories
or allChecks
:
130 self
.directories(args
)
132 # Check the cached remotes
133 if args
.cached
or allChecks
:
134 self
.cachedRepositories(args
)
136 # Check the dependencies
137 if args
.dependencies
or allChecks
:
138 self
.dependencies(args
)
140 # Check instances remotes
141 if args
.remotes
or allChecks
:
144 # Check instances wwwroot
145 if args
.wwwroot
or allChecks
:
148 # Check instances symlink
149 if args
.symlink
or allChecks
:
153 if args
.branch
or allChecks
:
156 # Check the master branch
157 if args
.masterbranch
or allChecks
:
158 self
.masterbranch(args
)
160 # Check what you see is what you get
164 def branch(self
, args
):
165 """Make sure the correct branch is checked out. Only on integration branches."""
167 print 'Checking integration instances branches'
169 if not self
._checkWorkplace():
172 instances
= self
.Wp
.list(integration
=True)
174 for identifier
in instances
:
175 M
= self
.Wp
.get(identifier
)
176 stablebranch
= M
.get('stablebranch')
177 currentbranch
= M
.currentBranch()
178 if stablebranch
!= currentbranch
:
179 print ' %s is on branch %s instead of %s' %
(identifier
, currentbranch
, stablebranch
)
181 print ' Checking out %s' %
(stablebranch
)
182 if not M
.git().checkout(stablebranch
):
183 print ' Error: Checkout unsucessful!'
185 def cachedRepositories(self
, args
):
186 """Ensure that the cached repositories are valid"""
188 print 'Checking cached repositories'
189 cache
= os
.path
.abspath(os
.path
.realpath(os
.path
.expanduser(self
.C
.get('dirs.mdk'))))
193 'dir': os
.path
.join(cache
, 'moodle.git'),
194 'url': self
.C
.get('remotes.stable')
197 'dir': os
.path
.join(cache
, 'integration.git'),
198 'url': self
.C
.get('remotes.integration')
204 name
= os
.path
.split(directory
)[1]
206 if os
.path
.isdir(directory
):
207 if os
.path
.isdir(os
.path
.join(directory
, '.git')):
208 print ' %s is not a bare repository' % name
210 print ' Renaming %s/.git directory to %s' %
(directory
, directory
)
211 os
.rename(directory
, directory
+ '.tmp')
212 os
.rename(os
.path
.join(directory
+ '.tmp', '.git'), directory
)
213 shutil
.rmtree(directory
+ '.tmp')
215 repo
= git
.Git(directory
, self
.C
.get('git'))
216 if repo
.getConfig('core.bare') != 'true':
217 print ' %s core.bare is not set to true' % name
219 print ' Setting core.bare to true'
220 repo
.setConfig('core.bare', 'true')
222 if repo
.getConfig('remote.origin.url') != d
['url']:
223 print ' %s uses an different origin (%s)' %
(name
, repo
.getConfig('remote.origin.url'))
225 print ' Setting remote.origin.url to %s' % d
['url']
226 repo
.setConfig('remote.origin.url', d
['url'])
228 if repo
.getConfig('remote.origin.fetch') != '+refs/*:refs/*':
229 print ' %s fetch value is invalid (%s)' %
(name
, repo
.getConfig('remote.origin.fetch'))
231 print ' Setting remote.origin.fetch to %s' %
'+refs/*:refs/*'
232 repo
.setConfig('remote.origin.fetch', '+refs/*:refs/*')
234 def dependencies(self
, args
):
235 """Check that various dependencies are met"""
237 print 'Checking dependencies'
241 for k
in ['git', 'php', 'java', 'recess', 'lessc', 'shifter', 'yuidoc']:
243 if not path
or not os
.path
.isfile(path
):
244 print ' The path to \'%s\' is invalid: %s' %
(k
, path
)
246 if hasErrors
and args
.fix
:
247 print ' Please manually fix the paths in your config file'
250 editor
= resolveEditor()
253 # Check if it is callable.
254 proc
= subprocess
.Popen(editor
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
)
260 print ' Could not resolve the path to your editor'
262 print ' Set $EDITOR, /usr/bin/editor, or use: mdk config set editor [path]'
264 def directories(self
, args
):
265 """Check that the directories are valid"""
267 print 'Checking directories'
268 for k
, d
in self
.C
.get('dirs').items():
269 d
= os
.path
.abspath(os
.path
.realpath(os
.path
.expanduser(d
)))
270 if not os
.path
.isdir(d
):
271 print ' %s does not exist' % d
273 print ' Creating %s' % d
276 if not self
._checkWorkplace():
279 # Checking extra directory.
280 instances
= self
.Wp
.list()
281 for identifier
in instances
:
282 d
= self
.Wp
.getPath(identifier
, 'extra')
283 if not os
.path
.isdir(d
):
284 print ' %s does not exist' % d
286 print ' Creating %s' % d
289 def masterbranch(self
, args
):
290 """Checks the current master branch and the value set in config."""
292 print 'Checking master branch'
294 if not self
._checkWorkplace():
297 repoPath
= self
.Wp
.getCachedRemote()
298 if not os
.path
.isdir(repoPath
):
302 self
.Wp
.updateCachedClones(verbose
=False)
304 print ' Could not update clone, please try again.'
307 repo
= git
.Git(repoPath
, self
.C
.get('git'))
308 result
= repo
.execute(['show', 'master:version.php'])
310 print ' Could not read the master version.php'
313 reBranch
= re
.compile(r
'^\s*\$branch\s*=\s*(?P<brackets>[\'"])?([0-9]+)(?P=brackets)\s*;')
315 for line in result[1].split('\n'):
316 if reBranch.search(line):
317 latestBranch = int(reBranch.search(line).group(2))
319 masterBranch = int(self.C.get('masterBranch'))
321 print ' Oops, could not identify the mater branch'
322 elif masterBranch != latestBranch:
323 print ' The config masterBranch is set to %d, expecting %d' % (masterBranch, latestBranch)
325 print ' Setting masterBranch to %d' % (latestBranch)
326 self.C.set('masterBranch', latestBranch)
330 """I wonder what is the purpose of this...
337 print 'The horse is a noble animal'
341 def symlink(self, args):
342 """Check that the symlinks exist"""
344 print 'Checking symlinks'
346 if not self._checkWorkplace():
349 instances = self.Wp.list()
350 for identifier in instances:
352 wwwLink = os.path.join(self.Wp.www, identifier)
353 if not os.path.exists(wwwLink):
354 print ' Missing link to www for %s' % (identifier)
356 print ' Creating www symlink for %s' % (identifier)
357 os.symlink(self.Wp.getPath(identifier, 'www'), wwwLink)
359 extraLink = os.path.join(self.Wp.getMdkWebDir(), identifier)
360 if not os.path.exists(extraLink):
361 print ' Missing link to extra for %s' % (identifier)
363 print ' Creating extra symlink for %s' % (identifier)
364 os.symlink(self.Wp.getPath(identifier, 'extra'), extraLink)
367 def remotes(self, args):
368 """Check that the correct remotes are used"""
370 print 'Checking remotes'
372 if not self._checkWorkplace():
376 'mine': self.C.get('remotes.mine'),
377 'stable': self.Wp.getCachedRemote() if self.C.get('useCacheAsUpstreamRemote') else self.C.get('remotes.stable'),
378 'integration': self.Wp.getCachedRemote(True) if self.C.get('useCacheAsUpstreamRemote') else self.C.get('remotes.integration')
380 myRemote = self.C.get('myRemote')
381 upstreamRemote = self.C.get('upstreamRemote')
383 instances = self.Wp.list()
384 for identifier in instances:
385 M = self.Wp.get(identifier)
387 remote = M.git().getRemote(myRemote)
388 if remote != remotes['mine']:
389 print ' %s: Remote %s is %s, not %s' % (identifier, myRemote, remote, remotes['mine'])
391 print ' Setting %s to %s' % (myRemote, remotes['mine'])
392 M.git().setRemote(myRemote, remotes['mine'])
394 expected = remotes['stable'] if M.isStable() else remotes['integration']
395 remote = M.git().getRemote(upstreamRemote)
396 if remote != expected:
397 print ' %s: Remote %s is %s, not %s' % (identifier, upstreamRemote, remote, expected)
399 print ' Setting %s to %s' % (upstreamRemote, expected)
400 M.git().setRemote(upstreamRemote, expected)
402 def wwwroot(self, args):
403 """Check the wwwroot of the instances"""
405 print 'Checking wwwroot'
407 if not self._checkWorkplace():
410 instances = self.Wp.resolveMultiple(self.Wp.list())
414 if not M.isInstalled():
417 actual = M.get('wwwroot')
418 expected = self.Wp.getUrl(M.get('identifier'))
419 if actual != expected:
420 print ' %s: Found %s, not %s' % (M.get('identifier'), actual, expected)
422 print ' Setting %s on %s' % (expected, M.get('identifier'))
423 M.updateConfig('wwwroot', expected)
425 def _checkWorkplace(self, indent=2):
426 """Returns whether the workplace is available or, and print a message if it is not."""
430 print ' ' * indent + 'The workplace could not be loaded, did you install the dependencies?'