Added a script for automating the creation of a Plone3 instance.
This commit is contained in:
		
							parent
							
								
									9cff9df299
								
							
						
					
					
						commit
						47334b885f
					
				
					 3 changed files with 254 additions and 12 deletions
				
			
		
							
								
								
									
										14
									
								
								bin/clean.py
									
										
									
									
									
								
							
							
						
						
									
										14
									
								
								bin/clean.py
									
										
									
									
									
								
							|  | @ -1,22 +1,12 @@ | ||||||
| # Imports ---------------------------------------------------------------------- | # Imports ---------------------------------------------------------------------- | ||||||
| import os, os.path | import os, os.path | ||||||
| from appy.shared import appyPath | from appy.shared import appyPath | ||||||
| from appy.shared.utils import FolderDeleter | from appy.shared.utils import FolderDeleter, cleanFolder | ||||||
| 
 | 
 | ||||||
| # ------------------------------------------------------------------------------ | # ------------------------------------------------------------------------------ | ||||||
| class Cleaner: | class Cleaner: | ||||||
|     exts = ('.pyc', '.class') |  | ||||||
|     def run(self, verbose=True): |     def run(self, verbose=True): | ||||||
|         print 'Cleaning folder', appyPath, '...' |         cleanFolder(appyPath, verbose=True) | ||||||
|         # Remove files with an extension listed in self.exts |  | ||||||
|         for root, dirs, files in os.walk(appyPath): |  | ||||||
|             for fileName in files: |  | ||||||
|                 ext = os.path.splitext(fileName)[1] |  | ||||||
|                 if (ext in Cleaner.exts) or ext.endswith('~'): |  | ||||||
|                     fileToRemove = os.path.join(root, fileName) |  | ||||||
|                     if verbose: |  | ||||||
|                         print 'Removing %s...' % fileToRemove |  | ||||||
|                     os.remove(fileToRemove) |  | ||||||
|         # Remove all files in temp folders |         # Remove all files in temp folders | ||||||
|         for tempFolder in ('%s/temp' % appyPath, |         for tempFolder in ('%s/temp' % appyPath, | ||||||
|                            '%s/pod/test/temp' % appyPath): |                            '%s/pod/test/temp' % appyPath): | ||||||
|  |  | ||||||
							
								
								
									
										237
									
								
								bin/new.py
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										237
									
								
								bin/new.py
									
										
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,237 @@ | ||||||
|  | #!/usr/bin/python2.4.4 | ||||||
|  | '''This script allows to create a brand new read-to-use Plone/Zone instance. | ||||||
|  |    As prerequisite, you must have installed Plone through the Unifier installer | ||||||
|  |    available at http://plone.org.''' | ||||||
|  | 
 | ||||||
|  | # ------------------------------------------------------------------------------ | ||||||
|  | import os, os.path, sys, shutil | ||||||
|  | from optparse import OptionParser | ||||||
|  | from appy.shared.utils import cleanFolder | ||||||
|  | 
 | ||||||
|  | # ------------------------------------------------------------------------------ | ||||||
|  | class NewError(Exception): pass | ||||||
|  | ERROR_CODE = 1 | ||||||
|  | WRONG_NB_OF_ARGS = 'Wrong number of args.' | ||||||
|  | WRONG_PLONE_VERSION = 'Plone version must be among %s.' | ||||||
|  | WRONG_PLONE_PATH = 'Path "%s" is not an existing folder.' | ||||||
|  | PYTHON_NOT_FOUND = 'Python interpreter was not found in "%s". Are you sure ' \ | ||||||
|  |     'we are in the folder hierarchy created by the Plone installer?' | ||||||
|  | PYTHON_EXE_NOT_FOUND = '"%s" does not exist.' | ||||||
|  | MKZOPE_NOT_FOUND = 'Script mkzopeinstance.py not found in "%s and ' \ | ||||||
|  |     'subfolders. Are you sure we are in the folder hierarchy created by ' \ | ||||||
|  |     'the Plone installer?' | ||||||
|  | WRONG_INSTANCE_PATH = '"%s" must be an existing folder for creating the ' \ | ||||||
|  |     'instance in it.' | ||||||
|  | 
 | ||||||
|  | # ------------------------------------------------------------------------------ | ||||||
|  | class NewScript: | ||||||
|  |     '''usage: %prog ploneVersion plonePath instancePath | ||||||
|  | 
 | ||||||
|  |        "ploneVersion"  can be plone25 or plone3 | ||||||
|  |         | ||||||
|  |        "plonePath"     is the (absolute) path to you plone installation. | ||||||
|  |                        Plone 2.5 is typically installed in /opt/Plone-2.5.5, | ||||||
|  |                        while Plone 3 is typically installed in /usr/local/Plone. | ||||||
|  |        "instancePath"  is the (absolute) path where you want to create your | ||||||
|  |                        instance (should not already exist).''' | ||||||
|  |     ploneVersions = ('plone25', 'plone3') | ||||||
|  | 
 | ||||||
