Mercurial > hgweb.cgi > hwm
changeset 26:795d652ed4f3
Create an ignore file upon repository creation. IN: -
author | John Schneiderman <JohnMS@CodeGNU.com> |
---|---|
date | Thu, 31 Jul 2014 20:40:48 -0500 |
parents | 5aaaa084fabd |
children | 1b4a20ee4b42 |
files | data/ignores.xml doc/ChangeLog doc/TODO setup.py src/__init__.py src/_app_info.py src/config.py src/enum.py src/hwm.py src/ignorepo.py src/manager.py src/manrepo.py |
diffstat | 12 files changed, 529 insertions(+), 49 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/ignores.xml Thu Jul 31 20:40:48 2014 -0500 @@ -0,0 +1,195 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!--**************************************************************************** +*** This file is part of HgWeb Manager. *** +*** *** +*** Copyright (C) 2014 *** +*** CodeGNU Solutions <Licensing _AT_ CodeGNU _DOT_ com> *** +*** *** +*** This program is free software: you can redistribute it and/or modify it *** +*** under the terms of the GNU Affero 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 Affero General Public License for more details. *** +*** *** +*** You should have received a copy of the GNU Affero General Public License*** +*** along with this program. If not, see <http://www.gnu.org/licenses/>. *** +*****************************************************************************--> +<ignores> + <group name="Common"> + <pattern description="Previous Saves" type="regex"> + .*(~|\.bak) + </pattern> + <pattern description="Temporaries" type="regex"> + (.*\.(tmp|swp)|tmp) + </pattern> + </group> + <group name="Python"> + <pattern description="Compiled Scripts" type="regex"> + .*\.py(o|c) + </pattern> + <pattern description="Eggs" type="regex"> + .*(\.egg(-info)?)|(develop-)?eggs + </pattern> + <pattern description="Distribution" type="glob"> + [s]dist + </pattern> + <pattern description="Compile" type="glob"> + {build,parts} + </pattern> + <pattern description="Libraries" type="glob"> + {bin,var} + </pattern> + <pattern description="Installs" type="glob"> + {.installed.cfg,pip-log.txt} + </pattern> + <pattern description="Unit Testing" type="glob"> + {.coverage,.tox} + </pattern> + <pattern description="Translations" type="glob"> + *.mo + </pattern> + <pattern description="Mr Developer" type="glob"> + .mr.developer.cfg + </pattern> + <pattern description="Eclipse Project" type="glob"> + *.pydevproject + </pattern> + </group> + <group name="Eclipse"> + <pattern description="Project Information" type="regex"> + \.(project|classpath|settings|loadpath|metadata) + </pattern> + <pattern description="Compile" type="glob"> + {bin,target} + </pattern> + <pattern description="Nibs" type="glob"> + *~.nib + </pattern> + <pattern description="Properties" type="glob"> + {local.properties,*.launch} + </pattern> + <pattern description="External tool builders" type="glob"> + .externalToolBuilders + </pattern> + <pattern description="CDT" type="glob"> + .cproject + </pattern> + <pattern description="PDT" type="glob"> + .buildpath + </pattern> + </group> + <group name="Xcode"> + <pattern description="Compile" type="glob"> + build + </pattern> + <pattern description="PBX User" type="glob"> + *.pbxuser + </pattern> + <pattern description="Modes" type="regex"> + .*\.mode(1|2)v3 + </pattern> + <pattern description="Perspective" type="glob"> + *.perspectivev3 + </pattern> + <pattern description="Workspace" type="glob"> + *.xcworkspace + </pattern> + <pattern description="Settings" type="glob"> + {xcuserdata,profile} + </pattern> + <pattern type="glob"> + *.moved-aside + </pattern> + </group> + <group name="Visual Studio"> + <pattern description="User-specific files" type="glob"> + *.{suo,user,sln.docstates,publishsettings} + </pattern> + <pattern description="Installation Directories" type="glob"> + {[Dd]ebug,[Rr]elease,[Pp]ublish,} + </pattern> + <pattern description="Installation Files" type="glob"> + *.{[Pp]ublish.xml,pubxml} + </pattern> + <pattern description="Compile Directories" type="glob"> + {[Bb]in,[Oo]bj,TestResults,ClientBin,x64,.builds,build} + </pattern> + <pattern description="Build files" type="glob"> + *.{ilk,meta,obj,pch,pdb,pgc,pgd,rsp,sbr,tlb,tli,tlh,dotCover,ipch,Cache,dbmdl,tmp_proj,log,pidb} + </pattern> + <pattern description="Generated files" type="glob"> + *_{i,p}.c + </pattern> + <pattern description="Source Control Files" type="glob"> + {*.vs[sp]scc,hgtfs.xml,*.scc} + </pattern> + <pattern description="Profiler files" type="glob"> + *.{psess,vsp,vspx} + </pattern> + <pattern description="NuGet" type="glob"> + packages + </pattern> + <pattern description="Style cop" type="glob"> + [Ss]tyle[Cc]op.* + </pattern> + <pattern description="Visual C++ cache files" type="glob"> + *.{aps,ncb,opensdf,sdf,cachefile} + </pattern> + <pattern description="Silverlight" type="glob"> + Generated_Code + </pattern> + <pattern description="Conversion" type="glob"> + {_UpgradeReport_Files,Backup*,UpgradeLog*.XML,UpgradeLog*.htm} + </pattern> + <pattern description="MSTest Result Files" type="glob"> + {[Tt]est[Rr]esult*,[Bb]uild[Ll]og.*} + </pattern> + <pattern description="SQL Server " type="glob"> + {App_Data/*.[lm]df,sql} + </pattern> + <pattern description="Guidance Automation Toolkit" type="glob"> + *.gpState + </pattern> + <pattern description="Windows Azure Build " type="glob"> + {csx,*.build.csdef} + </pattern> + <pattern description="Windows Store " type="glob"> + AppPackages + </pattern> + <pattern description="ReSharper add-in" type="glob"> + {_ReSharper*,*.[Rr]e[Ss]harper} + </pattern> + <pattern description="Installshield add-in" type="glob"> + [Ee]xpress + </pattern> + <pattern description="DocProject add-in" type="regex"> + DocProject/(Help/(Html2|html|(.*\.(Hx[TC]|hh[ckp])))|buildhelp/.*) + </pattern> + <pattern description="TeamCity add-in" type="glob"> + _TeamCity* + </pattern> + <pattern description="NCrunch add-in" type="glob"> + {*.ncrunch*,.*crunch*.local.xml} + </pattern> + <pattern type="glob"> + {~$*,*.pfx} + </pattern> + </group> + <group name="Windows"> + <pattern description="Image File Caches" type="regex"> + (eh)?[Tt]humbs\.db + </pattern> + <pattern description="Directory Configurations" type="glob"> + {[Dd]esktop.ini,$RECYCLE.BIN} + </pattern> + </group> + <group name="OS X"> + <pattern type="glob"> + .DS_Store + </pattern> + </group> +</ignores>
--- a/doc/ChangeLog Thu Jul 31 19:30:38 2014 -0500 +++ b/doc/ChangeLog Thu Jul 31 20:40:48 2014 -0500 @@ -20,6 +20,7 @@ 0000-00-00 John Schneiderman <Licensing _AT_ CodeGNU _DOT_ com> 0.3.0 - Unregistering repositories will now only do so on an exact match of the storage name. +- Generate default ignore file when creating a new managed repository. 2014-07-29 John Schneiderman <Licensing _AT_ CodeGNU _DOT_ com> 0.2.1 - Fixed issue where the configuration file copy started before it closed. - Fixed issue in Windows locking temporary file and preventing copying.
--- a/doc/TODO Thu Jul 31 19:30:38 2014 -0500 +++ b/doc/TODO Thu Jul 31 20:40:48 2014 -0500 @@ -27,7 +27,6 @@ *** *** *** Feature Goals *** *** *** -- Generate default ignore file using XML. - Allow selection of multiple ignore groups during creation. - Allow plug-ins addition and removal from repositories. - Installation tutorial. @@ -41,3 +40,4 @@ - Manager should prevent two repositories from using the same display name. - Manager should prevent two administrators from editing the same repository. - Manager should list all managed repositories like Display_Name(Storage_Name). +- Change functions to use exceptions for error handling, except for the command-line module.
--- a/setup.py Thu Jul 31 19:30:38 2014 -0500 +++ b/setup.py Thu Jul 31 20:40:48 2014 -0500 @@ -24,54 +24,60 @@ import sys sys.path.append('./src') -from hwm import YEARS,VERSION,DESCRIPTION,LONG_DESCRIPTION,SHORT_NAME +from _app_info import YEARS,VERSION,DESCRIPTION,LONG_DESCRIPTION,SHORT_NAME -required= \ - [ - 'mercurial', - 'mercurial.hg', - 'mercurial.ui', - 'mercurial.error', - 'mercurial.hgweb' - ] +required = [ + 'mercurial', + 'mercurial.hg', + 'mercurial.ui', + 'mercurial.error', + 'mercurial.hgweb' +] # Basic package setup information setup( - name=SHORT_NAME, - version=VERSION, - description=DESCRIPTION, - long_description=LONG_DESCRIPTION, - author='CodeGNU Solutions', - author_email='Licensing@CodeGNU.com', - url='http://www.codegnu.com', - download_url="http://www.codegnu.com/files/hwm-{0}.tar.gz".format(VERSION), - license='AGPLv3+', - packages=['HgWebManager'], + name = SHORT_NAME, + version = VERSION, + description = DESCRIPTION, + long_description = LONG_DESCRIPTION, + author = 'CodeGNU Solutions', + author_email = 'Licensing@CodeGNU.com', + url = 'http://www.codegnu.com', + download_url = "http://www.codegnu.com/files/hwm-{0}.tar.gz".format(VERSION), + license = 'AGPLv3+', + packages = ['HgWebManager'], package_dir = {'HgWebManager': 'src'}, - provides=['HgWebManager'], - requires=required, - platforms= [ - 'GNU/Linux', - 'POSIX', - 'Mac OS', - 'Windows' - ], - classifiers=[ - 'Development Status :: 3 - Alpha', - 'Environment :: Console', - 'Environment :: Web Environment', - 'Intended Audience :: Developers', - 'Intended Audience :: System Administrators', - 'Intended Audience :: Information Technology', - 'License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)', - 'Natural Language :: English', - 'Operating System :: MacOS', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX :: Linux', - 'Programming Language :: Python :: 2.7', - 'Topic :: Internet :: WWW/HTTP :: Site Management', - 'Topic :: Software Development :: Version Control', - 'Topic :: System :: Systems Administration' - ], - data_files= None, + provides = ['HgWebManager'], + requires = required, + platforms = [ + 'GNU/Linux', + 'POSIX', + 'Mac OS', + 'Windows' + ], + classifiers = [ + 'Development Status :: 3 - Alpha', + 'Environment :: Console', + 'Environment :: Web Environment', + 'Intended Audience :: Developers', + 'Intended Audience :: System Administrators', + 'Intended Audience :: Information Technology', + 'License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)', + 'Natural Language :: English', + 'Operating System :: MacOS', + 'Operating System :: Microsoft :: Windows', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python :: 2.7', + 'Topic :: Internet :: WWW/HTTP :: Site Management', + 'Topic :: Software Development :: Version Control', + 'Topic :: System :: Systems Administration' + ], + data_files = [ + ( + "share/" + SHORT_NAME, + [ + 'data/ignores.xml' + ] + ), + ], )
--- a/src/__init__.py Thu Jul 31 19:30:38 2014 -0500 +++ b/src/__init__.py Thu Jul 31 20:40:48 2014 -0500 @@ -24,7 +24,9 @@ __all__ = [ '_app_info.py' , 'config.py' + , 'enum.py' , 'hwm.py' + , 'ignorepo.py' , 'manager.py' , 'manrepo.py' ]
--- a/src/_app_info.py Thu Jul 31 19:30:38 2014 -0500 +++ b/src/_app_info.py Thu Jul 31 20:40:48 2014 -0500 @@ -21,13 +21,18 @@ # The shortened name of the application SHORT_NAME='HWM' + # The full name of the application LONG_NAME='HgWeb Manager' + # The current version of the application VERSION = '0.3.0' + # The current copyright years. YEARS = '2014' + # A short description of the application DESCRIPTION = 'Manages HgWeb Repositories' + # A long description of the application LONG_DESCRIPTION = 'An application to simplify the management of repositories shared through the Mercurial web publishing method.'
--- a/src/config.py Thu Jul 31 19:30:38 2014 -0500 +++ b/src/config.py Thu Jul 31 20:40:48 2014 -0500 @@ -21,6 +21,7 @@ class Manager(object): """ Manages the configurations for HgWebManager """ + # The configuration file parser. __parser = None # The absolute directory path and file name of the hg command.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/enum.py Thu Jul 31 20:40:48 2014 -0500 @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +#******************************************************************************* +#** This file is part of HgWeb Manager. *** +#** *** +#** Copyright (C) 2014 *** +#** CodeGNU Solutions <Licensing _AT_ CodeGNU _DOT_ com> *** +#** *** +#** This program is free software: you can redistribute it and/or modify it *** +#** under the terms of the GNU Affero 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 Affero General Public License for more details. *** +#** *** +#** You should have received a copy of the GNU Affero General Public License*** +#** along with this program. If not, see <http://www.gnu.org/licenses/>. *** +#******************************************************************************* + +def create(*sequential, **named): + """ Creates a type which mimics the behaviour of an enumeration. + + @param[in] sequential + @param[in] named + + @return An object which contains the supplied enumerated values. + """ + enums = dict(zip(sequential, range(len(sequential))), **named) + reverse = dict((value, key) for key, value in enums.iteritems()) + enums['reverse_mapping'] = reverse + return type('Enum', (), enums)
--- a/src/hwm.py Thu Jul 31 19:30:38 2014 -0500 +++ b/src/hwm.py Thu Jul 31 20:40:48 2014 -0500 @@ -27,17 +27,18 @@ from _app_info import * -# Configuration values for HgWebManager. try: + # Configuration values for HgWebManager. settings = config.Manager() except Exception as e: import sys + print >>sys.stderr, 'Settings Error:', e exit(1) def is_hgWeb_user(): - """Determines if the current running user is the expected HgWeb user. + """ Determines if the current running user is the expected HgWeb user. @return Gives true when the user is the expected HgWeb user, false else-wise. @@ -52,7 +53,7 @@ def __extract_values(args): - """Pulls the creation arguments out of the command-line. + """ Pulls the creation arguments out of the command-line. @param[in] args The command-line argument processor containing the creation argument values. @@ -78,6 +79,7 @@ def main(args): import argparse import manager + import sys if not is_hgWeb_user(): print >>sys.stderr, "Must execute as the Mercurial Web manager."
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ignorepo.py Thu Jul 31 20:40:48 2014 -0500 @@ -0,0 +1,126 @@ +# -*- coding: utf-8 -*- +#******************************************************************************* +#** This file is part of HgWeb Manager. *** +#** *** +#** Copyright (C) 2014 *** +#** CodeGNU Solutions <Licensing _AT_ CodeGNU _DOT_ com> *** +#** *** +#** This program is free software: you can redistribute it and/or modify it *** +#** under the terms of the GNU Affero 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 Affero General Public License for more details. *** +#** *** +#** You should have received a copy of the GNU Affero General Public License*** +#** along with this program. If not, see <http://www.gnu.org/licenses/>. *** +#******************************************************************************* + +""" + IMPORTS +""" +import enum + + +# The types of filtering available for an ignore file. +FilterType = enum.create('Glob', 'RegularExpression') + +def ignore_syntax_name(filltertype): + """ Provides the mapping between an enumerated filter type and the + Mercurial ignore section header. If an invalid argument is supplied, an + empty string is given. + + @param[in] filltertype Is the enumerated value to map. + + @return The string value of the supplied enumerated value. + """ + if FilterType.Glob == filltertype: + return 'glob' + elif FilterType.RegularExpression == filltertype: + return 'regexp' + else: + return '' + + +class Pattern(object): + """ Describes the properties of an entry in a Mercurial ignore file. """ + + # A short entry describing the purpose of the ignore entry. + __description = None + # The type of ignore entry used by Mercurial. + __filter = None + # The ignore expression to provide for an ignore file. + __value = None + + @property + def Description(self): + """ A short entry describing the purpose of the ignore entry when + provided, else gives miscellaneous. + """ + if self.__description: + return self.__description + else: + return 'Miscellaneous' + + @property + def Filter(self): + """ The type of ignore entry used by Mercurial. """ + return self.__filter + + @property + def Value(self): + """ The ignore expression to provide for an ignore file. """ + return self.__value + + def __init__(self, desc, fil, val): + """ Initialises the object with the values supplied. + + @param[in] desc The description of the ignore entry. + @param[in] fil The type of ignore filtering. + @param[in] val The ignore expression. + """ + self.__description = desc.strip() + if fil == 'glob': + self.__filter = FilterType.Glob + elif fil == 'regex': + self.__filter = FilterType.RegularExpression + else: + raise Exception("Invalid filter type '%s' supplied" % fil) + self.__value = val.strip() + +def extract(ignoreFile): + """ Parses an XML file for ignore file patterns. Generates a dictionary + object whose key is the name of the group for a collection of ignore + patterns. The value in each dictionary is an array of all the ignore + pattern objects within that group. Upon any parsing error, no ignore + patterns are supplied. + + @param[in] ignoreFile The directory path and file name to an XML file + that contains the ignore patterns to extract. + + @throw Exception When the name of a group is reused + in the XML file. + @throw xml.parsers.expat.ExpatError When the XML file cannot be parsed. + + @return The extracted grouped ignore patterns. + """ + from xml.dom import minidom + + ignores = {} + xmlIgnore = minidom.parse(ignoreFile) + for group in xmlIgnore.getElementsByTagName('ignores')[0].getElementsByTagName('group'): + groupName = group.getAttribute('name') + if groupName in ignores: + raise Exception("The group '%s' was already defined." % groupName) + else: + ignores[groupName] = [] + for pattern in group.getElementsByTagName('pattern'): + ignores[groupName].append(Pattern( \ + pattern.getAttribute('description'), \ + pattern.getAttribute('type'), \ + pattern.firstChild.nodeValue)) + return ignores +
--- a/src/manager.py Thu Jul 31 19:30:38 2014 -0500 +++ b/src/manager.py Thu Jul 31 20:40:48 2014 -0500 @@ -50,6 +50,8 @@ # Create the requested repository. if __addRepository(repository): print "Initialised repository directory." + if not create_ignores(repository): + return False else: print >>sys.stderr, "Failed to create repository directory." return False @@ -191,12 +193,115 @@ print >>sys.stderr, "Failed to delete the repository." return False +def create_ignores(repository): + """ Generates and commits an ignore file to an existing repository. + + @pre The repository to create an ignore file must already exist. The list + of ignore patterns to use is expected to be in an XML file named + "ignores.xml" and be in the hwm-ignore schema. + + @param[in] repository The targeted repository for the ignore file. + + @post The managed repository now has an ignore file committed by the + HgWeb manager. + + @return When the creation is successful gives true, else-wise false. + """ + import os + import ignorepo + import sys + + # Extract Ignore Collection + ignores = {} + try: + ignores = ignorepo.extract('ignores.xml') + except xml.parsers.expat.ExpatError as e: + print >>sys.stderr, "Failed to extract ignores, error: %s" % e.message + return False + + # Write Ignore File + ignoreFile = settings.RepositoryPath + os.sep + repository.StorageName + os.sep + '.hgignore' + with open(ignoreFile, 'w') as hgIgnore: + for group,patterns in ignores.items(): + hgIgnore.write("#\n#\tIgnore Group: %s\n#" % group) + # Reverse the sorting order to put the regular expressions first. + patterns.sort(key = lambda p: p.Filter, reverse = True) + filteringOn = None + for pattern in patterns: + if pattern.Filter == filteringOn: + hgIgnore.write("\n%s\t\t# %s" % (pattern.Value, pattern.Description)) + else: + hgIgnore.write("\nsyntax: %s\n%s\t\t# %s" % (ignorepo.ignore_syntax_name(pattern.Filter), pattern.Value, pattern.Description)) + filteringOn = pattern.Filter + hgIgnore.write('\n\n') + + if __addFile(repository, '.hgignore'): + print "Successfully added generated ignores." + else: + print >>sys.stderr, "Failed to add generated ignores." + return False + + if __commitChanges(repository, 'Created repository and set-up ignores.'): + print "Successfully committed generated ignores." + else: + print >>sys.stderr, "Failed to commit generated ignores." + return False + return True + #--- Functions for fulfilling repository management. +def __commitChanges(repo, message): + """ Commits all changes in a managed Mercurial repository as the HgWeb + manager. + + @pre The requested repository must already exist. + + @param[in] repo The targeted repository for which to commit. + @param[in] message The text to use as a commit message. + + @post Upon any error the standard error buffer contains an error message. + + @return When the process is successful gives true, else-wise false. + """ + import subprocess + import sys + import os + + try: + statusCode = subprocess.call([settings.HgCommand, '--repository', settings.RepositoryPath + os.sep + repo.StorageName, 'commit', '--message', message, '--user', settings.HgUser + ' - HgWeb Manager']) + except OSError as e: + print >>sys.stderr, "Commit Error({0}): {1}".format(e.errno, e.strerror) + return False + return (0 == statusCode) + +def __addFile(repo, fileName): + """ Marks a file as added in a managed Mercurial repository. + + @pre The requested repository must already exist. + + @param[in] repo The targeted repository for which to add the file. + @param[in] fileName The directory path and the file name to add + relative to the base of the repository. + + @post Upon any error the standard error buffer contains an error message. + + @return When the process is successful gives true, else-wise false. + """ + import subprocess + import sys + import os + + try: + statusCode = subprocess.call([settings.HgCommand, '--repository', settings.RepositoryPath + os.sep + repo.StorageName, 'add', settings.RepositoryPath + os.sep + repo.StorageName + os.sep + fileName]) + except OSError as e: + print >>sys.stderr, "Add Error({0}): {1}".format(e.errno, e.strerror) + return False + return (0 == statusCode) + def __renameStorage(oldRepo, newName): """ Changes the storage name of an existing repository.
--- a/src/manrepo.py Thu Jul 31 19:30:38 2014 -0500 +++ b/src/manrepo.py Thu Jul 31 20:40:48 2014 -0500 @@ -26,6 +26,10 @@ class Repository(object): + """ Describes the properties of a repository managed by HWM and served up + by HgWeb. + """ + # The directory name of the managed repository. __storageName = None # The displayed web-site name of the managed repository.