changeset 10:d90ce1288692

Modify an existing repository under the control of HgWeb. IN: -
author John Schneiderman <JohnMS@CodeGNU.com>
date Sat, 26 Jul 2014 11:51:45 -0500
parents cd579b9ef6b5
children 7a7c6a796a6d
files doc/ChangeLog doc/TODO src/hwm.py src/manager.py src/manrepo.py
diffstat 5 files changed, 191 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- 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 <http://www.gnu.org/licenses/>.    ***
 ********************************************************************************
+2014-07-26 John Schneiderman <Licensing _AT_ CodeGNU _DOT_ com> 0.2.0
+- Ability to modify an existing repository.
+- Load a managed repository object from it's storage name.
 2014-07-21 John Schneiderman <Licensing _AT_ CodeGNU _DOT_ com> 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.
--- 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.
--- 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)
--- 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
--- 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 <http://www.gnu.org/licenses/>.    ***
 #*******************************************************************************
 
+"""
+ 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