|  |     def createInstance(self): | ||||||
|  |         '''Calls the Zope script that allows to create a Zope instance and copy | ||||||
|  |            into it all the Plone packages and products.''' | ||||||
|  |         # Find the Python interpreter running Zope | ||||||
|  |         for elem in os.listdir(self.plonePath): | ||||||
|  |             pythonPath = None | ||||||
|  |             elemPath = os.path.join(self.plonePath, elem) | ||||||
|  |             if elem.startswith('Python-') and os.path.isdir(elemPath): | ||||||
|  |                 pythonPath = elemPath + '/bin/python' | ||||||
|  |                 if not os.path.exists(pythonPath): | ||||||
|  |                     raise NewError(PYTHON_EXE_NOT_FOUND % pythonPath) | ||||||
|  |                 break | ||||||
|  |         if not pythonPath: | ||||||
|  |             raise NewError(PYTHON_NOT_FOUND % self.plonePath) | ||||||
|  |         # Find the Zope script mkzopeinstance.py | ||||||
|  |         makeInstancePath = None | ||||||
|  |         for dirname, dirs, files in os.walk(self.plonePath): | ||||||
|  |             # Do not browse the buildout-cache | ||||||
|  |             for fileName in files: | ||||||
|  |                 if (fileName == 'mkzopeinstance.py') and \ | ||||||
|  |                    ('/buildout-cache/' not in dirname): | ||||||
|  |                     makeInstancePath = os.path.join(dirname, fileName) | ||||||
|  |         if not makeInstancePath: | ||||||
|  |             raise NewError(MKZOPE_NOT_FOUND % self.plonePath) | ||||||
|  |         # Execute mkzopeinstance.py with the right Python interpreter | ||||||
|  |         cmd = '%s %s -d %s' % (pythonPath, makeInstancePath, self.instancePath) | ||||||
|  |         print cmd | ||||||
|  |         os.system(cmd) | ||||||
|  |         # Now, make the instance Plone-ready | ||||||
|  |         productsFolder = os.path.join(self.instancePath, 'Products') | ||||||
|  |         libFolder = os.path.join(self.instancePath, 'lib/python') | ||||||
|  |         print 'Copying Plone stuff in the Zope instance...' | ||||||
|  |         if self.ploneVersion == 'plone25': | ||||||
|  |             self.installPlone25Stuff(productsFolder, libFolder) | ||||||
|  |         elif self.ploneVersion == 'plone3': | ||||||
|  |             self.installPlone3Stuff(productsFolder, libFolder) | ||||||
|  |         # Clean the copied folders | ||||||
|  |         cleanFolder(productsFolder) | ||||||
|  |         cleanFolder(libFolder) | ||||||
|  | 
 | ||||||
|  |     def installPlone25Stuff(self, productsFolder, libFolder): | ||||||
|  |         '''Here, we will copy all Plone3-related stuff in the Zope instance | ||||||
|  |            we've created, to get a full Plone-ready Zope instance.''' | ||||||
|  |         pass | ||||||
|  | 
 | ||||||
|  |     uglyChunks = ('pkg_resources', '.declare_namespace(') | ||||||
|  |     wrongPackages = [os.sep.join(['plone', 'app', 'content']), | ||||||
|  |                      os.sep.join(['.egg', 'wicked']) | ||||||
|  |                     ] | ||||||
|  |     def findPythonPackageInEgg(self, currentFolder): | ||||||
|  |         '''Finds the Python package that is deeply hidden into the egg.''' | ||||||
|  |         # Find the file __init__.py | ||||||
|  |         isFinalPackage = False | ||||||
|  |         for elem in os.listdir(currentFolder): | ||||||
|  |             elemPath = os.path.join(currentFolder, elem) | ||||||
|  |             if elem == '__init__.py': | ||||||
|  |                 f = file(elemPath) | ||||||
|  |                 content = f.read() | ||||||
|  |                 f.close() | ||||||
|  |                 # Is it a awful egg init ? | ||||||
|  |                 for chunk in self.uglyChunks: | ||||||
|  |                     if content.find(chunk) == -1: | ||||||
|  |                         isFinalPackage = True | ||||||
|  |                         break | ||||||
|  |                 # Goddamned, there are exceptions. | ||||||
|  |                 for wrongPackage in self.wrongPackages: | ||||||
|  |                     if currentFolder.endswith(wrongPackage) and \ | ||||||
|  |                         not isFinalPackage: | ||||||
|  |                         isFinalPackage = True | ||||||
|  |                 break | ||||||
|  |         if not isFinalPackage: | ||||||
|  |             # Find the subfolder and find the Python package into it. | ||||||
|  |             for elem in os.listdir(currentFolder): | ||||||
|  |                 elemPath = os.path.join(currentFolder, elem) | ||||||
|  |                 if os.path.isdir(elemPath): | ||||||
|  |                     return self.findPythonPackageInEgg(elemPath) | ||||||
|  |         else: | ||||||
|  |             return currentFolder | ||||||
|  | 
 | ||||||
