[csw-devel] SF.net SVN: gar:[12240] csw/mgar/gar/v2-uwatch2/bin/upstream_watch
wbonnet at users.sourceforge.net
wbonnet at users.sourceforge.net
Fri Jan 7 22:54:37 CET 2011
Revision: 12240
http://gar.svn.sourceforge.net/gar/?rev=12240&view=rev
Author: wbonnet
Date: 2011-01-07 21:54:37 +0000 (Fri, 07 Jan 2011)
Log Message:
-----------
Add upgrade to version command
Modified Paths:
--------------
csw/mgar/gar/v2-uwatch2/bin/upstream_watch
Modified: csw/mgar/gar/v2-uwatch2/bin/upstream_watch
===================================================================
--- csw/mgar/gar/v2-uwatch2/bin/upstream_watch 2011-01-07 18:44:52 UTC (rev 12239)
+++ csw/mgar/gar/v2-uwatch2/bin/upstream_watch 2011-01-07 21:54:37 UTC (rev 12240)
@@ -35,29 +35,54 @@
import sys
import string
import re
+import os
+import shutil
+import subprocess
+import pysvn
-# import httplib
-
from urllib2 import Request, urlopen, URLError
from optparse import OptionParser
-# import subprocess
-# import os
-# import shutil
+# ---------------------------------------------------------------------------------------------------------------------
+#
+#
+class InvalidSourceDirectoryContentException(Exception):
+ """Exception raised when a method is called on the Abstract command class
+ """
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def __init__(self, message):
+ self.message = message
+
+
# ---------------------------------------------------------------------------------------------------------------------
#
#
-class NoUpstreamProtocolException(Exception):
- """Exception raised when no protocol is specified in the upstream url
+class AbstractCommandMethodCallException(Exception):
+ """Exception raised when a method is called on the Abstract command class
"""
# -----------------------------------------------------------------------------------------------------------------
- # Just an empty class...
+ def __init__(self, message):
+ self.message = message
+
# ---------------------------------------------------------------------------------------------------------------------
#
#
+class SvnClientException(Exception):
+ """Exception raised when an error occur while using svnClient
+ """
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def __init__(self, message):
+ self.message = message
+
+# ---------------------------------------------------------------------------------------------------------------------
+#
+#
class UpstreamUrlRetrievalFailedException(Exception):
"""Exception raised when an unsuported protocol is specified in the upstream url
"""
@@ -84,22 +109,6 @@
# ---------------------------------------------------------------------------------------------------------------------
#
#
-class UpstreamProtocolNotSupportedException(Exception):
- """Exception raised when an unsuported protocol is specified in the upstream url
- """
-
- # -----------------------------------------------------------------------------------------------------------------
-
- def __init__(self, value):
- self.parameter = value
-
- def __str__(self):
- return repr(self.parameter)
-
-
-# ---------------------------------------------------------------------------------------------------------------------
-#
-#
class MissingArgumentException(Exception):
"""Exception raised when a command line argument is missing
"""
@@ -129,7 +138,10 @@
# Add options to parser
self.parser.add_option("-V", "--verbose", help="Activate verbose mode", action="store_true", dest="verbose")
self.parser.add_option("-c", "--current-version", help="Current package version", action="store", dest="current_version")
+ self.parser.add_option("-d", "--target-location", help="Target location. This is the directory in which the branch will be created (parent of the branch directory). Default value is ../branches", action="store", dest="target_location")
self.parser.add_option("-r", "--regexp", help="Version matching regular expression", action="store", dest="regexp")
+ self.parser.add_option("-s", "--source-directory", help="Source directory (place from where the build is copied). Default value is current directory", action="store", dest="source_directory")
+ self.parser.add_option("-t", "--target-version", help="Package target version", action="store", dest="target_version")
self.parser.add_option("-u", "--upstream-url", help="Upstream version page url", action="store", dest="upstream_url")
# -----------------------------------------------------------------------------------------------------------------
@@ -171,7 +183,18 @@
if args.upstream_url != None:
self._upstream_url = args.upstream_url
+ # This member variable defines the target version of the package during upgrade
+ if args.target_version != None:
+ self._target_version = args.target_version
+ # This member variable defines the target directory of the package during upgrade. This is the location of the new branch
+ if args.target_location != None:
+ self._target_location = args.target_location
+
+ # This member variable defines the source directory of the package during upgrade. This is the location of the trunk (most of the time)
+ if args.source_directory != None:
+ self._source_directory = args.source_directory
+
# -----------------------------------------------------------------------------------------------------------------
def __init__(self):
@@ -181,19 +204,20 @@
self._current_version = None
self._regexp = None
self._upstream_url = None
+ self._target_version = None
+ self._source_directory = "."
+ self._target_location = "../branches"
# -----------------------------------------------------------------------------------------------------------------
def getCurrentVersion(self):
return self._current_version
-
# -----------------------------------------------------------------------------------------------------------------
def getRegexp(self):
return self._regexp
-
# -----------------------------------------------------------------------------------------------------------------
def getUpstreamURL(self):
@@ -204,6 +228,21 @@
def getVerbose(self):
return self._verbose
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getSourceDirectory(self):
+ return self._source_directory
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getTargetLocation(self):
+ return self._target_location
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getTargetVersion(self):
+ return self._target_version
+
# ---------------------------------------------------------------------------------------------------------------------
#
#
@@ -226,8 +265,8 @@
# -----------------------------------------------------------------------------------------------------------------
def execute(self, opts, arguments):
- print "TODO Generate and error instead of this message\n"
- print self.name, "\n"
+ print "Internal error : Abstract command method called\n"
+ raise AbstractCommandMethodCallException("execute")
# ---------------------------------------------------------------------------------------------------------------------
#
@@ -405,14 +444,6 @@
# Exits through exception handling, thus return false to the command processor
return False
- except UpstreamProtocolNotSupportedException, (instance):
-
- # Display a cool error message :)
- print "Protocol " + instance.parameter + " is not supportd by upstream_watch"
-
- # Exits through exception handling, thus return false to the command processor
- return False
-
except UpstreamUrlRetrievalFailedException, (instance):
# Exits through exception handling, thus return false to the command processor
@@ -501,25 +532,272 @@
# Exits through exception handling, thus return false to the command processor
return False
- except UpstreamProtocolNotSupportedException, (instance):
+ except UpstreamUrlRetrievalFailedException, (instance):
+ # Exits through exception handling, thus return false to the command processor
+ return False
+
+ except NoUpstreamVersionFoundException, (instance):
+
+ # Exits through exception handling, thus return false to the command processor
+ return False
+
+
+# ---------------------------------------------------------------------------------------------------------------------
+#
+#
+class UpgradeToVersionCommand(UpstreamWatchCommand):
+ """UpgradeToVersion command. This command upgrade the build description from a version to another.
+ Current files in trunk are copied to a new branch. Branch is named accord to the following pattern :
+ PKG/branches/upgrade_from_CURRENTVERSION_to_DESTVERSION. After copy, version in the Makefile is modified.
+ An optional argument can be passed to commit after branch creation.
+
+ """
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def __init__(self, name):
+ super(UpgradeToVersionCommand, self).__init__(name)
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def checkArgument(self):
+
+ # Variable used to flag that we have a missing argument
+ argsValid = True
+
+ # FromVersion is mandatory
+ if self.configParser.getCurrentVersion() == None:
+ print "Error : Current version is not defined. Please use --current-version flag, or --help to display help"
+ argsValid = False
+
+ # ToVersion is mandatory
+ if self.configParser.getTargetVersion() == None:
+ print "Error : Target version is not defined. Please use --target-version flag, or --help to display help"
+ argsValid = False
+
+ # ToVersion is mandatory
+ if self.configParser.getTargetLocation() == None:
+ print "Error : Target directory is not defined. Please use --target-location flag, or --help to display help"
+ argsValid = False
+
+ # If arguments are not valid raise an exception
+ if argsValid == False:
+ raise MissingArgumentException("Some mandatory arguments are missing. Unable to continue.")
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def checkWorkingDirectory(self):
+ """ This method checks that the command is executed from a valid working directory. A valid working directory
+ is a directory in which we find a package buildDescription that means a Makefile and a gar directory or symlink
+ """
+
+ # Check that the Makefile exist
+ if os.path.isfile(self.configParser.getSourceDirectory() + "/Makefile") == False:
+ # No it does not exist, thus generate an error message
+ msg = "Error : there is no Makefile under %(src)s" % { "src" : os.path.abspath(self.configParser.getSourceDirectory()) }
+
+ # Then raise an exception
+ raise InvalidSourceDirectoryContentException(msg)
+
+ # Check that the gar directory exists (can be a directory or symlink)
+ if os.path.isdir(self.configParser.getSourceDirectory() + "/gar") == False:
+ # No it does not exist, thus generate an error message
+ msg = "Error : there is no gar directory under %(src)s" % { "src" : os.path.abspath(self.configParser.getSourceDirectory()) }
+
+ # Then raise an exception
+ raise InvalidSourceDirectoryContentException(msg)
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getGarRelativeTargetDirectory(self):
+ """ This method return None if gar directory is an actual directory, or a relative path if gar is a symlink to
+ a real directory. In case of a symlink pointing to another symlink, we do not try to get the absolute path
+ having one level of indirection is enough.
+ The target directory is a relative path. This path is adjusted to be consistent from the target directory. It
+ has to be modified since it is basically a relative path from the source directory.
+ """
+
+ # Get the newgar information
+ garDir = self.configParser.getSourceDirectory() + "/gar"
+ if os.path.islink(garDir):
+ garTarget = os.path.relpath(os.path.abspath(os.readlink(garDir)), os.path.abspath(targetDir))
+ else:
+ garTarget = None
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getGarRelativeTargetDirectory(self):
+ """ This method return None if gar directory is an actual directory, or a relative path if gar is a symlink to
+ a real directory. In case of a symlink pointing to another symlink, we do not try to get the absolute path
+ having one level of indirection is enough.
+ The target directory is a relative path. This path is adjusted to be consistent from the target directory. It
+ has to be modified since it is basically a relative path from the source directory.
+ """
+
+ # Get the newgar information
+ garDir = self.configParser.getSourceDirectory() + "/gar"
+ if os.path.islink(garDir):
+ garTarget = os.path.relpath(os.path.abspath(os.readlink(garDir)), os.path.abspath(self.getTargetDirectory()))
+ else:
+ garTarget = None
+
+ return garTarget
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getTargetDirectory(self):
+ """ This method return the target directory which is a computed value based on target location, current version
+ and target version
+ """
+
+ return self.configParser.getTargetLocation() + "/upgrade_from_" + self.configParser.getCurrentVersion() + "_to_" + self.configParser.getTargetVersion()
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def copySvnSourceToTarget(self, garRelativeTarget):
+ """ This method copy sources from the working copy to the target in the same working copy. If garRelativeTarget is not
+ None, it means gar directory is a symlink. Then once copy is done it is deleted in the target directory and
+ recreated to point to the new relative directory
+ """
+
+ try:
+ # Create a new svn client
+ svnClient = pysvn.Client()
+
+ # Do the actual copy in the svn working copy
+ svnClient.copy(os.path.abspath(self.configParser.getSourceDirectory()), self.getTargetDirectory())
+
+ # Backup the current directory
+ curDir = os.getcwd()
+
+ # Change to target directory
+ os.chdir(self.getTargetDirectory())
+
+ # Test if gar relative path is defined
+ if garRelativeTarget:
+ # Test if gar directory is a symlink and
+ if os.path.islink("./gar"):
+ os.remove("./gar")
+ os.symlink(garRelativeTarget, "./gar")
+ # No ... :( This a "should not happen error" since it was a symlink before the copy. Exit
+ else:
+ print "Internal error : gar is not a symlink but garRelativeTarget is defined"
+ return False
+ # No else but ... If gar relative path is not defined, we have copied a real directory. Nothing to do in this case
+
+ # Restore the working directory
+ os.chdir(curDir)
+
+ # SVN client exception handling
+ except pysvn.ClientError , e:
+ # Generate a cool error message
+ msg = "SVN Client error : " + e.args[0] + "\n" + "Error occured when executing command svnClient.copy(%(src)s, %(dest)s)" \
+ % { 'src' : os.path.abspath(self.configParser.getSourceDirectory()), 'dest' : self.getTargetDirectory() }
+
+ # Then raise the exception
+ raise SvnClientException(msg)
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def modifyVersion(self):
+ """ This method modifies the version in the Makefile. It replaces current version by new version.
+ Version has to be defined on a single line strting by VERSION, having some spaces or tabs then
+ and egal sign = then some tabs or spaces and the version vaue to finish the line
+ """
+
+ # Backup the current directory
+ curDir = os.getcwd()
+
+ # Change to target directory
+ os.chdir(self.getTargetDirectory())
+
+ # Array storing the Makefile lines
+ lines = []
+
+ # Iterate each line in the file
+ for line in open("./Makefile", 'r'):
+ # Match the file line by line
+ m = re.match(r"\s*VERSION\s*=\s*(?P<version>.*)", line)
+
+ # Test if this is a match
+ if m == None:
+ # No, thus output the current line without modifications
+ lines.append(line)
+ else:
+ # Yes it is a match, thus output the modified line
+ lines.append("VERSION = " + self.configParser.getTargetVersion() + "\n")
+
+ # Open the new Makefile for output
+ f = open("./Makefile", 'w')
+
+ # Iterates the array of lines and write each one to the Makefile
+ for element in lines:
+ f.write(element)
+
+ # Close the Makefile
+ f.close()
+
+ # Restore the working directory
+ os.chdir(curDir)
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def execute(self, opts, args):
+
+ try:
+
+ # Initialize configuration
+ self.configParser.initialize(opts)
+
+ # Need a way to check that all options needed are available
+ self.checkArgument()
+
+ # Check that the command is launched from a valid directory
+ self.checkWorkingDirectory()
+
+ # Generates the target directory
+ self.getTargetDirectory()
+
+ # Get the new gar information
+ garRelativeTarget = self.getGarRelativeTargetDirectory()
+
+ # Copy target directory to destination
+ self.copySvnSourceToTarget(garRelativeTarget)
+
+ # Modify the version inside the Makefile
+ self.modifyVersion()
+
+ # Exit after processing, eveythin gis ok, return true to the command processor
+ return True
+
+ # Handles exception that occurs when arguments are incorrect
+ except MissingArgumentException, instance:
+
# Display a cool error message :)
- print "Protocol " + instance.parameter + " is not supportd by upstream_watch"
+ print instance.parameter
# Exits through exception handling, thus return false to the command processor
return False
- except UpstreamUrlRetrievalFailedException, (instance):
+ # Handles SVN client exception
+ except SvnClientException , e:
+
+ # Display a cool error message :)
+ print e.message
# Exits through exception handling, thus return false to the command processor
return False
- except NoUpstreamVersionFoundException, (instance):
+ # Handles exceptions which might occur while checking source directory content
+ except InvalidSourceDirectoryContentException , e:
+
+ # Display a cool error message :)
+ print e.message
# Exits through exception handling, thus return false to the command processor
return False
-
# ---------------------------------------------------------------------------------------------------------------------
#
#
@@ -544,11 +822,13 @@
cmd = GetUpstreamLatestVersionCommand("get-upstream-latest-version")
self.commandArray[cmd.getName()] = cmd
+ cmd = UpgradeToVersionCommand("upgrade-to-version")
+ self.commandArray[cmd.getName()] = cmd
+
# -----------------------------------------------------------------------------------------------------------------
def execute(self, opts, arguments):
- """Cette methode teste les differents arguments et appelle la methode execute sur l'objet en charge du traitement
- L'ordre de declaration des tests est important. En effet certains flags declanchent des actions, d'autres des options.
+ """This method checks that an action is supplied and call the action handler
"""
# Check that an action verb is supplied. If none an error is returned
@@ -584,7 +864,7 @@
# Call the execute method on the command processor. This method is in charge to find the concrete command
return commandProcessor.execute(opts, args)
-# On sort en rendant le code de retour de main
+# Exit with main return code
if __name__ == '__main__':
sys.exit(main())
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
More information about the devel
mailing list