# HG changeset patch # User John Schneiderman # Date 1406393505 18000 # Node ID d90ce12886927fe17b3bb519464915769f7e683d # Parent cd579b9ef6b56728aca40d0682f8c9861ddecb46 Modify an existing repository under the control of HgWeb. IN: - diff -r cd579b9ef6b5 -r d90ce1288692 doc/ChangeLog --- a/doc/ChangeLog Wed Jul 23 08:36:18 2014 -0500 +++ b/doc/ChangeLog Sat Jul 26 11:51:45 2014 -0500 @@ -17,7 +17,10 @@ *** You should have received a copy of the GNU Affero General Public License*** *** along with this program. If not, see . *** ******************************************************************************** +2014-07-26 John Schneiderman 0.2.0 +- Ability to modify an existing repository. +- Load a managed repository object from it's storage name. 2014-07-21 John Schneiderman 0.1.0 - Ability to create a new repository. -- Ability to delete a new repository. +- Ability to delete an existing repository. - Configuration settings read from an INI file. diff -r cd579b9ef6b5 -r d90ce1288692 doc/TODO --- a/doc/TODO Wed Jul 23 08:36:18 2014 -0500 +++ b/doc/TODO Sat Jul 26 11:51:45 2014 -0500 @@ -27,15 +27,12 @@ *** *** *** Feature Goals *** *** *** -- Create a new repository in Windows and GNU/Linux. - Ability to package HWM and deploy. -- Modify display information of an existing repository. +- Generate default ignore file using XML. - Allow selection of multiple ignore groups during creation. -- Remove an existing repository. - Allow plug-ins addition and removal from repositories. - Installation tutorial. - User tutorials. -- Generate default ignore file using XML. - Actually parse the hgweb.config file to work with it. - Manager functions should take both an output and error device instead of defaulting to std. - Manager should handle the HgWeb configuration file settings. diff -r cd579b9ef6b5 -r d90ce1288692 src/hwm.py --- a/src/hwm.py Wed Jul 23 08:36:18 2014 -0500 +++ b/src/hwm.py Sat Jul 26 11:51:45 2014 -0500 @@ -21,7 +21,7 @@ #******************************************************************************* # The current version of the application -VERSION = "0.1.0" +VERSION = "0.2.0" # The current copyright years. YEARS = "2014" @@ -64,13 +64,13 @@ managed.StorageName = args.repository if args.name: - managed.DisplayName = args.name[0] + managed.DisplayName = args.name if args.description: - managed.Description = args.description[0] + managed.Description = args.description if args.contact: - managed.Contact = args.contact[0] + managed.Contact = args.contact return managed def main(args): @@ -83,11 +83,12 @@ exit(1) parser = argparse.ArgumentParser(description='Manages HgWeb Repositories') - parser.add_argument("repository", action="store", help='The storage-name of the repository.') - parser.add_argument("-a", "--action", action="store", nargs=1, type=str, choices=["create","delete"], help='Performs an action upon a managed HgWeb repository.') - parser.add_argument("-n", "--name", action="store", nargs='?', default="", help='The display name of the repository.') - parser.add_argument("-d", "--description", action="store", nargs='?', default="", help='The description of the repository.') - parser.add_argument("-c", "--contact", action="store", nargs='?', default="", help='The contact point of a repository.') + parser.add_argument("repository", action="store", help='The storage-name of the repository to perform an action upon.') + parser.add_argument("-a", "--action", action="store", nargs=1, type=str, choices=['create','modify','delete'], help='Performs an action upon a managed HgWeb repository.') + parser.add_argument("-n", "--name", action="store", nargs='?', default=None, help='The display name of the repository.') + parser.add_argument("-d", "--description", action="store", nargs='?', default=None, help='The description of the repository.') + parser.add_argument("-c", "--contact", action="store", nargs='?', default=None, help='The contact point of a repository.') + parser.add_argument("-r", "--rename", action="store", nargs='?', default=None, help='The new storage-name for the repository.') args = parser.parse_args() repo = __extract_values(args) @@ -97,6 +98,14 @@ exit(0) else: exit(1) + elif 'modify' == args.action[0]: + currentStorageName = repo.StorageName + if args.rename: + repo.StorageName = args.rename + if manager.modify(repo, currentStorageName): + exit(0) + else: + exit(1) elif 'delete' == args.action[0]: if manager.delete(repo): exit(0) diff -r cd579b9ef6b5 -r d90ce1288692 src/manager.py --- a/src/manager.py Wed Jul 23 08:36:18 2014 -0500 +++ b/src/manager.py Sat Jul 26 11:51:45 2014 -0500 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python # -*- coding: utf-8 -*- #******************************************************************************* #** This file is part of HgWeb Manager. *** @@ -42,14 +42,20 @@ @return When the creation is successful gives true, else-wise false. """ import sys + import os + import shutil print "Generating repository: " + repository.StorageName + if __doesRepositoryExist(repository): + print >>sys.stderr, "The repository '%s' already exists." % repository.StorageName + return False + # Create the requested repository. status = __generateRepository(repository) if 0 == status: print "Created repository directory." else: - print >>sys.stderr, "Failed to create repository directory.", status + print >>sys.stderr, "Failed to create repository directory. OS Error Code:", status return False # Fill out the details of about the repository. @@ -57,16 +63,79 @@ print "Wrote repository details." else: print >>sys.stderr, "Failed to write repository details." + os.chmod(settings.RepositoryPath + os.sep + repository.StorageName + os.sep + ".hg" + os.sep + "hgrc", stat.S_IWRITE) + shutil.rmtree(settings.RepositoryPath + os.sep + repository.StorageName) return False - # Add the new repository to the web register. + # Add the new repository to the web registry. if __registerRepository(repository): print "Registered repository with HgWeb." return True else: print >>sys.stderr, "Failed to register repository with HgWeb." + os.chmod(settings.RepositoryPath + os.sep + repository.StorageName + os.sep + ".hg" + os.sep + "hgrc", stat.S_IWRITE) + shutil.rmtree(settings.RepositoryPath + os.sep + repository.StorageName) return False +def modify(newRepository, currentStorageName): + """ Changes an existing repository in HgWeb. + + @pre The repository to modify must already exist. + + @param[in] newRepository The new values for the repository to + modify. + @param[in] currentStorageName The current storage name of the + repository to modify. + + @post The repository is changed with the requested modifications. + + @return When the modification is successful gives true, else-wise false. + """ + import sys + + print "Modifying repository: " + currentStorageName + oldRepository = manrepo.Repository(currentStorageName) + + if not __doesRepositoryExist(oldRepository): + print >>sys.stderr, "The repository '%s' is not valid." % oldRepository.StorageName + return False + + # Remove the current repository from the web registry. + if not __unregisterRepository(oldRepository): + print >>sys.stderr, "Failed to unregister repository from HgWeb." + return False + + # Update renamed repositories + if not oldRepository == newRepository: + if __renameStorage(oldRepository, newRepository.StorageName): + print "Renamed repository %s to %s." % (currentStorageName, newRepository.StorageName) + else: + print >>sys.stderr, "Failed to rename repository." + if __registerRepository(oldRepository): + print "Successfully rolled backed action." + else: + print >>sys.stderr, "Failed to roll back action." + return False + + # Fill out the details of about the new repository. + if __writeRepositoryDetails(newRepository): + print "Wrote repository new details." + else: + print >>sys.stderr, "Failed to write repository new details." + if __registerRepository(oldRepository): + print "Successfully rolled backed action." + else: + print >>sys.stderr, "Failed to roll back action." + return False + + # Add the new repository to the web registry. + if __registerRepository(newRepository): + print "Registered %s to HgWeb." % newRepository.StorageName + else: + print >>sys.stderr, "Failed to register repository from HgWeb." + return False + return True + def delete(repository): """ Removes an existing repository from HgWeb. @@ -80,6 +149,8 @@ @return When the deletion is successful gives true, else-wise false. """ + import sys + if not __doesRepositoryExist(repository): print >>sys.stderr, "The repository %s is not valid." % repository.StorageName return False @@ -98,6 +169,39 @@ print >>sys.stderr, "Failed to delete the repository." return False + + +#--- Functions for fulfilling repository management. + + + +def __renameStorage(oldRepo, newName): + """ Changes the storage name of an existing repository. + + @param[in] oldRepo The existing repository to whose storage name is + expected to be changed. + @param[in] newName The new storage name for the repository. + + @return Gives true when the storage name is successfully renamed, + else-wise gives false. + """ + import os + import sys + import shutil + + try: + if oldRepo.StorageName == newName: + return True + elif os.exists(settings.RepositoryPath + os.sep + newName): + print >>sys.stderr, "Cannot rename the existing repository %s because the name %s already exists." % (oldRepo.StorageName, newName) + return False + else: + shutil.move(settings.RepositoryPath + os.sep + oldRepo.StorageName, settings.RepositoryPath + os.sep + newName) + except OSError as e: + print >>sys.stderr, "Rename error({0}): {1}".format(e.errno, e.strerror) + return False + return True + def __doesRepositoryExist(repo): """ Checks to see if a repository exists. @@ -107,7 +211,9 @@ """ import os - if os.path.exists(settings.RepositoryPath + os.sep + repo.StorageName) \ + if (repo is None) or (repo.StorageName is None): + return False + elif os.path.exists(settings.RepositoryPath + os.sep + repo.StorageName) \ and os.path.isdir(settings.RepositoryPath + os.sep + repo.StorageName) \ and os.path.isfile(settings.RepositoryPath + os.sep + repo.StorageName + os.sep + ".hg" + os.sep + "hgrc"): return True @@ -115,7 +221,7 @@ return False def __unregisterRepository(repo): - """ Unregisters the repository with HgWeb. + """ Removes the registration of the supplied repository from HgWeb. @param[in] repo The targeted repository for which to unregister. @@ -183,7 +289,8 @@ return True def __writeRepositoryDetails(repo): - """ Writes the details of a repository to the repository configuration file. + """ Writes the details of a repository to the repository's + configuration file. @param[in] repo The targeted repository for which to write the configuration details. @@ -198,24 +305,31 @@ import sys config = settings.RepositoryPath + os.sep + repo.StorageName + os.sep + ".hg" + os.sep + "hgrc" - with open(config, "a") as hgrc: + if os.path.exists(config): + hgrcMode = os.stat(config)[stat.ST_MODE] + os.chmod(config, hgrcMode | stat.S_IWRITE) + else: + hgrcMode = stat.S_IRUSR & stat.S_IRGRP & stat.S_IROTH + + with open(config, "w") as hgrc: hgrc.write("[web]\n") + if repo.DisplayName is None: hgrc.write("name = " + repo.StorageName + '\n') else: hgrc.write("name = " + repo.DisplayName + '\n') + if repo.Description is not None: hgrc.write("description = " + repo.Description + '\n') + if repo.Contact is not None: hgrc.write("contact = " + repo.Contact + '\n') - # Ensure the repository details isn't accidentally modified. - hgrcMode = os.stat(config)[stat.ST_MODE] os.chmod(config, hgrcMode & ~stat.S_IWUSR & ~stat.S_IWGRP & ~stat.S_IWOTH) # Ensure the repository isn't accidentally deleted, moved, or blocked. if "win32" == sys.platform: - status = __guardWindowsRepository(repository) + status = __guardWindowsRepository(repo) if 0 != status: exit(status) return True @@ -240,7 +354,7 @@ return status def __registerRepository(repo): - """ Registers the repository with HgWeb. + """ Adds the registration details of the repository to HgWeb. @param[in] repo The targeted repository for which to register in HgWeb. @@ -256,11 +370,9 @@ import stat try: - with open(settings.HgWebPath + os.sep + 'hgweb.config', "a") as hgweb: + with open(settings.HgWebPath + os.sep + 'hgweb.config', 'a') as hgweb: hgweb.write('\n' + repo.StorageName + " = " + settings.RepositoryPath + os.sep + repo.StorageName) return True except IOError as e: - print >>sys.stderr, "Web Configuration error({0}): {1}".format(e.errno, e.strerror) - os.chmod(settings.RepositoryPath + os.sep + repo.StorageName + os.sep + ".hg" + os.sep + "hgrc", stat.S_IWRITE) - shutil.rmtree(settings.RepositoryPath + os.sep + repo.StorageName) + print >>sys.stderr, "Web registration error({0}): {1}".format(e.errno, e.strerror) return False diff -r cd579b9ef6b5 -r d90ce1288692 src/manrepo.py --- a/src/manrepo.py Wed Jul 23 08:36:18 2014 -0500 +++ b/src/manrepo.py Sat Jul 26 11:51:45 2014 -0500 @@ -20,6 +20,12 @@ #** along with this program. If not, see . *** #******************************************************************************* +""" + IMPORTS +""" +from hwm import settings + + class Repository(object): # The directory name of the managed repository. __storageName = None @@ -66,9 +72,38 @@ """ Sets the contact point for the managed repository. """ self.__contact = value - def __init__(self): - """ Initialises the object with default values. """ - self.__storageName = None - self.__displayName = None - self.__description = None - self.__contact = None + def __init__(self, repoName=None): + """ Initialises the object with the values from a managed repository + + @param[in] repoName The storage name of the managed repository + to load. + """ + from mercurial import ui, hg, error + import os + + self.__storageName = repoName + if repoName: + try: + repo = hg.repository(ui.ui(), settings.RepositoryPath + os.sep + repoName) + self.__displayName = repo.ui.config('web', 'name', default=None) + self.__description = repo.ui.config('web', 'description', default=None) + self.__contact = repo.ui.config('web', 'contact', default=None) + except error.RepoError: + self.__displayName = None + self.__description = None + self.__contact = None + else: + self.__displayName = None + self.__description = None + self.__contact = None + + def __eq__(self, other): + """ Determines if two repositories are the same repository. + + @param[in] other The repository on the right-hand-side of the + equality operator to compare against. + + @return Gives true when both repositories represent the same one, + else-wise gives false. + """ + return self.__storageName == other.__storageName