|  |     def getSubFolder(self, folder): | ||||||
|  |         '''In p_folder, we now that there is only one subfolder. This method | ||||||
|  |            returns the subfolder's absolute path.''' | ||||||
|  |         for elem in os.listdir(folder): | ||||||
|  |             elemPath = os.path.join(folder, elem) | ||||||
|  |             if (elem != 'EGG-INFO') and os.path.isdir(elemPath): | ||||||
|  |                 return elemPath | ||||||
|  |         return None | ||||||
|  | 
 | ||||||
|  |     def installPlone3Stuff(self, productsFolder, libFolder): | ||||||
|  |         '''Here, we will copy all Plone3-related stuff in the Zope instance | ||||||
|  |            we've created, to get a full Plone-ready Zope instance.''' | ||||||
|  |         # All Plone 3 eggs are in buildout-cache/eggs. We will extract from | ||||||
|  |         # those silly overstructured folder hierarchies the standard Python | ||||||
|  |         # packages that lie in it, and copy them in the instance. Within these | ||||||
|  |         # eggs, we need to distinguish: | ||||||
|  |         # - standard Python packages that will be copied in | ||||||
|  |         #   <zopeInstance>/lib/python (ie, like Appy applications) | ||||||
|  |         # - Zope products that will be copied in | ||||||
|  |         #   <zopeInstance>/Products (ie, like Appy generated Zope products) | ||||||
|  |         eggsFolder = os.path.join(self.plonePath, 'buildout-cache/eggs') | ||||||
|  |         for name in os.listdir(eggsFolder): | ||||||
|  |             eggMainFolder = os.path.join(eggsFolder, name) | ||||||
|  |             if name.startswith('Products.'): | ||||||
|  |                 # A Zope product. Copy its content in Products. | ||||||
|  |                 innerFolder= self.getSubFolder(self.getSubFolder(eggMainFolder)) | ||||||
|  |                 destFolder = os.path.join(productsFolder, | ||||||
|  |                                           os.path.basename(innerFolder)) | ||||||
|  |                 shutil.copytree(innerFolder, destFolder) | ||||||
|  |             else: | ||||||
|  |                 # A standard Python package. Copy its content in lib/python. | ||||||
|  |                 # Go into the subFolder that is not EGG-INFO. | ||||||
|  |                 eggFolder = self.getSubFolder(eggMainFolder) | ||||||
|  |                 if not eggFolder: | ||||||
|  |                     # This egg is malformed and contains basic Python files. | ||||||
|  |                     # Copy those files directly in libFolder. | ||||||
|  |                     for fileName in os.listdir(eggMainFolder): | ||||||
|  |                         if fileName.endswith('.py'): | ||||||
|  |                             fullFileName= os.path.join(eggMainFolder, fileName) | ||||||
|  |                             shutil.copy(fullFileName, libFolder) | ||||||
|  |                     continue | ||||||
|  |                 eggFolderName = os.path.basename(eggFolder) | ||||||
|  |                 if eggFolderName == 'Products': | ||||||
|  |                     # Goddamned. This should go in productsFolder and not in | ||||||
|  |                     # libFolder. | ||||||
|  |                     innerFolder = self.getSubFolder(eggFolder) | ||||||
|  |                     destFolder = os.path.join(productsFolder, | ||||||
|  |                                               os.path.basename(innerFolder)) | ||||||
|  |                     shutil.copytree(innerFolder, destFolder) | ||||||
|  |                 else: | ||||||
|  |                     packageFolder = self.findPythonPackageInEgg(eggFolder) | ||||||
|  |                     # Create the destination folder(s) in the instance, | ||||||
|  |                     # within libFolder | ||||||
|  |                     destFolders = [] | ||||||
|  |                     if packageFolder != eggFolder: | ||||||
|  |                         destFolders = [eggFolderName] | ||||||
|  |                         remFolders = packageFolder[len(eggFolder):] | ||||||
|  |                         remFolders = remFolders.strip(os.sep) | ||||||
|  |                         if remFolders.find(os.sep) != -1: | ||||||
|  |                             # There are more subfolders | ||||||
|  |                             destFolders += remFolders.split(os.sep)[:-1] | ||||||
|  |                     if destFolders: | ||||||
|  |                         # We must create the subfolders (if not yet created) | ||||||
|  |                         # before copying the Python package. | ||||||
|  |                         baseFolder = libFolder | ||||||
|  |                         for subFolder in destFolders: | ||||||
|  |                             subFolderPath=os.path.join(baseFolder,subFolder) | ||||||
|  |                             if not os.path.exists(subFolderPath): | ||||||
|  |                                 os.mkdir(subFolderPath) | ||||||
|  |                                 # Create an empty __init__.py in it. | ||||||
|  |                                 init = os.path.join(subFolderPath, | ||||||
|  |                                                     '__init__.py') | ||||||
|  |                                 f = file(init, 'w') | ||||||
|  |                                 f.write('#Makes me a Python package.') | ||||||
|  |                                 f.close() | ||||||
|  |                             baseFolder = subFolderPath | ||||||
|  |                         destFolder = os.sep.join(destFolders) | ||||||
|  |                         destFolder = os.path.join(libFolder, destFolder) | ||||||
|  |                         if not os.path.exists(destFolder): | ||||||
|  |                             os.makedirs(destFolder) | ||||||
|  |                     else: | ||||||
|  |                         destFolder = libFolder | ||||||
|  |                     destFolder = os.path.join( | ||||||
|  |                         destFolder, os.path.basename(packageFolder)) | ||||||
|  |                     shutil.copytree(packageFolder, destFolder) | ||||||
|  | 
 | ||||||
