b357d6d35df6bd9cbba428dd85e1c5c307146d6d
2 # -*- coding: utf-8 -*-
7 Copyright (c) 2012 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
28 from .tools
import mkdir
, process
, stableBranch
29 from .exceptions
import CreateException
30 from .config
import Conf
37 class Workplace(object):
39 """The name of the directory that contains the PHP files"""
42 """The name of the directory that contains Moodle data"""
45 """The name of the directory that contains extra files"""
48 """The name of the directory that makes extraDir web accessible, see getMdkWebDir"""
51 """The path to the storage directory"""
54 """The path to MDK cache"""
57 """The path to the web accessible directory"""
60 def __init__(self
, path
=None, wwwDir
=None, dataDir
=None, extraDir
=None, mdkDir
=None):
62 path
= C
.get('dirs.storage')
64 wwwDir
= C
.get('wwwDir')
66 dataDir
= C
.get('dataDir')
68 extraDir
= C
.get('extraDir')
70 mdkDir
= C
.get('mdkDir')
73 self
.path
= os
.path
.abspath(os
.path
.realpath(os
.path
.expanduser(path
)))
74 self
.cache
= os
.path
.abspath(os
.path
.realpath(os
.path
.expanduser(C
.get('dirs.mdk'))))
75 self
.www
= os
.path
.abspath(os
.path
.realpath(os
.path
.expanduser(C
.get('dirs.www'))))
77 if not os
.path
.isdir(self
.path
):
78 raise Exception('Directory %s not found' % self
.path
)
82 self
.dataDir
= dataDir
83 self
.extraDir
= extraDir
86 def checkCachedClones(self
, stable
=True, integration
=True):
87 """Clone the official repository in a local cache"""
88 cacheStable
= self
.getCachedRemote(False)
89 cacheIntegration
= self
.getCachedRemote(True)
91 if not os
.path
.isdir(cacheStable
) and stable
:
92 logging
.info('Cloning stable repository into cache...')
94 # For faster clone, we will copy the integration clone if it exists.
95 if os
.path
.isdir(cacheIntegration
):
96 shutil
.copytree(cacheIntegration
, cacheStable
)
97 repo
= git
.Git(cacheStable
, C
.get('git'))
98 repo
.setRemote('origin', C
.get('remotes.stable'))
99 # The repository is not updated at this stage, it has to be done manually.
101 logging
.info('This is going to take a while...')
102 process('%s clone --mirror %s %s' %
(C
.get('git'), C
.get('remotes.stable'), cacheStable
))
104 if not os
.path
.isdir(cacheIntegration
) and integration
:
105 logging
.info('Cloning integration repository into cache...')
107 # For faster clone, we will copy the integration clone if it exists.
108 if os
.path
.isdir(cacheStable
):
109 shutil
.copytree(cacheStable
, cacheIntegration
)
110 repo
= git
.Git(cacheIntegration
, C
.get('git'))
111 repo
.setRemote('origin', C
.get('remotes.integration'))
112 # The repository is not updated at this stage, it has to be done manually.
114 logging
.info('Have a break, this operation is slow...')
115 process('%s clone --mirror %s %s' %
(C
.get('git'), C
.get('remotes.integration'), cacheIntegration
))
117 def create(self
, name
=None, version
='master', integration
=False, useCacheAsRemote
=False):
118 """Creates a new instance of Moodle.
119 The parameter useCacheAsRemote has been deprecated.
122 name
= self
.generateInstanceName(version
, integration
=integration
)
124 if name
== self
.mdkDir
:
125 raise Exception('A Moodle instance cannot be called \'%s\', this is a reserved word.' % self
.mdkDir
)
127 installDir
= self
.getPath(name
)
128 wwwDir
= self
.getPath(name
, 'www')
129 dataDir
= self
.getPath(name
, 'data')
130 extraDir
= self
.getPath(name
, 'extra')
131 linkDir
= os
.path
.join(self
.www
, name
)
132 extraLinkDir
= os
.path
.join(self
.getMdkWebDir(), name
)
134 if self
.isMoodle(name
):
135 raise CreateException('The Moodle instance %s already exists' % name
)
136 elif os
.path
.isdir(installDir
):
137 raise CreateException('Installation path exists: %s' % installDir
)
139 self
.checkCachedClones(not integration
, integration
)
140 self
.updateCachedClones(stable
=not integration
, integration
=integration
, verbose
=False)
141 mkdir(installDir
, 0755)
144 mkdir(extraDir
, 0777)
146 repository
= self
.getCachedRemote(integration
)
148 # Clone the instances
149 logging
.info('Cloning repository...')
150 process('%s clone %s %s' %
(C
.get('git'), repository
, wwwDir
))
153 if os
.path
.islink(linkDir
):
155 if os
.path
.isfile(linkDir
) or os
.path
.isdir(linkDir
): # No elif!
156 logging
.warning('Could not create symbolic link. Please manually create: ln -s %s %s' %
(wwwDir
, linkDir
))
158 os
.symlink(wwwDir
, linkDir
)
161 if os
.path
.isfile(extraLinkDir
) or os
.path
.isdir(extraLinkDir
):
162 logging
.warning('Could not create symbolic link. Please manually create: ln -s %s %s' %
(extraDir
, extraLinkDir
))
164 os
.symlink(extraDir
, extraLinkDir
)
166 # Symlink to dataDir in wwwDir
167 if type(C
.get('symlinkToData')) == str:
168 linkDataDir
= os
.path
.join(wwwDir
, C
.get('symlinkToData'))
169 if not os
.path
.isfile(linkDataDir
) and not os
.path
.isdir(linkDataDir
) and not os
.path
.islink(linkDataDir
):
170 os
.symlink(dataDir
, linkDataDir
)
172 logging
.info('Checking out branch...')
173 repo
= git
.Git(wwwDir
, C
.get('git'))
175 # Removing the default remote origin coming from the clone
176 repo
.delRemote('origin')
178 # Setting up the correct remote names
179 repo
.setRemote(C
.get('myRemote'), C
.get('remotes.mine'))
180 repo
.setRemote(C
.get('upstreamRemote'), repository
)
182 # Creating, fetch, pulling branches
183 repo
.fetch(C
.get('upstreamRemote'))
184 branch
= stableBranch(version
)
185 track
= '%s/%s' %
(C
.get('upstreamRemote'), branch
)
186 if not repo
.hasBranch(branch
) and not repo
.createBranch(branch
, track
):
187 logging
.error('Could not create branch %s tracking %s' %
(branch
, track
))
189 repo
.checkout(branch
)
190 repo
.pull(remote
=C
.get('upstreamRemote'))
192 # Fixing up remote URLs if need be, this is done after pulling the cache one because we
193 # do not want to contact the real origin server from here, it is slow and pointless.
194 if not C
.get('useCacheAsUpstreamRemote'):
195 realupstream
= C
.get('remotes.integration') if integration
else C
.get('remotes.stable')
197 repo
.setRemote(C
.get('upstreamRemote'), realupstream
)
202 def delete(self
, name
):
203 """Completely remove an instance, database included"""
205 # Instantiating the object also checks if it exists
208 # Deleting the whole thing
209 shutil
.rmtree(os
.path
.join(self
.path
, name
))
211 # Deleting the possible symlink
212 link
= os
.path
.join(self
.www
, name
)
213 if os
.path
.islink(link
):
219 # Delete the extra dir symlink
220 link
= os
.path
.join(self
.getMdkWebDir(), name
)
221 if os
.path
.islink(link
):
229 dbname
= M
.get('dbname')
230 if DB
and dbname
and DB
.dbexists(dbname
):
233 def generateInstanceName(self
, version
, integration
=False, suffix
='', identifier
=None):
234 """Creates a name (identifier) from arguments"""
236 if identifier
!= None:
237 # If an identifier is passed, we use it regardless of the other parameters.
239 name
= identifier
.replace(' ', '_')
242 if version
== 'master':
243 prefixVersion
= C
.get('wording.prefixMaster')
245 prefixVersion
= version
249 name
= C
.get('wording.prefixIntegration') + prefixVersion
251 name
= C
.get('wording.prefixStable') + prefixVersion
254 if suffix
!= None and suffix
!= '':
255 name
+= C
.get('wording.suffixSeparator') + suffix
260 """Returns an instance defined by its name, or by path"""
261 # Extracts name from path
263 path
= os
.path
.abspath(os
.path
.realpath(name
))
264 if not path
.startswith(self
.path
):
265 raise Exception('Could not find Moodle instance at %s' % name
)
266 (head
, name
) = os
.path
.split(path
)
268 if not self
.isMoodle(name
):
269 raise Exception('Could not find Moodle instance %s' % name
)
270 return moodle
.Moodle(os
.path
.join(self
.path
, name
, self
.wwwDir
), identifier
=name
)
272 def getCachedRemote(self
, integration
=False):
273 """Return the path to the cached remote"""
275 return os
.path
.join(self
.cache
, 'integration.git')
277 return os
.path
.join(self
.cache
, 'moodle.git')
279 def getExtraDir(self
, name
, subdir
=None):
280 """Return the path to the extra directory of an instance
282 This also creates the directory if does not exist.
284 path
= self
.getPath(name
, 'extra')
286 path
= os
.path
.join(path
, subdir
)
287 if not os
.path
.exists(path
):
291 def getMdkWebDir(self
):
292 """Return (and create) the special MDK web directory."""
293 mdkExtra
= os
.path
.join(self
.www
, self
.mdkDir
)
294 if not os
.path
.exists(mdkExtra
):
295 mkdir(mdkExtra
, 0777)
299 def getPath(self
, name
, mode
=None):
300 """Returns the path of an instance base on its name"""
301 base
= os
.path
.join(self
.path
, name
)
303 return os
.path
.join(base
, self
.wwwDir
)
305 return os
.path
.join(base
, self
.dataDir
)
306 elif mode
== 'extra':
307 return os
.path
.join(base
, self
.extraDir
)
311 def getUrl(self
, name
, extra
=None):
312 """Return the URL to an instance, or to its extra directory if extra is passed"""
313 base
= '%s://%s' %
(C
.get('scheme'), C
.get('host'))
315 if C
.get('path') != '' and C
.get('path') != None:
316 base
= '%s/%s' %
(base
, C
.get('path'))
320 wwwroot
= '%s/%s' %
(base
, name
)
322 wwwroot
= '%s/%s/%s/%s' %
(base
, self
.mdkDir
, name
, extra
)
326 def isMoodle(self
, name
):
327 """Checks whether a Moodle instance exist under this name"""
328 d
= os
.path
.join(self
.path
, name
)
329 if not os
.path
.isdir(d
):
332 wwwDir
= os
.path
.join(d
, self
.wwwDir
)
333 dataDir
= os
.path
.join(d
, self
.dataDir
)
334 if not os
.path
.isdir(wwwDir
) or not os
.path
.isdir(dataDir
):
337 if not moodle
.Moodle
.isInstance(wwwDir
):
342 def list(self
, integration
=None, stable
=None):
343 """Return the list of Moodle instances"""
344 dirs
= os
.listdir(self
.path
)
347 if d
== '.' or d
== '..': continue
348 if not os
.path
.isdir(os
.path
.join(self
.path
, d
)): continue
349 if not self
.isMoodle(d
): continue
350 if integration
!= None or stable
!= None:
352 if not integration
and M
.isIntegration(): continue
353 if not stable
and M
.isStable(): continue
357 def resolve(self
, name
=None, path
=None):
358 """Try to find a Moodle instance based on its name, a path or the working directory"""
360 # A name was passed, is that a valid instance?
362 if self
.isMoodle(name
):
363 return self
.get(name
)
366 # If a path was not passed, let's use the current working directory.
369 path
= os
.path
.realpath(os
.path
.abspath(path
))
371 # Is this path in a Moodle instance?
372 if path
.startswith(self
.path
):
374 # Get the relative path identifier/some/other/path
375 relative
= os
.path
.relpath(path
, self
.path
)
377 # Isolating the identifier, it should be the first directory
378 (head
, tail
) = os
.path
.split(relative
)
380 (head
, tail
) = os
.path
.split(head
)
382 if self
.isMoodle(tail
):
383 return self
.get(tail
)
387 def resolveMultiple(self
, names
=[]):
388 """Return multiple instances"""
389 if type(names
) != list:
390 if type(names
) == str:
393 raise Exception('Unexpected variable type')
395 # Nothing has been passed, we use resolve()
403 # Try to resolve each instance
406 M
= self
.resolve(name
=name
)
410 logging
.info('Could not find instance called %s' % name
)
413 def updateCachedClones(self
, integration
=True, stable
=True, verbose
=True):
414 """Update the cached clone of the repositories"""
419 caches
.append(os
.path
.join(self
.cache
, 'integration.git'))
421 caches
.append(os
.path
.join(self
.cache
, 'moodle.git'))
424 if not os
.path
.isdir(cache
):
427 repo
= git
.Git(cache
, C
.get('git'))
430 logging
.info('Fetching cached repository %s...', os
.path
.basename(cache
))
432 logging
.debug('Fetching cached repository %s...', os
.path
.basename(cache
))
434 raise Exception('Could not fetch in repository %s' %
(cache
))