|  |     def manageArgs(self, args): | ||||||
|  |         '''Ensures that the script was call with the right parameters.''' | ||||||
|  |         if len(args) != 3: raise NewError(WRONG_NB_OF_ARGS) | ||||||
|  |         self.ploneVersion, self.plonePath, self.instancePath = args | ||||||
|  |         # Check Plone version | ||||||
|  |         if self.ploneVersion not in self.ploneVersions: | ||||||
|  |             raise NewError(WRONG_PLONE_VERSION % str(self.ploneVersions)) | ||||||
|  |         # Check Plone path | ||||||
|  |         if not os.path.exists(self.plonePath) \ | ||||||
|  |            or not os.path.isdir(self.plonePath): | ||||||
|  |             raise NewError(WRONG_PLONE_PATH % self.plonePath) | ||||||
|  |         # Check instance path | ||||||
|  |         parentFolder = os.path.dirname(self.instancePath) | ||||||
|  |         if not os.path.exists(parentFolder) or not os.path.isdir(parentFolder): | ||||||
|  |             raise NewError(WRONG_INSTANCE_PATH % parentFolder) | ||||||
|  | 
 | ||||||
|  |     def run(self): | ||||||
|  |         optParser = OptionParser(usage=NewScript.__doc__) | ||||||
|  |         (options, args) = optParser.parse_args() | ||||||
|  |         try: | ||||||
|  |             self.manageArgs(args) | ||||||
|  |             print 'Creating new %s instance...' % self.ploneVersion | ||||||
|  |             self.createInstance() | ||||||
|  |         except NewError, ne: | ||||||
|  |             optParser.print_help() | ||||||
|  |             print | ||||||
|  |             sys.stderr.write(str(ne)) | ||||||
|  |             sys.stderr.write('\n') | ||||||
|  |             sys.exit(ERROR_CODE) | ||||||
|  | 
 | ||||||
|  | # ------------------------------------------------------------------------------ | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     NewScript().run() | ||||||
|  | # ------------------------------------------------------------------------------ | ||||||
|  | @ -32,6 +32,21 @@ class FolderDeleter: | ||||||
|         os.rmdir(dirName) |         os.rmdir(dirName) | ||||||
|     delete = staticmethod(delete) |     delete = staticmethod(delete) | ||||||
| 
 | 
 | ||||||
|  | # ------------------------------------------------------------------------------ | ||||||
|  | extsToClean = ('.pyc', '.pyo') | ||||||
|  | def cleanFolder(folder, exts=extsToClean, verbose=False): | ||||||
|  |     '''This function allows to remove, in p_folder and subfolders, any file | ||||||
|  |        whose extension is in p_exts.''' | ||||||
|  |     if verbose: print 'Cleaning folder', folder, '...' | ||||||
|  |     # Remove files with an extension listed in exts | ||||||
|  |     for root, dirs, files in os.walk(folder): | ||||||
|  |         for fileName in files: | ||||||
|  |             ext = os.path.splitext(fileName)[1] | ||||||
|  |             if (ext in exts) or ext.endswith('~'): | ||||||
|  |                 fileToRemove = os.path.join(root, fileName) | ||||||
|  |                 if verbose: print 'Removing %s...' % fileToRemove | ||||||
|  |                 os.remove(fileToRemove) | ||||||
|  | 
 | ||||||
| # ------------------------------------------------------------------------------ | # ------------------------------------------------------------------------------ | ||||||
| class Traceback: | class Traceback: | ||||||
|     '''Dumps the last traceback into a string.''' |     '''Dumps the last traceback into a string.''' | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Gaetan Delannay
						Gaetan Delannay